Inspiration

I run a small veteran-owned defense startup. To keep defense work, a company like mine has to protect Controlled Unclassified Information under DFARS 252.204-7012, post a self-assessed score to the Supplier Performance Risk System (SPRS), and keep a system security plan that matches the system it describes.

On July 13, 2026, Phase 2 of CMMC, the phase that brings third-party assessments, was suspended. The Phase 1 self-assessments remain in force. For a small contractor that leaves the posted score as the only check anyone makes, and a score that claims more than the system does is a False Claims Act exposure for the person who posted it.

In practice the plan gets written once, the score is a number someone typed, and nothing notices when either goes stale. I wanted a score I could defend line by line, where every point traces to something a machine observed or to a dated statement with my name on it.

What it does

Warrant assesses a real system: a BookStack wiki on Google Cloud Run with a Cloud SQL database, deployed by the pipeline itself. It stands in for an internal technical-data wiki and holds no actual CUI.

It observes, then decides. On every pipeline, Warrant probes the live site and reads the cloud configuration the way an assessor would: TLS, routes locked without a session, the default administrator refused, login throttling, the login notice, cookie flags, CSP, an ordinary user refused every management page, MFA required, IAM grants, keyless CI sign-in and database TLS. A requirement is met only when every check it needs ran and passed. Anything unobserved is unknown, and unknown scores as unmet.

It scores honestly. Today the score reads -155: 48 of 313 deductible points earned. It is low on purpose. 177 of those points depend on a person's signed attestation, and the checks built so far cover 12 of the 49 requirements a pipeline could prove. The number moves only when a check passes or a person signs.

It shows what the software ships with. A CI job starts the official BookStack image with no cloud and no hardening and runs the same checks. As shipped, 2 of 8 assessable checks fail, and the worst is that the administrator password printed in BookStack's own documentation signs in.

Agents do the paperwork; evidence decides. Three GitLab Duo Agent Platform flows act under their own service accounts:

Warrant Intake reads DFARS 252.204-7012 and files one issue per obligation, each with a quote that CI checks against the rulebook character for character. A paraphrase fails the pipeline. Warrant Findings files, closes, updates and reopens finding issues exactly as the deterministic findings job computed. It decides nothing. A finding closes only after a later pipeline observes the requirement met, and it reopens if the requirement slips. Warrant Fix opens a merge request for one finding, limited by policy to the files that fix may touch. I merge it; only an observed pass closes the finding.

It refuses releases that regress. Every change deploys as a candidate revision with no traffic. The same checks run against production and the candidate. If anything met in production is not met on the candidate, the gate refuses, the promote job never runs, and production keeps serving. On October 17 I merged a change that removed the login notice satisfying requirement 3.1.9. The gate refused it, production kept showing the notice throughout, and Warrant Findings filed the refusal and said so on the merge request. That refusal was planted on purpose, and it is labelled and counted apart from real ones. The next merge restored the notice and the gate released it.

Signatures expire. An attestation carries a signing date and an expiry. When it lapses, the requirement drops to unknown, the score falls with no commit, and a finding names the signature that ran out. A pipeline runs every morning without deploying, so a lapse or a change made outside the pipeline surfaces within a day.

How I built it

GitLab CI in six stages: verify, package, configure, evidence, release, report. Deploys go through GitLab environments, candidate first, production only after the gate. GitLab Duo Agent Platform flows, versioned in the repository, each with its own service account and a tool list that leaves out merging and deploying. Google Cloud Run tagged revisions for the candidate, Cloud SQL behind the Auth Proxy, Secret Manager, and workload identity federation, so no service account key exists anywhere. Python for every check, decision and score, with 140 offline tests. No model touches a number: a test fails if any scoring module imports a model SDK. GitLab Pages for the judges' page, built from the project's public API without a token, so a judge can repeat every call.

It covers all nine lifecycle stages: Plan (Intake), Create (Fix), Verify, Package, Secure (the checks), Release (the gate), Configure (candidate deploys from a versioned baseline), Monitor (the daily schedule) and Govern (the score, the generated system security plan, expiring attestations).

Where a person decides

This is a Supervised build, and the supervision has edges. I merge every change, including every fix a flow proposes. I sign every attestation, in a commit under my own name, and the scorer ignores attestations for requirements the pipeline is supposed to prove. Everything else, from filing a finding to closing it to refusing a release, follows from evidence without a click.

Challenges I ran into

The first real refusal exposed two bugs. The Findings flow read the latest successful pipeline, and a refused release fails its pipeline on purpose, so the refusal was invisible to it. It now reads the pipeline it is told about. Then GitLab's scoped labels evicted each other: an issue cannot carry two labels with the same prefix, so marking a refusal as seeded quietly removed the refusal label. The flow reported that itself instead of papering over it.

A scheduled pipeline treats every "changes" rule as true, so the daily monitor would have redeployed production every morning. Schedules now observe and never deploy.

Accomplishments that I'm proud of

Every number on the judges' page comes from public pipelines, job artifacts and issues, read without signing in. The findings cycle has run end to end on the live system: findings filed while a control was missing, fixed through a merge request, and closed only after a pipeline observed the fix.

What I learned

An honest score is a low score, and that is the point. The work that matters is making each point traceable, and making the gaps visible enough that nobody mistakes them for coverage.

What's next

More checks: 37 requirements a pipeline could prove are still unobserved. Branch protection set by a project Maintainer, so the agents' separation does not rest on their tool lists alone. Private networking for the database. Measured recall for Intake on standing obligations. And other systems besides BookStack, because the method does not depend on the wiki.

Built With

Share this project:

Updates

Submission history