Inspiration

A pipeline can show which commit built an image. It cannot show that the model inside the image is the one whose evaluation numbers approved the release. If someone swaps the model, or reuses an old evaluation, a green pipeline looks the same. We wanted a gate that checks the artifact itself, and agents that help without being trusted to decide.

What it does

Gasket is a release gate for ML models on GitLab. A deterministic program, the Witness, takes the model out of the release image, hashes it, and recomputes accuracy and the fairness gap from that exact model on hash-verified data. It blocks the release when the model, image, data, policy or commit do not match the evidence, when the recomputed numbers differ from the claimed ones, or when the policy is not met. A blocked release stays on the dashboard with its reason.

Agents advise. The Witness decides. People approve.

GitLab Duo flows work around the gate and never decide:

  • The Impact Analyst reads each merge request and posts an advisory note; the Witness recomputes scope and risk and keeps the stricter result.
  • The Release Investigator starts automatically on a failed pipeline, checks whether a release gate failed, and turns a block into an evidence-backed issue.
  • The Release Repair flow, when a person asks, proposes a fix to the training files as a merge request. A CI check fails any change outside those files.

None of them has an approve, merge or deploy tool.

One full cycle, all recorded. A merge request with a deliberately injected fairness regression was blocked: fairness gap 0.1421 against a limit of 0.05, reason FAIRNESS_GAP_EXCEEDED. The Investigator opened an issue on its own. On request, the Repair flow wrote a one-file fix as a merge request against the blocked branch. A person approved and merged it, fresh CI rebuilt the image, and the Witness decided again: fairness gap 0.0085. The release was signed, deployed to Cloud Run and checked live. The dashboard shows all steps, from block to live check.

The gate also guards itself. A change to the policy, the Witness, the evaluation script and data, or the gate's own CI jobs is held with POLICY_CHANGE_REQUIRES_REVIEW until a person starts an approval job. The approval records who, when and the diff hash, and goes inside the evidence signed with Sigstore. The job refuses automation accounts, so an agent cannot approve a policy change.

Passing evidence is signed with Sigstore keyless signing (GitLab CI identity) and logged in Rekor. Anyone can verify a released bundle with one cosign command. The signature shows the evidence came from the project's pipeline and is unaltered; it does not show the evidence is correct. A public dashboard (a static snapshot whose data files are signed with Sigstore; the page code itself is not signed) shows every run, the seal comparing the two model hashes, the chain of custody, the agents and the policy.

After a person approves a release, the approved image is deployed to Cloud Run as a candidate revision with no traffic, verified through /identity and /score, and only then promoted to serve the public demo URL. Two checks then run against the live service: it must report the model hash in the signed evidence, and the fixed holdout scored through it must reproduce the evaluated metrics. It's a demo service: there are no real users or production traffic. All data is synthetic and the thresholds are a demo policy; the service is a demonstration, not a real lending model.

How we built it

  • GitLab CI/CD with the gate as a CI/CD component (typed inputs, used by this project through its own commit), merge request pipelines, manual approval jobs, rules:changes, dotenv reports and OIDC id_tokens.
  • GitLab Duo custom flows (three, each with a tool list kept in the repository and shown on the dashboard) and merge request approval rules.
  • Security: CycloneDX SBOM and a Trivy scan of the release image (the gate), plus GitLab secret detection, dependency scanning and Container Scanning of the release image by digest (additional, non-blocking).
  • Package and Release: the release image in the container registry, the approved model in the package registry, and a GitLab Release carrying the signed evidence, provenance, SBOM, scan summary and model card.
  • Sigstore: cosign keyless signing and Rekor, with the certificate identity checked on verification.
  • Google Cloud Run deployed through GitLab OIDC and Workload Identity Federation, with no stored key.
  • Dashboard: a static page built from public GitLab records (pipelines, jobs, artifacts, merge requests, issues), refreshed daily and hosted as a snapshot.
  • Python, scikit-learn and pandas for the demo model, Flask for the demo service, plain JavaScript for the dashboard, and over 550 automated tests.

Challenges

  • Keeping agents useful but not in charge. The design rule is that agents advise, the Witness decides, people approve. We checked every flow's tool list against that rule.
  • Honesty about what ran. The dashboard shows a solid marker only for a real event and a dashed one for recorded-only or not run, and a claims register ties every statement to a record.
  • What a public project exposes. Job logs and Duo sessions need a sign-in, so the dashboard states when a reason is inferred.
  • A guard that protects its own definition. Gasket protects the release path when the project's trusted CI policy is protected. In this demo namespace, preventing removal of the guard itself requires Owner-level pipeline execution policy; Gasket detects missing guard evidence afterward.
  • GitLab API rate limits while building the snapshot.

Accomplishments

  • A released evidence bundle anyone can verify with one cosign command (release 2957104159, Rekor entry 3284985001); adding one byte to the file makes verification fail.
  • The policy guard held its own merge request and the main pipeline that followed, and the recorded approvals are inside the signed bundle.
  • A stale-evidence block: an image built with one model and given another model's evaluation evidence was blocked with MODEL_HASH_MISMATCH (pipeline 2956396432).
  • A complete block-to-live chain (above), with every step linked on the dashboard.
  • A real surprise: two builds with the same training code, data, seed and library versions produced different model files (the solver stopped after 220 and 425 iterations, and 7 of 1,200 holdout predictions differ). That is why Gasket binds the evaluation to the exact bytes in the release image, instead of trusting that a rebuild gives the same model.
  • A dashboard where nothing is guessed: a run is tied to a merge request only through a record.

Honest limits

  • Separation of duties. Two-person approval is not demonstrated. Adding a teammate as a project member and enabling "Prevent approval by author" require the Maintainer or Owner role, which hackathon entrants do not have in this namespace. The approval rule, the recorded approvals, the self-approval flag and the agent-approval refusal are implemented; a second human approver is configuration, not code.
  • Re-verification runs weekly on a fixed holdout. It is not live traffic monitoring yet.

What we learned

A check is only as strong as what it binds to: hashing the exact artifact matters more than a green status. Separating "advises" from "decides" makes agents easier to trust and to audit. A control that lives in the file it protects needs an outside backstop, and being plain about that is better than hiding it.

What's next

A signing identity policy limited to a protected ref and environment, live drift monitoring, several models and teams in one project, publishing the component in the GitLab CI/CD catalog, a pipeline execution policy that makes the guard tamper-proof, and a second human approver. None of these is built.

Built With

  • ai-safety
  • artifact-verification
  • continuous-deployment
  • continuous-integration
  • devsecops
  • docker
  • flask
  • gitlab-ci/cd
  • gitlab-duo
  • google-cloud-run
  • machine-learning
  • ml-security
  • mlops
  • model-governance
  • model-monitoring
  • model-provenance
  • model-verification
  • openid-connect
  • policy-as-code
  • python
  • rekor
  • reproducible-machine-learning
  • responsible-ai
  • sigstore
  • software-supply-chain-security
Share this project:

Updates

Submission history