-
-
This image is currently processing. Please be patient...
Live on Google Cloud Run: https://receipt-ledger-436511308590.us-central1.run.app GitLab project (MIT): https://gitlab.com/nobanks-lab/life-after-code Pipeline history: https://gitlab.com/nobanks-lab/life-after-code/-/pipelines Path A, Hands-off. Lifecycle stages covered: 9 of 9.
Inspiration
Hands-off delivery is finally possible. An agent can take an issue, write the fix, and a pipeline can ship it. But the moment nobody is watching, the first question from security, compliance, or your own team is: prove it. Prove the tests ran. Prove the scan was clean. Prove nobody skipped a step or quietly edited a result. Logs are not proof, because whoever runs the system can change them. We wanted hands-off delivery that leaves evidence a skeptic would accept.
What it does
Paper Trail turns every step of the GitLab delivery loop into a cryptographically signed receipt, and refuses to ship anything the receipts cannot prove.
- A human opens an issue and assigns it to Ship with receipts, a custom GitLab Duo Agent Platform flow. That is the last human action.
- The agent reads the issue, implements the change, adds tests, runs them, pushes a branch, and opens a merge request labeled
hands-off. It then requests a GitLab Duo code review, applies the review's suggestions, replies on the thread, and comments on the issue. - Every CI job (provenance, unit tests, SAST, secret detection, security review, container build, config render) writes a receipt: canonical JSON, SHA-256 hashed, signed with Ed25519 in
after_script, so failed jobs write receipts too. - A governance gate seals the receipts into a hash chain and checks it against
governance/policy.json: chain integrity, a receipt for every required stage, no failed steps, no security finding above the allowed severity, no runtime finding above a stricter severity, and minimum test coverage. - Only if the gate passes does a CI job (not the agent) arm auto-merge. The agent cannot merge, and it never holds the signing key, so it cannot forge a receipt.
- On main, the pipeline deploys to staging on Google Cloud Run, smoke tests it against a latency budget, promotes to production, publishes a GitLab Release whose notes list every receipt, watches production and rolls back automatically to the previous revision if it is unhealthy, seals the full chain into the package registry, and posts "Shipped. Here are the receipts." on the source issue, which then closes.
- The live ledger app on Cloud Run reads every chain from the package registry and verifies it in the browser. Paste any chain into it: an untouched chain verifies and passes the gate, and changing a single character makes it INVALID.
What actually happened (this is all in the public pipeline history)
- We tightened the policy so runtime code may not ship with medium security findings. The gate blocked production:
FAIL runtime_security: 5 findings in runtime code. - Issue #2 (fix the path traversal behind those findings) was assigned to the flow. The agent fixed it with tests and opened MR !3; GitLab Duo approved; the gate merged it.
- The next main pipeline failed the staging smoke test (p95 3.6 s against a 3 s budget), so promotion stopped and production stayed on the last good version.
- Issue #3 (make it fast enough) went to the agent. It opened MR !4. GitLab Duo's code review found a real flaw (the warm-up request hit the wrong endpoint, so the slow path stayed cold) and requested changes. The agent applied the suggestion, replied, Duo approved, and the gate merged it.
- Pipeline 2979106326 then went all green to production: 11 signed steps, 9 of 9 lifecycle stages, gate PASS 10/10. The receipt for the code change records it as authored by agent:Ship with receipts.
Honest note: on issue #3, the flow version running at the time finished after opening the MR, so we mentioned it once on the MR to have it pick up the review. We then updated the flow (v1.2) so it waits for the Duo review and addresses it in the same session. Everything else in the loop, including the merge, the deploy, and the report, ran with no human.
How we built it
- GitLab Duo Agent Platform: two custom flows in
.gitlab/duo/flows/, created in the AI Catalog and enabled with triggers: Ship with receipts (assign / mention: issue to tested MR, review loop, issue comment) and Receipt auditor (reviewer: flags MRs that weaken governance, keys, or the receipt jobs). GitLab Duo Code Review reviews every agent MR. - Receipts:
src/receipts.js(canonical JSON, SHA-256, Ed25519, hash chaining, seal) andscripts/receipt.mjs(emit, seal, verify, gate, keygen). Zero npm dependencies; only Node 22 built-ins. - Policy:
src/policy.jsplusgovernance/policy.json, run by thegovernance-gatejob. - Pipeline:
.gitlab-ci.ymlwith 11 stages: plan, test, secure, package, configure, gate, staging, production, release, monitor, seal. GitLab SAST and Secret Detection templates feedsecurity-review, which turns their reports into a severity receipt and separates runtime code from tests and scripts. - Google Cloud: Docker image pushed to the GitLab Container Registry and Google Artifact Registry; Cloud Run staging and production services, scale to zero, 256 MiB; rollback to the recorded previous revision.
- GitLab write-backs: Releases API, generic package registry (one
chain.jsonper pipeline), issue notes, and a project access token that only the gate job uses, to arm auto-merge.
Challenges we ran into
- Flow agents run as a service account that cannot merge, which is the right default. We turned it into the design: the agent proposes, the gate disposes. Auto-merge is armed by a CI job only after the policy passes on a verified chain.
- Receipts for failures: emitting in
after_scriptmeans a job that crashes still leaves signed evidence of what it was doing. - Cloud Run reserves some paths ending in
z(like/healthz), so health checks live at/health. - Real performance: the staging smoke budget caught a slow cold start, which became issue #3 for the agent.
Accomplishments that we're proud of
- A real hands-off loop that blocked itself twice for the right reasons, then fixed itself through agent issues and shipped.
- An AI code reviewer and an AI author disagreeing, and the right answer winning, with the receipts recording all of it.
- Anyone can verify any release, with no keys and no access, from the live page.
What we learned
Autonomy is only useful if it is accountable. The fastest way to earn trust for hands-off delivery is to make the evidence impossible to fake and cheap to check.
What's next
Receipt verification as a reusable CI component, signing keys in a cloud KMS, and the auditor flow promoted to a required reviewer for any change to governance.
Built With
- docker
- ed25519
- gitlab
- gitlab-ci
- gitlab-duo
- google-artifact-registry
- google-cloud-run
- javascript
- node.js
Log in or sign up for Devpost to join the conversation.