Preview

Zero-Knowledge Architecture

A technical deep-dive into Sovernote’s end-to-end encryption, key derivation, zero-knowledge authentication, and privacy-preserving real-time collaboration.

Version 1.7  ·  April 2026  ·  Subject to change during open beta

1. Executive Summary

Sovernote is a local-first workspace. The core guarantee is that no plaintext content or private key material ever leaves the user’s device. The cloud sync and collaboration server is architecturally incapable of reading user data — not as a policy promise, but as a cryptographic fact.

This document describes precisely how that guarantee is implemented: how keys are derived, how authentication works without a password, how documents are encrypted, and how real-time collaboration operates on opaque ciphertext.

The intended audience is security researchers, enterprise customers evaluating the product, and technically curious users who want to verify the privacy claims rather than take them on faith.

What kind of privacy?

Sovernote’s privacy guarantee is best described as anti-exploitation privacy, not military-grade operational security. The specific threat it is designed to eliminate is the company as adversary: Sovernote cannot profile users, monetise their behaviour, feed documents into advertising or AI training pipelines, or sell data to third parties — not as a policy promise, but because the server only ever holds ciphertext it cannot decrypt.

Concretely, the architecture protects against:

  • Behavioural profiling and targeted advertising
  • Monetisation of document content or usage patterns
  • Surveillance capitalism (selling behaviour data to brokers)
  • Feeding user content into AI training pipelines without consent
  • Server-side data breaches exposing readable content

This is not a system designed for users facing nation-state adversaries or physical-device seizure. Local data is intentionally stored as plain SQLite (see §3) so that users can access their own data with any tool — that openness is itself a data-ownership guarantee. Users with high-threat operational security requirements should layer OS full-disk encryption and their own physical security practices on top.

2. Threat Model

This section defines, precisely, what the system protects against, what capabilities each adversary class is assumed to have, what is explicitly out of scope, and what metadata is unavoidably visible to each party even when the cryptographic guarantees hold.

2.1 Adversary classes and assumed capabilities

AdversaryAssumed capabilitiesWhat they can observeWhat the system guarantees
A1 — Passive network adversary Full read access to all network traffic between client and server (e.g. ISP, VPN provider, BGP hijacker). Cannot modify traffic in transit. TLS record sizes and timing; connection endpoints; number of requests; WebSocket frame sizes and timing patterns. No plaintext content, no private keys, no password material in any observable packet. TLS 1.3 encrypts even record metadata by default.
A2 — Active network adversary (MitM) Can intercept and modify traffic; may attempt TLS stripping or certificate substitution. As A1, plus ability to replay or inject requests if TLS is successfully stripped. Certificate pinning in the Tauri desktop app. HSTS with long max-age on all web origins prevents downgrade. Challenge nonces are single-use; replayed login requests are rejected server-side.
A3 — Compromised sync server Full read/write access to the server database, file system, and process memory. Can modify response payloads. Cannot modify the client binary already installed on user devices. Encrypted document blobs (yjs_updates); encrypted metadata (properties JSON); Ed25519 and X25519 public keys; usernames; timestamps; workspace membership lists; document graph structure (parent/child relationships, document count, update frequency). No plaintext document content. No private keys. No password or password hash. Decryption is computationally infeasible without the user’s private key, which the server never receives.
A4 — Malicious Sovernote employee Direct database access, ability to deploy server-side code changes, access to server logs and backups. Same as A3. Additionally: IP addresses and connection metadata in server access logs (retained for a limited period per the Privacy Policy). Same as A3. The cryptographic guarantee does not depend on employee trustworthiness — it is enforced by the architecture.
A5 — Shared-machine attacker (web) Physical access to the same machine after the user’s browser session ends. Can read localStorage, sessionStorage, browser history, cached network responses, browser memory dumps. Any data the browser has cached or persisted. Private keys and derived seed are held only in JS heap memory, in a plain atom that is never written to any storage. On tab close the garbage collector reclaims it. sessionStorage (session JWT) is cleared by the browser on tab close. No document plaintext is cached locally by the web app.
A6 — Legal / compelled disclosure (server) Court order or equivalent compelling Sovernote to produce user data. Same as A3 — only ciphertext, public keys, and metadata. Sovernote can produce only what it holds: ciphertext that is unreadable without the user’s private key. There is no key escrow, no recovery mechanism, and no way for Sovernote to comply with a content-disclosure order beyond producing opaque blobs.

2.2 Metadata leakage analysis

Even when document content is perfectly encrypted, structural and behavioural metadata is unavoidably visible to some parties. The following is a precise inventory of what leaks and to whom.

Metadata itemVisible toMitigation / acceptance rationale
Document count per workspace A3, A4, A6 Unavoidable to support sync routing. Not mitigated; accepted as a necessary structural leak.
Document tree structure (parent/child hierarchy, nesting depth) A3, A4, A6 Parent IDs are stored as plaintext foreign keys for query routing. Partially mitigated in a future version by encrypting the parent field; accepted for now.
Update timestamp and frequency per document A3, A4, A6 Stored as updated_at. Reveals which documents are actively edited and when. Not mitigated; accepted.
Encrypted content size (ciphertext byte length) A3, A4, A6 Ciphertext length is approximately equal to plaintext length (+28 bytes for nonce + GCM tag per update). Can reveal approximate document size or edit volume. Not padded in the current protocol version; may be addressed in a future version.
Workspace membership (who collaborates with whom) A3, A4, A6 Required for routing and access-control enforcement. Not mitigated; accepted.
Username (normalised) A3, A4, A6 Stored as the login identifier. Users who prefer pseudonymity are encouraged to register with a handle rather than their real name or email.
IP address of client connections A4, A6 (via server logs) Standard server access logs. Retained for a limited period. Use of a VPN shifts this leak to the VPN provider rather than Sovernote.
Request timing and size patterns (traffic analysis) A1, A2 A sufficiently resourced passive observer can infer editing activity patterns from WebSocket frame timing even without reading content. This adversary is out of scope; no padding or timing obfuscation is currently implemented.

