Docs · Security architecture

Security architecture

The engineer's version. For the plain-language summary — what's encrypted, what's scoped, what we can't read — start with the security page.

Identity is cryptographically derived, not assigned#

Every database identity is Identity::from_claims(iss, sub) — a hash over the immutable issuer string and your subject claim. The auth gateway validates your OIDC token against the identity provider's JWKS (ES256, offline signature check), then mints one claim-bearing connection token per session. Modules verify at registration that the connecting identity equals the derivation — there is no anonymous write path and no server-side session table to compromise into impersonation.

Row-level security, enforced host-side#

Every public table across all four modules carries a visibility filter (44 rules) evaluated by the database host itself: a subscription's result set is intersected with owner = :sender-style predicates before any row is serialized. A hostile client crafting subscriptions gets its own rows and nothing else. Writes are separately owner-guarded inside every reducer — creates verify the parent's owner, moves verify both endpoints.

Sync is a per-field CRDT#

Every mutable field carries a hybrid logical clock (u64). Updates apply last-writer-wins per field, with the HLC comparison as the deterministic tiebreak — so concurrent edits from two devices merge without coordination, and offline replay is naturally idempotent (a stale replayed write loses the clock comparison and becomes a no-op). No operational-transform server, no merge dialogs, no idempotency-key bookkeeping. Deletes are tombstones (deleted + deleted_hlc), never row removal.

The vault's envelope, precisely#

Every object is sealed as PDR1 ‖ kek_id ‖ wrapped-DEK ‖ nonce ‖ ciphertext: a fresh XChaCha20 data-encryption key per object, wrapped by a named master key. Key rotation is a KEK retire path, not a re-encrypt crisis. A boot canary per KEK refuses to serve if the key material doesn't verify. Reads verify content hashes (SHA-256 leaves, BLAKE3 chunks) before bytes are served — corruption is detected, not delivered. Derived objects (thumbnails) pass through the identical seal; images are stripped of EXIF/XMP on ingest; decode-bomb limits bound processing.

Sync to disk: the desktop engine#

The desktop app (Pandora) pairs with a device-scoped credential minted through a TV-style code flow — the app never sees your password, and the credential is revocable from any browser. Sync is content-addressed with FastCDC delta uploads (an edited gigabyte re-uploads kilobytes), and concurrent edits fork server-side into visible name.conflict-device siblings, so every device converges on the same tree by construction — nothing is silently overwritten.

Offline-first, concretely#

Clients hold their working set in RAM from push-maintained subscriptions and mirror it to IndexedDB. Mutations enqueue locally and replay in insertion order on reconnect; the HLC guard makes the replay idempotent. Search and ranking run entirely client-side over those caches — which is also why field-level E2EE is retrofittable: the server never needs plaintext to make the product work, and the conflict-resolution layer never inspects field contents.

Threat model, summarized#

Designed to protect against:

  • Server storage compromise — vault objects are envelope-encrypted; an attacker with the object store sees sealed blobs. Reads are verified against content hashes, so swapped or relocated objects are refused rather than served.
  • Hostile or buggy clients — row-level security is evaluated by the host, not trusted to the client; writes are owner-guarded in every reducer.
  • Token theft from disk — session tokens are held in memory only; there is nothing in localStorage or cookies to exfiltrate.
  • Location leakage in photos — EXIF/XMP metadata is stripped losslessly on every web ingest.

Not protected against (stated, not hidden):

  • A compromised sync server reading structured data — boards, goals and canvas text are scoped, not end-to-end encrypted. The honest blocker is key distribution across devices and the recovery model (user-held recovery code versus custodial escrow), plus how consented sharing interacts with encrypted fields. We'd rather ship it designed than bolt it on; the architecture above was chosen so it lands as an additive migration, not a rewrite.
  • A malicious server operator with the vault master key — vault encryption today is server-side envelopes, not zero-knowledge. A ZK vault tier is designed and on the roadmap.

Something here unclear, or a claim you'd like to challenge? That's the right instinct — write to hello@getquest.gg.