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

CLAUDE.md#

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview#

This is an ATProto OAuth application monorepo with separate frontend and backend packages. The backend is an XRPC server built with Elysia, and the frontend is built with React Router v7 (SSR) handling OAuth authentication.

Current Status: Core functionality implemented. OAuth authentication working on client-side (SSR), XRPC server operational, post creation and storage functional. This is a full-stack ATProto microblogging application with ATProto-native architecture.

Monorepo Structure#

This project is organized as a Bun workspace monorepo:

watproto/
├── packages/
│   ├── server/              # XRPC API server (ATProto endpoints, post storage)
│   │   ├── src/             # Server source code
│   │   │   ├── db/          # Database migrations and schema
│   │   │   └── lib/         # XRPC server, ID resolver, utilities
│   │   ├── data/            # SQLite database files
│   │   ├── CLAUDE.md        # Server-specific documentation
│   │   └── package.json     # Server dependencies
│   ├── client/              # React Router v7 frontend (SSR with OAuth)
│   │   ├── app/             # React Router application code
│   │   │   ├── routes/      # File-based routes (login, editor, oauth)
│   │   │   ├── components/  # React components (Header, PostFeed, etc.)
│   │   │   └── lib/         # OAuth client, XRPC client, DB, utilities
│   │   ├── data/            # SQLite database (OAuth state storage)
│   │   ├── CLAUDE.md        # Client-specific documentation
│   │   └── package.json     # Client dependencies
│   └── lexicon/             # ATProto lexicon type definitions
│       ├── types/           # Generated TypeScript types
│       ├── index.ts         # Lexicon exports
│       └── package.json     # Package configuration
├── lexicons/                # ATProto lexicon schemas (JSON)
├── lex.config.js            # Lexicon generation configuration
├── lexicons.json            # Lexicon metadata
├── package.json             # Workspace root configuration
├── tsconfig.root.json       # Base TypeScript configuration
├── CLAUDE.md                # This file (monorepo-wide documentation)
└── README.md                # Project setup and usage instructions

Current Architecture:

React Router Frontend (SSR) - packages/client
    ├── OAuth Client (ATProto OAuth flow)
    ├── Session Management (SQLite state DB)
    └── XRPC Client
           ↓ XRPC calls (ATProto RPC protocol)
XRPC Server - packages/server
    ├── Post Storage (public/private posts, tags)
    ├── Account Management
    └── ID Resolution
           ↓ ATProto operations
ATProto Network

Common Commands#

Typical Development Workflow#

  1. Terminal 1 (Server): cd packages/server && bun run dev
  2. Terminal 2 (Client): cd packages/client && bun run dev
  3. Access: Open http://127.0.0.1:5173 in browser

Workspace Commands (from root)#

  • bun install - Install dependencies for all packages
  • bun run dev - Start both server and client in development mode
  • bun run dev:server - Start server only (port 3000)
  • bun run dev:client - Start client only (port 5173)
  • bun run build - Build client for production
  • bun run typecheck - Type check all packages
  • bun run lex - Generate TypeScript types from ATProto lexicons

Server Commands (from packages/server)#

  • bun run dev - Start development server with hot reload on port 3000
  • bun run start - Start production server
  • bun run debug - Start server with Bun debugger
  • bun run db:codegen - Regenerate database schema types from database
  • bun run db:migrate - Run database migrations manually
  • bun run typecheck - Run TypeScript type checking

Client Commands (from packages/client)#

  • bun run dev - Start development server with hot reload (port 5173)
  • bun run build - Build production bundle
  • bun run start - Start production server
  • bun run lint - Run ESLint
  • bun run typecheck - Run TypeScript type checking

Architecture#

Core Stack#

Backend (packages/server):

  • Runtime: Bun
  • Web Framework: Elysia (type-safe, fast web framework)
  • Protocol: XRPC (ATProto RPC) via @atcute/xrpc-server
  • Database: SQLite with Kysely query builder
  • Features: Post storage, account management, ID resolution
  • TypeScript: Strict mode enabled with path aliases