2.3 Side-channel risks

Side-channelRiskMitigation
Argon2id timing on slow hardware An attacker observing login latency could distinguish Argon2id completion from network round-trip time, confirming a valid username exists. Not a key-recovery risk. Server returns HTTP 401 for both invalid username and invalid signature after a fixed minimum delay, preventing username enumeration via timing.
Ed25519 private key in JS heap A speculative execution exploit (Spectre-class) running in a co-located browser tab could attempt to read cross-tab memory containing the private key. Modern browsers enable Site Isolation and COOP/COEP headers by default, preventing cross-origin memory access. Sovernote’s web app sets Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Embedder-Policy: require-corp.
AES-GCM nonce bias from weak PRNG If crypto.getRandomValues() produces biased output, nonce collision probability increases above the Birthday-bound estimate in §6.3. All major browser and OS PRNG implementations (ChaCha20-based in Chrome/Firefox/Safari, CryptGenRandom/BCryptGenRandom on Windows, /dev/urandom on Linux/macOS) are cryptographically strong. The risk is negligible on supported platforms.
Keystroke / clipboard inference A malicious browser extension or screen-capture malware could observe the user’s password as it is typed, before Argon2id derivation. Out of scope per §2.4. OS-level malware on the user’s device is outside the threat model of any software-only solution.
Cold-boot attack on in-memory key An attacker with physical access to a running device could attempt to dump RAM and recover the in-memory derived seed or private key. Partially mitigated by OS memory encryption (Apple Silicon Secure Enclave, AMD SME/SEV). Full mitigation requires locking the device when unattended; software cannot prevent a physical RAM dump of a running machine.

2.4 Explicitly out of scope

ScenarioRationale
Full device compromise (OS-level malware, physical access to unlocked device)No software-only defence is effective once the OS is controlled by the adversary. Full-disk encryption (FileVault / BitLocker / LUKS) is the recommended mitigation for physical access.
Legal compulsion directed at the userA user can be compelled to disclose their own password. This system cannot protect against self-disclosure.
Denial-of-service against the sync serverAvailability attacks do not affect the confidentiality or integrity of encrypted data. Infrastructure-level rate limiting and DDoS mitigations are in place but are outside this document’s scope.
Supply-chain attack on the client binary distributionA compromised build pipeline or package registry could ship a modified client. Mitigated by reproducible builds (planned) and code-signing of all distributed binaries (current). A self-hosted server with a client compiled from published source is the strongest mitigation.
State-level traffic analysisA nation-state adversary with access to backbone traffic can perform large-scale timing correlation. Nemo’s anonymisation does not protect against this adversary class. Tor or equivalent is required for that threat model.

2.5 Security properties summary

PropertyHolds?Notes
Confidentiality of document content vs. serverYesAES-256-GCM; server never receives plaintext or keys
Confidentiality of document content vs. networkYesTLS 1.3 in transit; encrypted at rest on server
Integrity of document contentYesGCM authentication tag detects any modification
Authentication without password transmissionYesEd25519 challenge-response; see §5
Forward secrecy for key envelopesYesEphemeral X25519 DH per ECIES envelope
No server-side key escrowYesServer stores only public keys
Post-quantum confidentialityPartialAES-256 is Grover-safe; EC primitives are not Shor-safe (see §15)
Metadata confidentiality (document structure, size, timing)NoUnavoidable structural leakage; documented in §2.2
Unobservability / traffic analysis resistanceNoOut of scope; see §2.4

2.6 Design goals

  1. The server stores no private keys, no plaintext content, and no password hashes.
  2. The same credentials always derive the same keypair deterministically, on every device, with no network roundtrip.
  3. Losing a device does not mean losing data; losing a password means losing cloud access but not the local copy.
  4. Real-time multi-user collaboration is possible without the server ever seeing decrypted content.
  5. The web app remains safe to use on shared or untrusted machines.
  6. Metadata leakage is documented precisely rather than minimised or obscured.

3. Architecture Overview

Sovernote operates in three modes, each with a different trust perimeter:

ModeBackendData locationEncryption at rest
Local (offline) Rust / SQLite inside Tauri binary User’s device only Intentionally no application-layer encryption (see §3 note). OS full-disk encryption recommended.
Cloud sync Express + Hocuspocus + SQLite on instance node Device + encrypted cloud replica AES-256-GCM, client-side; server stores ciphertext only
Web app Same cloud node; key in memory only Server (ciphertext); no local cache AES-256-GCM, in-session only; key zeroed on tab close

In local mode there is no network activity at all. This document focuses primarily on the cloud sync and web app modes, where the cryptographic architecture is most relevant.

Note on local data ownership: Local SQLite data is stored without application-layer encryption by deliberate design choice. Encrypting local data would require the application to hold a decryption key, which necessarily means that key can also be lost, forgotten, or held hostage by a software update. Keeping local data in plain SQLite preserves unconditional data ownership: the user can open, read, export, or migrate their database with any standard SQLite tool, independent of Sovernote software. The threat model for local data is physical device access, which is best addressed by OS full-disk encryption (FileVault / BitLocker / LUKS) — not by an additional application-layer key that introduces its own recovery problem.

4. Identity & Key Derivation

4.1 The username as a permanent cryptographic identity

At registration the user chooses an immutable username that serves as both their login identifier and the salt for key derivation. It can be an email address or a pseudonymous handle; either choice works.

