> ## Documentation Index
> Fetch the complete documentation index at: https://continuum-three-olive.vercel.app/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Security

> Trust tiers, cryptographic primitives, token lifetimes, per-peer auth, multi-account isolation, execution-host trust, content sealing, and the release channels.

This page summarizes Continuum's trust model and security mechanisms. The normative specification — including the 14-scenario threat model — ships inside the app's in-product documentation; the highlights are reproduced here.

## Trust tiers

Continuum's trust model spans the host that runs your agents, the clients that drive it, the relay and push paths between them, the cloud backend that holds account state, the inference gateway, the provider CLIs it spawns, and the release channels.

| Tier | Component                                                                   | Trust                                               | Notes                                                                                                                                                                                                                                                                                       |
| ---- | --------------------------------------------------------------------------- | --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1    | Host daemon — the Mac app, or the Go `continuum-agent` on Linux and Windows | **Trusted root**                                    | Holds keychain entries, spawns provider CLIs, owns the HTTP/WebSocket control server (default HTTP `21731`, falling back through `21741`; the WebSocket port is the next free port above it). Compromising the host compromises everything; no other tier has equivalent privilege.         |
| 2    | Paired clients — iPhone, Apple Watch, web app, Android                      | **Trusted peer**                                    | Reach the host through the account mesh or a tailnet. Each derives the same per-pairing symmetric key as the host and can decrypt every relay frame and sealed push body.                                                                                                                   |
| 3    | Remote execution hosts — second Mac, Linux box, VPS, BYOC, managed runner   | **Trusted compute, host-authed**                    | Run sessions on behalf of a hub. Each enforces the same peer filter, bearer check, and tailnet identity check as the local host. See [Execution hosts](#execution-host-trust).                                                                                                              |
| 4    | Pairing relay                                                               | **Untrusted**                                       | Sees opaque XChaCha20-Poly1305 envelopes only. Never holds the session key. Its log records sender role, envelope type, and byte length — never body content.                                                                                                                               |
| 5    | Push gateway                                                                | **Untrusted for payload**                           | Holds the operator's Apple push signing key and forwards sealed payloads. Cannot decrypt the body; only the paired iPhone can.                                                                                                                                                              |
| 6    | Continuum Cloud backend                                                     | **Trusted for account state; holds sealed content** | Owns identity, device enrollment, billing, and org policy, and stores your mirrored transcripts sealed at rest. Tenant tables enforce and *force* Postgres row-level security. This tier is only reachable at all once you sign in — see [Privacy](/docs/privacy).                               |
| 7    | Hosted inference gateway                                                    | **Trusted with prompts, by definition**             | Only in the path when you run a Continuum-hosted model. Upstream provider keys live server-side and never reach a client. See [Gateway enforcement](#gateway-enforcement).                                                                                                                  |
| 8    | Provider CLIs                                                               | **Sandboxed children**                              | Each CLI runs as a child process, owns its own auth state, and handles its own network egress. Continuum does not proxy or inspect their traffic. Per-account env scrubbing isolates one subscription's credentials from another — see [Multi-account isolation](#multi-account-isolation). |
| 9    | Release channels                                                            | **Authenticated**                                   | Mac via Sparkle EdDSA signatures, Windows and Linux via `electron-updater`, the CLI via a SHA-256 manifest with optional signature enforcement. See [Release channels](#release-channels).                                                                                                  |

## Cryptographic primitives

These cover the relay and push path that lets a paired device reach your host when the two are not on the same network.

* **Key exchange.** X25519 ECDH between host and client (CryptoKit `Curve25519.KeyAgreement`). Fresh ephemeral keypairs per pairing; the relay sees only the 32-byte public keys, never the shared secret.
* **Key derivation.** HKDF-SHA256 with `salt = sessionId` and separate `info` strings for the relay channel and the push payload channel. 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 from the platform CSPRNG. The 192-bit nonce never collides in practice, so there is no birthday-bound message ceiling. The envelope header (version, sender role, type) is bound into the AEAD associated data, so a captured host→client frame cannot be reflected back as client→host — a flipped role changes the AAD and verification fails.
* **Replay protection.** Per-direction sequence numbers carried inside the encrypted plaintext, validated against a per-direction window that persists across reconnects. The protocol version byte is bound into the HKDF `info` string, so a downgrade derives a different key and AEAD fails closed.

## Per-peer bearer auth

Every daemon endpoint — HTTP and WebSocket, loopback included — requires `Authorization: Bearer <token>`. The daemon accepts one of two tokens:

* A **pairing token** presented by paired clients.
* A **per-launch loopback token** generated fresh on each app launch for the host's own in-process client. It is never persisted and never crosses the wire.

Token comparison is constant-time. For relay frames, only SHA-256 hashes of the bearer tokens are stored and compared. Cross-side reuse — presenting a client's token on the host side, or replaying a token from a different session — is rejected before any frame is forwarded, and the role check repeats on every envelope: if the declared sender does not match the role assigned at connect time, the socket closes. A fail-closed lockout throttles bearer brute-forcing ahead of the identity check.

Because remote frames are replayed into the daemon as loopback requests, every forwarded request carries an explicit **relay origin marker**. Trust-granting operations require an origin that is both loopback *and* not relay-forwarded, so arriving over the relay can never be mistaken for sitting at the machine.

### Token and code lifetimes

There is no single pairing TTL — the artifacts have deliberately different lifetimes, and the short one is the one you handle by hand.

| Artifact                                           | Lifetime                                                                                         |
| -------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| Device enrollment code (the `XXXX-XXXX` one-liner) | **10 minutes**, single use                                                                       |
| Email sign-in device code                          | **10 minutes**                                                                                   |
| Relay rendezvous grant (per-device mesh channel)   | 30 days                                                                                          |
| Per-connect creation proof                         | \~5 minutes                                                                                      |
| Account / upload session token                     | 30 days **sliding on inactivity**, with a 10-minute grace for the previous token during rotation |
| Legacy relay pairing bundle                        | 30 days                                                                                          |

The Mac shows the enrollment code's countdown inline: "Code expires in 10 min."

<Note>
  QR-code pairing has been removed from the shipping surfaces. Devices join through email sign-in, and hosts are added with a one-line enroll command or through the **Advanced** options (Tailscale, SSH, cloud). See [Pairing](/docs/pairing).
</Note>

## Network peer filter

The daemon listener accepts connections only from loopback (`127.0.0.0/8`, `::1`) and the Tailscale CGNAT range (`100.64.0.0/10` and the `fd7a:115c:a1e0::/48` IPv6 prefix). Any other peer is dropped on accept, before auth runs. Bearer auth is still required afterward, so the peer filter is defense-in-depth, not the sole gate.

The daemon does not terminate TLS itself; transport confidentiality on the remote path comes from the sealed relay envelopes and, on a tailnet, from WireGuard.

## Tailnet identity verification

Non-loopback connections from a Tailscale network are additionally verified via Tailscale Whois (60-second cache). This is fail-closed: a peer that cannot be resolved to a tailnet identity is rejected with a `401`. It applies uniformly to the local daemon and to remote execution hosts.

<a id="multi-account-isolation" />

## Multi-account isolation

Continuum supports multiple accounts per provider on one machine (Settings → Providers → **Add account…**). Isolation is enforced at spawn time:

* **Config-dir isolation, not a HOME swap.** Each secondary account gets a deterministic config root, and at spawn the daemon points that provider's own config variable at it — `CLAUDE_CONFIG_DIR` for Claude, `CODEX_HOME` for Codex, `XDG_DATA_HOME` for the OpenCode-family providers, `GROK_HOME` for Grok, and a scoped `HOME` for Antigravity's `agy`, which honors nothing narrower. `HOME` is deliberately **not** overridden in the general case: a full HOME swap broke `git`, `ssh`, `gh`, and `node` resolution inside worktrees. Cursor authenticates with an API key and has no config root to isolate.
* **Env scrubbing on child spawn.** Before launching a per-instance child, the daemon strips every inherited `CLAUDE_*`, `ANTHROPIC_*`, `CODEX_*`, `OPENAI_*`, `GEMINI_*`, `GOOGLE_APPLICATION_CREDENTIALS`, `OPENCODE_*`, `OPENROUTER_*`, and `CURSOR_*` variable, so a stray key in the parent environment cannot leak into another account's spawn. Per-instance credentials are injected **after** the scrub. Non-Anthropic agents always get the Claude credential scrub even on the primary account, so a subscription token can never leak into a third-party agent.
* **Per-instance keychain partitioning.** Each non-primary instance reads its token from a distinct keychain partition — a per-instance access group *plus* a per-instance service-name suffix. Because the access group is part of the lookup key, a query scoped to account A cannot read account B's entry, and the double bind means even a misconfigured entitlement can't cross partitions.
* **Fail-closed spawn resolution.** A session's pinned account is resolved before every spawn. A pin to an account the registry no longer carries, a non-isolatable instance, or an account with no stored token all **refuse to spawn** with a re-authenticate error — never a silent fallback to your default subscription. The defended failure mode is wrong-account billing.

Account names are validated before any path is built (no `/`, `\`, NUL, whitespace, or leading `.`), which rejects the `..` path escape that could otherwise let account removal delete a sibling account's data.

<a id="execution-host-trust" />

## Execution hosts

Another Mac, a Linux box, a VPS, your own cloud account, or a Continuum-managed runner can run agent sessions on behalf of a hub.

* **How the host is provisioned.** On Linux, a bundled Go `continuum-agent` plus a service unit; the agent generates its own bearer token, which the hub captures. On macOS, Continuum itself runs in headless-agent mode under a per-user launch agent with a provisioned token. Token files are staged into a `0700` directory and written `0600`, so no secret lands in a world-readable file.
* **What the hub trusts about the host.** The hub reaches the remote daemon with a provisioned bearer. The remote daemon applies the **same** gates as the local one: peer filter, per-peer bearer, and fail-closed tailnet identity on every non-loopback request. A remote host trusts the hub only insofar as it presents a valid bearer from a verified peer; it is not an open relay.
* **Credentials on the host.** When the hub needs a provider subscription there, it pushes that credential explicitly and refreshes it as it rotates. Hosts are compute, not credential custodians — they hold only what the hub deliberately pushes.
* **Repo-controlled hooks do not run.** Worktrees the Mac provisions are created with `core.hooksPath` pointed at `/dev/null`, so cloning or checking out a repository cannot execute a hook script that repository controls.
* **GitHub access for managed cloud runs is short-lived and single-repo.** Access is re-checked immediately before create or wake — a suspended installation, a revoked grant, an archived repo, or missing push permission fails closed. The token is scoped to one repository, refreshed before expiry, and delivered through a credential helper over an owner-only socket, so it never lands in git config, a command line, or shell history.

Remote interactive sessions are gated by wire version; a hub paired to an older remote daemon degrades visibly rather than failing silently.

## Content sealing and erasure

Once you sign in, transcript pages mirrored to the cloud are sealed at rest with **AES-256-GCM under a per-member key**. Removing a member destroys that key, and every page encrypted under it becomes permanently unrecoverable — erasure that holds across replicas and backups without hunting down individual rows.

Tenant tables have Postgres row-level security **enabled and forced**, with policies keyed on the requesting organization. Forced RLS means the policy applies even to the table owner, so an application bug that forgets a predicate still cannot read across tenants.

<Warning>
  This seal is encryption **at rest**, not end-to-end: Continuum holds the master key and unwraps it to render your own history back to you. [Privacy](/docs/privacy#prompt-inspection) states the consequence plainly, along with the append-only audit trail that constrains it. If you never sign in, no transcript is mirrored and none of this applies.
</Warning>

<a id="gateway-enforcement" />

## Hosted inference: gateway-side enforcement

Organization policy for [hosted inference](/docs/features/hosted-inference) is enforced by the gateway, not by the client UI. One code path serves every hosted entry point, and it checks, in order: the global model allowlist, model pricing, subscription entitlement, the **organization's model allowlist for that member or team**, and an atomic budget reservation.

A blocked model returns `403` with the reason — *"…is not permitted for your team by your organization admin."* An exhausted allocation returns `429`, distinguishing a free-tier allowance, an organization-set member cap, and a spent weekly budget. Because the checks live behind the API rather than in front of it, an older or modified client cannot route around them.

Upstream provider credentials are server-side only, sealed with AES-256-GCM under a key held outside the database and bound to their purpose by the AEAD associated data. Clients never receive more than a four-character key hint.

## Share links

Publishing a transcript to `continuumcode.ai/share/<id>` runs the redactor **server-side, before storage** — no client, and not the host proxy, can widen what a link exposes.

The redactor is an **allowlist walker**: each message is rebuilt from a known key set and anything unrecognized is dropped, so a transcript field added tomorrow cannot leak by default. Raw tool-input JSON — whole file contents, full shell commands, credentials passed as flags — is never stored at all, because the share page does not render it. On top of that, known secret shapes are masked: private key blocks, Anthropic, OpenAI, GitHub, Slack, AWS, and Google keys, JWTs, database connection strings, `KEY=value` dotenv lines, and `api_key:`-style assignments, plus home directory paths, email addresses, and private IP ranges. Terminal control characters are stripped so captured output cannot smuggle escape sequences into the page.

Links can carry an expiry, can be revoked, and record a view count. Unlisted-ness rests on the id's entropy, not on obscurity of the route.

## Environment variables

Environment values never leave the machine, and the wire type enforces it: the record Continuum publishes for an environment variable has **no value field at all** — only the key name, a fingerprint, whether a value exists, and its rotation state. Reveal is a local action against your own keychain, with no endpoint that returns a value. Agents are briefed with the **key names only**.

Keys that could hijack a process — loader, hook, and routing variables such as `LD_PRELOAD`, `DYLD_INSERT_LIBRARIES`, `NODE_OPTIONS`, `PATH`, `BASH_ENV`, `GIT_SSH_COMMAND`, `GIT_ASKPASS`, and the proxy variables — are refused on import, **reported rather than silently dropped**, and stripped again at spawn, swap, and revive time so a legacy stored key cannot reactivate.

## Audit logs

The daemon writes append-only JSONL audit entries under `~/.clawdmeter/audit/`. The directory is created `0700` and re-tightened on every launch, so peer IPs and repo paths stay private on a multi-user machine; files are created `0600`.

Current streams: `sends.jsonl`, `swaps.jsonl`, `autopilot.jsonl`, `auto-router.jsonl`, `mobile-commands.jsonl`, `cowork.jsonl`, `creds.jsonl`, and `sidecar-ask.jsonl`. Entries record metadata — timestamps, session ids, operation type, hashed prompt fingerprints, byte counts — not content. Files rotate at 1 MB or 7 days, whichever comes first.

Prompt text is **hashed by default**, and there is no UI that turns that off. Recording plaintext is a deliberate defaults-key opt-in (`clawdmeter.audit.includePlaintext`, default false) intended for debugging your own machine; the same key gates whether the wire inspector retains request and response bodies.

The push gateway's log records attempt outcomes, hashed device tokens, and byte sizes — never the notification body. The relay's records sender role, envelope type, and byte count — never body bytes. Retention there is 90 days by default.

## Kill-switch and rate limits

The push gateway has an `APNS_DISABLED` kill-switch that short-circuits the request handler before schema validation, auth, or rate-limit checks — toggling it stops all push delivery within about 30 seconds. A per-sender hourly cap (default 60 pushes per hour, bound to the verified pairing identity rather than the supplied device token) limits damage from a compromised signing key.

The daemon enforces 1 send per second and 1 swap per 5 seconds per session; model, effort, and mode swaps share the swap budget. Exceeding either returns a `429`.

## ACP file-system trust boundary

When Continuum drives Codex, Cursor, Grok, or Gemini over the ACP harness, the agent can request file reads and writes. Every request is validated through a repo trust gate: the path is canonicalized, symlinks resolved and `..` collapsed, and the canonical result must sit at or under the repo root — defeating traversal, absolute-path escape, and symlink escape. The gate is TOCTOU-aware: it returns the resolved canonical path and the caller opens **that** exact path, never re-resolving the attacker-supplied string. Terminal and read output returned to the agent is capped as an anti-DoS measure.

<a id="release-channels" />

## Release channels

| Platform                | Chain                                                                                                                                                                                                                                                                                                                                                                                                      |
| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| macOS                   | Sparkle 2.9.2 against the appcast at `continuumcodeapp.github.io/continuum-updates/updates/appcast.xml`. The release pipeline gates on Developer ID signing verified against the project team, Apple notarization and stapling, and an EdDSA signature whose public key must match the one embedded in the app. If Sparkle validation or installation fails, the UI falls back to the GitHub release page. |
| Windows / Linux desktop | `electron-updater` against the `continuum-updates` releases.                                                                                                                                                                                                                                                                                                                                               |
| `continuum` CLI         | `continuum update` verifies a SHA-256 manifest; signature checking can be enforced with `--require-signature` and a minisign public key.                                                                                                                                                                                                                                                                   |

Two release gates worth naming, because they fail the build rather than shipping something weaker:

* **A tagged Windows release cannot publish unsigned.** The pipeline refuses to proceed without a signing certificate, and after packaging it runs Authenticode verification on every executable, failing the release if any file does not verify. (Untagged development artifacts are explicitly built unsigned and say so.)
* **Dependencies cannot resolve without a lockfile.** A missing lockfile aborts the build rather than silently resolving fresh versions.

The CLI archives are published unsigned by design — there is no cross-platform certificate — and say so in their manifest, backed by GitHub build provenance.

<Note>
  Unsigned Windows binaries can still be blocked by Smart App Control or WDAC, and a newly signed binary needs SmartScreen reputation to accumulate. If Windows warns on first launch, see [Troubleshooting](/docs/reference/troubleshooting).
</Note>

## Threat model reference

The full 14-scenario threat model — including relay operator curiosity, leaked pairing material, MITM, replay, nonce reuse, lost device, forged push, DoS, operator account compromise, client WebSocket suspension, enterprise proxy MITM, timing side-channels, and protocol or transport downgrade — ships inside the app's in-product documentation.

The operator infrastructure compromise scenario is the irreducible trust root and is explicitly accepted. Mitigations are account-hygiene only; there is no in-product defense against a rogue relay or push operator. This is why those paths carry no plaintext — the operator sees only opaque envelopes either way. The cloud backend is a different case: it holds sealed content and the key to unseal it, which is why that tier is disclosed separately in [Privacy](/docs/privacy).

## Reporting a vulnerability

If you find a security issue, email the maintainers directly rather than filing it in public. Continuum is published by Montauk Analytics Inc; reach the team via [continuumcode.ai](https://continuumcode.ai).
