Inspiration

AI coding agents can now open merge requests, fix failing pipelines, and push changes on their own. The usual way to keep them safe is a system prompt that says "don't touch CI" or "don't delete tests". But a prompt is a request, not a control. Prompt injection in an issue, a file, or a tool output can talk an agent out of it. We wanted guardrails that hold even when the agent is wrong, confused, or compromised.

Airlock's idea: autonomous agents that can ship, but cannot break the rules. The rules live in the CI/CD pipeline, where they are deterministic and auditable, not in the prompt.

What it does

Airlock adds programmatic boundaries around AI agents on the GitLab Duo Agent Platform:

  • Dual-agent triage (airlock_triage): A read-only reader agent treats issue text as untrusted data and emits strict JSON (type, severity, summary, acceptance criteria, suspicious-instruction flags). A separate writer agent has only create_issue_note and sees only that JSON, never the raw issue.
  • Control-plane policy gate: policy_gate.py blocks any MR that touches .gitlab-ci.yml, tools/, policy/, deploy/, CODEOWNERS, or deletes tests, unless a human applies control-plane-approved. It diffs with --no-renames to stop rename evasion, and checks the label events API so labels applied by agent accounts are ignored. MRs over 10 files or 300 lines need size-approved.
  • Verify-twice security gate: sast_gate.py fails closed. It requires the SAST report to exist and blocks on High/Critical findings, so passing unit tests alone can't let an unfixed vulnerability through.
  • Historical regression guardrail: regression_gate.py keeps an incident memory (BOLA/IDOR, unsafe eval, path traversal), matches MR changes against past failure surfaces, and reproduces the bug in CI to block a merge until it's verifiably fixed.
  • State and readiness integrity: state_guard.py requires 100% of checklist checks before an agent can claim "ready", locks terminal states, and rejects stale updates with version tokens and a 5-minute TTL.
  • Bounded self-healing: Fix-pipeline attempts are capped at 3, then the flow escalates to a human (fix_budget.py).
  • Human release gate: Staging publishes automatically. publish-production is a manual job.
  • Governance scorecard: Every run produces a ledger and a markdown scorecard of gate outcomes.

Verified evidence

These are live GitLab pipelines, not mockups:

  • MR !2 blocked red with POLICY VIOLATION for tampering with .gitlab-ci.yml
  • MR !3 blocked red for a deleted test, caught by --no-renames (rename evasion defense)
  • MR !1 clean pass on all MR jobs
  • Main branch pipeline #2993995494: wheel built, unit tests passed, SAST and secret detection ran, staging published, smoke test passed, scorecard generated, production left pending human approval

The full table with links is in the repo README.

How we built it

Python (standard library plus pytest) for the gate scripts, GitLab CI/CD for enforcement, GitLab SAST (Semgrep) and Secret Detection for scanning, the GitLab REST API for label provenance, the Generic Package Registry for staging/smoke testing, and a small interactive dashboard and CLI playground for exploring the guardrails. The Duo flow definitions implement the reader/writer triage split. The test suite has 53 unit tests.

Challenges

  • Rename evasion: a plain diff lets an agent "rename" a protected file out of the policy. --no-renames fixed it.
  • Agents applying their own approval labels: a label alone proves nothing, so we verify who applied it through label events and fail closed if the token is missing.
  • Running SAST on MR pipelines without wasting CI minutes on duplicate branch pipelines.
  • Staying honest about limits: a gate is a guardrail, not a security boundary. An MR pipeline runs its own CI file. We document this and the mitigations (protected branches, CODEOWNERS, an external CI config).

What we learned

  • Constraints beat guidance. Telling an agent what it cannot do and enforcing it outside the model works better than asking nicely.
  • Fail closed by default: missing report, missing token, or missing label should mean "block".
  • The most useful part of a threat model is the residual-risk column.

Known limitations and what's next

We would rather state these than hide them:

  • The Plan/Create flow sessions and the Fix CI/CD Pipeline loop are built but have not yet been captured as live verified runs.
  • The label-provenance check has not yet been exercised on a live MR.
  • The 3-attempt cap relies on commit-pattern matching, so a CI-managed counter would be stronger.
  • Next: protect the gate job itself with an external CI config and CODEOWNERS, constrain the free-text fields passed from reader to writer, and flag weakened test assertions, not just deleted tests.

Links

Architecture for regression_gate.py and state_guard.py was adapted from the Time Machine CI and ReturnReady Guardian Transcend showcase projects.

Built With

  • gitlab
  • gitlab-ci-cd
  • gitlab-duo-agent-platform
  • gitlab-package-registry
  • gitlab-rest-api
  • gitlab-sast
  • pytest
  • python
  • secret-detection
  • semgrep
  • vercel
Share this project:

Updates

posted an update —

Subject: Please update the demo video link: Airlock (GitLab Transcend Hackathon)

Hello Hackathon Team,

I made a mistake while submitting my project, Airlock: Deterministic CI Guardrails for AI Coding Agents. The demo video link in my submission is incorrect because I pasted the wrong one by accident.

Please consider this the correct and final demo video for my submission: https://youtu.be/jFqkEme0DG8

If I can update it myself in the submission form before the deadline, please let me know and I will do so right away. Otherwise, I would be grateful if you could replace the old link with this one.

Thank you for your time and understanding.

Best regards

Log in or sign up for Devpost to join the conversation.