The critical invariant: the username can never change. Changing it would change the Argon2id salt, producing a different keypair, permanently locking out all previously encrypted data. The registration UI makes this explicit.

A separate, mutable email field is collected for billing and notifications. It does not affect the keypair.

FieldMutablePurpose
usernameNoArgon2id salt; login credential; collaborator lookup handle
emailYesContact address for billing and notifications

4.2 Deriving a deterministic user ID

A stable, server-safe identifier is derived from the username without revealing the raw username to the server in an easily reversible form:

user_id = UUID( SHA-256( lowercase( trim( username ) ) ) )

This user_id is used as the Argon2id salt and as the primary key in the server’s user registry. The normalisation step (lowercase + trim) ensures that Alice and alice always produce the same identity.

4.3 Argon2id key derivation

The user’s password is combined with the user_id salt and run through Argon2id on the client. Argon2id is the Password Hashing Competition winner and the current OWASP recommendation for password-based key derivation; its memory-hard parameters make brute-force attacks economically infeasible even with a full database dump.

seed = Argon2id(
  password    : user_supplied_password,
  salt        : user_id,              // derived above — never fetched from server
  memory      : 64 MiB,
  iterations  : 3,
  parallelism : 1,
  output_len  : 32 bytes
)

The 32-byte seed is never stored anywhere. It exists only in memory, for the duration of the derivation step, and is immediately used to generate the two keypairs described below.

4.4 Ed25519 keypair (authentication)

An Ed25519 signing keypair is derived deterministically from the seed. The public key is registered with the server at sign-up. The private key never leaves the device.

The ECDSA nonce-reuse catastrophe — and why Ed25519 is immune

The most widely deployed EC signing standard is ECDSA. ECDSA requires a random nonce k per signature. If k is ever reused across two different messages signed with the same private key, the private key is immediately and trivially recoverable via linear algebra — no brute force required. This is not a theoretical concern:

  • Sony PlayStation 3 (2010) — a constant k was inadvertently used for all firmware signatures. Two publicly signed messages were sufficient to extract the entire platform root key, enabling arbitrary code execution on every PS3 ever manufactured.
  • Bitcoin Android wallet (2013) — a defective PRNG produced repeated nonces across signing operations, allowing attackers to recover private keys and drain thousands of wallets from public blockchain data alone.

Ed25519 eliminates this attack class entirely. RFC 8032 §5.1.6 specifies that the per-signature nonce is derived deterministically from a hash of the private key and the message. There is no per-signature randomness. Nonce reuse is impossible by construction — even if the system PRNG is completely broken.

Why not RSA

RSA-2048 achieves 112 bits of classical security; Ed25519 achieves 128 bits with a 32-byte key and 64-byte signature (vs. 256-byte RSA-2048 key, 256-byte signature — 8× larger on the wire). RSA private-key operations require large-exponent modular exponentiation, which is significantly slower. Naive RSA-OAEP and RSA-PKCS1v1.5 implementations are vulnerable to Bleichenbacher-class padding oracle attacks; Ed25519 has no padding surface at all.

Additional Ed25519 properties: operations are constant-time by design (no timing side-channel); the twisted Edwards addition formula is complete (no exceptional points to mis-handle); cofactor 8 of Curve25519 is handled correctly and automatically by all standard libraries (@noble/curves, libsodium, and the Rust ed25519-dalek crate used in the Tauri backend).

4.5 X25519 keypair (key agreement)

An X25519 key agreement keypair is also derived from the same seed. The public key is stored on the server. The private key never leaves the device.

X25519 is used for document key exchange: when a workspace owner shares access with a collaborator, they encrypt the document’s symmetric key to the collaborator’s X25519 public key. Only the collaborator’s private key can decrypt it.

Why X25519, not ECDH over NIST P-256/P-384

ECDH over NIST curves requires that the implementation validates the peer’s public key is an actual point on the curve before computing the shared secret. If this check is omitted or incorrect, an attacker can supply a crafted point on a small-order subgroup and recover the private key after a small number of exchanges. This invalid-curve attack has been demonstrated against real-world TLS implementations (e.g., Java’s JSSE prior to 2017). The check is easy to forget and easy to get wrong.

X25519 uses a Montgomery-form parameterisation where every possible 255-bit value is a valid public key. The scalar multiplication function is defined to handle all inputs safely — there are no invalid points, no small-subgroup points to reject, and no validation step that can be forgotten. Key clamping (clearing bits 0–2 and 255, setting bit 254 of the scalar) is applied automatically by the algorithm, ensuring correct cofactor handling without any caller action.

X25519 and Ed25519 share the same underlying Curve25519, so the security of both primitives rests on the discrete-logarithm hardness of a single, extensively studied elliptic curve.

4.6 Full derivation diagram

username (immutable)
      │
      ▼  UUID( SHA-256( normalise(username) ) )
  user_id   ←── used as Argon2id salt
      │
password ──┤
      │
      ▼  Argon2id (client-side, memory-hard)
  32-byte seed
      │
      ├──▶  Ed25519 keypair   (signing)      pubkey → server registry
      └──▶  X25519  keypair   (key agreement) pubkey → server registry

Because the derivation is fully deterministic and client-side, the same password on any device always produces the same keypair. There is nothing to sync. There is no key backup on the server. If the device is lost, deriving the keypair from the credentials on a new device is sufficient.

5. Authentication Protocol

5.1 Challenge-response login

Login is performed via a signed challenge-response protocol. No password or password derivative is ever transmitted.

// Step 1: derive all credentials locally (no network)
user_id = UUID(SHA-256(normalise(username)))
seed    = Argon2id(password, salt: user_id)
keypair = { ed25519, x25519 } from seed

// Step 2: fetch a one-time nonce
nonce ← GET /api/auth/challenge?username=<username>
        ← { nonce: "<32 cryptographically random bytes, single-use, TTL: 30s>" }

