Sleeper

Sleeper · release-gate agent · CockroachDB × AWS Agentic Memory Hackathon

The release gate that remembers.

The xz-utils backdoor was planted over 28 months — one innocuous commit, one plausible email at a time. No code review caught it, because no review sees 28 months at once. Sleeper keeps every commit, email and maintainer change for a package in CockroachDB. When a release matches the shape of a slow takeover, it holds that release — one transaction, full audit trail.

Case file · from the public record
package
xz-utils — the compression library the 5.6.x backdoor shipped in
cve
CVE-2024-3094 · CVSS 10.0
reach
rolling & pre-release channels only — Debian sid/testing, Fedora Rawhide, openSUSE Tumbleweed, Arch, Kali. No stable or LTS cut shipped itsources below.
planted
≈ Oct 2021 → Feb 2024 — 28 months
found
2024-03-29 — 34 days after it shipped, via 500 ms of SSH latency. Luck, not process.
in replay
HELD at the 5.6.0 upload
  • 28 moto plant the backdoor, one clean commit at a time
  • 25events in the replayed timeline, each publicly cited
  • 0of them suspicious alone — the tell lives only in the arc
  • 1transaction to hold the release: all four writes or none

01 · The incident

28 months of clean reviews

The arc Sleeper replays — 18 of the 25 events in data/xz-timeline.json, reconstructed from the public record and cited per event. Read each one the way its reviewer did: alone, on the day it happened. Every one passed.

  1. 2021the stage
  2. ≈ 2021-06-15
    Lasse Collin · maintainercommit

    One unpaid volunteer: build fixes, translation updates, small portability corrections, at a slow and irregular cadence. The project has effectively one active maintainer and a years-long backlog of unreviewed mailing-list patches.

    source: tukaani-project/xz history

  3. ≈ 2021-10-29
    “Jia Tan”email · first activity

    The account's first recorded public activity: small, correct, unremarkable contributions. Nothing objectionable — the account simply has no history before this date. Tenure starts counting here.

    source: research.swtch.com/xz-timeline

  4. 2022the squeeze
  5. 2022-04-19
    “Jia Tan”email

    First patch to the xz-devel list: small, plausible, technically competent. The maintainer engages normally.

    source: xz-devel archive

  6. 2022-04-22
    “Jigar Kumar” · no prior historyemail

    Three days later, another account with no history appears — and its first-ever message is about maintainership, not code: schedule pressure over unmerged patches.

    source: xz-devel archive

  7. 2022-05-19
    “Jigar Kumar”email
    “Patches spend years on this mailing list. 5.2.0 was released in 2015. There is no reason to think anything is coming soon.”

    A complaint about the maintainer's throughput, from an account that has contributed no patches of its own.

    source: xz-devel archive

  8. 2022-06-07
    “Jigar Kumar”email
    “Progress will not happen until there is new maintainer. … Why delay what your repo needs?”

    The handover is now demanded outright. Still zero code from this account.

    source: xz-devel archive

  9. 2022-06-08
    Lasse Collin · maintaineremail
    “I haven't lost interest but my ability to care has been fairly limited mostly due to longterm mental health issues … Jia Tan has helped me off-list with xz and he might have a bigger role in the future at least with xz Utils.”

    Under pressure, a tired and under-resourced maintainer names a successor in public.

    source: xz-devel archive

  10. 2022-06-14
    “Dennis Ens” · no prior historyemail

    A second no-history account joins on the same theme. Two independent-looking voices now push the same handover, weeks apart.

    source: xz-devel archive

  11. 2022-06-21
    “Jigar Kumar”last email

    Final recorded message, still on the maintainership theme. With the handover trajectory set, the account goes silent — and is never seen in any project again.

    source: xz-devel archive

  12. What the arc holds at this point

    Two accounts whose only activity is demanding a handover. Zero code from either. A burned-out maintainer naming a successor. Read alone, every message was fine — the pattern is not inside any single event.

  13. ≈ 2022-11-30
    “Jia Tan”maintainer change

    Commit access granted; self-merged commits follow shortly, without external review. From first-ever public activity to write access on near-universal infrastructure: roughly 13 months.

    source: tukaani-project/xz history

  14. 2023the takeover
  15. 2023-03-20
    “Jia Tan”maintainer change

    The oss-fuzz contact for xz is switched to a JiaT75-controlled address. Automated security reports now route away from the original maintainer.

    source: google/oss-fuzz

  16. ≈ 2023-05-04
    “Jia Tan”release

    Signs and publishes a release as acting release manager. Tarball production — the step that will later carry the backdoor — is now under this actor's control.

    source: xz releases

  17. 2023-06-27
    “Hans Jansen” · no meaningful historycommit

    Contributes IFUNC resolver machinery to crc64_fast.c. Defensible on performance grounds, merged — and precisely the mechanism the backdoor will use to hook symbol resolution at runtime.

    source: commit ee44863

  18. 2023-07-08
    “Jia Tan”commit

    A pull request to oss-fuzz disables IFUNC-related detection for xz, justified as suppressing false positives. The fuzzer most likely to notice anomalous IFUNC behaviour stops looking.

    source: google/oss-fuzz

  19. ≈ 2023-07-19
    “Jia Tan”commit

    Landlock sandboxing is disabled through a deliberately malformed configure check — reads as a build-system quirk in isolation, removes a hardening layer in practice.

    source: tukaani-project/xz history

  20. What the arc holds at this point

    The actor now signs the releases, receives the project's security reports, and has switched off the fuzzer's eyes. Each change was individually defensible. The concentration on build machinery rather than library code is visible only in aggregate.

  21. 2024the payload
  22. 2024-02-23
    “Jia Tan”payloadcommit
    “Tests: Add a few test files.”

    Two binary blobs land as test-suite corpus files. They are the encrypted payload stages of the backdoor. Binary test fixtures are conventionally not read by reviewers.

    source: commit cf44e4b

  23. 2024-02-24
    “Jia Tan”5.6.0 shipsrelease

    The 5.6.0 tarball is published. Its build-to-host.m4 differs from the repository: the loader stage exists only in the distributed tarball, never in the git history — so no amount of reviewing the repo can find it.

    source: oss-security disclosure

  24. Sleeper holds here.

    Replayed through the agent, the arc — the actor's whole trajectory read back out of CockroachDB, never out of the seed file, with the last 90 days in close focus — matches a takeover shape in the playbook. 5.6.0 is held in one ACID transaction: hold, trust state, distro advisory, audit row. All four writes or none.

    The decision comes from the years, not from this event's diff — a 90-day slice of this attack is three innocuous commits. And this is a ground-truth replay of a real incident, not a benchmark — accuracy belongs to a separate harness, over held-out arcs the playbook never saw. See the honest ledger.

    reproduce: npm run replay → then npm run explain -- --hold <uuid>

  25. 2024-03-29
    Andres Freund · unrelated engineerdisclosure

    34 days and a second poisoned release later — while the accounts that built the mechanism lobbied distributions to ship it — a PostgreSQL developer chases ~500 ms of unexplained sshd latency and finds the backdoor. CVE-2024-3094, CVSS 10.0. A performance anomaly found it. No review did.

    source: oss-security disclosure

How far it got, in checkable terms. The poisoned 5.6.x reached rolling and pre-release channels and no further. Debian testing, unstable and experimental carried it from 5.5.1alpha-0.1 up to and including 5.6.1-1 (DSA-5649); Fedora Rawhide and Fedora 40, still unreleased at disclosure (Red Hat — that URL keeps the slug of its first posting; the text names Fedora Linux 40 and Rawhide. Fedora 40 shipped 2024-04-23); openSUSE Tumbleweed and MicroOS, 2024-03-07 to 2024-03-28 (openSUSE); Kali, 2024-03-26 to 2024-03-29 (Kali); and Arch, as 5.6.0-1 and 5.6.1-1, where the loader never fired because it only injected in Debian- and Fedora-style build environments (Arch). Every stable and enterprise line that published a statement says it was clear: “no Debian stable versions are known to be affected”, “no versions of Red Hat Enterprise Linux (RHEL) are affected by this CVE”, and the malicious file “is not present in SUSE Linux Enterprise and/or openSUSE Leap”.

That is the impact statement, and it is a near miss rather than a catastrophe: 34 days of exposure, and not one stable or LTS cut carried it. Had 5.6.x survived into a Debian stable or RHEL release, the affected population would have been orders of magnitude larger and the removal far slower — and what stopped it was one engineer chasing a latency anomaly, which is not a control anyone can schedule. No install-base figure appears on this page: every number here carries a source, and that one has none we are willing to stand behind.

