Storage
The SDK has two storage APIs:
client.kv— a per-user, encrypted-at-rest key/value store for small structured values (settings, a to-do, an index). Private to the signed-in user, with a realtime cross-device change feed.client.storage— file storage for blobs of any size, shared through a Space and openable by anyone holding the file’s manifest.
Key/value — client.kv
Section titled “Key/value — client.kv”Collections & ids
Section titled “Collections & ids”Data is organized as collection + id. A collection is just a namespace;
think of it like a table, and the id like a row key.
await client.kv.set("todos", "t1", { title: "Ship docs", done: false });
const todo = await client.kv.get<{ title: string; done: boolean }>("todos", "t1");// → { title: "Ship docs", done: false } (decrypted locally)
const existed = await client.kv.delete("todos", "t1"); // → true
const ids = await client.kv.list("todos"); // → ["t1", "t2", …] (ids only)get returns null for a missing key. list returns the ids in a
collection — not the values (see querying, below).
Encryption
Section titled “Encryption”By default set seals the value with AES-256-GCM before it leaves the device.
You can opt out per call to store plaintext — useful for data that needs to be
readable by something other than this user, or that you’ll encrypt yourself:
await client.kv.set("public-profile", "ada", { handle: "@ada" }, { encrypt: false });get transparently decrypts sealed values and passes plaintext through, so
reads don’t care which way it was written.
Realtime change feed
Section titled “Realtime change feed”Subscribe to changes to the signed-in user’s data — across all their devices:
const off = client.kv.on("change", (e) => { // e.collection, e.id, e.type ("set" | "delete"), e.data (decrypted | null) if (e.collection === "todos") refreshTodos();});
// lateroff();This rides the personal space’s own websocket (see Spaces), so a write on one device updates the others with no extra setup.
Querying
Section titled “Querying”There’s no server-side query. Because values are encrypted client-side, the accelerator can’t index or filter them — that’s the privacy trade-off. List the collection and filter after decryption:
const ids = await client.kv.list("todos");const todos = await Promise.all(ids.map((id) => client.kv.get("todos", id)));const open = todos.filter((t) => t && !t.done);See the To-do app example for a complete feature, and the
client.kv reference for signatures.
Files — client.storage
Section titled “Files — client.storage”client.kv holds small JSON values. For files — anything from a few KB to
gigabytes — use client.storage. It chunks the file, encrypts each chunk with
its own AES-256-GCM key, and Reed–Solomon erasure-codes the chunks into shards
stored in a global, content-addressed store. The output is a manifest:
the per-chunk keys + shard hashes needed to reassemble the file.
The model has three parts worth understanding:
- Files are written to a Space. A file belongs to a SharedSpace (get one
from
client.space); the write is metered against that space.writeFilerequires aspaceId— there’s no “personal” file write. - The manifest is the capability. Shard bytes are open ciphertext; the
manifest holds the keys. Anyone you give the manifest to can open the file
from anywhere via
readByManifest— that’s how you share a file. - Your shared files are discoverable. On each write the SDK also records the
manifest in your own space, so
listFiles()(no argument) shows the files you’ve shared without enumerating every Space.
// Write a file into a space (obtained from client.space).const { stat, manifest } = await client.storage.writeFile({ spaceId, data: file, // Uint8Array | Blob | File metadata: { name: file.name, type: file.type },});
// Read it back — by space + id, or straight from the manifest (the capability).const { data } = await client.storage.readFile(spaceId, stat.id);const shared = await client.storage.readByManifest(manifest); // no membership needed
// Discover + manage.const mine = await client.storage.listFiles(); // files I've sharedconst inSpace = await client.storage.listFiles({ spaceId }); // files in a spaceawait client.storage.deleteFile(spaceId, stat.id);See the client.storage reference for full signatures
and the encrypted chat example for files in practice.