Reads flow top-down through the query handler to the database (SQLite by default, or Postgres). Writes flow through the procedure handler to the user's PDS, then HappyView indexes the record locally. All record data enters the system through Tap, which handles both real-time firehose events and historical backfill. HappyView syncs collection filters to Tap and discovers repos via the relay for backfill, but Tap performs all record fetching.
Client GET /xrpc/{method}?params
-> xrpc::xrpc_get()
-> LexiconRegistry lookup (must be Query type)
-> If Lua script attached: execute script (has access to db API)
-> Else: default SQL query on records table (collection from target_collection)
-> JSON response
Client POST /xrpc/{method} + session cookie or Bearer token
-> Claims extractor (cookie, API key, or service auth JWT)
-> xrpc::xrpc_post()
-> LexiconRegistry lookup (must be Procedure type)
-> If Lua script attached: execute script (has access to Record API)
-> Else: default create/update (auto-detect based on uri field)
-> Restore OAuth session from atrium store (by DID)
-> atrium handles DPoP proof generation and token refresh
-> Proxy to user's PDS (createRecord or putRecord)
-> Upsert record locally
-> Forward PDS response
Client request + session cookie or Bearer token
-> AdminAuth extractor:
1. Claims validation (cookie, API key, or service auth JWT)
2. DID lookup in users table (auto-bootstrap super user if empty)
3. Permission check (403 if missing required permission)
-> Admin handler
-> JSON response
Tap WebSocket connection (tap::spawn)
-> Collection filters synced to Tap on startup and lexicon changes
-> Record events:
create/update -> UPSERT into records table
delete -> DELETE from records table
-> Lexicon schema events (com.atproto.lexicon.schema):
-> Update tracked network lexicons in DB and registry
-> Reconnects automatically on errors or collection filter changes
POST /admin/backfill
-> Create backfill_jobs record (status = running)
-> Relay listReposByCollection -> list of DIDs
-> Send DIDs to Tap in batches of 1000 (POST /repos/add)
-> Mark job as completed
-> Tap fetches records asynchronously and delivers via WebSocket
# Unit tests (no database needed)cargo test --lib# All tests including end-to-end (SQLite by default)cargo test# Or run against Postgresdocker compose -f docker-compose.test.yml up -dTEST_DATABASE_URL=postgres://happyview:happyview@localhost:5433/happyview_test cargo testdocker compose -f docker-compose.test.yml down
End-to-end tests use wiremock to mock external services (PLC directory, PDSes) and a real database for full integration coverage. By default tests use SQLite; set TEST_DATABASE_URL to a Postgres connection string to test against Postgres.