Skip to main content

Threat model

What AgentPack defends against, and — just as importantly — what it does not.

In scope

1. Cross-agent data exposure via a compromised device

Defense: every Cloud Function rewrites agent_id from the verified device binding before any SQL executes. A leaked device key k bound to agent A cannot read, write, or delete any row owned by B, even if the caller forges the body.

2. Prompt injection in inbound email

Defense: postbox.detect_prompt_injection(body) returns a 0–1 score. postbox.ingest() stamps injection_score and quarantined=(score > 0.8) on every inbound row. The SDK and Pocket UI display a badge. Agents that choose to auto-act on inbound mail can gate on the score.

3. Audit tampering

Defense: audit.events is hash-chained and read-only to every role except service_role. audit.verify_chain() is idempotent and a daily cron records the result in audit.monitor. Silent row deletes leave first_bad_id non-null.

4. Email sender abuse

Defense: postbox.outbound_suppressions hard-blocks sends to addresses that have hard-bounced recently. is_hard_bounce(reason) auto-adds addresses after record_bounce events classified as terminal. Per-agent and per-minute/day quotas live in postbox.send_quotas.

5. Tunable-driven privilege escalation

Defense: identity.set_safe_setting() has an allow-list of keys and their JSON-schema contract. A caller cannot write an arbitrary service_role_key through /settings/set.

6. Stale credentials

Defense: device keys, delegations, and capability tokens have TTLs and revoke_* RPCs. Revocations append to the audit log. Verifiers check revocation before honoring a token.

7. PSK reuse across peer pairs

Defense: WireGuard PSKs in mesh.psk are anchored on (agent_lo, agent_hi) pairs and rotated per-pair. mesh.rotate_psk(a, b) generates a fresh 32-byte secret and never returns it through the edge layer — callers re-fetch via wg_config through their normal authed path.

8. Webhook replay

Defense: inbound webhooks carry an HMAC of the body plus an x-agentpack-webhook-timestamp header. agent-postbox/webhook/rotate supports dual-secret grace windows so rotations don't drop deliveries.

Out of scope

We are explicit about what we don't defend against. Listing them keeps the security story honest.

1. A compromised Firebase service role key

If FIREBASE_SERVICE_ROLE_KEY leaks, everything is readable and writable. The Go identity service's Ed25519 signing key is held outside Firebase precisely because of this — capability tokens stay unforgeable even in a full DB compromise, but the ledger and data would be exposed. Rotate the service role key on any suspicion.

2. A hostile Firestore admin

AgentPack assumes the DBA is trusted. Encrypted column values (secrets) protect against DB snapshots and replicas, not against live SQL access.

3. DoS on the edge runtime

Cloud Functions share a runtime. We rely on the platform's rate limits; AgentPack has no per-IP throttle beyond what postbox.send_quotas enforces at the application layer.

4. A malicious agent acting within its scope

If agent A is compromised, it can send spam from A's outbound address, burn A's budget, and leak A's memory. Cross-agent damage is what we stop. Self-harm by one agent is contained by the Governor budgets but not fully prevented.

5. Supply-chain compromise of a model provider

AgentPack does not inspect model outputs. An exfiltration attack encoded in a response body will pass through unless the agent integrates something like the Governor's allow-listed domain policy.

6. Pocket PWA device theft

The Pocket install stores a device key at rest with OS keychain protection but will trust the device. A stolen, unlocked phone with Pocket open is equivalent to an authenticated session on that agent.

Defense-in-depth choices

  • Least privilege by default. Edge functions bind to one schema each.
  • No SELECT * to service_role by convention. RPCs return only what the caller needs, which also keeps payloads tight.
  • Everything interesting is logged. audit.events is the single source of truth for "what happened." If a mitigation exists, it should append a row.
  • Rotate, don't expire. Every long-lived credential (device key, webhook secret, WireGuard PSK) has a rotate RPC so operators never feel pressure to keep a weak secret alive.