Skip to content

java/audit-publication: audit a published release from the outside - #5

Merged
bernardladenthin merged 2 commits into
mainfrom
feat/audit-publication
Oct 26, 2026
Merged

bernardladenthin merged 2 commits into
mainfrom
feat/audit-publication

Conversation

@bernardladenthin

Copy link
Copy Markdown
Owner

The publication gate proves a package was complete when it was frozen. Nothing proved afterwards that Central published it, that the release still holds it, or that the tag's run made it. After 6.0.0, java-llama.cpp ran those checks by hand (its docs/post-release-check.md). This adds the repository-neutral checks as java/audit-publication; the code is lib/actionlib/java/audit.py.

Check Passes when
run exactly one successful publish run on the tag, at the tag's commit, and that commit is on the default branch (cancelled runs of a re-pointed tag are only listed)
jobs nothing failed or was cancelled; the skipped jobs are exactly the other channel's: check-snapshot, publish-snapshot and its three consumers, by their display names from the workflow at the tag
assets the release files are exactly what release-assets.json and the POMs at the tag promise, each with its .asc
central every Central file and signature is served by repo1 with the GitHub bytes: the published .sha256 (else .sha1), signatures byte for byte, no second download. Absent files are polled for wait-minutes
signatures the release key comes from a keyserver over HTTPS (no dirmngr) into a throwaway keyring, the import must be exactly the requested fingerprint, every signature verifies, and one subkey signed them all
provenance gh attestation verify of one file, pinned to refs/tags/<tag> and the publish workflow; every release file is a subject of that run's statement with its digest
bytecode per-pattern class-file floors (first match wins), with an AAR's classes.jar scanned too; a rule that scanned nothing fails, and a corrupt jar is a finding, not a crash
next the default branch is at the next minor -SNAPSHOT (only ever a warning)

Configuration: the new post-release section of .github/actions-config.json holds signing-key (the primary fingerprint), bytecode and workflow. It is read from the workspace; the contract, the POMs and the workflow come from root, a checkout of the tag. So a release made before a repository adopted the audit can still be audited with today's settings (second commit).

Run against java-llama.cpp v6.0.0 (real release, real Central, real attestations), all 8 checks pass:

  • 60 files and 60 signatures, all by subkey D5C0…D767 of EA6E…48ED2
  • 118 Central files served with the release's bytes
  • 120 files attested by run 40239731871 (189 subjects: the package job also attests the native libraries)
  • 5 skipped jobs, exactly the snapshot channel
  • next warns: main was still at 6.0.0

docs/publication.md gains "After the release: the audit", with the trigger: workflow_run on the publish workflow. release: published does not work, because a release created with the workflow token starts no workflow.

Tests: 157 green on Windows and Linux, 19 of them new; each check is covered with the failure it exists for. runscripts: 0 problems.

Still open: a run on a GitHub runner. java-llama.cpp#573 pins this branch's head (630e3a9) in its new post-release.yml. After the merge: tag a release (v1.1.0: a new action, nothing changes for existing callers) and move that pin.

🤖 Generated with Claude Code

https://claude.ai/code/session_01AJGh28Gu8pLWYbTCqG1LZf

bernardladenthin and others added 2 commits October 26, 2026 09:51
The gate proves the package was complete when it was frozen. Nothing proved
afterwards that Central published it, that the release still holds it, or
that the run the tag names made it. java-llama.cpp did these checks by hand
after 6.0.0 (its docs/post-release-check.md). This adds the checks that are
not specific to one repository.

actionlib.java.audit runs on a checkout of the tag with the release files
downloaded:
- run and jobs: one successful run on the tag, at its commit, on the default
  branch. Nothing failed, and the skipped jobs are exactly the other
  channel's (by display name, read from the workflow).
- assets: exactly the files release-assets.json and the POMs promise, each
  signed.
- central: every Central file and signature is served by repo1 with the
  GitHub bytes (published .sha256/.sha1, signatures byte for byte). Absent
  files are polled, since Central publishes asynchronously.
- signatures: the release key comes from a public keyserver over HTTPS into
  a throwaway keyring, and one subkey signed everything.
- provenance: gh attestation verify, pinned to the tag and the workflow;
  every file is a subject of that run with its digest.
- bytecode: class-file floors per file pattern, including an AAR's
  classes.jar.
- next: the default branch is at the next minor -SNAPSHOT (a warning only).

Configured in the new post-release section of actions-config.json. Run
against java-llama.cpp v6.0.0, every check passes: 60 files and 60
signatures from one subkey, 118 Central files with the release's bytes,
120 files attested by run 40239731871. The next check warns (main is still
at 6.0.0). 19 tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AJGh28Gu8pLWYbTCqG1LZf
…ease from root

A repository adopts the audit after some of its releases, so the tag of an
older release carries no post-release configuration. The settings (signing
key, bytecode floors) and the repository's identity now come from the
current directory, the repository running the audit. The contract, the POMs
and the publish workflow come from --root (input `root`), a checkout of the
tag. One workflow can then audit java-llama.cpp 6.0.0 with today's
settings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AJGh28Gu8pLWYbTCqG1LZf
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant