Who Deedglass trusts for what, what each attacker can try, which control stops or records it, the
test that shows it, and what stays possible. Version 2 (S34, security pass 2), written with every
v1 feature built: the Gate and policies, the MCP wrapper and gateway, connectors, the vault and the
egress door, the Witness and the decoder, deeds and keys, people and sessions, approvals in email
and Slack, the Console and its event stream, Insights with its optional AI wording, the CLI, the
SDKs and deedglass init.
The framing, unchanged since S18: assume the model is already manipulated. Deedglass doesn’t
measure how often a model falls for an injection; it limits and records what the model’s tool calls
can do, what a stolen key can do, what a curious org neighbour can see, what a hostile upstream can
make Deedglass do, and what a network attacker can forge.
An agent’s valid key and whatever the policy lets it call
A2
A stolen agent key
The key, maybe after it was revoked or the agent suspended; captured signed intents
A3
A member of another org
Valid credentials of every kind in their own org on the same instance
A4
A curious or malicious viewer in the same org
A viewer session
A5
A hostile upstream
An MCP server or API an org connected: it chooses its tool names, descriptions, results and timing
A6
A network attacker (no TLS break)
The ability to send requests to public routes and to observe timing
A7
Someone who forwards or replays an approval link or a Slack payload
A link or a captured interaction
A8
An auditor-facing forger
An exported chain they can edit before an auditor reads it
Out of scope, stated: a compromised Deedglass host or seal key, a malicious operator,
model-level injection rates, volumetric DDoS, and a compromised database superuser of the control
plane.
Each row: S spoofing, T tampering, R repudiation, I information disclosure, D denial of service,
E elevation of privilege. “Test” names the test or attack case that exercises the control.
Deeds cut from the end aren’t visible in the chain alone; keep the heads you receive.
S
A forger presents another key
Verify trusts only the keys it is given; small-order keys refused
A13, deedglass verify --pin
An auditor who fetches keys.json from a compromised host trusts that host.
E
The seal key is stolen
0600 files, rotation, revocation
TestKeyFiles
A compromised Deedglass host can sign false deeds. Keep seal keys on a separate volume, rotate them, and have auditors pin keys.json from a copy they fetched earlier.
A compromised Deedglass host can sign false deeds. Keep seal keys on a separate volume, rotate
them, and have auditors pin keys.json from a copy they fetched earlier.
A manipulated model acting within policy succeeds. Policies, budgets, approvals and Stop limit
what that means; Deedglass records it.
Description scanning is a heuristic. A reworded instruction in a tool description can reach the
model; poisoned tool results aren’t scanned at all.
Writes outside the watched tables, or outside Postgres, are not seen by L2.
A forwarded, unused approval link works once for whoever holds it, as the person it was sent to.
A copy of the control-plane database together with DEEDGLASS_VAULT_KEY opens every credential.
A stored deed is edited (database superuser, backup restore, a bad migration)
Normal paths can’t: the append-only trigger and the app role’s grants refuse it. If they are bypassed, any verifier sees body_hash_mismatch or chain_prev_hash_mismatch; S06’s ledger check names the first break.
A deed is removed from the middle of a chain
chain_seq_gap names the missing seq.
Deeds are removed from the end of a chain
Not visible in the chain alone. Exports record the head (S05, S06); keep the heads you receive.
Two deeds claim the same parent (a fork)
The chain’s advisory lock and UNIQUE (org_id, prev_hash): the second insert aborts.
A signature made for something else is presented as a seal
Domain tags (deedglass/deed/v1\n, deedglass/bundle/v1\n, deedglass/intent/v1\n); seal keys are never intent keys.
A small-order or malformed public key enters a key set
Refused at key-file load, at registration and when a verifier parses keys.json (A13).
The seal key is stolen
Out of scope to prevent. The thief can seal false deeds with any issued_at. Mitigations: 0600 key files, the seed is never logged, prod refuses test keys, rotation, and revocation (a revoked key fails every deed). KMS or HSM signing is planned.
A payload leaks through a deed
The schema is closed and holds digests only; free text is capped (agent name, tool, references, reason).
An agent’s API key leaks (.env, CI logs, an MCP config)
For an agent in required mode, every call without a valid signed intent is refused (intent_required). Otherwise the holder acts as the agent until the key is revoked.
A proxy or middlebox changes an argument
args_digest covers the exact arguments: args_mismatch.
A captured request is replayed
The nonce’s primary key: intent_replay. Nonces are pruned only after the intent can no longer be accepted (expired first).
Algorithm confusion
No alg field; Ed25519 only; the signed input is domain-separated (deedglass/intent/v1\n).
Weak or malicious intent keys
Small-order, non-canonical and off-curve keys are refused at registration (422), and stored keys are parsed again on every read.
JSON ambiguity (duplicates, big integers, non-canonical bytes)
The strict parser runs before JCS; the intent’s bytes must equal their JCS form: intent_invalid.
Clock abuse
60 seconds of skew; a lifetime of at most 5 minutes.
On-behalf-of spoofing
A signed on_behalf_of is bound to the agent’s key and a differing header is refused; an unsigned one is recorded as the agent’s claim. v2 verifies people against the org’s identity provider.
Header smuggling
One value per OBO header, a strict grammar (422), and Go refuses CR and LF.
A client’s token passed to an upstream MCP server
StripInboundCredentials, which S15 must call on every forwarded request.
DNS rebinding against /mcp/*
A browser Origin other than the base URL’s is refused with 403.
Enumerating connectors through the metadata
The same document for every well-formed id, with no database read.
An intent error starts an OAuth flow in a client
Intent errors are 403 permission_error, never 401.
The agent’s runtime is compromised
Not defended by intents: the attacker holds both keys. Policies, budgets, approvals and Stop still apply.
A prompt-injected agent signs a harmful, well-formed action
Not an intent problem: the signature records what was meant. Policy decides (S10 to S12).
It is not a penetration-test report. Each row names the control and the test that exercises it;
the review (SECURITY_REVIEW_2026-10.md) records what was checked and
found, and LIMITATIONS.md every known limit.