Frontend (packages/client):

  • Runtime: Bun
  • Framework: React Router v7 (file-based routing, SSR)
  • OAuth: ATProto OAuth Client (Node implementation) - runs in SSR
  • Database: SQLite for OAuth session state (server-side)
  • Protocol: XRPC client via @atcute/client
  • Identity Resolution: @atcute/identity-resolver + @atcute/identity (DID/handle resolution)
  • UI Library: React 19
  • Styling: Tailwind CSS v4 + DaisyUI
  • Build Tool: Vite
  • TypeScript: Strict mode enabled with path aliases

Shared (packages/lexicon):

  • Purpose: ATProto lexicon type definitions
  • Generator: @atcute/lex-cli
  • Source: Lexicon schemas in lexicons/ directory

Deployment Strategy:

  • Client: SSR deployment (Vercel, Netlify with SSR, Cloudflare Pages)
    • Note: Requires SSR support due to OAuth and database
  • Server: Node/Bun hosting (Fly.io, Railway, etc.)
  • Communication: XRPC protocol (not REST)

OAuth Flow Architecture#

Client implements ATProto OAuth in SSR (see packages/client/CLAUDE.md for details):

  1. Login: User visits /login route, enters handle
  2. OAuth Flow: React Router SSR initiates OAuth with ATProto
  3. Callback: /oauth/$ route handles callback, stores session in SQLite
  4. Session: Session stored in client's database (data/state.db)
  5. Protected Routes: Loader functions check session before rendering

Architecture Notes:

  • OAuth client runs in React Router's SSR layer (Node.js context)
  • Session state stored in SQLite database on client server
  • Uses @atproto/oauth-client-node for ATProto OAuth flow
  • XRPC client uses authenticated sessions for API calls to server

Database Architecture#

Server Database (packages/server/data/):

  • Location: SQLite with WAL mode
  • Tables:
    • accounts - User accounts with DID, handle, metadata
    • public_posts - Public posts visible to all
    • private_posts - Private posts for account holders
    • tags - Tag associations for posts
  • Details: See packages/server/CLAUDE.md for full schema

Client Database (packages/client/data/):

  • Location: SQLite database for OAuth state
  • Purpose: Store OAuth sessions, states, and tokens
  • Implementation: Managed by @atproto/oauth-client-node
  • Details: See packages/client/CLAUDE.md for session management

Environment Variables#

Server (packages/server/.env.local):

  • PORT - Server port (default: 3000)
  • DATABASE_URL - SQLite database path (default: ':memory:')
  • SERVICE_DID - DID that points to this service (required)

