-
-
Not just us: agents killing their own session with pkill, filed against five coding agents
-
GitLab Duo finds the self-kill hiding in the agent's fix: the gateway is PID 1; review sent back
-
The canary rolls itself back on a cold start; the medic asks a person; then 62 live calls, 0 dropped
-
The closeout note on the issue: 44 minutes, who acted at each step, actions by a person: 1
-
Try it: the production gateway's dry run at /try; nothing is executed, a hostile regex is refused
-
stop_port live in production: stops the dev server on :9000, refuses the gateway's own port
-
Adopted abandoned project: a sandbox escape with a reserved CVE, fixed and live in 22 min, actions by a person: 0
-
The loop's fixes for the abandoned cli-mcp-server, sent back upstream as pull requests #25-#28
Inspiration
We run AI agents against production machines through a relay. One day an agent was asked to apply a fix and "restarted the relay to pick it up". The relay was the agent's own connection. The restart cut it, nothing was left to bring the relay back, and production was down for 19 minutes.
It is not just us. The same failure is filed against coding agents again and again: an agent runs a broad kill to stop a dev server and takes itself down with it. Claude Code #3068 (pkill node), OpenCode #7950 (taskkill /IM node.exe), GitHub Copilot CLI #3033, Hermes Agent #3397 and Harbor #2311 (15 of 15 attempts). The fixes proposed there are prompt rules and deny lists.
Lifeline is for teams that let AI agents operate servers through MCP: SRE and platform teams, and anyone running a remote-control relay for coding agents.
What it does
Lifeline is an MCP gateway that knows what it depends on. Every tool call is read against its self-dependency graph (process, port, Cloud Run service, code, credentials, host). A call that would cut that lifeline is refused with a receipt, and the change the agent wanted is shipped by GitLab with no person in the loop: intake → mender flow → merge request → tests, incident replay, four scans, trust gate → GitLab Duo Code Review (changes requested: back to the mender) → merge armed on the checked SHA → Cloud Run canary with a live agent session counting dropped calls → the new revision must refuse the incident again → release → a closeout note on the issue with the clock, who acted at each step and the number of actions by a person. Failure rolls back, opens an incident and starts the medic flow.
Try it in 10 seconds: the production gateway's dry run. Type the command an agent would send; the same code that guards real sessions answers; nothing runs.
Verify it: run 11, the bug from those reports (26 Oct 2026, UTC; the run in the video)
The scripted agent did what the agents in those reports did: it sent pkill python to stop its stale dev server. On this gateway that would kill the MCP server behind it.
- 17:48 the production gateway refuses and keeps the session; issue #19 quotes the command as sent
- 17:55 the mender opens MR !13:
stop_port(port)frees one TCP port and refuses if the owner is the gateway or one of its ancestors - 18:02 GitLab Duo Code Review finds the self-kill hiding in the fix: the ancestor walk stopped at
pid > 1, and in a container the gateway is PID 1, sostop_port(8080)would have sent SIGTERM to the gateway. Changes requested; review-fix closes !13 and sends the review back - 18:08 MR !14 protects PID 1 with tests; 18:13 Duo approves; merged by the mender's service account
- 18:18 the staging canary rolls itself back: an instance started by autoscaling took 20 s to be ready and a live call waited past 15 s (#21). The medic traces it to a cold start and asks for a person because the fix is in a gate it may not edit (#22)
- 18:23 a person turns on startup CPU boost; 18:32 canary 10 → 50 → 100 %: 62 live calls, 0 dropped
44 minutes, one action by a person, in a gate, after the medic asked; the closeout note on #19 lists who acted at each step. That is the design: agents ship the change, and the gates they may not touch stay with people. The fully hands-off runs (no person at all): run 10 (#18, a new tool through two Duo review rounds, 67 min), run 9 (#17, 20 min), run 8, run 6, run 4.
On record (recompute with no token: python3 docs/numbers.py)
- Adopted an abandoned MCP shell server (cli-mcp-server, last commit July 2025, reports unanswered): the intake files its stranded reports one at a time and the loop ships the fix. 5 shipped with no person in 15–28 min, among them a startup crash and a sandbox escape with a reserved CVE (closeout); the 4 new fixes are upstream PRs (#25–#28). A second, archived MCP server (terminal-controller-mcp) followed: 2 more shipped, among them a timeout that left the command's children running, fixed by killing the command's own process group so the server is never in it.
- 12 hands-off runs with no person: 5 from refused calls (20–67 min), 7 adopted reports (15–28 min); run 11 (one person action, in a gate) and run 12 (a person fixed the loop mid-run) shipped too
- 14 of 16 merges by the mender's service account (
merged_by), approvals by GitLabDuo; !2 and !18 show the owner - GitLab Duo Code Review requested changes 14 times (a FIFO hang, a symlink race, the gateway as PID 1, gaps in shell filtering); the loop sent each back, and twice stopped after two rounds for a person
- 735 live agent calls through the production canaries of the hands-off runs, 0 dropped; 8 rollouts rolled themselves back and opened an incident; 5 medic escalations
- A labelled control (!15) removing
ops/from the trust paths: the gate first passed it (it ran the MR's own copy); it now runs the base branch's copy and failed it. - Ten lifecycle stages, each with a dated receipt (docs/STAGES.md); a one-page guide for judges (docs/JUDGING.md)
How we built it
- Guard: deterministic parser + graph in Python inside the fork (no model in the decision path), 305 tests, including the bypasses found by red-teaming it (
ps aux | grep python | awk '{print $2}' | xargs kill -9passed until 26 Oct). A caller's regular expression (pkill -f '<regex>') is matched in a separate worker process with a time limit: building the public page we found that.*.*.*.*.*.*QQQkept the old check busy for 12 seconds on the gateway's only event loop. - GitLab Duo Agent Platform: two custom flows in the AI Catalog (mender, medic), AGENTS.md, agent-config.yml, review instructions, Flows API from CI with an environment-scoped steward token.
- GitLab CI: lint/types/tests, incident replay, SAST, secret detection, dependency and container scanning, wheel to the package registry, image to the container registry, trust gate, review push-back, merge arming, canary deploy, release, closeout, monitor, intake (restarts stalled flows, also mid-review), medic; DORA metrics from environments and incidents.
- Google Cloud: Cloud Run (staging + production, canary by revision tags), a Cloud Storage receipt queue the runtime can only write to, keyless Workload Identity Federation.
Challenges
Flows cannot merge, release or start other flows, and bots cannot fire triggers, so CI closes the loop. Participants cannot create trigger tokens or project tokens, which led to the better design: the gateway holds no GitLab credential. Pipeline jobs act with the owner's steward token, so GitLab shows the owner's name on them; the closeout note attributes each entry to its job. Flows sometimes stall on the platform; the intake restarts a flow that produced no merge request in 15 minutes (run 10). Run 12 (#24) found two gaps in the loop's own recovery: a review round's flow was dropped by the platform and the intake counted the closed merge request as progress, and GitLab Duo's review session failed on the platform twice. A person fixed both during the run; Duo then approved, and the closeout job posted the run on #24 by itself (122 min).
Accomplishments
GitLab Duo Code Review found real bugs in the agents' code again and again, and the loop sent each back: a FIFO hang, a symlink race, the self-kill hiding in a fix for self-kills (the gateway is PID 1 in a container), gaps in the adopted project's shell filtering. The incident is replayed on every pipeline against real processes, and the deployed revision must refuse it again before it gets traffic. A live agent session calls through every canary; one dropped call fails the deploy.
What we learned
Agents need to know what they are standing on. The pipeline needs it too, so agents may not edit their own gates. And the guard must not be the thing that hangs.
What's next
Ship the guard as a library for other MCP servers; learn the graph from service manifests.
After 27 Oct 13:00 UTC the agents stand down: the gateway keeps refusing and the intake keeps filing issues, but no new flows start. /try stays up for judging.
Built With
- docker
- gitlab
- gitlab-ci
- gitlab-duo
- google-cloud
- google-cloud-run
- mcp
- python