# Potable
Potable is an Elixir implementation of core AT Protocol repository primitives.
It provides deterministic encoding, content addressing, signed commits, Merkle
Search Tree indexing, CAR export/import, and a pure-functional repository engine.
Potable is designed to be used as a substrate inside larger systems,
not as a full server implementation.
## What Potable Implements
- DAG-CBOR codec with IPLD constraints (`Potable.DagCbor`)
- CID parsing/creation/verification (`Potable.CID`)
- Merkle Search Tree (`Potable.MST`)
- Algorithm-aware signing (`Potable.Signing`: Ed25519, ES256K, ES256)
- Atproto commit objects v3 (`Potable.Commit`)
- CAR v1 export/import (`Potable.CAR`)
- Pure-functional repo engine (`Potable.Repository`)
- Blockstore behaviour and in-memory backend (`Potable.BlockStore`, `Potable.BlockStore.Memory`)
- Firehose primitives (`Potable.EventLog*`, `Potable.Sync.*`)
- Identity verifier resolver behaviour and static resolver (`Potable.Identity.Resolver`, `Potable.Identity.StaticResolver`)
- Supporting AT identifiers and helpers (`Potable.DID`, `Potable.NSID`, `Potable.AtUri`, `Potable.TID`, etc.)
## Design Guarantees
- Deterministic DAG-CBOR encoding for valid inputs
- Deterministic MST root for equivalent key/value sets
- Commit signing and signature verification with algorithm-aware key metadata
- DID/revision-aware verifier resolution via `verify_repo_with_resolver/4`
- Content-addressed integrity checks (CID vs bytes)
- Strict CID decode/validation for `b...`, `z...`, and `Qm...` inputs
- CAR import with typed errors (no runtime raises for content failures)
- Pure-functional repository operations (no hidden process state)
## Quickstart
```elixir
alias Potable.{BlockStore, Repository, Signing}
{:ok, store} = BlockStore.Memory.start_link()
{pub, priv} = Signing.generate_keypair()
did = "did:plc:potable-demo-1234567890"
{:ok, head} = Repository.create_repo(did, priv, store)
post = %{
"$type" => "app.bsky.feed.post",
"text" => "hello from potable",
"createdAt" => "2026-02-13T00:00:00Z"
}
writes = [{:create, "app.bsky.feed.post", "post-1", post}]
{:ok, head} = Repository.apply_writes(head, did, writes, priv, store)
{:ok, loaded} = Repository.get_record(head, "app.bsky.feed.post", "post-1", store)
{:ok, listed} = Repository.list_records(head, "app.bsky.feed.post", [limit: 10], store)
:ok = Repository.verify_repo(head, pub, store)
{:ok, car_bytes} = Repository.export_car(head, store)
{:ok, store2} = BlockStore.Memory.start_link()
{:ok, imported_head} = Repository.import_car(car_bytes, store2)
{:ok, _loaded_again} = Repository.get_record(imported_head, "app.bsky.feed.post", "post-1", store2)
```
## Module Map
| Area | Modules |
| --- | --- |
| Repository engine | `Potable.Repository`, `Potable.Commit`, `Potable.MST`, `Potable.CAR`, `Potable.Signing` |
| Codecs and formats | `Potable.DagCbor`, `Potable.CID`, `Potable.Varint` |
| Storage | `Potable.BlockStore`, `Potable.BlockStore.Memory`, `Potable.Blob`, `Potable.Blob.Storage`, `Potable.Blob.MemoryStorage` |
| Sync and firehose primitives | `Potable.EventLog`, `Potable.EventLog.Memory`, `Potable.Sync.CommitEnvelope`, `Potable.Sync.Cursors`, `Potable.Sync.CAR` |
| Identifiers and parsing | `Potable.DID`, `Potable.NSID`, `Potable.AtUri`, `Potable.Handle`, `Potable.TID`, `Potable.Record`, `Potable.Identity.Resolver`, `Potable.Identity.StaticResolver` |
## Documentation
See the full manual in `potable/docs/`:
1. [`docs/00_overview.md`](docs/00_overview.md)
2. [`docs/01_quickstart.md`](docs/01_quickstart.md)
3. [`docs/02_data_formats.md`](docs/02_data_formats.md)
4. [`docs/03_mst.md`](docs/03_mst.md)
5. [`docs/04_commits_and_signatures.md`](docs/04_commits_and_signatures.md)
6. [`docs/05_repository_engine.md`](docs/05_repository_engine.md)
7. [`docs/06_storage_backends.md`](docs/06_storage_backends.md)
8. [`docs/07_known_limitations.md`](docs/07_known_limitations.md)
9. [`docs/08_conformance_and_interop.md`](docs/08_conformance_and_interop.md)
10. [`docs/09_sync_and_firehose_primitives.md`](docs/09_sync_and_firehose_primitives.md)
## Development
```bash
cd potable
mix test
mix docs
```
## Current Limitations
- `Potable.MST.put/4` and `Potable.MST.delete/3` rebuild from flattened pairs (deterministic, not optimized).
- Potable includes firehose primitives, but does not ship a wire transport server (for example websocket `subscribeRepos`) or full XRPC service layer.
- Full production federation interop still requires external key distribution/trust plumbing (DID docs, key rotation policy, resolver policy).
- Continue adding cross-implementation ES256K/ES256 vectors against upstream repos as implementations evolve.