Skip to main content
This page summarizes Continuum’s trust model and security mechanisms. The full normative specification, including the 14-scenario threat model, lives at docs/security.md.

Trust tiers

Continuum has six trust tiers.
TierComponentTrustNotes
1Local Mac daemonTrusted rootHolds Keychain entries, spawns provider CLIs, owns the in-process HTTP/WebSocket daemon (AgentControlServer). Compromising the daemon compromises everything; no other tier has equivalent privilege.
2Paired iPhone / Apple WatchTrusted peerPairing uses QR + bearer token + per-pairing ECDH. The iPhone derives the same symmetric key as the Mac and can decrypt every relay frame and every APNS notification body.
3Cloudflare relay WorkerUntrustedSees opaque XChaCha20-Poly1305 envelopes only. Never holds the session key. Audit log records sender role, envelope type, and byte length — never body content.
4Cloudflare APNS gateway WorkerUntrusted for payloadHolds the operator’s APNS .p8 signing key. Forwards sealed payloads to Apple. Cannot decrypt the body; only the paired iPhone can.
5Provider CLIsSandboxed childrenEach CLI (claude, codex, opencode, cursor-agent, agy, grok) runs as a child process, owns its own auth state, and handles its own network egress. Continuum does not proxy or inspect their traffic.
6Sparkle appcast + GitHub ReleasesAuthenticated release channelAppcast served from GitHub Pages. Sparkle verifies EdDSA update signatures. The release script gates Developer ID signing, notarization, and asset verification before publishing.

Cryptographic primitives

  • Key exchange. X25519 ECDH between Mac and iPhone. Fresh ephemeral keypairs per pairing; private keys live in process memory and are zeroized on session close or 15-minute TTL expiry.
  • Key derivation. HKDF-SHA256 with salt = sessionId and separate info strings for the relay channel ("clawdmeter.relay.v1") and the APNS payload channel ("clawdmeter.apns.v1"). Different info strings derive sibling keys so a compromise in one channel does not affect the other.
  • Authenticated encryption. XChaCha20-Poly1305 AEAD with random 24-byte nonces. Forward secrecy by construction — nothing on disk can decrypt prior captured ciphertext.
  • Replay protection. Monotonically increasing seq counters per direction. The wire-protocol version byte is bound into the HKDF info string so a version flip derives a different key and AEAD fails closed.

Per-peer bearer auth

The QR code issues two 256-bit bearer tokens: macTok for the Mac and iosTok for the iPhone. The relay Worker stores only their SHA-256 hashes. On every WebSocket open, the Worker constant-time-compares the presented bearer against both hashes and assigns a peer role. Cross-side reuse (presenting iosTok on the Mac side, or replaying a token from a different session) is rejected before any frame is forwarded. The role check repeats on every envelope. If header.from does not match the role assigned at connect time, the socket is closed. The QR TTL is 15 minutes. Stale codes expire automatically.

Tailscale Whois verification

Non-loopback connections to the daemon from the Tailscale network are additionally verified via Tailscale Whois (60-second cache). Whois is fail-closed: if verification is unavailable, the connection is rejected.

Audit logs

The daemon writes audit entries for sends, model swaps, autopilot changes, and mobile command receipts to ~/.clawdmeter/audit/. Entries record metadata — timestamps, session IDs, operation type, hashed identifiers — not content. Prompts, model responses, code, and repo paths are never written to audit logs. The APNS gateway audit log records push attempt outcomes, hashed device tokens, and byte sizes — never the notification body. Relay audit records sender role, envelope type, and byte count — never body bytes. Retention is 90 days by default (KV TTL).

Kill-switch and rate limits

The APNS gateway has a APNS_DISABLED kill-switch that runs before auth or rate-limit checks. Toggling it stops all push delivery within approximately 30 seconds across all Cloudflare edges. A per-device hourly push cap (default 60 pushes per device per hour) limits damage from a compromised operator .p8 key. The daemon enforces a 1-send-per-second and 1-model-swap-per-5-seconds rate limit per session.

F3 HOME isolation

The type-level seam for per-provider HOME isolation (running claude_personal and claude_work as separate instances with isolated credentials) is in the codebase as ProviderInstanceId.homePathOverride. The runtime enforcement — env scrubbing on child spawn and Keychain partitioning — is not yet wired. Until F3-wire ships, all instances share the same Keychain access group and home directory per provider.

ACP file-system trust boundary

When Continuum drives Cursor or Grok over ACP, the agent can request file reads and writes. Continuum only advertises these capabilities for repos on the autopilot trust list. Every request is validated through RepoTrustGate: the path is canonicalized, symlinks resolved, and the canonical result must sit at or under the repo root. The implementation is TOCTOU-aware: the gate returns the resolved path and all I/O uses that resolved path.

Update chain

Mac updates go through Sparkle 2.9.2. The release pipeline gates on:
  • Developer ID Application code signing
  • Apple notarization and stapling
  • Sparkle EdDSA signature for the update DMG
  • Appcast hosted on GitHub Pages at https://darshanbathija.github.io/Continuum/updates/appcast.xml
If Sparkle validation or installation fails, the UI falls back to the GitHub release page.

Threat model reference

The full 14-scenario threat model — including relay operator curiosity, leaked QR, MITM, replay, nonce reuse, lost device, forged APNS, DoS, operator Cloudflare account compromise, iOS WS suspension, enterprise proxy MITM, timing side-channels, and protocol downgrade — is documented in docs/security.md. Threat #10 (operator Cloudflare account compromise) is the irreducible trust root and explicitly accepted. Mitigations are account-hygiene only; there is no in-product defense against a rogue operator.

Reporting a vulnerability

Email the maintainers directly rather than opening a public GitHub issue. Contacts are listed in CONTRIBUTING.md and infra/apns-gateway/ROTATION.md.