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.