Skip to content
You are reading the HappyView v2 docs, which are no longer updated. Read the v3 docs.

Architecture

Guide for contributors working on HappyView itself. For a user-facing overview, see the Introduction.

graph LR
  Application

  Application -->|"GET /xrpc/{method}"| Query
  Application -->|"POST /xrpc/{method}"| Procedure

  subgraph HappyView
    Query["Query Handler<br/><small>Lua Script (Optional)</small>"]
    Procedure["Procedure Handler<br/><small>Lua Script (Optional)</small>"]
    Spaces["Spaces<br/><small>Permissioned Data</small>"]
  end

  Procedure --> DB
  Query --> DB
  Spaces --> DB

  Procedure -->|proxy write| PDS["User PDS"]

  DB[("SQLite / PostgreSQL<br/><small>records · lexicons · spaces</small>")]

  Jetstream["Jetstream<br/><small>WebSocket</small>"] -->|record events| DB
  Relay["Relay<br/><small>listReposByCollection</small>"] -->|repo discovery| Backfill
  Backfill["Backfill Worker"] -->|listRecords| PDS
  Backfill --> DB

  Labeler["Labeler<br/><small>WebSocket (out-of-band)</small>"] -->|label events| DB

Queries go through the query handler to the database (SQLite by default, or Postgres). Writes go through the procedure handler to the user’s PDS, then HappyView indexes the record locally. Real-time record events stream in via Jetstream; historical records are backfilled in-process by discovering repos via the relay’s listReposByCollection and fetching records directly from each PDS. Labelers are external services that emit content labels over a direct WebSocket connection — they operate out-of-band, outside the relay/repo system. Spaces provide permissioned data containers with membership-gated access, per-user repo state tracking (LtHash + signed commits), and cross-service credential-based authentication.

sequenceDiagram
    participant C as Client
    participant X as xrpc_get()
    participant R as LexiconRegistry
    participant L as Lua Script
    participant D as Database

    C->>X: GET /xrpc/{method}?params
    X->>R: Lookup (must be Query type)
    alt Lua script attached
        R->>L: Execute script
        L->>D: db.query / db.get / db.raw
        D-->>L: Results
        L-->>X: Response table
    else No script
        R->>D: Default SQL query (collection from target_collection)
        D-->>X: Results
    end
    X-->>C: JSON response
sequenceDiagram
    participant C as Client
    participant A as Claims Extractor
    participant X as xrpc_post()
    participant R as LexiconRegistry
    participant L as Lua Script
    participant S as OAuth Session
    participant P as User PDS
    participant D as Database

    C->>A: POST /xrpc/{method} + DPoP auth + X-Client-Key
    A->>X: Validated claims
    X->>R: Lookup (must be Procedure type)
    alt Lua script attached
        R->>L: Execute script (Record API)
        L->>S: Record:save()
    else No script
        R->>S: Default create/update (auto-detect from uri field)
    end
    S->>P: Proxy write (createRecord or putRecord)
    P-->>S: PDS response
    S->>D: Upsert record locally
    S-->>C: Forward PDS response
sequenceDiagram
    participant C as Client
    participant A as AdminAuth Extractor
    participant U as Users Table
    participant H as Admin Handler
    participant D as Database

    C->>A: Request + Bearer token
    A->>A: Validate claims (API key or service auth JWT)
    A->>U: DID lookup
    alt Users table empty
        U-->>A: Auto-bootstrap as super user
    else User found
        U-->>A: Load permissions
    end
    A->>A: Permission check (403 if missing)
    A->>H: Authorized request
    H->>D: Database operation
    D-->>H: Result
    H-->>C: JSON response
sequenceDiagram
    participant J as Jetstream WebSocket
    participant H as HappyView
    participant D as Database
    participant R as LexiconRegistry

    H->>J: Connect (collection filters from indexed lexicons)
    loop Stream events
        J->>H: Record commit event
        alt create / update
            H->>D: UPSERT into records table
        else delete
            H->>D: DELETE from records table
        end
    end
    J->>H: Lexicon schema event (com.atproto.lexicon.schema)
    H->>D: Update tracked network lexicons
    H->>R: Update in-memory registry
    Note over H,D: Cursor persisted to instance_settings for resume on reconnect
    Note over H,J: Reconnects on collection filter changes (lexicon add/remove)
