an atproto pds written in F# (.NET 9) 🦒
pds fsharp giraffe dotnet atproto

docs: add project roadmap and expand README

Changed files
+221 -1
+79 -1
README.md
··· 1 1 # PDSharp 2 2 3 - Personal Data Server written in F# 3 + > A Personal Data Server (PDS) for the AT Protocol, written in F# with Giraffe. 4 + 5 + ## Goal 6 + 7 + Build and deploy a single-user PDS that can host your AT Protocol repository, serve blobs, and federate with Bluesky. 8 + 9 + ## Requirements 10 + 11 + - .NET 9.0 SDK 12 + - [Just](https://github.com/casey/just) (optional, for potential future task running) 13 + 14 + ## Getting Started 15 + 16 + ### Restore & Build the project 17 + 18 + ```bash 19 + dotnet restore 20 + dotnet build 21 + ``` 22 + 23 + ### Run the tests 24 + 25 + ```bash 26 + dotnet test 27 + ``` 28 + 29 + ### Run the Server 30 + 31 + ```bash 32 + dotnet run --project PDSharp/PDSharp.fsproj 33 + ``` 34 + 35 + The server will start at `http://localhost:5000`. 36 + 37 + ### Verify 38 + 39 + Check the `describeServer` endpoint: 40 + 41 + ```bash 42 + curl http://localhost:5000/xrpc/com.atproto.server.describeServer 43 + ``` 44 + 45 + ## Configuration 46 + 47 + The application uses `appsettings.json` and supports Environment Variable overrides. 48 + 49 + | Key | Env Var | Default | Description | 50 + | ----------- | ------------------- | ----------------------- | ------------------------- | 51 + | `DidHost` | `PDSHARP_DidHost` | `did:web:localhost` | The DID of the PDS itself | 52 + | `PublicUrl` | `PDSHARP_PublicUrl` | `http://localhost:5000` | Publicly reachable URL | 53 + 54 + Example `appsettings.json`: 55 + 56 + ```json 57 + { 58 + "PublicUrl": "http://localhost:5000", 59 + "DidHost": "did:web:localhost" 60 + } 61 + ``` 62 + 63 + ## Architecture 64 + 65 + ### App (Giraffe) 66 + 67 + - `XrpcRouter`: `/xrpc/<NSID>` routing 68 + - `Auth`: Session management (JWTs) 69 + - `RepoApi`: Write/Read records (`putRecord`, `getRecord`) 70 + - `ServerApi`: Server meta (`describeServer`) 71 + 72 + ### Core (Pure F#) 73 + 74 + - `DidResolver`: Identity resolution 75 + - `RepoEngine`: MST, DAG-CBOR, CIDs, Blocks 76 + - `Models`: Data types for XRPC/Database 77 + 78 + ### Infra 79 + 80 + - SQLite/Postgres for persistence 81 + - S3/Disk for blob storage
+142
roadmap.txt
··· 1 + ================================================================================ 2 + *roadmap.txt* PDSharp: F#/Giraffe PDS Implementation Roadmap 3 + ================================================================================ 4 + PHASE 1: IMPLEMENTATION (Build) 5 + ================================================================================ 6 + Milestone A: Giraffe XRPC Shell 7 + -------------------------------------------------------------------------------- 8 + - [x] Implement /xrpc/com.atproto.server.describeServer (GET) 9 + - [x] Implement NSID-based routing with structured error responses 10 + DoD: describeServer responds with stable JSON 11 + -------------------------------------------------------------------------------- 12 + Milestone B: Identity + Crypto Primitives 13 + -------------------------------------------------------------------------------- 14 + - DID document fetch/parse for signing key and PDS endpoint 15 + - SHA-256 hashing, ECDSA sign/verify (p256 + k256), low-S enforcement 16 + DoD: Sign and verify atproto commit hash with low-S 17 + -------------------------------------------------------------------------------- 18 + Milestone C: DAG-CBOR + CID 19 + -------------------------------------------------------------------------------- 20 + - Canonical DAG-CBOR encode/decode with IPLD link tagging 21 + - CID creation/parsing (multicodec dag-cbor, sha2-256) 22 + DoD: Record JSON → stable DAG-CBOR bytes → deterministic CID 23 + -------------------------------------------------------------------------------- 24 + Milestone D: MST Implementation 25 + -------------------------------------------------------------------------------- 26 + - Merkle Search Tree per repository spec 27 + - Key depth = leading zero bits in SHA-256(key) counted in 2-bit chunks 28 + - Node encoding: (l, e[p,k,v,t]) with key prefix compression 29 + DoD: Insert/update/delete yields reproducible root CID 30 + -------------------------------------------------------------------------------- 31 + Milestone E: Commit + BlockStore + putRecord 32 + -------------------------------------------------------------------------------- 33 + - BlockStore: cid → bytes, indexed by DID/rev/head 34 + - Commit signing: UnsignedCommit → DAG-CBOR → sha256 → ECDSA sign 35 + - Implement com.atproto.repo.putRecord/createRecord 36 + DoD: Write and read records by path/AT-URI 37 + -------------------------------------------------------------------------------- 38 + Milestone F: CAR Export + Sync Endpoints 39 + -------------------------------------------------------------------------------- 40 + - CARv1 writer (roots = commit CID, blocks stream) 41 + - Implement: sync.getRepo, sync.getBlocks, sync.getBlob 42 + DoD: External services can fetch repo snapshot + blocks 43 + -------------------------------------------------------------------------------- 44 + Milestone G: subscribeRepos Firehose 45 + -------------------------------------------------------------------------------- 46 + - Monotonic sequence number + commit event generation 47 + - WebSocket streaming for subscribeRepos 48 + DoD: Relay/client receives commit events after writes 49 + -------------------------------------------------------------------------------- 50 + Milestone H: Account + Sessions 51 + -------------------------------------------------------------------------------- 52 + - Implement: server.createAccount, server.createSession, refreshSession 53 + - Password/app-password hashing + JWT issuance 54 + DoD: Authenticate and write records with accessJwt 55 + -------------------------------------------------------------------------------- 56 + Milestone I: Lexicon Validation + Conformance 57 + -------------------------------------------------------------------------------- 58 + - Lexicon validation for writes (app.bsky.* records) 59 + - Conformance testing: diff CIDs/CARs/signatures vs reference PDS 60 + DoD: Same inputs → same outputs for repo/sync surfaces 61 + ================================================================================ 62 + PHASE 2: DEPLOYMENT (Self-Host) 63 + ================================================================================ 64 + Milestone J: Topology + Domain Planning 65 + -------------------------------------------------------------------------------- 66 + - Choose PDS hostname (pds.example.com) vs handle domain (example.com) 67 + - Obtain domain, DNS access, VPS with static IP, reverse proxy 68 + DoD: Clear plan for PDS location, handle, and DID resolution 69 + -------------------------------------------------------------------------------- 70 + Milestone K: DNS + TLS + Reverse Proxy 71 + -------------------------------------------------------------------------------- 72 + - DNS A/AAAA records for PDS hostname 73 + - TLS certs (ACME) via Caddy/Nginx/Traefik 74 + DoD: https://<pds-hostname> responds with valid cert 75 + -------------------------------------------------------------------------------- 76 + Milestone L: Deploy PDSharp 77 + -------------------------------------------------------------------------------- 78 + - Deploy built PDS with persistence (SQLite/Postgres + blob storage) 79 + - Verify /xrpc/com.atproto.server.describeServer 80 + DoD: describeServer returns capabilities payload 81 + -------------------------------------------------------------------------------- 82 + Milestone M: Account Creation 83 + -------------------------------------------------------------------------------- 84 + - Create account using admin tooling 85 + - Verify authentication: createSession 86 + DoD: Obtain session and perform authenticated write 87 + -------------------------------------------------------------------------------- 88 + Milestone N: Smoke Test Repo + Blobs 89 + -------------------------------------------------------------------------------- 90 + - Write record via putRecord 91 + - Upload blob, verify retrieval via sync.getBlob 92 + DoD: Posts appear in clients, media loads reliably 93 + -------------------------------------------------------------------------------- 94 + Milestone O: Account Migration (Optional) 95 + -------------------------------------------------------------------------------- 96 + - Export/import from bsky.social 97 + - Update DID service endpoint 98 + - Verify handle/DID resolution 99 + DoD: Handle unchanged, DID points to your PDS 100 + -------------------------------------------------------------------------------- 101 + Milestone P: Reliability 102 + -------------------------------------------------------------------------------- 103 + - Backups: repo storage + database + blobs 104 + - Restore drill on fresh instance 105 + - Monitoring: uptime checks for describeServer + getBlob 106 + DoD: Restore from backup passes smoke tests 107 + -------------------------------------------------------------------------------- 108 + Milestone Q: Updates + Security 109 + -------------------------------------------------------------------------------- 110 + - Update cadence with rollback plan 111 + - Rate limits and access controls at proxy 112 + - Log retention and disk growth alerts 113 + DoD: Update smoothly, maintain stable federation 114 + ================================================================================ 115 + QUICK CHECKLIST 116 + ================================================================================ 117 + [ ] describeServer endpoint working 118 + [ ] Crypto primitives (sha256, ECDSA p256/k256, low-S) 119 + [ ] DAG-CBOR + CID generation correct 120 + [ ] MST producing deterministic root CIDs 121 + [ ] putRecord + blockstore operational 122 + [ ] CAR export + sync endpoints 123 + [ ] subscribeRepos firehose 124 + [ ] Authentication (createAccount, createSession) 125 + [ ] Lexicon validation 126 + [ ] Domain + TLS configured 127 + [ ] PDS deployed and reachable 128 + [ ] Account created, session works 129 + [ ] Writes + blobs verified 130 + [ ] Backups + monitoring in place 131 + ================================================================================ 132 + REFERENCES 133 + ================================================================================ 134 + https://atproto.com/guides/self-hosting 135 + https://github.com/bluesky-social/pds 136 + https://atproto.com/specs/repository 137 + https://atproto.com/specs/sync 138 + https://atproto.com/specs/blob 139 + https://docs.bsky.app/docs/api/com-atproto-server-describe-server 140 + https://docs.bsky.app/docs/api/com-atproto-server-create-session 141 + https://docs.bsky.app/docs/api/com-atproto-repo-put-record 142 + https://docs.bsky.app/docs/api/com-atproto-sync-get-blob