// Step 3: sign the nonce and authenticate
token ← POST /api/auth/login {
  username,
  signature: Ed25519.sign(nonce, ed25519.privateKey)
}
// Server:
//   1. Verifies the Ed25519 signature against the stored public key
//   2. Marks the nonce as consumed (rejects any replay within the TTL window)
//   3. Issues an HttpOnly session cookie

The server stores no password and no password hash. The only secrets it holds are the Ed25519 and X25519 public keys, which are by definition not secret.

Attack scenarios and protocol responses

AttackWhat the attacker hasWhy it fails
Network eavesdropping Captured login request (username + signature over nonce) The nonce is single-use and server-invalidated on first use. The captured signature cannot be applied to any future nonce. No password is in the request to harvest.
Replay attack Captured login request replayed verbatim, within the TTL Server tracks consumed nonces and immediately rejects any second use of a nonce that has already been verified. HTTP 401 is returned.
Server database dump username, ed25519_public_key, x25519_public_key, encrypted blobs Public keys are not secret — by definition. Signing future challenges still requires the Ed25519 private key, which was never transmitted to or stored on the server. Encrypted blobs are unreadable without the X25519 private key, also never on the server.
Offline brute-force against stolen hash Attacker wants to guess the user’s password There is no password hash to steal — only a public key. An attacker wanting to brute-force the password must run a full Argon2id derivation (64 MiB, ∼1 s) per guess. There is no online oracle that accepts raw passwords.
Phishing for credentials Username and password captured from a fake login page Possessing credentials alone is insufficient. The attacker must also fetch a live challenge nonce from the real server (requiring an active session window) and sign it within 30 seconds — an active, time-critical, server-cooperating operation, not a passive replay.

Protocol security level

The protocol’s security reduces to the unforgeability of Ed25519 signatures. Ed25519 provides 128 bits of classical security (equivalent to an exhaustive AES-128 key search) and approximately 64 bits of security against a quantum adversary running Shor’s algorithm. The 64-bit post-quantum margin is a known limitation addressed in the protocol versioning plan.

5.2 Session token

On successful authentication the server issues a short-lived JWT delivered as an HttpOnly; SameSite=Strict; Secure cookie. JavaScript cannot read this cookie, eliminating the primary XSS token-theft vector. Cross-site requests are refused by the browser due to SameSite=Strict.

For WebSocket connections (Hocuspocus real-time sync), a separate 60-second token is fetched immediately before each connection attempt via GET /api/auth/ws-token. This short-lived token is passed as the Hocuspocus token parameter and is validated server-side in the onAuthenticate hook. The long-lived session JWT never travels over the WebSocket wire.

5.3 What the server stores per user

FieldValue
usernameNormalised login identifier (immutable)
emailContact address (mutable)
ed25519_public_keyFor challenge-response signature verification
x25519_public_keyFor collaborators to encrypt document keys to this user

Not stored: password, password hash, private keys, derived seed, plaintext document content, plaintext metadata.

6. Document Encryption

6.1 Per-document symmetric key

Each document has a unique random 256-bit symmetric key generated on the client at creation time. This key is never stored or transmitted in plaintext.

doc_key = crypto.getRandomValues(new Uint8Array(32))  // 256-bit AES key

6.2 Encrypting the document key (owner)

The document symmetric key is encrypted with the owner’s X25519 public key using an ECIES-style envelope (X25519 key agreement + AES-256-GCM). The resulting ciphertext is stored alongside the document record.

encrypted_doc_key = ECIES.encrypt(doc_key, owner.x25519_public_key)

To load a document, the client retrieves encrypted_doc_key and decrypts it with their X25519 private key — which was derived locally and never left the device.

6.3 Encrypting document content (Yjs updates)

Sovernote uses Yjs CRDTs as the document format. Edits are represented as binary update deltas. Before any update is sent to the server or stored in the cloud, it is encrypted:

encrypted_update = AES-256-GCM.encrypt(
  plaintext : yjs_update_bytes,
  key       : doc_key,               // 256-bit, unique per document
  nonce     : crypto.getRandomValues(new Uint8Array(12)),  // 96-bit, fresh per update
  tag_len   : 128 bits               // 16-byte authentication tag
)
// wire format: version_byte (1) ‖ nonce (12) ‖ ciphertext ‖ tag (16)

The server’s yjs_updates table stores only these opaque blobs and their document ID. The Hocuspocus relay receives and forwards them without inspecting their contents. The server never sees the Yjs state.

AES-256-GCM is an AEAD scheme — not just encryption

AES-GCM is an Authenticated Encryption with Associated Data (AEAD) construction. It simultaneously provides:

  • Confidentiality via AES in counter mode (CTR): the key stream is computationally indistinguishable from random to any party without the key.
  • Integrity and authenticity via GHASH/GMAC: a 128-bit authentication tag is computed over the nonce, any associated data, and the entire ciphertext. Any single-bit modification to the ciphertext, the nonce, or the associated data causes tag verification to fail deterministically. The probability of an adversary constructing a valid forgery is 2−128 per attempt.

The application must reject all updates with invalid tags unconditionally and must not apply them to the Yjs document state. This prevents both corruption and chosen-ciphertext attacks. Unlike AES-CBC + HMAC (which requires careful encrypt-then-MAC ordering to avoid padding oracle attacks such as POODLE and Lucky 13), GCM’s integrated design makes misuse far harder.

Why AES-256, not AES-128

AES-128 provides 128 bits of classical security but only ∼64 bits of security against Grover’s quantum search algorithm. AES-256 provides 256 bits classical / ∼128 bits post-quantum. The extra cost is negligible: the difference between the 10-round (AES-128) and 14-round (AES-256) schedules is imperceptible with AES-NI hardware acceleration, which is present on every x86 CPU manufactured since ∼2010 and on all Apple Silicon cores.

