# PDSharp
A Personal Data Server (PDS) for the AT Protocol, written in F# with Giraffe.
## Goal
Build and deploy a single-user PDS that can host your AT Protocol repository, serve blobs, and federate with Bluesky.
## Requirements
.NET 9.0 SDK
## Getting Started
### Restore & Build the project
```bash
dotnet restore
dotnet build
```
### Run the tests
```bash
dotnet test
```
### Run the Server
```bash
dotnet run --project PDSharp/PDSharp.fsproj
```
The server will start at `http://localhost:5000`.
## Configuration
The application uses `appsettings.json` and supports Environment Variable overrides.
| Key | Env Var | Default | Description |
| ----------- | ------------------- | ----------------------- | ------------------------- |
| `DidHost` | `PDSHARP_DidHost` | `did:web:localhost` | The DID of the PDS itself |
| `PublicUrl` | `PDSHARP_PublicUrl` | `http://localhost:5000` | Publicly reachable URL |
Example `appsettings.json`:
```json
{
"PublicUrl": "http://localhost:5000",
"DidHost": "did:web:localhost"
}
```
## API Testing
Server Info
```bash
curl http://localhost:5000/xrpc/com.atproto.server.describeServer
```
### Record Operations
Create a record
```bash
curl -X POST http://localhost:5000/xrpc/com.atproto.repo.createRecord \
-H "Content-Type: application/json" \
-d '{"repo":"did:web:test","collection":"app.bsky.feed.post","record":{"text":"Hello, ATProto!"}}'
```
Get a record
```bash
curl "http://localhost:5000/xrpc/com.atproto.repo.getRecord?repo=did:web:test&collection=app.bsky.feed.post&rkey="
```
Put a record
```bash
curl -X POST http://localhost:5000/xrpc/com.atproto.repo.putRecord \
-H "Content-Type: application/json" \
-d '{"repo":"did:web:test","collection":"app.bsky.feed.post","rkey":"my-post","record":{"text":"Updated!"}}'
```
### Sync & CAR Export
Get entire repository as CAR
```bash
curl "http://localhost:5000/xrpc/com.atproto.sync.getRepo?did=did:web:test" -o repo.car
```
Get specific blocks
```bash
curl "http://localhost:5000/xrpc/com.atproto.sync.getBlocks?did=did:web:test&cids=," -o blocks.car
```
Get a blob by CID
```bash
curl "http://localhost:5000/xrpc/com.atproto.sync.getBlob?did=did:web:test&cid="
```
### Firehose (WebSocket)
Subscribe to real-time commit events using [websocat](https://github.com/vi/websocat):
Open a WebSocket connection
```bash
websocat ws://localhost:5000/xrpc/com.atproto.sync.subscribeRepos
```
Then create/update records in another terminal to see CBOR-encoded commit events stream in real-time.
Open a WebSocket connection with cursor for resumption
```bash
websocat "ws://localhost:5000/xrpc/com.atproto.sync.subscribeRepos?cursor=5"
```
## Architecture
App (Giraffe)
- `XrpcRouter`: `/xrpc/` routing
- `Auth`: Session management (JWTs)
- `RepoApi`: Write/Read records (`putRecord`, `getRecord`)
- `ServerApi`: Server meta (`describeServer`)
Core (Pure F#)
- `DidResolver`: Identity resolution
- `RepoEngine`: MST, DAG-CBOR, CIDs, Blocks
- `Models`: Data types for XRPC/Database
Infra
- SQLite/Postgres for persistence
- S3/Disk for blob storage