Files
README.md

Folio #

A local-first database for AT Protocol data. Embed Folio on any client (native or web) to get a local database that functions offline, syncs optimistically, and creates a fast, low-friction user experience.

The public repository API loads collections on first read, then reads and edits locally. Synchronization refreshes only loaded collections and publishes edits:

let client = folio::Client::new(did, store, transport);
let mut repo = client.get_pub_repo();
let record = repo.get(&key).await?; // first read loads this collection
let records = repo.list(&key.collection).await?; // uses the local cache
repo.put(&key, updated_record.clone()).await?;
assert_eq!(repo.get(&key).await?, Some(updated_record));
repo.sync().await?;

Authentication belongs to the supplied transport. The current memory store is not durable, and synchronization trusts the PDS rather than verifying repository signatures and Merkle proofs.

folio-transport-http provides a native authenticated HTTP transport. Construction and repository selection are synchronous operations. The client discovers the account's PDS when needed; the transport authenticates on its first request. Session refresh remains with the host application.

client.get_priv_repo(space_uri)? selects the account's repository in a Space. Space reads, edits, and sync are not implemented yet.

The runnable E2E example shows login, lazy loading, local reads and edits, sync, and deletion using only Folio's public APIs:

cargo run -p folio --example e2e --locked

It uses the same root .env credentials and creates, updates, and deletes one uniquely keyed public record.

The live E2E test uses ATPROTO_HANDLE, ATPROTO_DID, and ATPROTO_PASSWORD from the ignored root .env. It creates one uniquely keyed record in the account's existing public repository, checks local reads and remote writes, and deletes the record through Folio before finishing:

cargo test -p folio --test e2e --locked -- --nocapture

Browser packages #

Enter devenv shell for Rust (including wasm32-unknown-unknown), Bun, wasm-pack, and cargo-make. Alternatively install those tools yourself and add the Wasm target with rustup target add wasm32-unknown-unknown.

cargo make build     # wasm-pack --target web, frozen Bun install, JS + declarations
cargo make test      # build, TypeScript check, Bun integration tests using real Wasm
cargo make rust-test # Rust library tests; excludes the opt-in live-account test
cargo make pack      # installable tarballs in target/packages

The Bun workspace contains @folio/client and @folio/react-query. Generated Wasm and JavaScript are ignored; rebuilding is required after a fresh checkout. The installable client includes its Wasm loader, binary, and TypeScript declarations. cargo make defaults to the test workflow. No task publishes a package or contacts a live ATProto account.

The React Query adapter provides connectFolio, folioListOptions, and folioRecordOptions. Connect before mounting queries, save through Folio with useMutation({ networkMode: "always", retry: false, mutationFn: ... }), and call folio.sync() separately to publish. Query invalidation reads cached data.