Testing implementation for private data in ATProto with ATPKeyserver and ATCute tools

update documentation

+111 -2
+6 -1
CLAUDE.md
··· 107 107 - **OAuth**: ATProto OAuth Client (Node implementation) - runs in SSR 108 108 - **Database**: SQLite for OAuth session state (server-side) 109 109 - **Protocol**: XRPC client via @atcute/client 110 + - **Identity Resolution**: @atcute/identity-resolver + @atcute/identity (DID/handle resolution) 110 111 - **UI Library**: React 19 111 112 - **Styling**: Tailwind CSS v4 + DaisyUI 112 113 - **Build Tool**: Vite ··· 260 261 - ✅ Session management with SQLite 261 262 - ✅ XRPC server and client 262 263 - ✅ Post creation and storage (public and private) 264 + - ✅ Post deletion (transactional, removes post and associated tags) 263 265 - ✅ Tag system for posts 264 266 - ✅ User authentication flow 265 - - ✅ Basic UI components (Header, PostFeed, UserMenu) 267 + - ✅ UI components 268 + - Header, PostFeed, UserMenu 269 + - GlobalSpinner (navigation state feedback) 270 + - Delete post button (visible to post authors only) 266 271 - ✅ Lexicon type generation 267 272 - ✅ Social graph (follow relationships) 268 273 - Follow/unfollow operations with transaction-safe count updates
+42 -1
packages/client/CLAUDE.md
··· 53 53 - **OAuth**: @atproto/oauth-client-node (ATProto OAuth) 54 54 - **Database**: SQLite (OAuth state storage) 55 55 - **Protocol**: XRPC via @atcute/client 56 + - **Identity Resolution**: @atcute/identity-resolver + @atcute/identity (DID/handle resolution) 56 57 - **UI Library**: React 19 57 58 - **Styling**: Tailwind CSS v4 + DaisyUI 58 59 - **Build Tool**: Vite ··· 73 74 │ ├── components/ # React components 74 75 │ │ ├── Header.tsx # App header with navigation 75 76 │ │ ├── UserMenu.tsx # User dropdown menu 76 - │ │ ├── PostFeed.tsx # Post list component 77 + │ │ ├── PostFeed.tsx # Post list component with delete button 78 + │ │ ├── GlobalSpinner.tsx # Navigation state progress indicator 77 79 │ │ └── FlashMessages.tsx # Toast notifications 78 80 │ ├── lib/ # Server-side utilities (.server.ts) 79 81 │ │ ├── oauth.server.ts # OAuth client setup ··· 189 191 ) 190 192 } 191 193 ``` 194 + 195 + ### UI Components 196 + 197 + **GlobalSpinner** (`components/GlobalSpinner.tsx`): 198 + - Displays a progress bar during navigation transitions 199 + - Uses React Router's `useNavigation()` hook to track navigation state 200 + - Styled with DaisyUI progress component 201 + - Integrated into root layout for global navigation feedback 202 + 203 + **PostFeed** (`components/PostFeed.tsx`): 204 + - Displays a list of posts with author info, content, tags, and timestamps 205 + - Shows delete button for posts authored by the current user 206 + - Delete action handled via React Router Form with POST method 207 + - Uses XRPC `app.wafrn.content.deletePost` endpoint 208 + - Responsive card-based layout with DaisyUI styling 209 + 210 + ### Identity Resolution 211 + 212 + **ATProto Identity Resolution** (`lib/idResolver.server.ts`): 213 + The client uses @atcute/identity-resolver and @atcute/identity for resolving ATProto identities: 214 + 215 + - **Handle Resolution**: Composite resolver with DNS (via DoH) and HTTP (.well-known) methods 216 + - Uses race strategy for fastest response 217 + - DNS resolution via Cloudflare's DoH JSON API 218 + - HTTP resolution via .well-known/atproto-did 219 + 220 + - **DID Document Resolution**: Supports PLC and Web DIDs 221 + - PLC DIDs: Resolved via PLC directory 222 + - Web DIDs: Resolved via HTTPS .well-known endpoint 223 + 224 + **Utility Functions**: 225 + - `atUriToDid(uri)` - Extract DID from AT-URI 226 + - `didToHandle(did)` - Resolve DID to handle via DID document 227 + 228 + **Benefits of @atcute packages**: 229 + - More modular than @atproto/identity 230 + - Configurable resolution strategies 231 + - Better error handling 232 + - Consistent with other @atcute packages 192 233 193 234 ### Authentication Flow 194 235
+63
packages/server/CLAUDE.md
··· 71 71 │ │ ├── account.ts # Account management logic 72 72 │ │ ├── follow.ts # Follow relationship management 73 73 │ │ ├── profile.ts # Profile management (wafrn-specific + cache) 74 + │ │ ├── post.ts # Post operations (delete, etc.) 75 + │ │ ├── auth.ts # Service authentication (JWT generation) 74 76 │ │ ├── xrpcServer.ts # XRPC server instance and configuration 75 77 │ │ └── idResolver.ts # ATProto identity resolution 76 78 │ └── db/ ··· 106 108 - Create and store public posts 107 109 - Create and store private posts 108 110 - Retrieve posts by account or tag 111 + - Delete posts (transactional, removes post and associated tags) 109 112 - Tag management for posts 110 113 111 114 2. **Social Graph**: ··· 186 189 - `PORT` - Server port (optional, default: 3000) 187 190 - `DATABASE_URL` - SQLite database path (optional, default: ':memory:') 188 191 - `SERVICE_DID` - DID that points to this service (required) 192 + - `SERVER_SIGNING_PRIVATE_KEY` - Multikey-encoded P256 private key for service authentication (required) 189 193 190 194 ## Key Implementation Details 191 195 ··· 302 306 - Private key JWT authentication with ES256 303 307 - DPoP-bound access tokens enabled 304 308 309 + ### Post Management Patterns 310 + 311 + **Delete Post** (`lib/post.ts`): 312 + The `deletePost()` function handles transactional deletion of posts: 313 + 314 + ```typescript 315 + import { deletePost } from '@api/lib/post' 316 + 317 + // Delete a post (checks authorization via authorDid) 318 + const deletedUri = await deletePost( 319 + 'at://did:plc:abc123/app.wafrn.content.publicPost/tid123', 320 + 'did:plc:abc123' 321 + ) 322 + // Returns URI if deleted, null if not found or unauthorized 323 + ``` 324 + 325 + **Implementation details**: 326 + - Validates URI format using `parseResourceUri()` 327 + - Checks post collection type (public or private) 328 + - Runs deletion in transaction (post + tags for public posts) 329 + - Verifies author ownership before deletion 330 + - Returns deleted URI or null 331 + 332 + ### Service Authentication 333 + 334 + **JWT Generation** (`lib/auth.ts`): 335 + The server can authenticate itself to other services (like keyserver) using service JWTs: 336 + 337 + ```typescript 338 + import { generateServiceAuthToken } from '@api/lib/auth' 339 + 340 + // Generate service auth token for calling another service 341 + const token = await generateServiceAuthToken( 342 + 'did:plc:keyserver123', // audience DID 343 + 'com.example.method' // optional XRPC method 344 + ) 345 + 346 + // Use in Authorization header 347 + const response = await fetch(serviceUrl, { 348 + headers: { 349 + 'Authorization': `Bearer ${token}` 350 + } 351 + }) 352 + ``` 353 + 354 + **Key Functions**: 355 + - `getPrivateKeyFromEnv()` - Load P256 private key from environment 356 + - `generateServiceAuthToken(audience, method?)` - Create signed JWT 357 + - Uses ES256 algorithm 358 + - 60-second expiration 359 + - Includes service DID as issuer 360 + - Optional XRPC method in `lxm` claim 361 + 362 + **Environment Requirements**: 363 + - `SERVER_SIGNING_PRIVATE_KEY` - Multikey-encoded P256 private key 364 + - `SERVICE_DID` - DID of this service (issuer) 365 + 305 366 ### Database Patterns 306 367 - Use `Insertable<TableName>` type for insert operations 307 368 - Upsert pattern: `.onConflict((oc) => oc.columns([...]).doUpdateSet(...))` 308 369 - Reference excluded values in upserts: `data.ref('excluded.column_name')` 370 + - Use transactions for operations that modify multiple tables (e.g., delete post + tags) 309 371 310 372 ### Migration Workflow 311 373 1. Create migration: manually create timestamped file in `src/db/migrations/` ··· 324 386 - `watproto.post.create` - Create new public or private post 325 387 - `watproto.post.list` - List posts by account 326 388 - `watproto.post.listByTag` - List posts filtered by tag 389 + - `app.wafrn.content.deletePost` - Delete cached post from AppView (public or private) 327 390 328 391 **Account Management**: 329 392 - `watproto.account.create` - Create or update account record