Nonce strategy and collision analysis

The 96-bit (12-byte) nonce is the NIST-recommended size for GCM and is always generated fresh by crypto.getRandomValues() for each update. GCM requires that the same (key, nonce) pair is never reused; reuse would allow an attacker to XOR two ciphertexts and recover the key stream. The Birthday-bound collision probability for n random 96-bit nonces under a single document key is:

P(collision) ≈ n² / 2⁹⁷

At n = 2³² (around 4 billion updates per document key):
P(collision) ≈ 2⁶⁴ / 2⁹⁷ = 2⁻³³ ≈ 1.2 × 10⁻¹⁰  (negligible)

At n = 10⁶ (a very large real-world document):
P(collision) ≈ 10¹² / 2⁹⁷ ≈ 10⁻¹⁷  (astronomically small)

Doc key rotation on collaborator revocation resets the nonce space entirely. Combined with per-update fresh random nonces, nonce collisions are not a practical concern at any realistic document size.

Why AES-256-GCM and not ChaCha20-Poly1305

Both are modern AEAD standards of equivalent security. AES-256-GCM was chosen because: AES-NI hardware acceleration makes it faster than ChaCha20 on virtually all Sovernote target hardware; it is listed in FIPS 140-2/140-3 approved algorithm sets, which may matter to regulated-sector and enterprise customers. ChaCha20-Poly1305 is a valid alternative and may be adopted for mobile-only paths where AES-NI is absent.

6.4 Metadata encryption

All document metadata stored in the properties JSON column — including the document title, tags, and any user-defined fields — is encrypted client-side before being written to the server. The server cannot perform server-side title previews, search, or content indexing. Full-text search runs locally against a decrypted client-side index.

6.5 Yjs update compaction

As a document is edited, individual update deltas accumulate in yjs_updates. When row count reaches a threshold, the client merges all deltas into a single snapshot via Y.encodeStateAsUpdate(). This compacted snapshot is itself encrypted before being stored, replacing the individual delta rows.

Compaction is safe at any time — including during active multi-device sessions — because of how Yjs CRDTs work. When an offline device reconnects, the Hocuspocus state vector exchange transmits only the diff between the offline device’s state and the current server state, regardless of how many times the server-side history has been compacted.

7. Real-time Collaboration

7.1 Yjs CRDTs and the zero-knowledge relay

Real-time collaboration is powered by Yjs, a Conflict-free Replicated Data Type (CRDT) library. CRDTs allow multiple clients to independently produce and merge edits without a central coordinator that understands the content. This property is essential to zero-knowledge collaboration: the server can relay and merge encrypted update blobs without ever needing to parse them.

The Hocuspocus WebSocket server handles:

  • Maintaining the current encrypted document state
  • Broadcasting encrypted updates to all connected clients
  • Performing state vector exchange for reconnecting offline clients
  • Persisting encrypted updates to SQLite

At no point during any of these operations does Hocuspocus decrypt, inspect, or transform the content of any update.

7.2 Collaboration key exchange

When a workspace owner invites a collaborator, the document symmetric key must be shared with them in a way that the server cannot intercept in plaintext:

  1. The owner looks up the collaborator’s X25519 public key by username via the server’s public key registry.
  2. The owner encrypts the document symmetric key with the collaborator’s public key (client-side).
  3. The encrypted envelope is stored on the server alongside the document record.
  4. When the collaborator next loads the document, they retrieve the encrypted envelope and decrypt it with their own X25519 private key.

The server stores the X25519 public keys of all users (these are by definition public) and relays the encrypted key envelopes. At no step does it have access to any private key or plaintext symmetric key.

7.3 Collaborator revocation

When a collaborator’s access is revoked, the document symmetric key must be rotated to prevent the former collaborator from decrypting future updates:

  1. The owner generates a new doc_key.
  2. The owner re-encrypts all existing document content with the new key.
  3. The owner re-encrypts the new doc_key for each remaining collaborator.
  4. The server replaces the old ciphertext and key envelopes with the new versions.

This operation is client-side and potentially expensive for large documents; the UI surfaces a progress indicator during key rotation.

8. Platform-specific Security Considerations

8.1 Tauri desktop app (trusted device)

The Tauri app runs a full Rust backend locally. The derived seed (or the derived keypair) is stored in the OS keychain via Tauri Stronghold (macOS Keychain / Windows Credential Manager), unlocked by the user’s system login or biometrics. The user does not need to re-enter their Sovernote password on subsequent launches.

Local data is intentionally stored unencrypted at the application layer. The SQLite database file on disk is plain, readable SQLite — not encrypted by Sovernote. This is a deliberate data-ownership guarantee: the user can inspect, export, or migrate their workspace using any SQLite tool without requiring Sovernote software or credentials. The appropriate protection for local data at rest is OS full-disk encryption (FileVault on macOS, BitLocker on Windows, LUKS on Linux), which is recommended for all users. Application-layer encryption of local data would introduce a key-management problem (what if the key is lost?) that full-disk encryption solves at the OS level without any additional recovery risk.

8.2 Web app (untrusted machine)

The web app is designed to be safely usable on shared or untrusted machines. It applies stricter key-storage rules than the desktop app:

DataTauri (trusted device)Web app (untrusted machine)
Private key / derived seed OS keychain (persistent) Memory only — never written to any storage
Session token Secure persistent storage sessionStorage — cleared when the tab closes
Decrypted document content In-memory while open In-memory while open
Node URLs, UI preferences localStorage via atomWithStorage localStorage via atomWithStorage (no secrets)
Encrypted document blobs SQLite on device Never cached locally

