Inspiration

linkding is a popular self-hosted bookmark manager (about 11,000 GitHub stars) with about 1,400 tests and one maintainer. On GitHub its life after code is short: tests on pull requests, Dependabot, and a hand-started image build. There is no security triage, no deployment, no monitoring and no record of what a change costs to deliver. On 18 October it had 42 open pull requests, including Dependabot's PyJWT security upgrade (open since 1 October) and a devalue upgrade (open since 18 September). That is normal for a small open-source project, because nobody has time to be the release manager, the security engineer and the on-call SRE. I wanted to see whether GitLab Duo agents, held inside a deterministic pipeline, could do those jobs while the maintainer keeps exactly one decision: ship this change or not.

The name comes from viriditas, Latin for "greenness": the life force that makes things grow and heal. That is what this project gives software after its code is written: it keeps growing through small, proven changes, heals itself when production breaks, and measures what it costs the planet.

What it does

Viriditas moves linkding to GitLab and automates everything after the code is written, across all nine DevSecOps stages, on Google Cloud.

  • An issue becomes a merge request. A custom flow plans the change (small model), a developer agent implements it with tests, and a read-only verifier sub-agent must approve.
  • Every change is proven before a person is asked. The pipeline runs lint, 1,455 tests, SAST, secret detection, dependency and container scanning. It builds one signed image (Sigstore, keyless), deploys it as a no-traffic preview revision on Cloud Run, and smoke-tests it. It measures the change's carbon cost (SCI).
  • New advisories are caught within a day, even when no code changes. A daily scheduled scan of the production dependencies and image compares with the previous day. A new Critical or High advisory with a fix opens a security issue, which the Issue to MR flow turns into an upgrade through the same gate. On its first day it found 4 new High advisories, three in Django REST framework (one let API token revocation silently fail), and the upgrade was built, reviewed and shipped within hours (#6 → !24).
  • Agents start only where judgment is needed. A red pipeline starts the Fix CI/CD Pipeline flow. New findings start Security Triage: a read-only assessor, then a separate fixer. Duo Code Review uses project instructions.
  • One human decision. All the evidence lands in a Change Receipt on the merge request. The Release Gate flow writes a risk brief and asks the maintainer to Approve, Modify or Reject. Approval is a deterministic tool step that applies a label, so no model output can release anything. A webhook takes the label to an event relay on Cloud Run, and a pipeline then merges, deploys the verified digest, publishes a release and reports back on the issue.
  • Production watches itself. A Cloud Monitoring alert calls the relay. The pipeline confirms the breach. On a hard failure (broken pages or 5xx errors) it rolls traffic back to the previous release, at most once per approved release; slowness alone is left to the maintainer. Then the Incident Responder flow finds the change that caused it and opens an incident issue.
  • The pipeline optimizes itself. A weekly Carbon Optimizer flow reads the measured energy history and proposes savings through the same gate. Its own merge request cut SCI by 13.9%, measured over 5 runs on each side.

How I built it

  • Five custom flows in Flow Registry v1 YAML, published to the AI Catalog from the repository by a script (flows as code), and linted in CI against GitLab's prompt-safety rules. Three foundational flows: Code Review, Fix CI/CD Pipeline, Developer.
  • A GitLab CI pipeline of about 60 jobs. Deterministic jobs gather facts first, such as which security findings are new on this branch, and start a flow through the Flows API only when there is something to judge. Each flow has a per-merge-request attempt cap, visible as a scoped label.
  • Google Cloud: Cloud Run (scale to zero, Montréal region), Cloud Monitoring alerts and a lifecycle dashboard, Secret Manager, Artifact Registry as a proxy for GitLab's registry, Workload Identity Federation (no service account keys), and OpenTofu for all of it, with plans on merge requests and state in GitLab. SQLite survives scale to zero with Litestream replication to Cloud Storage.
  • Python standard-library automation (automation/, 109 tests): flow orchestration, SCI calculator, evidence collector, release tooling, probes and the event relay.
  • Agent skills (.agents/skills), AGENTS.md, review instructions and a sandboxed runner configuration with a network allow-list.

Challenges

  • Triggers fire only on human actions, so a flow cannot start the next flow. The pipeline is the orchestrator instead, through the Flows API.
  • My project role cannot set pipeline variables or create bot tokens. Every event-driven run is a "dispatch" pipeline that reads current state itself (approved merge requests, production health).
  • The safety net caught my own drill. A bug I planted on purpose (!6) was caught by upstream's tests, Duo Code Review and the Fix CI/CD Pipeline flow before release. To test the incident path I had to deploy the rejected build by hand: a game day. Production rolled itself back in 11 minutes, with no human involved (#4).
  • Scale to zero makes the first visit slow. linkding's startup script started Django five times; one process and precompiled bytecode cut the first visit after idle from 17.5 s to 7.8 s.
  • Measuring honestly. I found that my own SCI report had been leaving out the security stage, and corrected it and said so.

Accomplishments

  • Every stage ran for real, with evidence in the pipeline history. Releases r20 to r295 shipped through the gate, and the Release Gate said hold when it should (a red pipeline, open review threads, a new High finding).
  • The two security fixes still waiting upstream went from finding to production here: a PyJWT upgrade that resolved 1 Critical and 5 High findings, and a devalue fix (High) that Security Triage made on its own mid-review.
  • The daily security watch's first real run: 4 High advisories in unchanged dependencies (Django REST framework, zod) → issue #6 opened by the scan → upgrade MR by the Issue to MR flow → Duo review, Release Gate, human approval → production, within hours (#6, !24).
  • Two days after a Critical advisory for certifi (the app's TLS trust store) was published, the pipeline flagged it on an unrelated merge request and Security Triage fixed it, with a regression test, before the change shipped (!21).
  • Measured carbon: −13.9% SCI per merge request pipeline after the Carbon Optimizer's change, docs-only changes 20% cheaper again because they skip end-to-end tests, and 0.077 gCO2e per 1,000 production requests.
  • Turning off Cloud Run's startup CPU boost cut CPU per cold start from 27.2 to 8.2 vCPU-seconds (−70%), with no measurable loss of speed.

What I learned

Agents are most useful between deterministic steps: facts in, judgment in the middle, fixed tools out. The best moments came when an agent disagreed with me with good reason, like the Release Gate holding my own merge request until I explained a refusal. Measure before you optimize: the CPU boost looked like a free speed-up and turned out to cost three times the CPU for nothing. And trust the agent's doubt: three times, the production check rolled back for "slow" responses, and three times the Incident Responder said the change was unlikely to be the cause and pointed at something else. It was right. One stalled network request on the checker's side had been magnified by a bad percentile formula, and fixing the check came from taking the agent's diagnosis seriously.

What's next

Package the pipeline as a GitLab CI/CD component so other small projects can adopt it, and offer it to linkding's maintainer.

Built With

  • cosign
  • django
  • docker
  • eco-ci
  • gitlab
  • gitlab-ci
  • gitlab-duo-agent-platform
  • google-artifact-registry
  • google-cloud
  • google-cloud-monitoring
  • google-cloud-run
  • google-secret-manager
  • green-software-foundation-sci
  • litestream
  • opentofu
  • playwright
  • pytest
  • python
  • ruff
  • semgrep
  • sigstore
  • sqlite
  • uwsgi
  • workload-identity-federation
Share this project:

Updates

Submission history