≈ marks dates public sources give only to within a window — flagged approximate: true in the data. Event text is faithful paraphrase or direct quotation of public material, per the provenance note in data/xz-timeline.json.

Seven of the 25 events are not drawn above: four routine contribution beats, and three post-ship rows — the 5.6.1 rebuild of the backdoor and the push to get distributions shipping it — folded into the final row.

02 · Agentic memory design

What a review cannot see, memory can

The memory layer is the product. Events are ingested into CockroachDB, and the behavioural arc is rebuilt by reading that history back out of the cluster, bounded to what was knowable at the assessment timestamp. The database is the memory, not a log of it.

The decision is not made on the incoming event. It is made on what the incoming event means given everything already in the database.
Per-commit view

What one code review sees

The payload commit, as its reviewer met it. And the loader stage never entered git at all — it existed only in the release tarball. For this attack the per-event view is not merely thin; it is structurally blind.

Cross-year arc view

What 28 months of memory sees

  • An unusually fast rise: no account history at all → write access in roughly 13 months.
  • Concentration on build machinery — release tooling, CI, fuzzer config — rather than library code.
  • Pressure accounts with zero code that vanished the moment the handover completed.
  • A maintainer reviewing his own release artifacts: the review gate quietly became one person.

None of these is a hand-written rule. They are what the arc looks like once years of events are rolled into one summary (Bedrock Claude), embedded (Bedrock Titan), and searched against known takeover shapes in CockroachDB.

The gate is two-sided — so it does not cry wolf

A bare “similarity to a known takeover ≥ X” rule flags any contributor who is new and prolific — which describes most good first-time maintainers. Sleeper holds only when the arc is close to a takeover shape and meaningfully closer to it than to the nearest ordinary-contributor shape. Several benign arcs in the playbook are written to resemble takeovers on the surface — fast rise, handover, brand-new account — so the second test has teeth.

close to takeover AND separated from benign → HOLD

Structural signals — tenure, escalation speed, build-system concentration, no-code pressure accounts — are computed from memory and cited in the hold rationale, but they do not vote. Mixing a hand-tuned rule engine into the decision would quietly turn the benchmark into a measurement of the rules rather than of the memory.

03 · Architecture

One event arrives. Years answer.

The production shape: one webhook-shaped event lands, is embedded and written, then assessed against everything already in memory. The demo drives the same loop over the xz corpus.

  1. Webhook-shaped eventreplayed xz timeline
  2. Agent loopLambda handler / local server
  3. AWS BedrockTitan embeds · Claude rolls the arc
  4. CockroachDBevents · actor_arcs · playbook — 3 vector indexes
  5. Two-sided gatetakeover-close AND benign-far
  6. One ACID transactionhold + trust + advisory + audit
  7. Audit surfacenpm run explain · MCP client
STAGE 01

Ingest and embed every signal

Every commit, email, maintainer change and release is embedded through Bedrock Titan Text Embeddings V2 (1024 dimensions, matching VECTOR(1024)) and written to events. The vector index is declared inline in CREATE TABLE on purpose: backfilling one onto a non-empty table blocks writes, so the index exists before the first row lands. Webhook delivery is at-least-once, so every event carries a deterministic event_key under a UNIQUE index — a redelivery can never double-weight a signal inside the very memory the decision is derived from.