Re-authentication on page reload is intentional. The keypair exists only for the duration of the browser tab. When the tab closes, the private key is gone. The next visit requires the user to re-enter their password to re-derive it. This mirrors the security model of Proton Mail’s web app.

Implementation invariant: the Jotai atom holding the keypair or derived seed must never use atomWithStorage. It must be a plain in-memory atom<CryptoKeyPair | null>(null), populated on login and zeroed on logout. Accidental persistence to localStorage would silently compromise the privacy guarantee on shared machines.

8.3 Mobile app

The mobile app (iOS / Android) follows the same model as the Tauri desktop app — private keys are stored in the platform secure enclave (iOS Keychain / Android Keystore), and local data is encrypted at the file-system level by the OS. Network sync follows the same protocol as the desktop client.

9. Private AI & the E2EE Compatibility Problem

AI assistance and end-to-end encryption appear to be in fundamental tension: if a model runs on a remote server, it must be able to read the content it processes — breaking the zero-knowledge guarantee. Sovernote resolves this tension with two architecturally distinct layers that never require the server to see plaintext.

9.1 The core incompatibility

To process text, a model needs plaintext. This means:

  • An AI feature that sends your document content to a cloud API (OpenAI, Anthropic, Google, etc.) is a privacy break, regardless of the provider’s own privacy policy.
  • An AI feature that runs on the sync server is equally a privacy break: the server, which should only ever hold ciphertext, would be decrypting content to feed a model.
  • An AI feature that silently transmits selected context to any external service — even “anonymously” — without explicit per-request user consent is a privacy break.

This is why most notes apps that have added AI features have quietly abandoned their previous privacy claims, or have no meaningful privacy claims to begin with. Sovernote’s architecture does not permit either path: the sync server cannot decrypt content even if instructed to, because it does not hold the keys.

9.2 Robo — fully on-device inference

Robo is a fine-tuned small-parameter language model (SLM) that runs entirely on the user’s device via Ollama. It will ship as a free optional companion app separate from the main Sovernote installation.

PropertyDetail
Execution locationUser’s device only — the model binary and weights are downloaded locally by Ollama
Data flowSovernote decrypts the document locally (as it does for display), then passes plaintext to the local Ollama process over localhost. No data leaves the device.
Network trafficZero: local IPC / loopback only. The Ollama API endpoint at http://localhost:11434 is not exposed to any external network.
Model training dataRobo is trained on Sovernote’s public schema and documentation only — no user data is used for training, because no user data ever reaches Sovernote’s infrastructure
Privacy guaranteeIdentical to using the editor without AI: plaintext exists on-device in memory while the document is open, and nowhere else
Encrypted document requirementRobo only operates on already-decrypted in-memory state. It never reads raw ciphertext from disk or the sync server.

Because inference runs locally, Robo is available offline, has no per-query cost, and adds no latency beyond the model’s own inference time. The trade-off is hardware: local inference requires a capable CPU or GPU. Robo’s SLM is specifically selected for the parameter range that runs acceptably on mid-range consumer hardware.

9.3 Nemo — anonymous relay for cloud LLMs

Nemo is a privacy-preserving proxy for cases where on-device capability is insufficient — general knowledge lookups, research, creative tasks that benefit from a frontier model. The design constraint is that no workspace content and no user identity ever reaches a third-party model provider.

PropertyDetail
What is sent to the cloud modelOnly the user’s explicitly typed prompt. No document content, no workspace metadata, no user ID, no account linkage is included unless the user deliberately pastes it into the prompt.
AnonymisationRequests are routed through a Sovernote relay node that strips all HTTP headers that could identify the originating device or account (IP, User-Agent, authentication cookies). The cloud provider sees the relay’s IP only.
No persistent contextNemo sessions are stateless. The relay does not log prompts, does not maintain conversation history server-side, and does not associate requests with a user account.
Explicit consent per queryNemo is not an ambient background feature. The user must explicitly open the Nemo panel and type a prompt. There is no automatic document summarisation, no silent context injection, and no background telemetry.
What Sovernote’s server handlesToken authentication for billing (optional subscription) and outbound relaying. The server never interprets, logs, or stores prompt content.
What the cloud provider receivesAn anonymised API call from the relay’s IP, containing only the user-typed prompt. Indistinguishable from any other API consumer using the same relay pool.

Robo-assisted prompt screening

Because Robo runs locally, it can screen a Nemo-bound prompt before it leaves the device. When the user submits a Nemo prompt, Robo analyses it for patterns that suggest sensitive workspace content — document titles, database values, named entities that appear in the open workspace, or fragments resembling encrypted field values.

If a potential data-leakage risk is detected, the UI surface an inline warning:

⚠ This prompt may contain content from your workspace.
   Review it before sending to an external model.
   [Edit prompt]  [Send anyway]

The check is advisory, not a gate. The user can dismiss the warning and proceed if they intentionally want to include context. The goal is to catch accidental paste-and-forget scenarios, not to restrict deliberate choices. When Robo is not installed, the warning step is skipped and Nemo operates without screening.

Full transparency requires stating these limitations explicitly:

  • User-pasted content (partially mitigated): If the user manually copies document text into a Nemo prompt, that content is sent to the cloud model. Robo-assisted prompt screening (see above) attempts to catch this and warn the user before submission, but the warning is advisory — the user can proceed if the inclusion is intentional.
  • Cloud provider logging: Nemo routes through a Sovernote relay, but the destination cloud provider (e.g. the API endpoint operator) may log the prompt on their infrastructure. Users should treat Nemo prompts as inputs to a third-party service, not as end-to-end encrypted communications.
  • Traffic analysis: A powerful network adversary observing the relay’s outbound traffic could correlate request timing if they also monitor the user’s connection to the relay. This adversary model is out of scope; Nemo’s anonymisation target is the cloud provider and Sovernote’s own server, not a state-level traffic analyser.

