Docs/OBEP Security Model raw .md

OBEP Security Model

This document states the trust model plainly so a customer's security team can audit it from the open /obep code alone. The governing rule: anything whose secrecy would create a vulnerability is in open OBEP; anything whose secrecy only protects the business may be in closed OpenErrand. The proof the split is honest: even a fully malicious OpenErrand relay cannot read a vault or widen a playbook, because the open extension re-verifies everything.

#Trust anchors (all open, all in /obep)

  • Playbook signing/verification — Ed25519 over RFC 8785 (JCS) canonical JSON. The tenant signs; the relay holds only the public key; the extension re-verifies the signature and the content hash against a locally-trusted key before executing. (protocol, extension enforcer)
  • Permission enforcement — default-deny, per-action, client-side, identical for deterministic steps and LLM-fallback commands. (enforcement)
  • The wire protocol + token formats — pairing tokens, identity assertions, and task tokens are all open and verifiable. (wire, token)
  • The relay protocol contract — encoded as the runnable conformance suite.

#"Even a malicious relay cannot…"

Attack Why it fails
Swap in a wider playbook Extension recomputes the hash and re-verifies the tenant signature; a widened body fails both.
Re-sign a widened playbook The relay never holds the tenant private key; a different key fails the extension's check.
Read a credential fillSecret carries only a vault key reference; the value is AES-GCM encrypted on-device and resolved at the moment of use. The relay never sees it.
Route across tenants tenantId is derived from the app's authenticated API key, never from a message; routing requires a matching (tenantId, userId) binding.
Read another tenant's audit /audit/:tenantId requires that tenant's API key (cross-tenant ⇒ 403).
Replay a task/pairing token Tokens are single-use (nonce-tracked) and short-lived (exp).

#Layered defenses for "no sensitive data leaves" (in order of strength)

  1. Capture minimization (strongest). Default is the stripped interactive-element list — labels/refs/types, no values, no screenshot. No payload ⇒ nothing to leak.
  2. Playbook domain allowlist. Can't reach a surface ⇒ can't capture it. Hard stop.
  3. Egress lock. The extension's connect-src CSP permits only secure transports (https:/wss:, plus localhost for dev) — never cleartext remote origins — and the code opens exactly one connection: to the relay (and, if configured, your decider) endpoint you set. The single reachable endpoint is fixed by configuration, not by the CSP host list; lock it down in fleet deployments via managed-config pinning (ENTERPRISE_DEPLOYMENT.md).
  4. Local redaction (layer 4, best-effort). Regex + Luhn + entropy over labels/DOM before transmission. Catches structured PII/keys on allowed pages we didn't anticipate. Not a guarantee — unstructured PII (names) needs NER and is out of scope. (redact)
  5. Dry-run recorder + audit. Developers see leaks before launch; runtime audit logs that a capture occurred (domain + hash), never the content.

Redaction is layer 4, not layer 1. Capture minimization and the allowlist are what keep secrets in; redaction is the safety net. Customers must not treat redaction as a guarantee.

#Credential vault

  • AES-GCM via Web Crypto, key derived from a user passphrase (PBKDF2, 210k iters), encrypted before anything touches chrome.storage.local (never sync).
  • Namespaced per binding, with the binding key as AES AAD ⇒ cross-binding reads fail cryptographically.
  • Decrypt-at-moment-of-use; the key lives only in service-worker memory.
  • We cannot decrypt a vault server-side and cannot recover a lost passphrase — by design.

#Extension hardening

  • Outbound-only WSS; reconnect with exponential backoff + jitter.
  • Sender validation: a relay-signed, single-use task token is verified against the relay's public key before the extension acts — a message is never trusted just because it arrived on the socket.
  • Only touches tabs it opened; no remote code, no eval; strict CSP.
  • Kill switch detaches the connection, aborts tasks, and locks the vault instantly.

#What we deliberately cannot do

  • Decrypt a user's vault server-side (we never hold the key).
  • Recover a lost vault passphrase (offer re-entry, not a backdoor).