Agent-safety threat model

@honua/sdk-js/agent-safety is the deterministic trust boundary between untrusted agent plan proposals and host-owned effect execution, and @honua/sdk-js/agent-tools is the bounded runtime-action surface those plans ultimately drive. Both entrypoints are in the SDK's stable tier (see docs/decisions/agent-surface-stabilization.md), so the contracts described here are semver-protected: security reviews may cite this document knowing a breaking change to any envelope or verification step requires a major version.

This document enumerates the threats the surface is designed to stop, the mechanism that stops each one, and the conformance test that exercises it. Companion reference: docs/agent-safety.md (full API walkthrough) and docs/nl-map-control.md (the NL layer that consumes this boundary end to end).

Trust boundaries and assumptions

Threats and mitigations

Every threat below is exercised by a committed conformance test; the tests run in the default npm test suite.

1. Envelope forgery

Threat. An attacker fabricates or alters an approval envelope (or the dry run it binds to) to authorize a plan no reviewer signed — forging digests, swapping the approver, or presenting an approval for a different plan/policy.

Mitigation. An approval binds planDigest, policyDigest, bindingsDigest, and evaluatedAt to one exact dry run, which is itself recomputed (revalidateDryRun) from the supplied plan and policy — a forged dry run cannot digest-match. The envelope digest is recomputed over the canonical unsigned payload, the signer identity (algorithm + key ID) must match the configured verifier, and the host verifier.verify must return exactly true. Any mutated field fails integrity-failed or signature-invalid before an effect is possible.

Tests.

2. Replay (single-use consumption)

Threat. A valid, signed approval is presented twice — concurrently or sequentially — to run the same approved step more than once (duplicate mutation, publish, job, …).

Mitigation. Approvals are use: "single" per step. Authorization requires the host AgentApprovalUseConsumer to atomically consume the (approval digest, step ID, input digest) key and return an authenticated consumption record (nonce, consumption time, opaque token), which the SDK immediately re-verifies with the same store. A second consumption attempt returns nothing and fails policy-denied; the consumption record is bound into the receipt so post-hoc verification also proves single use. Expiry (expiresAt, bounded maxClockSkewMs) limits the replay window of any stolen envelope.

Tests.

3. Effect-budget bypass

Threat. A plan, approval, or execution result exceeds the reviewed row/byte/step/effect budget — a widened approval, an aggregate that hides a per-step overrun, an oversized operation payload, or an executor returning more data than approved.

Mitigation. dryRunAgentPlan computes a frozen AgentEffectBudgetV1 and rejects plans over policy ceilings (which are themselves capped by AGENT_SAFETY_HARD_LIMITS). Approvals may only narrow step limits — widening fails policy-denied, multi-step narrowing requires explicit stepLimits, and aggregate approvedRows/approvedBytes must equal the per-step sums. Operation parameters are bounded by policy byte/node/depth budgets before the replay store is touched. Execution evidence and receipts exceeding the approved per-step rows/bytes are refused before signing and on verification.

Tests.

4. Receipt tampering

Threat. An execution receipt is altered after signing — rows, result digest, approval binding, step ID, or use digest — to misrepresent what was executed or how much data was returned; or a receipt is minted from publicly visible digests without a real authenticated consumption.

Mitigation. issueAgentExecutionReceipt signs the canonical union of evidence and the plan/policy/bindings/approval digests; the receipt digest is recomputed on verification and the receipt signer identity must match the verifier. verifyAgentExecutionReceipt deterministically repeats every issuance check (budget, binding, consumption authentication via the host verify callback, approval signature, digest equality) and rejects any modified field. Receipts are append-only evidence: nothing in the SDK mutates or re-issues one.

Tests.

5. Plan-fingerprint mismatch

Threat. The operation a host is about to execute quietly differs from what the reviewer approved: same step ID and tool but a substituted query-plan body, different fields, a different source, or drifted parameters — including a "fingerprint-consistent" forgery where the attacker recomputes content hashes over altered content.

Mitigation. Every plan step carries queryPlan.fingerprint (from @honua/sdk-js/query-planner), a canonical parametersDigest, and an inputDigest recomputed from the visible tool/effect/source/query-plan/fields identity — the claims cannot disagree at parse time. At authorization, digestAgentOperationInput recomputes the digest from the proposed operation and requires exact equality with the approved step's digest before the replay store is consumed; any divergence (id, fingerprint, fields, parameters) fails integrity-failed. The same binding is revalidated when receipts are issued and verified. In the NL layer, plan execution additionally re-derives effects and tool identity from plan content, so recomputed-fingerprint forgeries are rejected there too.

Tests.

Adjacent hardening (context, not primary threats)

What this model does not cover

The SDK does not manage signing keys, persist the replay store or audit log, authenticate approvers, or constrain what a host-authorized executor does with data it legitimately received. Prompt-injection resistance of any LLM driving the surface is out of scope here; the mitigation posture is that a compromised model can only propose plans, and every effect still requires a signed, budgeted, single-use, context-checked approval.