9.5 Why this architecture resolves the tension

AI scenarioSovernote’s approachE2EE preserved?
AI operates on document contentRobo: local model on decrypted in-memory state. No data leaves device.Yes — by architecture
User queries a cloud LLMNemo: user types a freestanding prompt. No document content or identity transmitted. Robo screens the prompt locally before dispatch and warns if workspace content is detected.Yes — by explicit design constraint
Server-side AI summarisation / searchNot offered. The server cannot decrypt content; this feature class is architecturally impossible.N/A — feature does not exist
Automatic context injectionNot implemented. No background AI agent reads or summarises documents without user action.N/A — feature does not exist

The E2EE guarantee is not relaxed to accommodate AI features. Instead, AI features are designed around the guarantee: local where the model is small enough, explicitly opt-in and context-free where a cloud model is involved.

10. Backend Constraints by Design

Because the server only ever holds ciphertext, several features that are standard in cloud notes applications are intentionally unavailable server-side:

FeatureStatusReason
Server-side full-text searchNot availableServer cannot read document content; search runs against a local decrypted index
Server-side title previewsNot availableTitles are encrypted as part of the properties blob
Admin content accessNot availableNo Sovernote employee can read user documents
Account-based password resetNot availablePassword loss means keypair loss; no server-side recovery path exists
Server-side content moderationNot availableStructurally impossible without decryption

11. Known Constraints of the Zero-Knowledge Model

ScenarioConsequence
User changes password New keypair is derived. All document keys must be re-encrypted with the new public key — a client-side operation that scales with workspace size.
Password lost, no local copy Cloud access is permanently lost. The local device copy remains fully accessible.
Username change requested Not possible. The username is the Argon2id salt and cannot be changed without permanently breaking the keypair.
Contact email change Allowed at any time. Changing the email does not affect the keypair.
Collaborator revocation The document symmetric key must be rotated and re-encrypted for remaining collaborators.
Device loss / theft Credentials (username + password) on a new device re-derive the same keypair, restoring full cloud access. Recommend enabling OS full-disk encryption.

12. Infrastructure & Operational Security

11.1 Cloud node model

The paid sync service runs on a sharded set of instance nodes, each running Express + Hocuspocus + SQLite. Workspaces are routed to nodes by workspace ID; all members of a workspace are always assigned to the same node, enabling Hocuspocus collaboration without cross-node state sharing.

11.2 Transport security

  • All HTTP traffic uses TLS 1.2+ with HSTS enforced.
  • WebSocket connections are wss:// only in production.
  • Cookies carry Secure, HttpOnly, and SameSite=Strict attributes.

11.3 Database backup

Each instance node streams continuous SQLite WAL backups to object storage using Litestream. Backups are encrypted at rest. Near-real-time replication means recovery point objective (RPO) is measured in seconds, not hours.

11.4 Infrastructure access

  • Servers are accessible via SSH with key-based authentication only; password authentication is disabled.
  • No production server has publicly exposed management ports beyond SSH and HTTPS.
  • Dependencies are pinned and reviewed with pnpm audit and automated Dependabot alerts.

13. Open Source Backend & Self-Hosting Roadmap

The Sovernote client applications (desktop, mobile, web) will remain proprietary. The sync and collaboration backend will be open-sourced.

This section explains what that means for the security model, what it does not cover, and how users can independently verify the client-side privacy guarantees in the absence of client source code.

13.1 Why the server is the right thing to open

In a zero-knowledge architecture, the server’s role is structurally limited: it stores and relays ciphertext. Opening the server source allows anyone to verify that this is actually the case — that there is no hidden decryption path, no secret logging of plaintext, and no key-recovery mechanism. This is precisely the claim that most needs external scrutiny, and opening the backend satisfies it completely.

The client applications contain the encryption logic, but the privacy guarantee does not rely on the client being trusted in isolation. It relies on an architecture where even a fully compromised server cannot read documents, because the server never receives the keys. Auditing the server is therefore the more meaningful transparency action for users evaluating the hosted service.

13.2 What will be open-sourced

ComponentOSS statusPlanned licenceWhat it proves
Sync & collaboration server (apps/api) Planned AGPL-3.0 Server processes only ciphertext; no hidden decryption, logging, or key storage. AGPL prevents proprietary forks from undermining the privacy model.
Desktop & mobile app (apps/desktop) Proprietary Auditable via private security review (see §13.4) and behavioural network inspection (see §13.5).
Web app (apps/web) Proprietary Deployed JavaScript bundle is inspectable in-browser by any user; no obfuscation is applied to the cryptographic code paths.

13.3 Known limitation: closed client is a partial trust requirement

We state this explicitly: users of the hosted web app or the pre-built desktop app must extend some trust to Sovernote that the client code correctly implements the protocol described in this document. The backend being open-source removes the server from the trust model, but it does not remove the client.

The practical mitigations for this gap are:

  • Independent security audits of the client cryptographic module will be commissioned periodically and their results published.
  • Security researchers can request a private client source review (see §13.4).
  • The zero-knowledge architecture means a client that correctly implements the protocol leaks nothing even if the server is hostile — users can verify correct behaviour observationally (see §13.5).

13.4 Self-hosting

Once the backend is open-sourced, any user or organisation can run the full sync and collaboration stack on their own infrastructure. The architecture is designed to make this practical:

  • Single process, no external dependencies. Node.js + SQLite only. No Postgres, Redis, or message broker.
  • Standard Docker deployment. A docker-compose.yml and Dockerfile will be published. Bringing up an instance is a single docker compose up.
  • Litestream backup documented. The WAL-replication configuration used in the hosted service will be published for self-hosters to replicate to their own object storage (S3, Backblaze B2, etc.).
  • Client points to any server URL. Desktop and web clients accept a custom server URL at login. No recompilation required.

