Vow, uncensorable PDS written in Go

docs: update readme to fit upcomming refactor

+7 -14
+7 -14
readme.md
··· 9 9 10 10 - ✅ **IPFS storage** — repo blocks and blobs are stored on a local Kubo node and indexed in SQLite by DID and CID. 11 11 - ✅ **Keyless PDS** — the server never stores a private key. Every write is signed by the user's secp256k1 key in their Ethereum wallet (Rabby, MetaMask, etc.). 12 - - ✅ **Browser signer** — the account page connects over WebSocket and signs commits with the user's Ethereum wallet. No browser extension is needed; just keep the tab open. Standard ATProto clients do not need to know about it. 12 + - ✅ **Browser signer** — the account page connects over WebSocket and signs repo commits and PLC operations with the user's Ethereum wallet. No browser extension is needed; just keep the tab open. Standard ATProto clients do not need to know about it. 13 13 - ✅ **User-controlled DID** — when the user registers a key, the PDS transfers the `did:plc` rotation key to the user's wallet. After that, only the user can change their identity. 14 14 - 🔜 **x402 payments for IPFS storage** — blob uploads will be gated by on-chain payments via the [x402 protocol](https://x402.org), using the same Ethereum wallet. 15 15 ··· 146 146 147 147 ### BYOK (Bring Your Own Key) 148 148 149 - The PDS **never stores or uses a private key**. Every write from the Bluesky app, Tangled, or any other standard ATProto client is held open until the user's Ethereum wallet returns a signature through the browser signer on the account page. 149 + The PDS holds one key — the **rotation key** — used only for lightweight PDS-level operations (service-auth JWTs, genesis DID creation, and PLC operations before the user has registered their wallet). It is never used to sign user content. 150 + 151 + Every repo write from the Bluesky app, Tangled, or any other standard ATProto client is held open until the user's Ethereum wallet returns a signature through the browser signer on the account page. 150 152 151 153 #### End-to-end signing flow 152 154 ··· 169 171 170 172 **What requires a wallet signature:** 171 173 172 - Only operations that change the user's repo or identity need a wallet signature: 174 + Only operations that change the user's repo content, or identity operations after the user has taken ownership of their rotation key, need a wallet signature: 173 175 174 176 - **Repo writes** — `createRecord`, `putRecord`, `deleteRecord`, `applyWrites` 175 - - **Identity operations** — PLC operations, handle updates 177 + - **Identity operations** — PLC operations and handle updates, **once the user's wallet key is the rotation key**. Before that, the PDS rotation key signs them directly. 176 178 - **x402 payments** — EIP-712 payment authorisations for gated pinning 177 179 178 - Read-only operations (browsing feeds, loading profiles, fetching notifications, etc.) do **not** prompt the wallet. The PDS proxies them to the AppView using cached service-auth JWTs — see [Service auth caching](#service-auth-caching) below. 179 - 180 - #### Service auth caching 181 - 182 - In standard ATProto, the PDS signs service-auth JWTs itself because it holds the signing key. In Vow, the signing key is in the user's wallet, so without caching, every proxied request (feeds, profiles, notifications, and so on) would trigger a wallet prompt. 183 - 184 - Vow solves this by **caching service-auth JWTs**. When the proxy needs a token for an `(aud, lxm)` pair, it checks an in-memory cache first. Only a cache miss triggers a wallet signing request. Cached tokens live for 30 minutes with a 15-second reuse margin, so in practice the wallet is prompted at most **once every ~30 minutes per remote service endpoint** instead of on every request. 185 - 186 - Tokens requested explicitly via `com.atproto.server.getServiceAuth` (where the caller controls the expiry) bypass the cache and always go to the wallet. 180 + Read-only operations (browsing feeds, loading profiles, fetching notifications, etc.) do **not** prompt the wallet. Service-auth JWTs for proxied requests are signed directly by the PDS rotation key — fast, in-process, no wallet involved. 187 181 188 182 #### WebSocket connection 189 183 ··· 345 339 - [x] `com.atproto.server.requestEmailConfirmation` 346 340 - [x] `com.atproto.server.requestEmailUpdate` 347 341 - [x] `com.atproto.server.requestPasswordReset` 348 - - [x] `com.atproto.server.reserveSigningKey` 349 342 - [x] `com.atproto.server.resetPassword` 350 343 - [x] `com.atproto.server.updateEmail` 351 344