AI code attribution
A signed answer to "which code did the AI write"
Procurement is starting to ask this question, and the standard answer today is a Co-authored-by: trailer or a git note: written by the vendor, about the vendor, editable by the vendor at any time. pr attest reads your git history and produces a signed document instead, one the receiving side re-derives from their own clone rather than takes on faith.
Last updated
The problem: authorship claims nobody can check
CISA's 2026 SBOM guidance now asks for AI coverage. AI-BOM language is showing up in vendor contracts. If you ship software to an enterprise customer, or you are the agency delivering code to one, the question "which of this was AI-generated" is arriving whether you have an answer ready or not.
The recommended practice, everywhere, is a Co-authored-by: or Generated-by: git trailer, or a git note. All of it is written by the party being asked the question, and all of it can be edited afterwards with a rebase and a force push. It is a claim, not evidence, and the counterparty's lawyer knows the difference.
Install
uv tool install provenrail # or: pip install provenrail
pr attest # reads your git history, writes a signed document
No signup and no server round trip to produce the document. Signing happens with a local key. Sending anything anywhere is a separate, explicit step, covered below.
What pr attest actually does
pr attest reads the commits in a range, detects AI-assisted commits from commit trailers and author or committer identity, and writes a signed JSON document naming, for every finding, the source it came from: a trailer or an identity match, so a reader can weigh a finding rather than trust it.
Detected tools: Claude Code, GitHub Copilot, Cursor, Devin, OpenAI Codex, Aider, Gemini, Windsurf, Cline, plus generic Generated-by:, Assisted-by: and AI-Assisted: trailers for anything not on that list.
Add --blame and the document also reports what share of the lines currently in the tree came from AI-assisted commits, which is the number procurement is actually asking for. One enormous generated commit and one typo fix count equally as commits and not at all equally as code, so line share and commit share are reported separately. Lines from commits outside the attested range are reported as unattributed, never as human, because the document made no finding about them either way.
Two more commands complete the loop:
pr attest-anchorsends a 32-byte Merkle root and a commit count to the anchor service and gets back an RFC 3161 timestamp from an independent authority, FreeTSA. The document never leaves the machine and no line of customer code is ever transmitted or stored. That constraint is also what lets a sole proprietor run the anchor service at all: there is nothing in it a breach could expose.pr attest-verifyis what the receiving side runs, against their own clone. It checks three things and needs all three: the signature has not changed since it was signed, every attested commit exists in this repository with the same author, dates and subject, and re-running the detectors on this repository reaches the same verdict as the document claims. A document that passes the first two checks and fails the third was edited before it was signed, which a signature alone can never catch.
What signing actually buys
Git commit ids are content hashes of the commit and its history. Binding a set of findings to a set of commit ids means the document cannot later be pointed at a different tree, and it cannot be quietly rewritten once a dispute has started. An anchor adds one more fact: the document existed no later than a time an independent authority signed, not merely a time the vendor typed into a field.
If the agent's own session was recorded by Provenrail, signed at the time and hash-chained, a commit authored inside that session's window is reported at evidence grade recorded instead of asserted, citing the session id and the first and last record hash. The wording of that finding is deliberately narrow: it says an agent session was running when this commit was authored, not that the agent wrote this commit. A recorder cannot see a developer typing in a different window, and this document does not claim it can.
What it does not prove
This is the part that has to be as visible as the claim, because a document that hides what it fails to cover invites reliance it cannot carry.
- It does not prove the findings are true. A developer who strips the trailer from a commit produces a commit this reports as human-authored, and no signature changes that.
- Detection covers the nine named tools and the generic trailers, nothing else. A tool not on that list is not detected, so AI involvement is understated rather than overstated. That is the direction a supplier does not benefit from, which is the point of building it this way.
ai_assistedmeans an agent was involved in producing the commit. It does not measure how much of the commit the agent wrote, and it says nothing about the code's quality, licensing or security.- Merges are excluded, so the same change is never counted twice.
- A signature proves the document was produced by the holder of the named key and has not changed since. An anchor additionally proves it existed no later than the anchored time. Neither proves the findings are true.
The Claude Code guardrail plugin and this attestation solve adjacent but different problems: one stops a destructive command before it runs, this one answers what happened after the fact, in the commits that already landed.
A real run
Numbers below are from a small repository, shown here to illustrate the shape of the output rather than any specific customer's numbers:
$ pr attest --blame --out ai-attestation.json
AI authorship attestation for 0c45ff5de9f1 (3 commits)
AI-assisted 2 of 3 commits (66.67%), 2 of 3 lines added
Claude Code 1
Working tree 2 of 3 lines (66.67%) come from AI-assisted commits, 0 from commits outside this range
Written to ai-attestation.json. Anyone can check it against their own clone:
pr attest-verify ai-attestation.json
To make the time provable by someone who is not you:
pr attest-anchor ai-attestation.json
$ pr attest-verify ai-attestation.json --receipt attest-receipt.json
ATTESTATION VERIFIED
3 commits, 2 AI-assisted (66.67%), re-derived from this repository
signed by b3a186b824266251... and unchanged since
existed no later than 2026-09-07T12:31:40.000000Z, and that time is proved by an independent authority
What this does NOT prove:
- The findings are read from commit metadata: trailers, author and committer identity. A commit whose author removed that metadata is reported as human-authored, and no signature on this document changes that.
- Detection covers the tools listed in `detectors`. A tool not on that list is not detected, so AI involvement is understated rather than overstated.
The verifier prints the limits alongside the verdict, in the same terminal output, not in a separate document a reader has to go find.
Running it in CI
The answer to "which of this code did an AI write" ages the moment somebody rebases, and a document produced by hand months later is a document nobody can place in time. Running it on every push means the attestation exists, signed, at the moment the commits landed, which is the only point at which nobody has a reason to shade it.
- uses: actions/checkout@v4
with: {fetch-depth: 0} # a shallow clone would attest to a stump; the action refuses one
- uses: pofky/[email protected]
with:
blame: "true"
signing-key: ${{ secrets.PROVENRAIL_SIGNING_KEY }} # optional
licence-key: ${{ secrets.PROVENRAIL_LICENCE_KEY }} # optional, adds the independent timestamp
- uses: actions/upload-artifact@v4
with: {name: ai-attestation, path: ai-attestation.json}
Without a signing key one is generated for the job and discarded, which makes the document internally consistent but anonymous. Supply one from a repository secret when the signature should mean "this organisation said so". The key is removed from the workspace before the job ends either way.
Questions
- Does this prove the findings are true?
- No. It proves the document has not been altered since it was signed, and that it existed no later than a given, independently attested time. Whether the underlying findings are complete or correct depends on the commit metadata they were read from, which is why the document ships its own limits and
pr attest-verifyre-derives the findings instead of trusting the summary. - Do you receive our code?
- No.
pr attestandpr attest-verifyrun entirely against your local clone. If you choose to anchor,pr attest-anchorsends a 32-byte Merkle root and a commit count to the anchor service, nothing else, and no line of code is ever transmitted or stored. - What if a developer strips the trailer from a commit?
- That commit is reported as human-authored, because the finding is read from the commit's own metadata and there is nothing else to read it from. The document's own limits say this explicitly rather than leaving it to be discovered later. The one source this cannot happen to is a Provenrail-recorded agent session, which is signed independently of the commit message.
- Which tools are detected?
- Claude Code, GitHub Copilot, Cursor, Devin, OpenAI Codex, Aider, Gemini, Windsurf and Cline, matched against commit trailers and author or committer identity, plus generic
Generated-by:,Assisted-by:andAI-Assisted:trailers for anything else. A tool outside this list is not detected. - Is this an AI-BOM?
- It is a signed AI authorship attestation over your git history, which answers the code-authorship question an AI-BOM section typically asks. It does not claim conformance to any specific AI-BOM specification or schema, because the code does not emit one.