Self-hosting removes Sovernote from the trust model for the server component: the code running on your node is code you compiled from audited source. Encrypted blobs are on infrastructure you control, with no dependence on Sovernote’s uptime, jurisdiction, or business continuity.

13.5 Behavioural verification without source access

Users who rely on the hosted service and pre-built clients can verify the privacy guarantees observationally, without client source code:

  • Network request inspection: Open browser DevTools > Network while using the web app. No outbound request body or URL will contain recognisable plaintext document content — only binary blobs and JSON metadata that is itself encrypted.
  • WebSocket payload inspection: The yjs_updates frames visible in DevTools > Network > WS are opaque binary. There is no readable text, no JSON document structure, and no title or content visible.
  • DNS / firewall monitoring: The app makes no connections to any domain other than the configured API / Hocuspocus host during normal operation. There are no third-party analytics calls that carry document content.
  • Private source review: Security researchers and enterprise customers can request access to the client cryptographic module under NDA by contacting security@sovernote.com.

14. Cryptographic Primitive Summary

PurposeAlgorithmParametersClassical securityPost-quantum securityWhy chosen
Password-based key derivation Argon2id 64 MiB / 3 iter / p=1 / 32-byte output Determined by password entropy Determined by password entropy PHC winner; memory-hard defeats GPU/ASIC brute-force; hybrid id variant resists both side-channel and time–memory tradeoff attacks
Authentication signing Ed25519 Curve25519; 32-byte key; 64-byte signature 128-bit ∼64-bit (Shor) Deterministic per-signature nonce eliminates ECDSA nonce-reuse key recovery; constant-time; complete addition formula; no padding oracle
Asymmetric key agreement X25519 (ECDH) Curve25519; 32-byte public key 128-bit ∼64-bit (Shor) All 255-bit strings are valid public keys — invalid-curve attacks impossible by construction; automatic key clamping; same curve as Ed25519
Symmetric encryption AES-256-GCM 256-bit key; 96-bit random nonce; 128-bit tag 256-bit ∼128-bit (Grover) AEAD: integrated confidentiality + integrity + authenticity; FIPS 140-2 approved; AES-NI hardware acceleration on all target platforms
Document key sharing (envelope) ECIES (X25519 + AES-256-GCM) Ephemeral X25519 keypair per envelope 128-bit ∼64-bit (Shor) Ephemeral DH provides forward secrecy: compromise of the static X25519 key does not expose previously shared key envelopes
Username normalisation hash SHA-256 256-bit digest 128-bit collision / 256-bit preimage ∼128-bit preimage (Grover) Produces a fixed-length, safe identifier from arbitrary-length usernames; not used as a standalone security primitive

Client implementations use the @noble/curves and @noble/hashes libraries (independently audited, zero-dependency, pure TypeScript) for Ed25519, X25519, SHA-256, and AES-256-GCM, supplemented by the browser SubtleCrypto API where available. Argon2id runs via argon2-browser (WASM). The Tauri / Rust backend uses the ring crate for AES-GCM and SHA-256, and ed25519-dalek / x25519-dalek for Curve25519 operations.

15. Post-Quantum Readiness & Protocol Versioning

The elliptic-curve primitives in this protocol (Ed25519, X25519, ECIES) provide no meaningful security against a cryptographically relevant quantum computer running Shor’s algorithm. We state this explicitly rather than obscuring it.

Current quantum exposure

PrimitiveClassical securityPost-quantum securityThreat timeline
Ed25519 (signing)128-bit∼0-bit (Shor)A cryptographically relevant quantum computer: NSA / NIST consensus is 10–20+ years; no timeline is considered firm
X25519 / ECIES (key exchange)128-bit∼0-bit (Shor)
AES-256-GCM (symmetric)256-bit∼128-bit (Grover)128-bit post-quantum margin is considered safe for the foreseeable future per NIST SP 800-57
Argon2id (KDF output)∼128-bit∼64-bit (Grover on seed)Mitigated by requiring passphrase entropy > 64 bits; a 5-word passphrase achieves ∼65 bits

Protocol versioning scheme

Every ciphertext blob is prefixed with a 1-byte protocol version tag. Version 1 is the current suite. The tag exists specifically to enable algorithm migration without a flag-day re-encryption of all user data.

// Wire format for all encrypted blobs
version (1 byte) ‖ nonce (12 bytes) ‖ ciphertext (variable) ‖ GCM tag (16 bytes)

// Clients receiving an unknown version byte must refuse to decrypt
// and must surface a "please update your client" prompt.

Planned Version 2: hybrid classical + post-quantum suite

When NIST-standardised post-quantum algorithms are sufficiently mature and available in audited libraries, Sovernote will define a Version 2 protocol:

  • Key encapsulation: X25519 + ML-KEM-1024 (FIPS 203 / CRYSTALS-Kyber) — both shared secrets are combined via HKDF, so security holds as long as either primitive is unbroken.
  • Signatures: Ed25519 + ML-DSA-65 (FIPS 204 / CRYSTALS-Dilithium) — dual-signed; both signatures must verify independently.
  • Symmetric: AES-256-GCM unchanged — already post-quantum safe at the 128-bit level.

The hybrid approach means the transition introduces no regression: if either the classical or the post-quantum component has an undiscovered flaw, the other still provides full protection. Clients supporting both version tags handle the migration transparently as documents are re-saved.

16. Questions & Responsible Disclosure

If you have questions about this document, want to request a deeper technical briefing, or have identified a vulnerability, please reach out:

We welcome scrutiny. A privacy-first product should be able to defend its claims in public.