sql/schema.sql — abridged
CREATE TABLE events (
  package_id  STRING NOT NULL,
  actor_id    STRING NOT NULL,
  kind        STRING NOT NULL,  -- commit | email | …
  content     STRING NOT NULL,
  occurred_at TIMESTAMPTZ NOT NULL,
  source_url  STRING,             -- public citation
  event_key   STRING NOT NULL,  -- webhook dedup
  embedding   VECTOR(1024) NOT NULL,
  VECTOR INDEX events_pkg_embedding_idx
    (package_id, embedding vector_cosine_ops),
  UNIQUE INDEX events_event_key_idx (event_key)
);
STAGE 02

Roll the arc — from the database, not the feed

Nobody names the suspect. rankCandidates (src/agent.ts) enumerates every actor the package's memory has seen, ranks them by structural signals the memory layer already computed — can this account ship, how fast did trust escalate, how build-shaped is the history, how new is it, was a handover argued for — and the top few get an arc built. Ranking orders a list; it never reaches decide(), which still sees playbook matches and nothing else. The honest limit: on a package with one maintainer and one attacker this is a two-horse race, so the claim is only that the agent picks its own suspects out of memory, not that the ordering is hard-won.

For each candidate the history is read back out of CockroachDB — never out of the seed file — filtered to what was knowable at the assessment timestamp, so a replay cannot leak hindsight into a past decision. Bedrock Claude (Converse) rolls it into a 90-day behavioural arc summary, embedded and stored in actor_arcs under the same prefix-scoped index shape.

sql/schema.sql — abridged
CREATE TABLE actor_arcs (
  package_id  STRING NOT NULL,
  actor_id    STRING NOT NULL,
  window_start TIMESTAMPTZ NOT NULL,
  window_end   TIMESTAMPTZ NOT NULL,
  arc_summary  STRING NOT NULL,  -- Claude rollup
  embedding    VECTOR(1024) NOT NULL,
  VECTOR INDEX actor_arcs_pkg_embedding_idx
    (package_id, embedding vector_cosine_ops)
);
STAGE 03

Prefix-scoped retrieval — proven, not claimed

The ANN search over this package's own memory is pre-filtered by the leading vector-index column, so it is bounded to one package instead of scanning globally. The replay runs EXPLAIN on the live query and prints the plan; the prefix spans line is the proof, and the integration suite asserts it — on the deciding query as well as the one shown on camera.

the scoped query + its plan
WHERE package_id = $1
ORDER BY embedding <=> $2
LIMIT k

-- the EXPLAIN line the integration suite asserts on:
prefix spans: [/'xz-utils' - /'xz-utils']
STAGE 04

Match against the playbook — held-out excluded

A second ANN query compares the arc against takeover_playbook, deliberately unscoped by package — a takeover shape learned anywhere must be matchable from anywhere. Its index prefix is (held_out, embedding_model) instead: rows marked held_out exist only for evaluation, and the prefix keeps them out inside the index descent. A post-filter would let them consume top-k slots first and silently collapse the decision margin. The playbook itself contains no xz-derived arc: matching xz against a corpus containing xz would prove nothing.

sql/schema.sql — abridged
CREATE TABLE takeover_playbook (
  label       STRING NOT NULL,  -- 'takeover' | 'benign'
  source      STRING NOT NULL,  -- real-xz | synthetic
  held_out    BOOL NOT NULL
                DEFAULT false,      -- true = eval only
  arc_summary STRING NOT NULL,
  embedding   VECTOR(1024) NOT NULL,
  embedding_model STRING NOT NULL,
  -- no package_id prefix; held-out rows are
  -- dropped during the index descent:
  VECTOR INDEX takeover_playbook_embedding_idx
    (held_out, embedding_model, embedding vector_cosine_ops)
);
STAGE 05

The hold — four writes, one COMMIT

If the two-sided decision trips, Bedrock Claude composes the hold rationale and the distro advisory, and the agent commits everything as one transaction. A failure mid-write leaves no partial state — no blocked release with an orphaned advisory, no advisory for a hold that rolled back. An integration test kills the transaction mid-write and asserts nothing survives. The retraction path is symmetrical: commitUnhold flips trust state, queues a retraction advisory and appends an audit row in one transaction, and never deletes the hold it is retracting.

the shape of the hold commit
BEGIN;
  INSERT INTO release_hold      -- rationale + matched arc
  UPDATE      trust_state       -- package -> 'held'
  INSERT INTO distro_advisory_outbox
  INSERT INTO audit_log
