Skip to content

Threat Model

E2EE shifts most of the trust to the client. The server holds no keys; clients write the canonical state. That makes the question “what damage can a client cause?” load-bearing for the design — a single buggy implementation, a hostile keyholder inside an album, a stranded old build, or a too-new prototype all have to fail safely.

A faulty, malicious, or version-mismatched client must not be able to cause irreparable damage (loss of original bytes, loss of audit trail, undetected silent overwrite of user intent) and should not be able to cause more than transient damage (a quarantined asset surfaces to the user; a rejected write returns a clear error; a divergence is detected and reconciled). The recovery paths in Cryptography — Failure Modes cover key loss; this doc covers the write-path harm a wrong-but-signed client can attempt.

The threat model is not a primitives doc. Every primitive Capsule uses is declared in its owner doc; this doc references those declarations rather than re-stating them. Where a specific invariant lives, the relevant owner doc enforces it; where a defense spans multiple docs, the canonical statement lives in one of the sub-docs below.

The cross-cutting invariants here are enforced by code that lives across many crates: capsule-core::crypto::verify_asset (client-side validation chokepoint), capsule-api (server-side envelope checks at every write path), and the validation sub-doc’s invariants directly map to acceptance tests in the corresponding API crates.

Sub-docConcern
ScenariosDamage scenario → invariant map, the quarantine surface inventory, provenance immutability rules
ValidationServer- + client-side refuse-by-default checklists; protocol handshake; idempotency; atomicity
Schema RulesSchema evolution rules, forbidden client behaviors, deprecation policy, open questions

Every client request can be classified by one of these models. The defenses listed below apply to all of them — none of them are trusted to enforce their own correctness:

ClassDescriptionWhat authenticates themWhat stops them
HonestConforming implementation, correct keys, correct version.Session token + access token + device DSK + epoch write-tier signature.Nothing to stop. This is the baseline.
FaultyConforming intent, buggy implementation. Writes structurally invalid or semantically wrong manifests under real keys.Same as honest — the keys are correct.Server-side structural validation + client-side verify_asset chokepoint + quarantine surfaces.
MaliciousAdversary in possession of a current device’s DSK and the album’s epoch write-tier key. Writes deliberately malformed or destructive operations.Same as honest — the keys are real, because the adversary owns them.Provenance chain immutability + soft-delete window + per-album/per-event compartmentalization + audit trail for after-the-fact attribution.
OldA signed-in client that predates a feature, schema, or suite the server now considers minimum. Cannot produce structurally valid writes for albums pinned above its version.Authenticated, but X-Capsule-Protocol is below the server’s accepted range.Protocol handshake rejects writes with 426 Upgrade Required before any state is written.
NewA prototype or staging build that writes a protocol_version/crypto_suite_id/sidecar_schema ahead of what the receiver knows.Authenticated, but the version is higher than the receiver’s max known.Receiver’s refuse-by-default rule on unknown enum values, unknown schemas, and forward-jumping protocol versions; closed schema evolution boundary (see Schema Rules).

The deliberate choice in the matrix above: a malicious client with real keys is the hardest to stop, because confidentiality and authentication don’t help when the adversary already holds the keys. Capsule’s response is to ensure such an adversary can do nothing silently — every write produces a signed provenance record, soft-delete is the default, and history is append-only. The audit trail is the recovery surface.

Restating the boundary hierarchy from Core Principles as concentric containment shells, with the owner doc that enforces each:

ShellBoundaryOwner doc
Per-versionAlbum protocol pinning isolates a buggy v_k from v_{k-1} albums.Versioning
Per-albumMLS group + per-epoch AMK + per-epoch write-tier key.Cryptography — MLS + Cryptography — Keys
Per-event (manifest)Each lifecycle action is its own signed, chained record.Cryptography — Provenance
Per-userOwner Group Key, sponsored-account isolation.Cryptography — Keys
Per-peer (federation)Capability tokens, error budgets, quarantine for new peers.Federation
Per-device (peering)Device directory enforced via the TLS handshake.Peering — Establishing the Channel

A bug or compromise on one side of any shell cannot cross it.

Each owner doc gains a short section linking back to the relevant threat-model invariant. The mapping (for navigation):

Owner docThreat-model section(s) it ties into
Principles§ Damage Containment Layers
VersioningProtocol Negotiation, Atomicity
FilesystemServer Validation, Atomicity, Quarantine Surfaces
CryptographyProvenance Immutability, Scenario Map (signature/chain rows)
MetadataSchema Evolution, Scenario Map (CRDT rows)
Import & SynchronizationServer Validation, Idempotency
FederationServer Validation, Quarantine Surfaces
PeeringClient Validation, Scenario Map (peer rows)
AuthenticationForbidden Behaviors, Scenario Map (revoke-all row)
AuthorizationServer Validation
Backup & RecoveryQuarantine Surfaces
ThumbnailsScenario Map (derivative row)
AI/ML IntegrationsForbidden Behaviors (AI tag namespace); Scenario Map (embedding model row)
OrganizationAtomicity, Forbidden Behaviors
ClientsClient Validation, Deprecation Policy