How it works
01 /
Address the build by its commit
Use a full 40-character lowercase SHA and an allowed filename. The resolver verifies the repository, requested commit, publisher, digest and target key. File responses redirect to the public object only after those fields agree. Unsafe targets and upstream failures produce explicit errors.
02 /
Browse or automate against a folder
The commit-level URL returns an HTML table, or JSON when the Accept header prefers application/json. File links in the index use the same resolver. The folder index reads the publication database without consuming GitHub API calls.
03 /
Resolve safely as build state changes
A database lookup handles known publications, including closed PR mappings after squash merges. A bounded fallback can inspect trusted App comments through GitHub. Responses are not cached because a rerun on the same head can produce a different digest. Expired or missing artifacts return an actionable 404.
A concrete example
Workflow example
GET /artifacts/<owner>/<repo>/<40-character-sha>/report.html
# List the published set as JSON:
GET /artifacts/<owner>/<repo>/<40-character-sha>/
Accept: application/jsonKnow the boundary
Current limits
A commit address is stable, but it is not a promise that artifacts live forever or that two reruns are byte-identical. Consumers needing exact reproducibility should record and verify the returned digest. The current file endpoint supports GET, not arbitrary upload or mutation methods.
Common questions
Do I need the pull request number?
No. Consumers address the repository and commit; the resolver handles publication lookup.
Can I pin a specific artifact’s bytes?
Record the SHA-256 digest as well as the commit. Same-head reruns may change the published object, so a commit URL alone is insufficient to pin bytes.
Runnerless / early access
Want this in
your workflow?
Get early access ↗Register interest and tell us what slows your CI down. The optional follow-up questions come after your email is saved.