COMMIT;  -- all four or none
STAGE 06

“Explain your hold”

Every hold carries the matched arc, the similarity, the threshold in force, the EXPLAIN verdict and the structural evidence — in queryable rows, not logs. A downstream distro packager audits it in one statement. The reader resolves at runtime: with COCKROACH_MCP_API_KEY set the reads go through the Managed MCP Server, otherwise over the pg pool — and every run prints which path it took, verbatim, before any output. That MCP path is verified against the live Cloud servernpm run mcp:audit drives all four tools, and explain_query returns prefix spans: [/'xz-utils' - /'xz-utils'] computed by CockroachDB rather than by us (detail below).

terminal
$ npm run explain                  # plan + nearest events
$ npm run explain -- --hold <uuid> # one hold, full trail

-- every run names its path on the first line:
AUDIT PATH: direct SQL over the pg pool — NOT the Managed MCP Server

Why this is CockroachDB and not a vector database bolted onto a relational one:

the vector search that produces the decision and the state change that acts on it are the same system, so a hold cannot half-land. There is no window where a release is blocked with no advisory queued, or an advisory goes out for a hold that rolled back.

04 · Required tech, meaningfully integrated

CockroachDB × AWS

Every status below is exact: where something has not yet run against live infrastructure, it says so.

Persistent memory layer

CockroachDB

Distributed Vector Indexing in the code & tests
Three inline VECTOR INDEX declarations in sql/schema.sql. events and actor_arcs are indexed on (package_id, embedding vector_cosine_ops), so ANN search is prefix-scoped to one package's own history. takeover_playbook carries no package prefix on purpose — its prefix columns are held_out and embedding_model, which drop evaluation-only arcs during the index descent. Retrieval orders by <=> cosine distance, and EXPLAIN on the live queries proves the prefix spans pre-filter — asserted in the integration suite.
ccloud CLI scripted
scripts/provision.sh (npm run provision) creates the Basic cluster, the database, and two SQL identities split along the line that actually exists in the code — setup versus runtime. sleeper_admin owns DDL, one cluster setting, and the destructive setup paths (npm run schema, npm run seed, npm run replay). gate_svc is the running agent — webhook, decision, hold, unhold — with no DELETE anywhere, no DDL, and no writes to the playbook. Applied as real SQL with REVOKE admin included, because ccloud cluster user create makes admins and a split where both sides are admin is decoration. Only with --mcp-key is a third identity created: a read-only Cloud service account for the MCP audit surface, which lives on a different auth plane and is not a SQL identity.

An earlier version of this script created an ingest_svc holding INSERT, SELECT on events alone. It was deleted, not patched: it could not run the webhook it was named after — ingestHandler calls runReplay, which writes five more tables, so an events-only identity fails at upsertActorArc. A privilege boundary the code cannot honour reads as a security property while being a comment. If you provisioned before the split changed, DROP USER ingest_svc.
Managed MCP Server verified against the live Cloud server
src/mcp.ts drives four MCP read tools — select_query, explain_query, get_table_schema, show_statement — putting an explicit LIMIT on every SELECT, because the server silently caps an unlimited one at 25 rows and a truncated evidence trail must not pass as complete. npm run mcp:audit runs all four end to end against cockroachlabs.cloud/mcp.

Binding argument names to the schema the server advertises, instead of hardcoding them, is what made that first live run survivable: the real argument is query, not sql, and database is required on three of the four tools.

The run also disproved something this page used to claim. The audit path is not read-only at the protocol layer: tools/list is not role-filtered, so insert_rows is advertised to every identity — including one that cannot execute a single call — and no CockroachDB Cloud role grants MCP reads without also granting writes. What keeps this path read-only is that the client only ever builds SELECT, EXPLAIN and SHOW. The enforced boundary is at the SQL layer, above.
Inference + compute

AWS

