0.x security preview · audited primitives, no invented crypto

Own your data outright.

End-to-end-encrypted, passkey-unlocked SQLite that lives in your browser. It syncs between your devices through a relay that only ever sees ciphertext - no server, relay, or app ever holds a key.

Cloud storage is a leasehold: you occupy, someone else holds title. A freehold is property you own outright - no landlord, no lease, no one else's key.

XChaCha20-Poly1305 WebAuthn-PRF unlock Runs in any page - no COOP/COEP Rust → Wasm core

Why Freehold

Encryption you hold the key to - literally.

Every guarantee comes from a boundary the design can actually enforce, and every limit is stated plainly. No marketing crypto.

You hold the only key

A random 256-bit data key encrypts every database block. It's unwrapped inside a Web Worker from your passkey's WebAuthn-PRF output, lives only in wasm memory, and is zeroized on lock - never persisted in the clear, never off the device.

Server-blind by construction

Export, import, and sync move only sealed ciphertext plus non-secret lineage metadata. A relay can't read your data, link across your databases, or forge your state.

No invented cryptography

Only audited RustCrypto/dalek and WebCrypto primitives, used as-is: XChaCha20-Poly1305, HKDF, HMAC, Argon2id, Ed25519. Every security decision is designed before it's built.

Real SQLite, encrypted below

A full SQLite engine on an encrypting VFS: every file it touches - main DB, journal, temp - is sealed per-block. Three adversarial review passes; crash-injection clean.

Drops into any page

No cross-origin-isolation headers required. Pure ESM SDK with hand-written types - what you import is what runs. No build step for the JavaScript.

Honest about its limits

The design says exactly what it does not guarantee - availability, freshness against a fully-malicious provider, un-leaking already-exfiltrated data. Nothing hand-waved.

Architecture

Layers, each a boundary the one above rides on.

From the browser's own storage up to your app - every layer is specified, reviewed, and then built.

Your app@freehold/db - one class: enroll · unlock · sql · export · sync
Device trustper-device Ed25519 identity + vault-signed certificates
Data custodyapps are custodians: signed grants · zero-PII attestations · borrows
Syncversion-vector engine over a blind relay - stateless, key-committed authorization
Key envelopeN slots (passkey-PRF · recovery code · device) each wrapping the DEK - add/remove ≠ re-encrypt
Encrypting VFSXChaCha20-Poly1305 block device below SQLite · per-DB HKDF subkeys
Anti-rollbackdouble-buffered manifest + peer-attested sync epochs
BrowserOPFS (encrypted at rest) · WebAuthn · Web Worker

How it works

One passkey ceremony. Then it's just SQL.

Unlock asserts the passkey once and opens a session; the key is unwrapped in the worker and never leaves wasm memory. Every query after that is prompt-free.

app.js
import { FreeholdVault } from '@freehold/db';

const vault = await FreeholdVault.open({
  wasmUrl: new URL('./pkg/freehold.js', import.meta.url),
  rpName: 'My App',
});

await vault.enroll();            // register passkey, init vault
const code = await vault.addRecoveryCode(); // show once

await vault.unlock();            // one passkey ceremony…
await vault.sql('CREATE TABLE notes(id INTEGER, body TEXT)');
await vault.sql('INSERT INTO notes(body) VALUES (?)', ['hello 🔐']);
// …then no more prompts until lock()

const bytes = await vault.exportBundle(); // .freehold - no key inside
The two-device test. Export the .freehold bundle on device A, import it on device B, and unlock with your synced passkey or your recovery code. The bundle carries the encrypted image and the key envelope - but no key. No server is ever in the loop.
✓ enroll / unlock ✓ recovery code ✓ export / import ✓ server-blind sync ✓ zero-PII attestation ✓ standalone decryptor

Prefer to see it run? The passkey demo walks through enroll → unlock → export/import; the custody demo shows apps requesting scoped disclosures with revocation enforced at the broker.

The system

Four planes - and where each one stands.

Freehold is built and shipped in the open, design-doc first. This is an honest snapshot, not a wish list.

PlaneWhat it doesStatus
Freehold DB Encrypting VFS, key envelope, DEK rotation, server-blind bundle, standalone decryptor. Working · 3 review passes
Freehold Sync Version-vector engine, blind relay + HTTP adapter, stateless relay auth. Working · proven E2E
Data custody Signed grants, requester auth, zero-PII attestations - apps as custodians. Reference + E2E
Device trust Per-device identity + vault-signed certs; pairing / revocation / federation designed. Increment 1 built

Security posture

Conservative by design. Honest by default.

Status: pre-1.0 security preview - not yet externally audited. Suitable for evaluation and development; not yet for protecting secrets whose compromise you could not tolerate. 1.0 is gated on an external audit and a real cross-browser/device pass - not on feature count.

Freehold guarantees confidentiality from the sync path unconditionally - no server, relay, or peer ever sees a key or plaintext. It does not guarantee availability or freshness against a fully-dishonest provider: the anti-rollback epochs make staleness detectable and non-propagating, not impossible. Run a relay you control, or sync device-to-device, for a liveness guarantee.

See it hold a key no one else can.

Enroll a passkey, write some encrypted data, export a key-free bundle - all in your browser, nothing sent anywhere.