@ascorbic/pds 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +213 -0
- package/dist/cli.js +528 -0
- package/dist/index.d.ts +332 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3130 -0
- package/dist/index.js.map +1 -0
- package/package.json +67 -0
package/README.md
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# @ascorbic/pds
|
|
2
|
+
|
|
3
|
+
A single-user [AT Protocol](https://atproto.com) Personal Data Server (PDS) that runs on Cloudflare Workers. Host your own Bluesky identity with minimal infrastructure.
|
|
4
|
+
|
|
5
|
+
> **⚠️ Experimental Software**
|
|
6
|
+
>
|
|
7
|
+
> This is an early-stage project under active development. **Do not migrate your main Bluesky account to this PDS yet.** Use a test account or create a new identity for experimentation. Data loss, breaking changes, and missing features are expected.
|
|
8
|
+
|
|
9
|
+
## What is this?
|
|
10
|
+
|
|
11
|
+
A PDS is where your Bluesky data lives - your posts, follows, profile, and media. This package lets you run your own PDS on Cloudflare's edge network, giving you:
|
|
12
|
+
|
|
13
|
+
- **Your own identity** - Use your own domain as your Bluesky handle
|
|
14
|
+
- **Data ownership** - Your content lives in your Cloudflare account
|
|
15
|
+
- **Federation** - Works with the Bluesky network via relay sync
|
|
16
|
+
- **Low cost** - Runs on Cloudflare's generous free tier
|
|
17
|
+
|
|
18
|
+
## Quick Start
|
|
19
|
+
|
|
20
|
+
The fastest way to get started:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm create pds
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
This scaffolds a new project, installs dependencies, and runs the setup wizard to configure your PDS.
|
|
27
|
+
|
|
28
|
+
Then start the dev server:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
cd pds-worker
|
|
32
|
+
npm run dev
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Manual Installation
|
|
36
|
+
|
|
37
|
+
If you prefer to set things up yourself:
|
|
38
|
+
|
|
39
|
+
### 1. Install the package
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npm install @ascorbic/pds
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### 2. Create a worker entry point
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
// src/index.ts
|
|
49
|
+
export { default, AccountDurableObject } from "@ascorbic/pds";
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### 3. Configure wrangler.jsonc
|
|
53
|
+
|
|
54
|
+
```jsonc
|
|
55
|
+
{
|
|
56
|
+
"name": "my-pds",
|
|
57
|
+
"main": "src/index.ts",
|
|
58
|
+
"compatibility_date": "2024-12-01",
|
|
59
|
+
"compatibility_flags": ["nodejs_compat"],
|
|
60
|
+
"durable_objects": {
|
|
61
|
+
"bindings": [{ "name": "ACCOUNT", "class_name": "AccountDurableObject" }],
|
|
62
|
+
},
|
|
63
|
+
"migrations": [
|
|
64
|
+
{ "tag": "v1", "new_sqlite_classes": ["AccountDurableObject"] },
|
|
65
|
+
],
|
|
66
|
+
"r2_buckets": [{ "binding": "BLOBS", "bucket_name": "pds-blobs" }],
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### 4. Run the setup wizard
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
pnpm pds init
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
This prompts for your hostname, handle, and password, then generates signing keys and writes configuration.
|
|
77
|
+
|
|
78
|
+
## CLI
|
|
79
|
+
|
|
80
|
+
The package includes a CLI for setup and configuration:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
pds init # Interactive setup (writes to .dev.vars)
|
|
84
|
+
pds init --production # Deploy secrets to Cloudflare
|
|
85
|
+
pds secret key # Generate new signing keypair
|
|
86
|
+
pds secret jwt # Generate new JWT secret
|
|
87
|
+
pds secret password # Set account password
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Deploying to Production
|
|
91
|
+
|
|
92
|
+
1. [Enable R2 in your Cloudflare dashboard](https://dash.cloudflare.com/?to=/:account/r2/overview) (the bucket will be created automatically on first deploy).
|
|
93
|
+
|
|
94
|
+
2. Run the production setup to deploy secrets:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
npx pds init --production
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
3. Deploy your worker:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
wrangler deploy
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
4. Configure DNS to point your domain to the worker.
|
|
107
|
+
|
|
108
|
+
## Identity: DIDs and Handles
|
|
109
|
+
|
|
110
|
+
AT Protocol uses two types of identifiers:
|
|
111
|
+
|
|
112
|
+
- **DID** (Decentralized Identifier): Your permanent, cryptographic identity (e.g., `did:web:pds.example.com`). This never changes and is tied to your signing key.
|
|
113
|
+
- **Handle**: Your human-readable username (e.g., `alice.example.com`). This can be any domain you control.
|
|
114
|
+
|
|
115
|
+
The DID document (served at `/.well-known/did.json`) contains your public key and tells the network where your PDS is. The `alsoKnownAs` field links your DID to your handle.
|
|
116
|
+
|
|
117
|
+
### Handle Verification
|
|
118
|
+
|
|
119
|
+
Bluesky verifies that you control your handle domain. There are two methods:
|
|
120
|
+
|
|
121
|
+
#### Option A: Handle matches PDS hostname
|
|
122
|
+
|
|
123
|
+
If your handle is the same as your PDS hostname (e.g., both are `pds.example.com`):
|
|
124
|
+
|
|
125
|
+
- The PDS automatically serves `/.well-known/atproto-did` returning your DID
|
|
126
|
+
- No additional DNS setup needed
|
|
127
|
+
- This is the simplest option
|
|
128
|
+
|
|
129
|
+
#### Option B: Handle on a different domain
|
|
130
|
+
|
|
131
|
+
If you want a handle on a different domain (e.g., handle `alice.example.com` while PDS is at `pds.example.com`):
|
|
132
|
+
|
|
133
|
+
1. Add a DNS TXT record to your handle domain:
|
|
134
|
+
|
|
135
|
+
```
|
|
136
|
+
_atproto.alice.example.com TXT "did=did:web:pds.example.com"
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
2. Verify it's working:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
dig TXT _atproto.alice.example.com
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
This lets you use any domain you own as your Bluesky handle, even your personal website.
|
|
146
|
+
|
|
147
|
+
## Configuration
|
|
148
|
+
|
|
149
|
+
The PDS uses environment variables for configuration. Public values go in `wrangler.jsonc`, secrets are stored via Wrangler or in `.dev.vars` for local development.
|
|
150
|
+
|
|
151
|
+
### Public Variables (wrangler.jsonc)
|
|
152
|
+
|
|
153
|
+
| Variable | Description |
|
|
154
|
+
| -------------------- | ---------------------------------------- |
|
|
155
|
+
| `PDS_HOSTNAME` | Public hostname (e.g., pds.example.com) |
|
|
156
|
+
| `DID` | Account DID (did:web:... or did:plc:...) |
|
|
157
|
+
| `HANDLE` | Account handle |
|
|
158
|
+
| `SIGNING_KEY_PUBLIC` | Public key for DID document (multibase) |
|
|
159
|
+
|
|
160
|
+
### Secrets
|
|
161
|
+
|
|
162
|
+
| Variable | Description |
|
|
163
|
+
| --------------- | ------------------------------------- |
|
|
164
|
+
| `AUTH_TOKEN` | Bearer token for API write operations |
|
|
165
|
+
| `SIGNING_KEY` | Private signing key (secp256k1 JWK) |
|
|
166
|
+
| `JWT_SECRET` | Secret for signing session JWTs |
|
|
167
|
+
| `PASSWORD_HASH` | Bcrypt hash of password for app login |
|
|
168
|
+
|
|
169
|
+
## API Endpoints
|
|
170
|
+
|
|
171
|
+
### Public
|
|
172
|
+
|
|
173
|
+
| Endpoint | Description |
|
|
174
|
+
| ------------------------------------------- | ---------------------------- |
|
|
175
|
+
| `GET /.well-known/did.json` | DID document |
|
|
176
|
+
| `GET /.well-known/atproto-did` | Handle verification |
|
|
177
|
+
| `GET /xrpc/com.atproto.sync.getRepo` | Export repository as CAR |
|
|
178
|
+
| `GET /xrpc/com.atproto.sync.subscribeRepos` | WebSocket firehose |
|
|
179
|
+
| `GET /xrpc/com.atproto.repo.describeRepo` | Repository metadata |
|
|
180
|
+
| `GET /xrpc/com.atproto.repo.getRecord` | Get a single record |
|
|
181
|
+
| `GET /xrpc/com.atproto.repo.listRecords` | List records in a collection |
|
|
182
|
+
|
|
183
|
+
### Authenticated
|
|
184
|
+
|
|
185
|
+
| Endpoint | Description |
|
|
186
|
+
| ---------------------------------------------- | -------------------------- |
|
|
187
|
+
| `POST /xrpc/com.atproto.server.createSession` | Login (returns JWT) |
|
|
188
|
+
| `POST /xrpc/com.atproto.server.refreshSession` | Refresh JWT |
|
|
189
|
+
| `POST /xrpc/com.atproto.repo.createRecord` | Create a record |
|
|
190
|
+
| `POST /xrpc/com.atproto.repo.deleteRecord` | Delete a record |
|
|
191
|
+
| `POST /xrpc/com.atproto.repo.putRecord` | Create or update a record |
|
|
192
|
+
| `POST /xrpc/com.atproto.repo.uploadBlob` | Upload a blob |
|
|
193
|
+
| `POST /xrpc/com.atproto.repo.importRepo` | Import repository from CAR |
|
|
194
|
+
|
|
195
|
+
## Architecture
|
|
196
|
+
|
|
197
|
+
The PDS runs as a Cloudflare Worker with a Durable Object for state:
|
|
198
|
+
|
|
199
|
+
- **Worker**: Handles routing, authentication, and DID document serving
|
|
200
|
+
- **AccountDurableObject**: Stores repository data in SQLite, manages the Merkle tree
|
|
201
|
+
- **R2**: Stores blobs (images, videos)
|
|
202
|
+
|
|
203
|
+
## Limitations
|
|
204
|
+
|
|
205
|
+
- **Single-user only**: One account per deployment
|
|
206
|
+
- **No account creation**: The owner is configured at deploy time
|
|
207
|
+
- **did:web only**: Uses domain-based DIDs (did:plc support planned)
|
|
208
|
+
|
|
209
|
+
## Resources
|
|
210
|
+
|
|
211
|
+
- [AT Protocol Documentation](https://atproto.com)
|
|
212
|
+
- [Bluesky](https://bsky.app)
|
|
213
|
+
- [Cloudflare Workers](https://developers.cloudflare.com/workers/)
|