Bedrock — Titan Text Embeddings V2 in the code & tests
InvokeModel embeds every event on write and every arc summary before retrieval — 1024 dimensions, matching VECTOR(1024) in the schema.
Bedrock — Claude in the code & tests
Converse rolls a multi-year event history into one behavioural arc summary, then composes the hold rationale and the distro advisory that go into the transaction.
Lambda written & typechecked, not yet deployed
src/handler.ts exports the loop as ingestHandler — one webhook event in, assessed against everything already in memory — plus replayHandler. The connection pool is closed per invocation, because a socket held across an execution-context freeze comes back dead.

05 · Reproduce it

Run it locally

Prerequisites: Node.js ≥ 22 · a CockroachDB cluster with vector indexing (Cloud Basic, or a local v25.2+ node) · an AWS account with Bedrock access for Titan Text Embeddings V2 and a Claude model. Full methodology lives in DEMO.md in the repository.

0 · Clone it

terminal
$ git clone https://github.com/edycutjong/sleeper.git
$ cd sleeper
$ npm install

1 · Provision the cluster — CockroachDB ccloud CLI

terminal
$ ccloud auth login
$ ccloud cluster create basic sleeper-cluster --cloud GCP --spend-limit 0
$ ccloud cluster user create sleeper-cluster sleeper_admin # setup path: DDL, seed, replay
$ ccloud cluster user create sleeper-cluster gate_svc      # runtime path: no DELETE, no DDL
$ ccloud cluster sql --connection-url sleeper-cluster      # -> DATABASE_URL

npm run provision runs all of this and then applies the privilege split as real SQL — ccloud cluster user create alone makes two admins, which is not a boundary. --dry-run prints every command first; --mcp-key additionally creates the read-only Cloud service account the MCP audit surface authenticates as.

2 · Load, calibrate, replay — live Bedrock inference

terminal
$ npm install
$ cp .env.example .env   # DATABASE_URL + Bedrock model ids
$ npm run schema         # tables + the three vector indexes
$ npm run seed           # playbook and held-out arc corpora
$ npm run calibrate      # fits thresholds on the playbook split ONLY
$ npm start              # http://localhost:3000 -> "Replay the xz timeline"

Terminal equivalents: npm run replay, then npm run explain -- --hold <uuid>. npm run calibrate writes data/thresholds.json, which is deliberately not committed — a thresholds file fitted against a different embedding model would silently move the gate.

3 · No AWS account? — offline wiring check

terminal
$ cockroach start-single-node --insecure --listen-addr=localhost:26257 --store=/tmp/sleeper-crdb
$ cockroach sql --insecure -e 'CREATE DATABASE sleeper'
$ export DATABASE_URL='postgresql://root@localhost:26257/sleeper?sslmode=disable'
$ export SLEEPER_OFFLINE=1
$ npm run schema && npm run seed && npm run calibrate && npm run replay

Offline mode detects nothing. It swaps Bedrock for a deterministic hashed stand-in, which carries no notion of what a takeover means, so the replay ends with the gate open. It proves the schema, the SQL, the vector-index behaviour and the transaction semantics — nothing more. Every entry point prints which inference path it is on, and npm run bench refuses to run in this mode at all.

4 · Tests — 586 in the suite

terminal
$ npm test   # cluster-dependent tests skip without DATABASE_URL
$ DATABASE_URL='postgresql://root@localhost:26257/sleeper?sslmode=disable' npm test

# with no DATABASE_URL and no credentials:
Tests  520 passed | 66 skipped (586)

586 tests. On a bare clone with no credentials, 520 pass and 66 skip pending a live cluster — the 66 assert the prefix spans plan line, held-out exclusion, the all-or-nothing hold and unhold transactions, and point-in-time correctness (a decision can never see an event from after its own assessment timestamp). They namespace every row to a per-run package id, so they are safe to point at the demo cluster. Those counts are the literal output of npx vitest run on this commit — re-run it rather than trusting the number.

06 · Calibrated honesty

The honest ledger

What is demonstrated, what is measured, what is pending, what was cut — kept apart, because blurring them is how a single anecdote masquerades as a detection rate.

