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:

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.

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-verify re-derives the findings instead of trusting the summary.
Do you receive our code?
No. pr attest and pr attest-verify run entirely against your local clone. If you choose to anchor, pr attest-anchor sends 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: and AI-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.
← Back to Provenrail