A local-first platform where users own their data cryptographically — built on encrypted SQLite, CRDTs, and libp2p.
Here is the simplest thing you can do with the platform:
const platform = await createPlatform()
const rows = await platform.exec(
'SELECT * FROM notes WHERE scope_id = ?',
['user:alice']
)
That looks like any other database query. The difference is where it runs.
The exec call crosses into a Web Worker running an encrypted SQLite database stored on the user's device. The encryption key is derived from the user's 12-word mnemonic and never transmitted anywhere. No server was involved.
Add a second device:
// Same mnemonic → same keys → same identity
// Devices find each other on the LAN via mDNS
// Changes sync peer-to-peer — no server, no internet required
const platform = await createPlatform({ mnemonic })
Add a collaborator:
// Alice grants Bob editor access to a document
await platform.peers.grant(bobPublicKey, 'doc:abc123', 'EDITOR')
// Enforced inside the WASM binary via a BEFORE INSERT trigger
// Bob's writes to other scopes are rejected before they touch the database
Add a live query:
const unsub = await platform.live(
'doc-blocks',
'SELECT * FROM blocks WHERE scope_id = ? ORDER BY pos ASC',
['doc:abc123'],
diffs => {
applyDiff(blocks, diffs)
render(blocks)
}
)
The diff arrives when a remote peer's change lands locally. Column-level — only what changed. The developer does not manage WebSocket connections, conflict resolution, or presence channels.
The schema is the configuration
CREATE TABLE blocks (
id TEXT PRIMARY KEY,
scope_id TEXT,
content REALTIME_TEXT, -- syncs at 5ms, live queries fire on change
cursor EPHEMERAL_INT, -- syncs fast, never backed up
title LWW_TEXT -- last-write-wins, batched at 50ms
);
SELECT crsql_as_crr('blocks');
SELECT crsql_policy('blocks', 'OWNER:RW, EDITOR:RW, VIEWER:R');
The column type affinities configure sync strategy, CRDT merge behaviour, and access policy. One CREATE TABLE. The runtime handles the rest.
When you need a server
Some problems need centralization — aggregation, ranking, feed algorithms. The platform handles this through server peers: specialist participants on the same mesh that receive capability-gated data from users, do the work, and write results back through the same sync mechanism.
// Write a request row — server peer picks it up via live query
await platform.exec(`
INSERT INTO peer_requests VALUES (?, 'srv:ai:alice', 'ai.assistant', ?, 'pending', NULL, ?, ?)
`, [ulid(), JSON.stringify({ prompt: 'summarise my week' }), myPubKey, Date.now()])
// Response arrives through CRDT sync — no separate API layer
const unsub = await platform.live('ai-result',
'SELECT status, result FROM peer_requests WHERE id = ?', [id],
rows => { if (rows[0]?.status === 'complete') { show(rows[0].result); unsub() } }
)
The server peer only sees data from users who have issued it a capability grant. Aggregation happens server-side. The underlying data stays with users.
What exists today
- slf-db — the WASM binary. Encrypted SQLite with CRDT replication, live queries, sync outbox, and capability enforcement baked in.
- slf-worker — the Web Worker. Owns libp2p networking, identity, sync protocol, pairing, inbox, and leader election.
- slf-sdk — the main thread SDK. Comlink bridge to the worker, Drizzle adapter, and proxies for peers, files, identity, and publishing.
- slf-proxy — the Go binary. Gives native apps TCP connections and LAN discovery via mDNS. Same source compiles to an Electron child process and a Capacitor gomobile framework.
- slf-auth — the auth authority. Registers identities, issues attestations, manages @handles, and federates across instances via the same CRDT sync everything else uses.
The apps that will run on this — notes, health, finance, passwords — are next.
The hard open problem is recovery. Losing a mnemonic with no backup is permanent. WebAuthn sealing mitigates the daily-use risk — the mnemonic lives in the secure enclave, daily opens are biometric — but it does not solve the lost-all-devices scenario. That is an active design problem, not a solved one.
Codebase is private while the core stabilises. Questions welcome in the comments.
Top comments (0)