Client (packages/client/.env.local):

  • CLIENT_URL - Client URL (optional)
  • API_URL - XRPC server API URL (required, e.g., http://localhost:3000)
  • DEFAULT_PDS_URL - Default PDS URL for OAuth (required)
  • DB_PATH - SQLite database path (default: './data/state.db')
  • PORT - Client server port (default: 5173)
  • AUTH_SECRET - Secret for session encryption (required)
  • KEYSERVER_DID - DID that poins to a Keyserver for encryption / decryption (required)

Package-Specific Documentation#

For detailed implementation information, refer to the package-specific CLAUDE.md files:

  • Server: packages/server/CLAUDE.md

    • XRPC server implementation
    • Database patterns and migrations
    • Post storage and retrieval
    • Social graph (follow relationships)
    • Profile management (wafrn-specific + caching)
    • Account management (operational data)
    • ID resolution
  • Client: packages/client/CLAUDE.md

    • React Router v7 routing patterns
    • OAuth authentication flow (SSR)
    • XRPC client integration
    • Component architecture
    • Session management
    • Styling with Tailwind CSS + DaisyUI
  • Lexicon: packages/lexicon/

    • ATProto lexicon type definitions
    • Generated from schemas in lexicons/ directory
    • Shared types across server and client

Working with the Monorepo#

Adding New Features#

Backend API Endpoint:

  1. Navigate to packages/server
  2. Create route handler in src/ (or add to existing routes)
  3. Test with bun run dev
  4. See packages/server/CLAUDE.md for patterns

Frontend Feature:

  1. Navigate to packages/client
  2. Create route in app/routes/ (file-based routing)
  3. Create components in app/components/
  4. Test with bun run dev
  5. See packages/client/CLAUDE.md for patterns

Path Aliases#

The monorepo uses TypeScript path aliases for cleaner imports:

Configured in tsconfig.root.json:

  • @api/*packages/server/src/* - Server internal imports
  • @www/*packages/client/app/* - Client internal imports

Server Usage (packages/server):

import { db } from '@api/db/db'
import env from '@api/lib/env'
import { createOrUpdateAccount } from '@api/lib/account'
import { xrpcServer } from '@api/lib/xrpcServer'

Client Usage (packages/client):

// Client internal imports
import { Header } from '@www/components/Header'
import { xrpcClient } from '@www/lib/xrpcClient'

// Import server types (type-only imports)
import type { Account } from '@api/db/schema'

Lexicon Usage (both packages):

import { Watproto } from '@watproto/lexicon'

Benefits:

  • Clean, consistent import paths across the monorepo
  • No relative path traversal (../../..)
  • Type sharing between packages via workspace dependencies
  • Vite and Bun automatically resolve these paths

Note: Lexicon package is a workspace dependency used by both server and client.

Current Features & Future Expansion#

Implemented#

  • ✅ ATProto OAuth authentication (client-side SSR)
  • ✅ Session management with SQLite
  • ✅ XRPC server and client
  • ✅ Post creation and storage (public and private)
  • ✅ Post deletion (transactional, removes post and associated tags)
  • ✅ Tag system for posts
  • ✅ User authentication flow
  • ✅ UI components
    • Header, PostFeed, UserMenu
    • GlobalSpinner (navigation state feedback)
    • Delete post button (visible to post authors only)
  • ✅ Lexicon type generation
  • ✅ Social graph (follow relationships)
    • Follow/unfollow operations with transaction-safe count updates
    • Follower and following list queries (returns DIDs for batch enrichment)
    • Denormalized follow counts for fast profile queries
    • Federation-friendly (no foreign key constraints)
  • ✅ Profile system (separated architecture)
    • Operational account data (roles, status, moderation)
    • Wafrn-specific profile customization (HTML bio, custom fields)
    • Optional standard profile caching for performance
    • Batch profile queries for efficient rendering

Planned Features#

  • Job Queue & Workers: Background processing for posts, firehose consumption, timelines
  • Real-time Features: WebSocket support for notifications and live updates
  • Media Upload: Image and video upload with ATProto blob storage
  • Timeline Algorithm: Personalized feed ranking and filtering
  • Search: Full-text search for posts and user profiles
  • Multi-Account Support: Support being logged in to more than one account at the same time

See package-specific CLAUDE.md files for detailed implementation plans.

Getting Started with your local copy#

  1. Clone and install:

    git clone <repo-url>
    cd watproto
    bun install
    
  2. Configure environment variables: use the examples in this file

  3. Start development:

    # Terminal 1: Start server
    cd packages/server
    bun run dev
    
    # Terminal 2: Start client
    cd packages/client
    bun run dev
    
  4. Access the application:

    • Client: http://127.0.0.1:5173
    • Server API: http://localhost:4000
    • API Docs: http://localhost:4000/openapi

Troubleshooting#

  • Port conflicts: Change PORT in server .env.local or client will use next available port
  • CORS errors: Ensure server CORS is configured to allow client origin
  • Type errors: Run bun install at root to sync workspace dependencies
  • Database issues: If your local packages/server/data/*.db* files do not have important information you can just delete them and restart your server. Database files will be created from scratch.

For package-specific issues, see the CLAUDE.md file in that package.