-
-
Mistakeys: a hands-off GitLab Duo agent that kills a leaked API key in seconds while the real app keeps running.
-
The moment of rotation: the leaked key flips from 200 to 401 while the real app's consumer stays at 200. No downtime.
-
Incident #16, filed by the agent: six typed rotation steps, all ok, 0.007s key overlap. Fingerprints only, never the raw key.
-
Incident #17: the reporter verifies its cleanup commit (ba12754a) by reading the branch head before recording the SHA.
-
Re-running the failed pipeline returns already_handled with created: false. No duplicate incident, branch or commit.
-
Live status page at mistakeys.ajkadri.dev: the active key fingerprint and a chain of real rotations, each under 0.02s overlap.
-
The cleanup commit triggers a normal pipeline that goes green. From leaked key to clean branch with no human in the loop.
Inspiration
A leaked API key keeps working until someone revokes it. Small teams without a security engineer can detect a leak, but the hardest part comes after: changing the credential without breaking the application that uses it, proving the old key is dead, and cleaning up the repository. In January 2024, an exposed GitHub token let an attacker copy roughly 5,000 New York Times repositories (BleepingComputer). In May 2026, Money Forward had to invalidate and reissue keys hardcoded in source code (Money Forward). GitGuardian found that 64% of secrets valid in 2022 were still exploitable four years later (State of Secrets Sprawl 2026).
Most tools stop at detection. Mistakeys closes the loop.
What it does
When the leak scan fails in GitLab CI, a three-agent GitLab Duo flow takes over with no human in the loop:
- Gatekeeper confirms the failure is the exact leak-scan job (it ignores unrelated failures) and reads the fingerprint-only leak report.
- Rotator calls typed operations on the Mistakeys rotator: issue a replacement, update the running consumer, verify the replacement returns HTTP 200, activate it, revoke the leaked key, and verify the leaked key now returns HTTP 401.
- Reporter commits the cleanup directly to the isolated demo branch, verifies the commit by reading the branch head, and files a fingerprint-only incident report.
The cleanup push runs a normal pipeline that goes green. Re-running the failed pipeline returns already_handled with created: false: no duplicate incident, branch or commit.
Live status page: mistakeys.ajkadri.dev shows the active key fingerprint and the full chain of real rotations, each with an old/new key overlap under 0.02 seconds.
Built to the GitLab Security Standard
GitLab published its Security Standard v1.0 on October 6, 2026: "Autonomy isn't given. It's earned, one stage at a time." It calls for credentials that are "Centralized, least privilege, rotated, and revocable, with a defined scope and lifetime for every credential" (GitLab Security Standard). Mistakeys maps to all five stages:
| Stage | What Mistakeys does |
|---|---|
| Authorize | Short-lived GitLab OIDC ID token, verified against GitLab JWKS (issuer, audience, expiry, project path, RS256). No stored CI secret. |
| Isolate | The gatekeeper ignores non-leak failures. The reporter may change one fixture file on demo/* branches only; it never touches main, CI config, or merges. |
| Verify | The new key must return 200 on the real consumer before activation; the old key must return 401 after revocation. Revoked keys can never be reactivated. |
| Release | The cleanup commit triggers a normal pipeline that goes green. |
| Respond | A fingerprint-only incident issue linked to the triggering pipeline; idempotent retries. |
Proof
- Recording run (shown in the video): pipeline 2973913830 failed on the leak; agent session 9852737 replaced
sha256:b1486412ec0ewithsha256:256fb5a634f6(consumer stayed at 200, leaked key returned 401, overlap 0.007s), committed cleanup dc59f0db, green pipeline 2973919578, incident #16. Retry session 9853156 returnedalready_handled. - Latest run (current flow version): incident #17, pipeline 2975477227: rotated the key #16 created,
sha256:256fb5a634f6tosha256:245d30ec7e5d(overlap 0.014s), with cleanup commit ba12754a verified by the reporter's branch-head lookup. - Earlier verified run: incident #9, pipeline 2962697953.
All evidence uses fingerprints only. No raw key appears in any issue, session or report.
How we built it
- GitLab Duo Agent Platform: a custom three-component flow (
flows/mistakeys-recovery.yaml) triggered by the failed pipeline event, usinggitlab_api_get,create_commit,create_issueandcreate_issue_note. - Identity:
.gitlab/duo/agent-config.ymlissues a GitLab OIDC ID token to the flow, with a network policy that only allows the rotator's domain. The rotator verifies it withjoseagainst GitLab's JWKS. - Rotator, API and consumer: TypeScript (Node 22, Hono), in Docker on a VPS behind Caddy. The rotator is never exposed publicly; only typed operations exist, no general credential commands.
- CI/CD: tests, GitLab Secret Detection, a pinned Gitleaks scan with a custom rule (full reachable history on
main, push range on other branches), image builds tagged by commit, and a protected production deploy over a forced-command SSH key with health verification and rollback. - DevSecOps stages covered: Plan, Create, Verify, Package, Secure, Release, Configure, Monitor, Govern.
Challenges we ran into
- Flows can't read CI/CD variables, so the flow authenticates with a GitLab OIDC ID token instead, and the rotator needed outbound access only for GitLab's JWKS.
- There is no merge tool for flows, so the reporter commits the cleanup directly to the isolated demo branch instead of opening an MR.
create_commitreturns no SHA, so the reporter now reads the branch head and verifies the commit title before recording the SHA.- Proving two things at once: the app never broke (200 throughout) and the leaked key is really dead (401).
Accomplishments that we're proud of
Repeated live rotations of a real credential, each with sub-0.02-second overlap and zero downtime for the consumer, fully hands-off from a failed pipeline to a green cleanup pipeline and an audit-ready incident, without ever exposing the secret to the agent's output.
What we learned
Rotation is only trustworthy when the replacement is verified before the old key is revoked, and when the agent leaves enough evidence for a human to audit without ever seeing the secret. Agent autonomy is safest when it is earned through narrow, typed, verifiable steps.
What's next for Mistakeys
Provider adapters behind the same typed interface: GitLab project and deploy tokens, cloud provider access keys, and database passwords. orders-api currently stands in for any application key whose consumer can be updated and verified. Cleanup removes the key from the branch's current file; the revoked value stays in Git history by design.
Built With
- caddy
- docker
- gitlab
- gitlab-ci
- gitlab-duo
- gitlab-duo-agent-platform
- gitleaks
- node.js
- oidc
- typescript
- ubuntu
Log in or sign up for Devpost to join the conversation.