an atproto pds written in F# (.NET 9) 馃
pds fsharp giraffe dotnet atproto
1<!-- markdownlint-disable MD033 --> 2# PDSharp 3 4A Personal Data Server (PDS) for the AT Protocol, written in F# with Giraffe. 5 6## Goal 7 8Build and deploy a single-user PDS that can host your AT Protocol repository, serve blobs, and federate with Bluesky. 9 10## Requirements 11 12.NET 9.0 SDK 13 14## Getting Started 15 16### Restore & Build the project 17 18```bash 19dotnet restore 20dotnet build 21``` 22 23### Run the tests 24 25```bash 26dotnet test 27``` 28 29### Run the Server 30 31```bash 32dotnet run --project PDSharp/PDSharp.fsproj 33``` 34 35The server will start at `http://localhost:5000`. 36 37## Configuration 38 39The application uses `appsettings.json` and supports Environment Variable overrides. 40 41| Key | Env Var | Default | Description | 42| ----------- | ------------------- | ----------------------- | ------------------------- | 43| `DidHost` | `PDSHARP_DidHost` | `did:web:localhost` | The DID of the PDS itself | 44| `PublicUrl` | `PDSHARP_PublicUrl` | `http://localhost:5000` | Publicly reachable URL | 45 46Example `appsettings.json`: 47 48```json 49{ 50 "PublicUrl": "http://localhost:5000", 51 "DidHost": "did:web:localhost" 52} 53``` 54 55## API Testing 56 57<details> 58<summary>Server Info</summary> 59 60```bash 61curl http://localhost:5000/xrpc/com.atproto.server.describeServer 62``` 63 64</details> 65 66### Record Operations 67 68<details> 69<summary>Create a record</summary> 70 71```bash 72curl -X POST http://localhost:5000/xrpc/com.atproto.repo.createRecord \ 73 -H "Content-Type: application/json" \ 74 -d '{"repo":"did:web:test","collection":"app.bsky.feed.post","record":{"text":"Hello, ATProto!"}}' 75``` 76 77</details> 78 79<details> 80<summary>Get a record</summary> 81 82```bash 83curl "http://localhost:5000/xrpc/com.atproto.repo.getRecord?repo=did:web:test&collection=app.bsky.feed.post&rkey=<RKEY>" 84``` 85 86</details> 87 88<details> 89<summary>Put a record</summary> 90 91```bash 92curl -X POST http://localhost:5000/xrpc/com.atproto.repo.putRecord \ 93 -H "Content-Type: application/json" \ 94 -d '{"repo":"did:web:test","collection":"app.bsky.feed.post","rkey":"my-post","record":{"text":"Updated!"}}' 95``` 96 97</details> 98 99### Sync & CAR Export 100 101<details> 102<summary>Get entire repository as CAR</summary> 103 104```bash 105curl "http://localhost:5000/xrpc/com.atproto.sync.getRepo?did=did:web:test" -o repo.car 106``` 107 108</details> 109 110<details> 111<summary>Get specific blocks</summary> 112 113```bash 114curl "http://localhost:5000/xrpc/com.atproto.sync.getBlocks?did=did:web:test&cids=<CID1>,<CID2>" -o blocks.car 115``` 116 117</details> 118 119<details> 120<summary>Get a blob by CID</summary> 121 122```bash 123curl "http://localhost:5000/xrpc/com.atproto.sync.getBlob?did=did:web:test&cid=<BLOB_CID>" 124``` 125 126</details> 127 128### Firehose (WebSocket) 129 130Subscribe to real-time commit events using [websocat](https://github.com/vi/websocat): 131 132<details> 133<summary>Open a WebSocket connection</summary> 134 135```bash 136websocat ws://localhost:5000/xrpc/com.atproto.sync.subscribeRepos 137``` 138 139</details> 140 141<br /> 142Then create/update records in another terminal to see CBOR-encoded commit events stream in real-time. 143 144<br /> 145 146<details> 147<summary>Open a WebSocket connection with cursor for resumption</summary> 148 149```bash 150websocat "ws://localhost:5000/xrpc/com.atproto.sync.subscribeRepos?cursor=5" 151``` 152 153</details> 154 155## Architecture 156 157<details> 158<summary>App (Giraffe)</summary> 159 160- `XrpcRouter`: `/xrpc/<NSID>` routing 161- `Auth`: Session management (JWTs) 162- `RepoApi`: Write/Read records (`putRecord`, `getRecord`) 163- `ServerApi`: Server meta (`describeServer`) 164 165</details> 166 167<details> 168<summary>Core (Pure F#)</summary> 169 170- `DidResolver`: Identity resolution 171- `RepoEngine`: MST, DAG-CBOR, CIDs, Blocks 172- `Models`: Data types for XRPC/Database 173 174</details> 175 176<details> 177<summary>Infra</summary> 178 179- SQLite/Postgres for persistence 180- S3/Disk for blob storage 181 182</details>