Security

Security

Counterseal holds other people's production keys and runs a public log. This is the honest version of how it protects them, and where it stops.

The model

Agents are powerful and they make mistakes. Prompt rules ("never delete the database") live inside the model and can be ignored, forgotten or injected away. Counterseal moves the rules outside the model:

How keys are protected

How approvals work

  1. Your agent makes an irreversible call. The broker stops it and returns 428 held_for_approval with a link.
  2. You open the link (or the app, or the phone alert) and see the exact method, host, path, the rule that matched, why, and a preview of the request itself: the SQL, the GraphQL or the body.
  3. You approve with your passkey: Face ID, Touch ID, Windows Hello or a security key. The check happens on your device; the broker only verifies a signature.
  4. The approval covers that exact request, identified by a hash of its method, path, query, content type and body, once, for ten minutes. A different request, or the same one twice, is held again.
  5. A token can have at most 20 requests waiting and an account 100, and held requests wake your devices at most once a minute, so a flood of requests cannot wear you down into approving one.

A coding agent's commands

Two people, two passkeys (teams, not yet live)

A GitHub Actions deploy

An MCP server behind counterseal mcp wrap

Money caps, the breaker and Freeze

How the public log is protected

How agent keys work

One host

Signing in the command line

Controls, mapped to OWASP

The full self-assessment against OWASP ASVS 5.0, the Top 10 2025, the API Security Top 10 2023, the Top 10 for LLM Applications 2025 and the Top 10 for Agentic Applications 2026, with what is not met yet, is on the security standards page. Self-assessed, not certified.

AreaControlOWASP
Sign-inPasskeys only (WebAuthn, user verification required, origin and RP ID checked, single-use 5-minute challenges, signature counters); removing a passkey needs a fresh passkey check and signs out your other browsersA07
Sessions__Host- cookie, HttpOnly, Secure, SameSite=Strict, 12 h at most and 1 h without an action of yours; CLI sessions are bearer tokens stored only as SHA-256 hashes, last 7 days, and can be revoked in the app with everything they madeA07
CSRFWeb writes require the same Origin and an x-leash: 1 header; CORS only on the public log's read endpointsA01
AuthorizationEvery query is scoped by account; another account's holds, tokens, keys, agent keys and receipts return 404 (tested)A01, API1
Privilege separationCLI sessions can mint and revoke tokens, deny holds, make and revoke guard keys, revoke deploy gates, freeze the account, and make agent keys and receipts keys, but cannot approve holds, make a deploy gate, pre-approve irreversible operations, set money caps, reset a tripped breaker, unfreeze, delete vaulted keys, revoke agent keys, turn public receipts on or off, add alert devices, remove passkeys, or approve or revoke command line sign-insA01, A06
SecretsEnvelope encryption with AAD per row; no API returns a secret; tokens, sessions and receipts keys stored as hashesA04
InjectionAll SQL is parameterized; request bodies are size-capped while streaming and parsed as JSON onlyA05
SSRFUpstream hosts fixed per provider; no redirects; push endpoints limited to known push services; the checker's directory fetch only reaches https public host names on port 443, the fixed well-known path, no redirects, 5 s, 64 KBA01, API7
Rate limitsPer token (policy, default 120 a minute), per account, per IP (IPv6 by /64, salted hash), per receipts key; receipts quotas and a global daily cap; at most 20 waiting holds per token and 100 per account; ceilings per account (50 vault keys, 200 live tokens, 20 receipts keys, 20 agent keys, 10 alert devices); policy globs limited so they cannot backtrackAPI4
HeadersStrict CSP with Trusted Types, no innerHTML anywhere, HSTS, COOP, CORP, X-Frame-Options: DENY, Permissions-Policy, self-hosted fonts, no third-party requestsA02
Supply chainZero runtime dependencies in the Worker, the CLI and the SDK; GitHub Actions pinned by SHAA03, A08
IntegrityHash-chained audit log with a verify endpoint; RFC 6962 log with signed checkpoints; approvals bound to the exact requestA08
LoggingSecurity events (bad tokens, signatures, CSRF, replays, rate limits) and errors are written only to the live log stream, with the code, method and redacted path: never IPs, keys, cookies, tokens or bodies. Workers Logs are off, so none of it is storedA09
ErrorsClients get a generic 500 that leaks nothing; a broken log never blocks a broker decisionA10
AgentsExcessive agency is the threat this exists for: irreversible calls are held outside the model; the MCP tool tells the agent not to route around holdsLLM06

OWASP Top 10 2025 numbering. API = OWASP API Security Top 10 (2023), LLM = OWASP Top 10 for LLM Applications. The full threat model is in the repository (docs/THREAT_MODEL.md).

What Counterseal does not protect against

Report a vulnerability

Email security@gautamkhosla.com. Please don't open a public issue. You'll hear back within 7 days. Good-faith research is welcome; don't access other people's data or degrade the service. Our security.txt has the same details.