an atproto pds written in F# (.NET 9) 馃
pds fsharp giraffe dotnet atproto
5
fork

Configure Feed

Select the types of activity you want to include in your feed.

1# PDSharp 2 3> A Personal Data Server (PDS) for the AT Protocol, written in F# with Giraffe. 4 5## Goal 6 7Build 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 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## API Testing 38 39### Server Info 40 41```bash 42curl http://localhost:5000/xrpc/com.atproto.server.describeServer 43``` 44 45### Record Operations 46 47**Create a record:** 48 49```bash 50curl -X POST http://localhost:5000/xrpc/com.atproto.repo.createRecord \ 51 -H "Content-Type: application/json" \ 52 -d '{"repo":"did:web:test","collection":"app.bsky.feed.post","record":{"text":"Hello, ATProto!"}}' 53``` 54 55**Get a record** (use the rkey from createRecord response): 56 57```bash 58curl "http://localhost:5000/xrpc/com.atproto.repo.getRecord?repo=did:web:test&collection=app.bsky.feed.post&rkey=<RKEY>" 59``` 60 61**Put a record** (upsert with explicit rkey): 62 63```bash 64curl -X POST http://localhost:5000/xrpc/com.atproto.repo.putRecord \ 65 -H "Content-Type: application/json" \ 66 -d '{"repo":"did:web:test","collection":"app.bsky.feed.post","rkey":"my-post","record":{"text":"Updated!"}}' 67``` 68 69### Sync & CAR Export 70 71**Get entire repository as CAR:** 72 73```bash 74curl "http://localhost:5000/xrpc/com.atproto.sync.getRepo?did=did:web:test" -o repo.car 75``` 76 77**Get specific blocks** (comma-separated CIDs): 78 79```bash 80curl "http://localhost:5000/xrpc/com.atproto.sync.getBlocks?did=did:web:test&cids=<CID1>,<CID2>" -o blocks.car 81``` 82 83**Get a blob by CID:** 84 85```bash 86curl "http://localhost:5000/xrpc/com.atproto.sync.getBlob?did=did:web:test&cid=<BLOB_CID>" 87``` 88 89### Firehose (WebSocket) 90 91Subscribe to real-time commit events using [websocat](https://github.com/vi/websocat): 92 93```bash 94# Install websocat (macOS) 95brew install websocat 96 97# Connect to firehose 98websocat ws://localhost:5000/xrpc/com.atproto.sync.subscribeRepos 99``` 100 101Then create/update records in another terminal to see CBOR-encoded commit events stream in real-time. 102 103**With cursor for resumption:** 104 105```bash 106websocat "ws://localhost:5000/xrpc/com.atproto.sync.subscribeRepos?cursor=5" 107``` 108 109## Configuration 110 111The application uses `appsettings.json` and supports Environment Variable overrides. 112 113| Key | Env Var | Default | Description | 114| ----------- | ------------------- | ----------------------- | ------------------------- | 115| `DidHost` | `PDSHARP_DidHost` | `did:web:localhost` | The DID of the PDS itself | 116| `PublicUrl` | `PDSHARP_PublicUrl` | `http://localhost:5000` | Publicly reachable URL | 117 118Example `appsettings.json`: 119 120```json 121{ 122 "PublicUrl": "http://localhost:5000", 123 "DidHost": "did:web:localhost" 124} 125``` 126 127## Architecture 128 129### App (Giraffe) 130 131- `XrpcRouter`: `/xrpc/<NSID>` routing 132- `Auth`: Session management (JWTs) 133- `RepoApi`: Write/Read records (`putRecord`, `getRecord`) 134- `ServerApi`: Server meta (`describeServer`) 135 136### Core (Pure F#) 137 138- `DidResolver`: Identity resolution 139- `RepoEngine`: MST, DAG-CBOR, CIDs, Blocks 140- `Models`: Data types for XRPC/Database 141 142### Infra 143 144- SQLite/Postgres for persistence 145- S3/Disk for blob storage