Why no accuracy number appears on this page

  • No accuracy figure exists, and this page cites none. Recall, hold recall and false-positive rate need real embeddings, and npm run bench refuses to compute them against the offline stand-in — a quality number derived from a hash function would be a property of the hash. Latency is a different question: it is a property of the vector index and the row count, not of the model, so DEMO.md carries a p50/p95 measured on a local single-node cluster over a corpus of 8 searchable arcs, labelled as the floor it is rather than a scale result.
  • npm run bench is built to measure recall@k, hold recall, false-positive rate, precision and p50/p95 decision latency — only on held-out arcs the agent can never retrieve, against a lexical baseline, with the xz timeline excluded from every figure.
  • The xz replay is ground truth on a real incident: evidence the mechanic recognises the actual takeover. It is not a measurement and it feeds no metric.
  • Thresholds are fitted by leave-one-out on the playbook split only, never on the evaluation set, and the fitted file is gitignored.

True today

  • 586 tests — 520 pass on a bare clone, 66 skip until a live cluster is reachable.
  • The hold is all-or-nothing under an explicit rollback test, and history reads are bounded by the assessment timestamp.
  • A hold has a recorded way out: commitUnhold flips trust state, queues a retraction advisory and appends an audit row in one transaction, and the hold row itself is append-only.
  • Two SQL identities split setup versus runtime, provisioned by script and applied as real GRANTs: sleeper_admin (DDL and the destructive setup paths) and gate_svc (the running agent — no DELETE anywhere, no DDL, no playbook writes). That split is verified by execution, not by reading the grants: run against the live cluster, gate_svc is refused DELETE on every table, playbook INSERT, SET CLUSTER SETTING and DROP TABLE — every refusal SQLSTATE 42501, transcript in DEMO.md. With --mcp-key, a third identity is created for the audit surface: a read-only Cloud service account, which is not a SQL identity. An earlier ingest_svc was deleted rather than patched — it could not run the webhook it was named after.
  • Managed MCP Server client: verified end to end against the live Cloud servernpm run mcp:audit drives all four tools against cockroachlabs.cloud/mcp, and explain_query returns prefix spans: [/'xz-utils' - /'xz-utils'] computed server-side. The caveat that run produced: the credential is write-capable. No CockroachDB Cloud role grants MCP reads without writes, and tools/list is not role-filtered, so this path is read-only because the client only ever builds SELECT, EXPLAIN and SHOW — not because the server prevents anything. The enforced boundary is gate_svc, at the SQL layer.
  • Lambda handlers: written and typechecked, not yet deployed. Nothing here is running in production.
  • A decision with no benign neighbour to contrast against refuses to hold rather than passing on a similarity score alone.

Cut on purpose

  • One package, one flow, done deeply. No multi-package dashboard, no user accounts, no orchestration layer, no multi-region, no second demo scenario.
  • S3-backed tarball diffing — the build-vs-source tell — is described in the architecture and not wired in this build.
  • Offline mode is a wiring check, never presented as detection.
  • No users, no deployment, no uptime claim, and no adoption number. There is nothing to be honest about there yet.
  • Nothing on this page claims a capability the code does not have; where something is pending, it says so.

Where each judging criterion lives on this page

Criterion (5 × 20%)The evidence
Agentic Memory Design tie-break Memory is the decision: prefix-scoped vector retrieval over years of accumulated events; a 90-day slice of this attack is three innocuous commits. § What memory sees
Technical Implementation Three inline vector indexes, <=> retrieval with an EXPLAIN-asserted prefix spans pre-filter, one ACID hold transaction, 586 tests. § Architecture
Real-World Impact The exact pattern of CVE-2024-3094, replayed from the cited public record; an audit trail a distro packager queries in one statement. § The arc
Production Readiness Two privilege-scoped service identities, rollback-tested holds and unholds, point-in-time reads, gitignored thresholds — and an explicit ledger of what is not done. § This section
Creativity & Originality Retrieval is the actuator, not an answer — its output goes straight into the four-write transaction that blocks the release; nothing here terminates in a recommendation awaiting a human. And the agent enumerates its own suspects out of the package's memory rather than being handed the account to judge, which is precisely the state the world was in for the 28 months the real attack ran. § Architecture — stages 02 and 05