pds#
ATProto Personal Data Server storage for OCaml.
Overview#
A library for reading and writing ATProto PDS (Personal Data Server) storage format. This enables:
- Local PDS-compatible repositories without running a full PDS
- Offline repository manipulation (backup, migration, inspection)
- CAR import/export for interoperability
Installation#
Install with opam:
$ opam install nox-pds
If opam cannot find the package, it may not yet be released in the public
opam-repository. Add the overlay repository, then install it:
$ opam repo add samoht https://tangled.org/gazagnaire.org/opam-overlay.git
$ opam update
$ opam install nox-pds
Usage#
Create a repo, put and get records#
let put_and_get ~record_bytes =
Eio_main.run @@ fun env ->
Eio.Switch.run @@ fun sw ->
let fs = Eio.Stdenv.fs env in
let repo =
Pds.v ~sw
Eio.Path.(fs / "my-repo")
~did:(Atp.Did.of_string_exn "did:web:example.com")
in
Pds.put repo ~collection:"app.bsky.feed.post" ~rkey:"abc123" record_bytes;
(match Pds.find repo ~collection:"app.bsky.feed.post" ~rkey:"abc123" with
| Some data -> Fmt.pr "record: %d bytes@." (String.length data)
| None -> Fmt.pr "not found@.");
Pds.close repo
List a collection#
let list_posts repo =
Pds.list repo ~collection:"app.bsky.feed.post"
|> List.iter (fun (rkey, cid) ->
Fmt.pr "%s -> %a@." rkey Atp.Cid.pp cid)
Blobs#
let store_image repo image_bytes =
Pds.put_blob repo ~mime_type:"image/png" image_bytes
Import / export#
let backup repo = Pds.export_car repo
let restore repo car = Pds.import_car repo car
API#
Repository#
Pds.v ~sw path ~did-- create a new repositoryPds.open_ ~sw path-- open an existing repositoryPds.did tPds.close t
Records#
Pds.put t ~collection ~rkey dataPds.find t ~collection ~rkey-- returnsstring optionPds.delete t ~collection ~rkeyPds.list t ~collection-- returns(rkey * cid) list
Blobs#
Pds.put_blob t ~mime_type data-- returnsAtp.Blob_ref.tPds.blob t cid-- returnsstring option
Repository state#
Pds.head t-- current commit CIDPds.set_head t cidPds.checkout t-- MST at HEADPds.blockstore t-- underlying blockstore
Named refs#
Pds.ref t name/set_ref t name cid/delete_ref t namePds.list_refs t
CAR#
Pds.import_car t data-- returns number of imported blocksPds.export_car t
Storage layout#
<repo>/
├── pds.db # SQLite database
│ ├── blocks table # CID → DAG-CBOR bytes
│ ├── refs table # name → CID (branches)
│ └── meta table # did, version, etc.
└── blobs/ # Large binary data
├── ba/ # First 2 chars of CID
│ └── bafyrei... # Full CID as filename
└── ...
Related Work#
- ocaml-atp -- ATProto primitives (MST, CID, DAG-CBOR, CAR)
- ocaml-sqlite -- SQLite key-value store (used internally)
- Bluesky PDS -- Reference TypeScript implementation
Licence#
MIT License. See LICENSE.md for details.