A zero-knowledge personal life organizer. It stores notes, tasks, projects, friends, events, daily goals, and sleep tracking, all encrypted in the browser before anything reaches the server.
The server stores ciphertext and an OPAQUE registration record. It cannot read the vault, even with full disk access.
Enrollment is invite-only. To request access, email [email protected] with your desired username. You'll receive a single-use enrollment link (24 hour TTL) that lets you set your password directly in the browser.
unsafe-inline,
no unsafe-eval. All inline scripts are SHA-256 hashed at build
time.bin/deploy.ts typechecks the
client and server, rsyncs, builds in place, swaps client/build
behind a one-second service stop, and verifies with both a
loopback health check and a Playwright smoke test.Browser Server Storage
------- ------ -------
SvelteKit 5 SPA Bun HTTP CouchDB
OPAQUE client <- RFC 9807 -> OPAQUE server
AES-256-GCM
CBOR + gzip ciphertext -> couch proxy -> single
(allowlisted) doc per
user
Authentication uses OPAQUE (RFC 9807)
via @cloudflare/opaque-ts. The server never receives the password,
not even at registration. The stored record is not a password hash.
Cracking it requires the password.
The vault itself is one encrypted blob per user. The pipeline is
object -> CBOR -> gzip -> AES-256-GCM -> base64. The AES key is
derived from OPAQUE's export_key and never transmitted.
Enrollment is invite only. The operator runs a CLI to mint a
single-use token with a 24 hour TTL. The on-disk filename is the
SHA-256 of the token, so a directory listing cannot be replayed. The
/enroll endpoint is byte-identical to any other unknown path for
clients that do not already have a token.
Transport is HTTPS via Caddy. The CSP pins inline scripts by SHA-256
hash. There is no unsafe-inline and no unsafe-eval.
See SECURITY.md for the threat model.
@cloudflare/opaque-ts
v0.7, P-256 suite.bun install
bun run dev # couch + server + client concurrently
Typecheck:
cd server && bunx tsc --noEmit
cd client && bunx svelte-kit sync && bunx svelte-check
Tests:
bun test client/tests
Production build:
bun run build
The build emits a static SvelteKit bundle in client/build/ and a
single-file enroll.html for OPAQUE registration. The inline script
in enroll.html is hash-pinned in the server CSP.
Deploys are atomic. bin/deploy.ts runs typechecks locally, rsyncs
the source to the target host, builds in place, swaps client/build
behind a service stop, restarts, and runs both a loopback health
check and a Playwright smoke test against the public URL. Any failure
aborts before the swap.
bench/results.md has measurements of the full save and load
pipelines at four fixture sizes. Reproduce with bun run bench.
client/ SvelteKit 5 SPA
src/lib/
components/ UI (tiles, modals, dashboard)
stores/ auth and nav state
vault/ types, serialize, crypto, sync, photo
crypto/ OPAQUE client wrappers
server/ Bun API and CouchDB proxy
src/
routes/ auth, enroll
services/ opaque-config, enrollment, user-service,
couch-admin, jwt, rate-limit, http
bin/ operator CLIs (init-opaque, start-enrollment,
delete-user, build-enroll)
enroll-src/ standalone enrollment page source
bin/ deploy and smoke-test tooling
bench/ vault-pipeline benchmark