sequenceDiagram
    participant A as Admin
    participant H as HappyView
    participant D as Database
    participant Relay as Relay
    participant PLC as PLC Directory
    participant PDS as User PDS

    A->>H: POST /admin/backfill
    H->>D: Create backfill_jobs record (status = running)

    rect rgb(240, 248, 255)
    note over H,Relay: Phase 1: Discovery
    H->>Relay: listReposByCollection (paginated)
    Relay-->>H: List of DIDs
    H->>D: Insert backfill_repos rows
    end

    rect rgb(240, 255, 240)
    note over H,PDS: Phase 2: Pipelined resolve + fetch (concurrent)
    par Resolver task
        loop Unresolved DIDs
            H->>PLC: Resolve DID document
            PLC-->>H: PDS endpoint
            H->>D: Update backfill_repos.pds_endpoint
        end
    and Fetcher task
        loop Resolved DIDs (as they arrive)
            H->>PDS: listRecords (paginated)
            PDS-->>H: Records
            H->>D: UPSERT each record
        end
    end
    end

    H->>D: Mark job completed (or failed)
ColumnTypeDescription
uritext (PK)AT URI (at://did/collection/rkey)
didtextAuthor DID
collectiontextLexicon NSID
rkeytextRecord key
recordjsonbRecord value
cidtextContent identifier
indexed_attimestamptzWhen HappyView indexed this record
ColumnTypeDescription
idtext (PK)Lexicon NSID
revisionintegerIncremented on upsert
lexicon_jsonjsonbRaw lexicon definition
lexicon_typetextrecord, query, procedure, definitions
backfillbooleanWhether to backfill on upload
target_collectiontextFor queries/procedures: which record collection
created_attimestamptz
updated_attimestamptz
ColumnTypeDescription
iduuid (PK)
didtext (unique)User’s atproto DID
is_superbooleanWhether this is the super user (only one allowed)
created_attimestamptz
last_used_attimestamptzUpdated on each authenticated request
ColumnTypeDescription
user_iduuid (FK)References users.id
permissiontextPermission string (e.g. lexicons:create)
(PK)Composite primary key: (user_id, permission)
ColumnTypeDescription
iduuid (PK)
user_iduuid (FK)References users.id
nametextDescriptive label
key_hashtextSHA-256 hash of the full key
key_prefixtextFirst 11 characters for display
permissionstext[]Permissions granted to this key
created_attimestamptz
last_used_attimestamptz
revoked_attimestamptzSet when revoked (soft delete)
ColumnTypeDescription
didtext (PK)User’s atproto DID
session_datatextSerialized OAuth session (managed by atrium)
created_attimestamptz
updated_attimestamptz
ColumnTypeDescription
state_keytext (PK)OAuth state parameter
state_datatextSerialized state (managed by atrium)
created_attimestamptz
ColumnTypeDescription
keytext (PK)Setting name (e.g. app_name)
valuetextSetting value
updated_attimestamptzLast modified
ColumnTypeDescription
iduuid (PK)
event_typetextCategory.action format (e.g. user.created)
severitytextinfo, warn, or error
actor_didtextDID of the user who triggered the event
subjecttextWhat was affected (DID, NSID, URI, etc.)
detailjsonbEvent-specific data
created_attimestamptz
ColumnTypeDescription
keytext (PK)Variable name
valuetextVariable value (encrypted at rest)
created_attimestamptz
updated_attimestamptz
ColumnTypeDescription
idtext (PK)Internal space identifier
didtextDID in the space URI; the authority’s DID
authority_didtextThe space authority: the instance DID, or the creator’s DID when the instance has no service identity
creator_didtextDID of the user who created and administers the space
type_nsidtextSpace type as an NSID
skeytextSpace key (differentiates spaces of the same type)
display_nametextHuman-readable name (optional)
descriptiontextDescription (optional)
read_policytext (JSON)Policy gating credential issuance ($type-tagged)
write_policytext (JSON)Policy gating which writers the space tracks ($type-tagged)
app_accesstext (JSON){"$type":"com.atproto.simplespace.defs#open"} or {"$type":"com.atproto.simplespace.defs#allowList","allowed":[...]}
configtext (JSON)Space config (membership_public, records_public, extras)
revisiontextCurrent revision TID
created_attext
updated_attext
ColumnTypeDescription
idtext (PK)
space_idtext (FK)References spaces.id
didtextMember’s DID (or space URI for delegation)
can_readbooleanWhether the member can read the space
can_writebooleanWhether the member can write to the space
read_selfbooleanWhether reads are limited to the member’s own records
is_delegationbooleanWhether this member is a delegated space
granted_bytextDID of who granted membership
created_attext
ColumnTypeDescription
uritext (PK)at:// URI of the record
space_idtext (FK)References spaces.id
author_didtextDID of the record author
collectiontextLexicon NSID
rkeytextRecord key
recordjsonbRecord value
cidtextContent identifier
indexed_attext
ColumnTypeDescription
idtext (PK)
space_idtext (FK)References spaces.id
author_didtextDID of the repo author
lthash_statebytea2048-byte LtHash state
revtextCurrent revision
hashbyteaContent hash
ikmbyteaInput keying material for deniable signatures
sigbyteaSignature
macbyteaMessage authentication code
updated_attext
ColumnTypeDescription
idtext (PK)
space_idtext (FK)References spaces.id
author_didtextDID of the operation author
revtextRevision this operation belongs to
idxintegerIndex within the revision
actiontextcreate, update, or delete
collectiontextLexicon NSID
rkeytextRecord key
cidtextContent identifier (for create/update)
prevtextPrevious CID (for update/delete)
created_attext
ColumnTypeDescription
idtext (PK)
space_idtext (FK)References spaces.id
servicetextSubscribing service; one registration per space
endpointtextResolved service endpoint, or webhook URL
deliverytextxrpc (notifyWrite calls) or webhook (legacy payload)
registered_bytextDID of who registered
expires_attextWhen the registration expires
created_attext
ColumnTypeDescription
space_idtext (FK)References spaces.id
repo_didtextDID of a repo the space tracks
revtextThe repo’s latest revision
hashblobThe repo’s latest LtHash digest
space_revtextSpace revision assigned to this update
updated_attext
ColumnTypeDescription
issuertextIssuer of an exchanged delegation token
jtitextThe token’s jti; a token is exchanged once
expires_attextWhen the token expires
ColumnTypeDescription
idtext (PK)
space_idtext (FK)References spaces.id
token_hashtextSHA-256 hash of the invite token
created_bytextDID of the user who created the invite
accesstextAccess level granted: read, read_self, write
max_usesinteger?Maximum number of uses (null = unlimited)
usesintegerCurrent use count
expires_attext?Expiry timestamp (null = never)
revokedbooleanWhether the invite has been revoked
created_attext
ColumnTypeDescription
idtext (PK)
space_idtext (FK)References spaces.id
issued_totextDID the credential was issued to
token_hashtextHash of the credential token
jtitextThe credential’s jti claim
expires_attextWhen the credential expires
revoked_attext?When the credential was revoked
created_attext
ColumnTypeDescription
idtext (PK)
didtextThe space’s DID
space_idtext (FK)References spaces.id
signing_key_enctextEncrypted signing key (AES-256-GCM)
rotation_key_enctextEncrypted rotation key (AES-256-GCM)
created_bytextDID of who provisioned the key
created_attext
ColumnTypeDescription
idinteger (PK)Always 1 (singleton)
modetextdid_web, did_plc, or linked_account
didtextThe service’s DID
signing_key_enctextEncrypted signing key
rotation_key_enctext?Encrypted rotation key (did:plc only)
attached_account_didtext?Linked account DID (linked_account mode)
setup_completebooleanWhether setup has been finalized
created_attext
updated_attext
ColumnTypeDescription
idinteger (PK)
fragment_idtextDID document fragment identifier
service_typetextService type (e.g. AtprotoAppView)
access_modetextall or scoped to specific XRPCs
created_attext
updated_attext
ColumnTypeDescription
service_entry_idinteger (FK)References service_entries.id
lexicon_idtextLexicon NSID this entry handles
ColumnTypeDescription
idtext (PK)
fragment_idtextDID document fragment (e.g. #atproto_space)
key_typetextAlways Multikey
public_key_multibasetextPublic key in multibase encoding
private_key_enctextEncrypted private key (AES-256-GCM)
created_attext
ColumnTypeDescription
iduuid (PK)
collectiontextTarget collection (null = all)
didtextTarget DID (null = all)
statustextpending, running, pausing, paused, cancelling, cancelled, completed, failed
stagetextpending, discovering_repos, resolving_and_fetching, completed, failed, cancelled
total_reposintegerTotal DIDs discovered
resolved_reposintegerDIDs with PDS endpoint resolved
processed_reposintegerDIDs with records fetched
total_recordsintegerTotal records indexed
errortextError message if failed
started_attimestamptz
completed_attimestamptz
created_attimestamptz
Terminal window
# Unit tests (no database needed)
cargo test --lib
# All tests including end-to-end (SQLite by default)
cargo test
# Or run against Postgres
docker compose -f docker-compose.test.yml up -d
TEST_DATABASE_URL=postgres://happyview:happyview@localhost:5433/happyview_test cargo test
docker 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.