···11# HappyView
2233-HappyView is the best way to build an [AppView](https://atproto.com/guides/glossary#app-view) for the [AT Protocol](https://atproto.com). Upload your [lexicon](reference/glossary.md#at-protocol-terms) schemas and get a fully functional AppView, complete with [XRPC](reference/glossary.md#at-protocol-terms) endpoints, OAuth, real-time network sync, and historical [backfill](guides/backfill.md), without writing a single line of server code.
33+HappyView is the best way to build an [AppView](https://atproto.com/guides/glossary#app-view) for the [AT Protocol](https://atproto.com). Upload your [lexicon](reference/glossary.md#atproto-terms) schemas and get a fully functional AppView, complete with [XRPC](reference/glossary.md#atproto-terms) endpoints, OAuth, real-time network sync, and historical [backfill](guides/backfill.md), without writing a single line of server code.
4455Building an AppView from scratch means wiring up real-time event streams, record storage, XRPC routing, OAuth flows, and PDS write proxying before you can even think about your application. HappyView handles all of that. Define your data model with lexicons, add custom logic with Lua scripts when you need it, and ship your app.
66···8899- **Schema-driven endpoints:** Upload a [lexicon](guides/lexicons.md) and HappyView generates XRPC query and procedure routes, storage, and indexing from it — updatable at runtime with no restart.
10101111-- **Network sync built in:** Real-time record streaming via [Jetstream](https://github.com/bluesky-social/jetstream), historical [backfill](guides/backfill.md) from each user's PDS, and AT Protocol OAuth with DPoP-bound proxy writes back to the PDS.
1111+- **Network sync built in:** Real-time record streaming via [Jetstream](https://github.com/bluesky-social/jetstream), historical [backfill](guides/backfill.md) from each user's PDS, and atproto OAuth with DPoP-bound proxy writes back to the PDS.
12121313- **Customize with Lua, hooks, and plugins:** [Lua scripts](guides/scripting.md) for query and procedure logic, [index hooks](guides/index-hooks.md) that fire on every record change, WASM [plugins](guides/plugins.md) for external platform integration, and [labeler](guides/labelers.md) subscriptions for content moderation.
1414···24242525- **Runtime-configurable**: Lexicons can be added, updated, and removed without restarting the server. New endpoints and sync rules take effect immediately, so you can iterate on your data model in real time.
26262727-- **Protocol-native**: HappyView works with _any_ PDS, resolves DIDs through the directory, and follows AT Protocol conventions. It's a first-class citizen of the network, not a wrapper around it.
2727+- **Protocol-native**: HappyView works with _any_ PDS, resolves DIDs through the directory, and follows atproto conventions. It's a first-class citizen of the network, not a wrapper around it.
28282929## Next Steps
3030
···2233HappyView has two distinct authentication surfaces:
4455-- **XRPC** (`/xrpc/*`) — client-level identification via an **API client key** on every request, plus optional user-level AT Protocol OAuth for endpoints that need a specific user's identity (e.g. procedures that write to a PDS).
55+- **XRPC** (`/xrpc/*`) — client-level identification via an **API client key** on every request, plus optional user-level atproto OAuth for endpoints that need a specific user's identity (e.g. procedures that write to a PDS).
66- **Admin API** (`/admin/*`) — user-level authentication via admin API keys or service auth JWTs, gated by [permissions](../guides/permissions.md).
7788## Which endpoints require what?
···49495050### Authenticating users for procedures
51515252-Queries that don't care who is calling need nothing more than the client key. Procedures — and queries whose Lua scripts read the caller's DID — need a real AT Protocol OAuth session.
5252+Queries that don't care who is calling need nothing more than the client key. Procedures — and queries whose Lua scripts read the caller's DID — need a real atproto OAuth session.
53535454XRPC routes only accept **DPoP auth** (`Authorization: DPoP <token>` + `DPoP` proof header + `X-Client-Key`). Bearer tokens and service auth JWTs are not accepted on XRPC endpoints.
5555···103103104104### Service auth JWT
105105106106-HappyView also accepts standard AT Protocol inter-service auth JWTs in the `Authorization` header. Another AppView, relay, or PDS can sign a short-lived ES256 or ES256K JWT with its DID's signing key; HappyView resolves the issuer's DID document, verifies the signature against the `#atproto` verification method, and treats the issuer DID as the caller identity.
106106+HappyView also accepts standard atproto inter-service auth JWTs in the `Authorization` header. Another AppView, relay, or PDS can sign a short-lived ES256 or ES256K JWT with its DID's signing key; HappyView resolves the issuer's DID document, verifies the signature against the `#atproto` verification method, and treats the issuer DID as the caller identity.
107107108108For a service auth JWT to validate:
109109···186186187187#### 2. Run OAuth with the user's PDS
188188189189-Use the provisioned DPoP key as your DPoP keypair in a standard AT Protocol OAuth flow with the user's PDS. HappyView is not involved in this step — the app talks directly to the PDS authorization server.
189189+Use the provisioned DPoP key as your DPoP keypair in a standard atproto OAuth flow with the user's PDS. HappyView is not involved in this step — the app talks directly to the PDS authorization server.
190190191191#### 3. Register the session
192192
+5-5
packages/docs/docs/getting-started/dashboard.md
···11# Dashboard
2233-HappyView ships with a web dashboard that provides a visual interface for everything the [admin API](../reference/admin-api.md) offers. It runs as a separate Next.js application alongside the Rust backend and authenticates via AT Protocol OAuth.
33+HappyView ships with a web dashboard that provides a visual interface for everything the [admin API](../reference/admin-api.md) offers. It runs as a separate Next.js application alongside the Rust backend and authenticates via atproto OAuth.
4455On a fresh deployment with no users in the database, the first person to log in to the dashboard is automatically bootstrapped as the super user with all permissions — so log in with the handle you want to own the instance first.
66···23232424Toggle **Enable backfill** to index historical records when uploading a record-type lexicon.
25252626-**Network** lexicons are fetched from the AT Protocol network. Enter an NSID (e.g. `xyz.statusphere.status`) and HappyView resolves the schema automatically. If found, the lexicon JSON is displayed in a read-only editor. Click **Add** to track it. Network lexicons are kept up to date via the Jetstream subscription. See [Lexicons - Network lexicons](../guides/lexicons.md#network-lexicons) for how resolution works.
2626+**Network** lexicons are fetched from the atproto network. Enter an NSID (e.g. `xyz.statusphere.status`) and HappyView resolves the schema automatically. If found, the lexicon JSON is displayed in a read-only editor. Click **Add** to track it. Network lexicons are kept up to date via the Jetstream subscription. See [Lexicons - Network lexicons](../guides/lexicons.md#network-lexicons) for how resolution works.
27272828### JSON editor
29293030-The JSON editor provides real-time validation against the AT Protocol Lexicon v1 schema:
3030+The JSON editor provides real-time validation against the atproto Lexicon v1 schema:
31313232- Validation for Lexicon format
3333- Auto-complete for definition types (`record`, `query`, `procedure`, `subscription`), property types (`string`, `integer`, `boolean`, `ref`, `union`, `blob`, `cid-link`, etc.), and schema structure (`defs`, `main`, `properties`, `required`)
···41414242## Records
43434444-Navigate to **Records** to browse all indexed AT Protocol records. Records are grouped by collection and searchable. Each record shows its AT URI, author DID, and the raw record JSON.
4444+Navigate to **Records** to browse all indexed atproto records. Records are grouped by collection and searchable. Each record shows its AT URI, author DID, and the raw record JSON.
45454646## Backfill
4747···89899090### Accounts
91919292-Manage connected AT Protocol accounts used by the instance.
9292+Manage connected atproto accounts used by the instance.
93939494## Next steps
9595
+2-2
packages/docs/docs/getting-started/quickstart.md
···11# Quickstart
2233-This page walks you through the fastest path to a working HappyView instance. By the end, you'll have an AppView that indexes records from the AT Protocol network and serves XRPC endpoints.
33+This page walks you through the fastest path to a working HappyView instance. By the end, you'll have an AppView that indexes records from the atproto network and serves XRPC endpoints.
4455## 1. Deploy HappyView
66···16161717## 2. Log in to the dashboard
18181919-The built-in [dashboard](dashboard.md) is served at your instance's root URL. Log in with your AT Protocol identity — on a fresh deployment, the first handle to authenticate is automatically bootstrapped as the **super user** with all permissions, so use the handle you want to own the instance.
1919+The built-in [dashboard](dashboard.md) is served at your instance's root URL. Log in with your atproto identity — on a fresh deployment, the first handle to authenticate is automatically bootstrapped as the **super user** with all permissions, so use the handle you want to own the instance.
20202121## 3. Add your first lexicon
2222
+1-1
packages/docs/docs/guides/backfill.md
···29293030## Restoring deleted records
31313232-Deleting records from HappyView (via the dashboard or API) only removes them from the local database — the records still exist on the AT Protocol network. To restore deleted records, create a backfill job for the affected collection. The backfill will re-discover the repos and re-fetch all records from each PDS, restoring any that were previously deleted.
3232+Deleting records from HappyView (via the dashboard or API) only removes them from the local database — the records still exist on the atproto network. To restore deleted records, create a backfill job for the affected collection. The backfill will re-discover the repos and re-fetch all records from each PDS, restoring any that were previously deleted.
33333434## Next steps
3535
+1-1
packages/docs/docs/guides/labelers.md
···7878## Next steps
79798080- [Admin API — Labelers](../reference/admin/labelers.md) — full endpoint documentation
8181-- [AT Protocol API](../reference/lua/atproto-api.md) — access labels in Lua scripts with `get_labels` and `get_labels_batch`
8181+- [atproto API](../reference/lua/atproto-api.md) — access labels in Lua scripts with `get_labels` and `get_labels_batch`
8282- [Permissions](permissions.md) — manage user access to labeler operations
+2-2
packages/docs/docs/guides/lexicons.md
···11# Lexicons
2233-Lexicons are the core building block of HappyView. They're [AT Protocol schema definitions](https://atproto.com/specs/lexicon) that describe your data model, and HappyView uses them to decide which records to index from the network and what XRPC endpoints to serve.
33+Lexicons are the core building block of HappyView. They're [atproto schema definitions](https://atproto.com/specs/lexicon) that describe your data model, and HappyView uses them to decide which records to index from the network and what XRPC endpoints to serve.
4455-You don't write route handlers or database queries; you upload a lexicon and HappyView generates the infrastructure from it. There are two ways to add lexicons: uploading them via the [admin API](../reference/admin/lexicons.md) or [dashboard](../getting-started/dashboard.md), or fetching them directly from the AT Protocol network via [DNS authority resolution](#network-lexicons).
55+You don't write route handlers or database queries; you upload a lexicon and HappyView generates the infrastructure from it. There are two ways to add lexicons: uploading them via the [admin API](../reference/admin/lexicons.md) or [dashboard](../getting-started/dashboard.md), or fetching them directly from the atproto network via [DNS authority resolution](#network-lexicons).
6677## Supported lexicon types
88
+1-1
packages/docs/docs/guides/plugins.md
···11# Plugins
2233-HappyView uses WASM plugins to extend its functionality. Plugins can integrate with external platforms, sync data to users' AT Protocol identities, and more. Auth plugins — the first supported plugin type — enable users to link accounts from platforms like Steam, Xbox, itch.io, and others, then sync data like game libraries.
33+HappyView uses WASM plugins to extend its functionality. Plugins can integrate with external platforms, sync data to users' atproto identities, and more. Auth plugins — the first supported plugin type — enable users to link accounts from platforms like Steam, Xbox, itch.io, and others, then sync data like game libraries.
4455Official plugins for Steam, Xbox, itch.io, and other platforms are available in the [happyview-plugins](https://github.com/gamesgamesgamesgamesgames/happyview-plugins) repository.
66
+5-5
packages/docs/docs/guides/scripting.md
···6868| ---------------- | ------- | ------------------------------------------------------------------- |
6969| `now()` | string | Current UTC timestamp in ISO 8601 format |
7070| `log(message)` | — | Log a message (appears in server logs at debug level) |
7171-| `TID()` | string | Generate a fresh AT Protocol TID (13-character sortable identifier) |
7171+| `TID()` | string | Generate a fresh atproto TID (13-character sortable identifier) |
7272| `toarray(table)` | table | Mark a table as a JSON array for serialization (see [below](#toarray)) |
73737474### toarray
···85858686## Record API
87878888-The `Record` API is only available in **procedure** scripts. It handles creating, updating, loading, and deleting AT Protocol records. Writes are proxied to the caller's PDS and indexed locally.
8888+The `Record` API is only available in **procedure** scripts. It handles creating, updating, loading, and deleting atproto records. Writes are proxied to the caller's PDS and indexed locally.
89899090See the full [Record API reference](../reference/lua/record-api.md) for constructor, static methods, instance methods, fields, schema validation, and save behavior.
9191···127127local data = json.decode(resp.body)
128128```
129129130130-## AT Protocol API
130130+## atproto API
131131132132-The `atproto` table provides AT Protocol utility functions like DID resolution and label queries.
132132+The `atproto` table provides atproto utility functions like DID resolution and label queries.
133133134134-See the full [AT Protocol API reference](../reference/lua/atproto-api.md) for `atproto.resolve_service_endpoint`, `atproto.get_labels`, and `atproto.get_labels_batch`.
134134+See the full [atproto API reference](../reference/lua/atproto-api.md) for `atproto.resolve_service_endpoint`, `atproto.get_labels`, and `atproto.get_labels_batch`.
135135136136## JSON API
137137
···50505151## Step 5: Re-index your data
52525353-Since HappyView indexes records from the AT Protocol network, the simplest way to populate your new Postgres database is to re-run the backfill:
5353+Since HappyView indexes records from the atproto network, the simplest way to populate your new Postgres database is to re-run the backfill:
545455551. Upload your lexicons via the dashboard or admin API (or they will already be there if you exported and re-imported them)
56562. Run a backfill for each collection (dashboard or `POST /admin/backfill`)
+1-1
packages/docs/docs/reference/admin-api.md
···77The admin API supports two authentication methods:
88991. **API keys** — read/write tokens starting with `hv_`, passed as `Authorization: Bearer hv_...`. See the [API Keys guide](../guides/api-keys.md) for details.
1010-2. **Service auth JWT** — AT Protocol inter-service authentication via signed JWTs.
1010+2. **Service auth JWT** — atproto inter-service authentication via signed JWTs.
11111212In all cases the resolved DID is checked against the `users` table, and the user's permissions are loaded to authorize the request.
1313
+1-1
packages/docs/docs/reference/admin/domains.md
···11# Admin API: Domains
2233-Manage the domains a HappyView instance serves. Each domain gets its own AT Protocol OAuth client identity. The primary domain is set from `PUBLIC_URL` on first boot. All endpoints require the `settings:manage` permission.
33+Manage the domains a HappyView instance serves. Each domain gets its own atproto OAuth client identity. The primary domain is set from `PUBLIC_URL` on first boot. All endpoints require the `settings:manage` permission.
4455```sh
66# All examples assume $TOKEN is an API key (hv_...)
+1-1
packages/docs/docs/reference/admin/labelers.md
···24242525| Field | Type | Required | Description |
2626| ----- | ------ | -------- | ----------------------------- |
2727-| `did` | string | yes | The labeler's AT Protocol DID |
2727+| `did` | string | yes | The labeler's atproto DID |
28282929**Response**: `201 Created` (empty body)
3030
+1-1
packages/docs/docs/reference/admin/lexicons.md
···92929393## Network Lexicons
94949595-Network lexicons are fetched from the AT Protocol network via DNS TXT resolution and kept updated via the Jetstream subscription. See [Lexicons - Network lexicons](../../guides/lexicons.md#network-lexicons) for background.
9595+Network lexicons are fetched from the atproto network via DNS TXT resolution and kept updated via the Jetstream subscription. See [Lexicons - Network lexicons](../../guides/lexicons.md#network-lexicons) for background.
96969797### Add a network lexicon
9898
+1-1
packages/docs/docs/reference/admin/users.md
···27272828| Field | Type | Required | Description |
2929| ------------- | -------- | -------- | ---------------------------------------------------------------------------------- |
3030-| `did` | string | yes | The AT Protocol DID of the user to add |
3030+| `did` | string | yes | The atproto DID of the user to add |
3131| `template` | string | no | Permission template: `viewer`, `operator`, `manager`, or `full_access` |
3232| `permissions` | string[] | no | Explicit list of permissions to grant (used instead of or in addition to `template`) |
3333
+2-2
packages/docs/docs/reference/architecture.md
···195195| Column | Type | Description |
196196| -------------- | ------------- | ------------------------------------------------ |
197197| `id` | uuid (PK) | |
198198-| `did` | text (unique) | User's AT Protocol DID |
198198+| `did` | text (unique) | User's atproto DID |
199199| `is_super` | boolean | Whether this is the super user (only one allowed)|
200200| `created_at` | timestamptz | |
201201| `last_used_at` | timestamptz | Updated on each authenticated request |
···226226227227| Column | Type | Description |
228228| -------------- | ----------- | -------------------------------------------- |
229229-| `did` | text (PK) | User's AT Protocol DID |
229229+| `did` | text (PK) | User's atproto DID |
230230| `session_data` | text | Serialized OAuth session (managed by atrium) |
231231| `created_at` | timestamptz | |
232232| `updated_at` | timestamptz | |
+9-9
packages/docs/docs/reference/glossary.md
···11# Glossary
2233-Key terms used throughout the HappyView documentation. For a broader introduction to the AT Protocol, see the [official ATProto glossary](https://atproto.com/guides/glossary).
33+Key terms used throughout the HappyView documentation. For a broader introduction to the atproto, see the [official ATProto glossary](https://atproto.com/guides/glossary).
4455-## AT Protocol terms
55+## atproto terms
6677-**AppView** — A backend service that indexes AT Protocol records and serves them through an API. HappyView is an AppView. See the [ATProto docs](https://atproto.com/guides/glossary#app-view) for more.
77+**AppView** — A backend service that indexes atproto records and serves them through an API. HappyView is an AppView. See the [ATProto docs](https://atproto.com/guides/glossary#app-view) for more.
8899**DID** (Decentralized Identifier) — A persistent, globally unique identifier for an account (e.g. `did:plc:abc123`).
10101111-**Firehose** — A real-time stream of all record events (creates, updates, deletes) across the AT Protocol network. HappyView consumes a filtered slice of this via [Jetstream](https://github.com/bluesky-social/jetstream).
1111+**Firehose** — A real-time stream of all record events (creates, updates, deletes) across the atproto network. HappyView consumes a filtered slice of this via [Jetstream](https://github.com/bluesky-social/jetstream).
12121313**Handle** — A human-readable name for an account (e.g. `user.bsky.social`). Handles resolve to a DID via a DNS TXT record or an HTTP `.well-known/atproto-did` lookup.
14141515-**Lexicon** — A schema definition for AT Protocol data types and API methods. Lexicons define what records look like, what endpoints exist, and what parameters they accept. See [Lexicons](../guides/lexicons.md).
1515+**Lexicon** — A schema definition for atproto data types and API methods. Lexicons define what records look like, what endpoints exist, and what parameters they accept. See [Lexicons](../guides/lexicons.md).
16161717**NSID** (Namespaced Identifier) — A reverse-DNS identifier for a lexicon (e.g. `xyz.statusphere.status`). The authority is everything except the last segment.
1818···20202121**PLC directory** — A public service (e.g. `plc.directory`) that maps DIDs to their DID documents, which contain the user's PDS endpoint and other metadata.
22222323-**Record** — A single piece of data in an AT Protocol repository, identified by an AT URI (e.g. `at://did:plc:abc/xyz.statusphere.status/abc123`).
2323+**Record** — A single piece of data in an atproto repository, identified by an AT URI (e.g. `at://did:plc:abc/xyz.statusphere.status/abc123`).
24242525**Relay** — A network service that aggregates repository data from many PDSes. HappyView queries the relay during [backfill](../guides/backfill.md) to discover which repos contain records for a given collection, then fetches each repo's records directly from its PDS.
2626···28282929**TID** (Timestamp Identifier) — A 13-character sortable identifier used as a record key. Generated from the current timestamp.
30303131-**XRPC** — The HTTP-based RPC protocol used by the AT Protocol. Query methods map to GET requests, procedure methods map to POST requests. See [XRPC API](xrpc-api.md).
3131+**XRPC** — The HTTP-based RPC protocol used by the atproto. Query methods map to GET requests, procedure methods map to POST requests. See [XRPC API](xrpc-api.md).
32323333-**Jetstream** — A [filtered firehose](https://github.com/bluesky-social/jetstream) that delivers atproto record commit events as JSON over WebSocket. Not part of the core AT Protocol spec, but widely used. HappyView subscribes to Jetstream with a collection filter built from its indexed record lexicons, and persists a cursor for resume on reconnect.
3333+**Jetstream** — A [filtered firehose](https://github.com/bluesky-social/jetstream) that delivers atproto record commit events as JSON over WebSocket. Not part of the core atproto spec, but widely used. HappyView subscribes to Jetstream with a collection filter built from its indexed record lexicons, and persists a cursor for resume on reconnect.
34343535## HappyView-specific terms
36363737**Backfill** — The process of bulk-indexing existing records from the network. HappyView discovers repos via the relay and fetches each repo's records directly from its PDS. Runs when a new record-type lexicon is uploaded or triggered manually. See [Backfill](../guides/backfill.md).
38383939-**Network lexicon** — A lexicon fetched directly from the AT Protocol network via DNS authority resolution, rather than uploaded manually. See [Lexicons - Network lexicons](../guides/lexicons.md#network-lexicons).
3939+**Network lexicon** — A lexicon fetched directly from the atproto network via DNS authority resolution, rather than uploaded manually. See [Lexicons - Network lexicons](../guides/lexicons.md#network-lexicons).
40404141**Permission** — A granular access control right that authorizes a specific action in the admin API. HappyView defines 20 permissions organized by category (e.g. `lexicons:create`, `users:read`). See [Permissions](../guides/permissions.md).
4242
+3-3
packages/docs/docs/reference/lua/atproto-api.md
···11-# AT Protocol API
11+# atproto API
2233-The `atproto` table provides AT Protocol utility functions. Available in queries, procedures, and [index hooks](../../guides/index-hooks.md).
33+The `atproto` table provides atproto utility functions. Available in queries, procedures, and [index hooks](../../guides/index-hooks.md).
4455## atproto.resolve_service_endpoint
66···88local endpoint = atproto.resolve_service_endpoint(did)
99```
10101111-Resolves a DID to its AT Protocol service endpoint URL by fetching the DID document. Supports both `did:plc:*` (via the PLC directory) and `did:web:*` (via `.well-known/did.json`).
1111+Resolves a DID to its atproto service endpoint URL by fetching the DID document. Supports both `did:plc:*` (via the PLC directory) and `did:web:*` (via `.well-known/did.json`).
12121313| Parameter | Type | Description |
1414| --------- | ------ | ------------------------ |
+1-1
packages/docs/docs/reference/lua/record-api.md
···11# Record API
2233-The `Record` API is only available in **procedure** scripts. It handles creating, updating, loading, and deleting AT Protocol records. Writes are proxied to the caller's PDS and indexed locally.
33+The `Record` API is only available in **procedure** scripts. It handles creating, updating, loading, and deleting atproto records. Writes are proxied to the caller's PDS and indexed locally.
4455## Constructor
66
···72727373Cascading deletes are useful when your data model has parent-child relationships across collections. For example, deleting a post should also clean up its comments, reactions, or metadata records. This keeps the user's repo and the local index consistent.
74747575-Note that this only deletes records owned by `caller_did`. AT Protocol records can only be deleted by their owner. If the related records could have more than 100 matches, paginate through all of them before deleting.
7575+Note that this only deletes records owned by `caller_did`. atproto records can only be deleted by their owner. If the related records could have more than 100 matches, paginate through all of them before deleting.
···7272 ^^^^^^^ same rkey
7373```
74747575-This is a common AT Protocol pattern for keeping a primary record lean while storing auxiliary data (metadata, reactions, settings) in a companion collection.
7575+This is a common atproto pattern for keeping a primary record lean while storing auxiliary data (metadata, reactions, settings) in a companion collection.
+1-1
packages/docs/docs/reference/troubleshooting.md
···108108109109## OAuth or login issues
110110111111-HappyView handles AT Protocol OAuth internally via the `atrium-oauth` library. If users can't log in:
111111+HappyView handles atproto OAuth internally via the `atrium-oauth` library. If users can't log in:
1121121131131. Verify `PUBLIC_URL` is set correctly and the URL is publicly accessible (required for OAuth callbacks).
1141142. Check that the user's PDS authorization server is reachable.
+1-1
packages/docs/docs/reference/xrpc-api.md
···11# XRPC API
2233-[XRPC](https://atproto.com/specs/xrpc) is the HTTP-based RPC protocol used by the AT Protocol. HappyView dynamically registers XRPC endpoints based on your uploaded [lexicons](../guides/lexicons.md): query lexicons become `GET /xrpc/{nsid}` routes, procedure lexicons become `POST /xrpc/{nsid}` routes.
33+[XRPC](https://atproto.com/specs/xrpc) is the HTTP-based RPC protocol used by the atproto. HappyView dynamically registers XRPC endpoints based on your uploaded [lexicons](../guides/lexicons.md): query lexicons become `GET /xrpc/{nsid}` routes, procedure lexicons become `POST /xrpc/{nsid}` routes.
4455If a query or procedure lexicon has a [Lua script](../guides/scripting.md) attached, the script handles the request. Otherwise, HappyView uses built-in default behavior (described below).
66
+1-1
packages/docs/docs/sdk/lex-agent.md
···11# Lex Agent
2233-The Lex agent adapter is the recommended way to interact with HappyView from JavaScript. It creates an [`@atproto/lex`](https://www.npmjs.com/package/@atproto/lex) `Agent` from a `HappyViewSession`, so you can use `@atproto/lex`'s type-safe `Client` to make XRPC calls with HappyView's DPoP authentication. All requests are routed to your HappyView instance, which handles its own lexicons locally and proxies standard AT Protocol methods (e.g., `com.atproto.repo.createRecord`) to the user's PDS.
33+The Lex agent adapter is the recommended way to interact with HappyView from JavaScript. It creates an [`@atproto/lex`](https://www.npmjs.com/package/@atproto/lex) `Agent` from a `HappyViewSession`, so you can use `@atproto/lex`'s type-safe `Client` to make XRPC calls with HappyView's DPoP authentication. All requests are routed to your HappyView instance, which handles its own lexicons locally and proxies standard atproto methods (e.g., `com.atproto.repo.createRecord`) to the user's PDS.
4455The adapter gives you lexicon-level type checking on parameters, input bodies, and responses, and works with any library or tool that accepts an `@atproto/lex` `Agent`.
66
+1-1
packages/docs/docs/sdk/oauth-client-browser.md
···11# Browser Client
2233-The browser client handles the full OAuth redirect flow for browser apps authenticating with a HappyView instance. It wraps the [OAuth Client](./oauth-client.md) with Web Crypto, localStorage, and AT Protocol handle/DID resolution.
33+The browser client handles the full OAuth redirect flow for browser apps authenticating with a HappyView instance. It wraps the [OAuth Client](./oauth-client.md) with Web Crypto, localStorage, and atproto handle/DID resolution.
4455If you're starting a new app, consider using [`@happyview/lex-agent`](./lex-agent.md) with `@atproto/lex` instead — it provides type-safe XRPC calls and is the recommended way to interact with HappyView. This package is primarily useful if your app already uses `@atproto/oauth-client-browser` and you want to add HappyView authentication alongside it.
66
+1-1
packages/docs/docs/sdk/oauth-client.md
···37373838For public clients, `pkceVerifier` is included and must be passed back when registering the session. For confidential clients it will be `undefined`.
39394040-Use the returned `dpopKey` (a private JWK) as your DPoP keypair during your AT Protocol OAuth flow with the user's PDS.
4040+Use the returned `dpopKey` (a private JWK) as your DPoP keypair during your atproto OAuth flow with the user's PDS.
41414242## Session registration
4343
+3-3
packages/docs/docs/sdk/overview.md
···12121313**Starting a new app?** Use `@happyview/lex-agent` with `@atproto/lex`. It gives you type-safe XRPC calls through a `Client` that routes requests to your HappyView instance with DPoP authentication. This is the recommended way to interact with HappyView from JavaScript.
14141515-**Already using `@atproto/oauth-client-browser`?** Add `@happyview/oauth-client-browser` to get a `HappyViewBrowserClient` that handles the HappyView-specific DPoP key provisioning and session registration on top of the standard AT Protocol OAuth flow.
1515+**Already using `@atproto/oauth-client-browser`?** Add `@happyview/oauth-client-browser` to get a `HappyViewBrowserClient` that handles the HappyView-specific DPoP key provisioning and session registration on top of the standard atproto OAuth flow.
16161717**Building a server-side app or something more custom?** Use `@happyview/oauth-client` directly and provide your own `CryptoAdapter` and `StorageAdapter`.
1818···2121Third-party apps authenticate using HappyView's [DPoP key provisioning](../getting-started/authentication.md#dpop-key-provisioning-for-third-party-apps) flow:
222223231. The SDK requests a DPoP keypair from the HappyView instance.
2424-2. Your app runs a standard AT Protocol OAuth flow with the user's PDS using that keypair.
2424+2. Your app runs a standard atproto OAuth flow with the user's PDS using that keypair.
25253. The SDK registers the resulting tokens with HappyView.
2626-4. All subsequent XRPC requests are authenticated with DPoP proofs — HappyView handles its own lexicons locally and proxies standard AT Protocol writes to the user's PDS.
2626+4. All subsequent XRPC requests are authenticated with DPoP proofs — HappyView handles its own lexicons locally and proxies standard atproto writes to the user's PDS.
27272828## Quick start
2929
+52-138
packages/docs/docs/tutorials/statusphere.md
···11# Tutorial: Statusphere with HappyView
2233-[Statusphere](https://github.com/bluesky-social/statusphere-example-app) is an example AT Protocol application where users set their current status as a single emoji. It's a great way to learn how HappyView works because the data model is simple but the queries are interesting.
33+[Statusphere](https://github.com/bluesky-social/statusphere-example-app) is an example atproto application where users set their current status as a single emoji. It's a great way to learn how HappyView works because the data model is simple but the queries are interesting.
4455In this tutorial, you'll set up HappyView to act as the AppView for Statusphere. By the end, you'll have indexed records and working XRPC endpoints.
66···19192020For more background on how the app works, see the [ATProto Statusphere guide](https://atproto.com/guides/applications).
21212222-## Step 1: Upload the record lexicon
2222+## Step 1: Add the record lexicon
23232424-First, upload the `xyz.statusphere.status` lexicon to HappyView. This tells HappyView to start indexing Statusphere records from across the network as they're created, updated, or deleted.
2424+First, tell HappyView to start indexing Statusphere records. Since `xyz.statusphere.status` is [published on the atproto network](../guides/lexicons.md#network-lexicons), you can add it directly from the dashboard:
25252626-The examples below use `$TOKEN` as a placeholder for an API key. See [Authentication](../getting-started/authentication.md) and the [API Keys guide](../guides/api-keys.md) for how to get one.
2626+1. Go to **Lexicons > Add Lexicon > Network**
2727+2. Enter `xyz.statusphere.status`
2828+3. HappyView resolves the schema from its authority domain records and shows a preview
2929+4. Enable the **Backfill** toggle so HappyView fetches existing records from the network
3030+5. Click **Add**
3131+3232+HappyView now subscribes to `xyz.statusphere.status` via Jetstream and kicks off a backfill job to index historical records.
3333+3434+:::tip
3535+You can also add lexicons via the [admin API](../reference/admin/lexicons.md). This is useful for automation or CI/CD workflows:
27362837```sh
2938curl -X POST http://localhost:3000/admin/lexicons \
···5261 }'
5362```
54635555-HappyView now subscribes to `xyz.statusphere.status` via Jetstream. The `backfill` flag tells HappyView to also index existing status records from the network. You can monitor progress with `GET /admin/backfill/status` or the [dashboard](../getting-started/dashboard.md).
5656-5757-:::tip
5858-Since the `xyz.statusphere.status` lexicon is [published on the AT Protocol network](../guides/lexicons.md#network-lexicons), you can also add it as a network lexicon instead of uploading the JSON manually:
5959-6060-```sh
6161-curl -X POST http://localhost:3000/admin/network-lexicons \
6262- -H "Authorization: Bearer $TOKEN" \
6363- -H "Content-Type: application/json" \
6464- -d '{ "nsid": "xyz.statusphere.status" }'
6565-```
6666-6764:::
68656966## Step 2: Verify records are being indexed
70677171-Once the backfill starts processing, you should see records appearing. Check the stats:
7272-7373-```sh
7474-curl http://localhost:3000/admin/stats \
7575- -H "Authorization: Bearer $TOKEN"
7676-```
6868+Once the backfill starts, you should see records appearing in the dashboard:
77697878-```json
7979-{
8080- "total_records": 1234,
8181- "collections": [{ "collection": "xyz.statusphere.status", "count": 1234 }]
8282-}
8383-```
7070+1. The **home page** shows a live record count and per-collection breakdown
7171+2. Go to **Records** to browse individual indexed statuses
7272+3. Go to **Backfill** to watch the backfill job progress — you'll see the number of repos processed and records fetched
84738574## Step 3: Add a query lexicon for listing statuses
86758787-Now add a query endpoint to read the indexed data. Upload a query lexicon with `target_collection` pointing at the record collection from Step 1:
7676+Now add a query endpoint to read the indexed data:
88778989-```sh
9090-curl -X POST http://localhost:3000/admin/lexicons \
9191- -H "Authorization: Bearer $TOKEN" \
9292- -H "Content-Type: application/json" \
9393- -d '{
9494- "lexicon_json": {
9595- "lexicon": 1,
9696- "id": "xyz.statusphere.listStatuses",
9797- "defs": {
9898- "main": {
9999- "type": "query",
100100- "output": { "encoding": "application/json" }
101101- }
102102- }
103103- },
104104- "target_collection": "xyz.statusphere.status"
105105- }'
106106-```
7878+1. Go to **Lexicons > Add Lexicon > Local**
7979+2. Set the NSID to `xyz.statusphere.listStatuses`
8080+3. Set the type to **Query**
8181+4. Set `target_collection` to `xyz.statusphere.status` — this tells the query which record collection it operates on (`target_collection` is a HappyView-specific field, not part of the lexicon spec)
8282+5. Click **Add**
1078310884This creates a `GET /xrpc/xyz.statusphere.listStatuses` endpoint. Without a Lua script, it uses HappyView's built-in default behavior: listing records with `limit`, `cursor`, and `did` parameters, or fetching a single record by `uri`. Try it:
1098511086```sh
111111-curl "http://localhost:3000/xrpc/xyz.statusphere.listStatuses?limit=5"
8787+curl "http://localhost:3000/xrpc/xyz.statusphere.listStatuses?limit=5" \
8888+ -H "X-Client-Key: $CLIENT_KEY"
11289```
1139011491```json
···133110134111## Step 4: Enhance the query with a Lua script
135112136136-The default query behavior works, but let's customize it with a [Lua script](../guides/scripting.md). Here's a script that handles single-record lookups by URI and paginated listing with an optional DID filter:
113113+The default query behavior works, but let's customize it with a [Lua script](../guides/scripting.md). The script will handle single-record lookups by URI and paginated listing with an optional DID filter.
114114+115115+1. Click on **xyz.statusphere.listStatuses** in the lexicon list to open its detail page
116116+2. The Lua script editor is at the bottom of the page
117117+3. Paste in the following script:
137118138119```lua
139120function handle()
···154135end
155136```
156137157157-Re-upload the lexicon with parameters defined in the schema and the script attached:
158158-159159-```sh
160160-LEXICON='{
161161- "lexicon": 1,
162162- "id": "xyz.statusphere.listStatuses",
163163- "defs": {
164164- "main": {
165165- "type": "query",
166166- "parameters": {
167167- "type": "params",
168168- "properties": {
169169- "uri": { "type": "string" },
170170- "did": { "type": "string" },
171171- "limit": { "type": "integer" },
172172- "cursor": { "type": "string" }
173173- }
174174- },
175175- "output": { "encoding": "application/json" }
176176- }
177177- }
178178-}'
179179-180180-SCRIPT='function handle()
181181- if params.uri then
182182- local record = db.get(params.uri)
183183- if not record then
184184- return { error = "not found" }
185185- end
186186- return { record = record }
187187- end
188188-189189- return db.query({
190190- collection = collection,
191191- did = params.did,
192192- limit = tonumber(params.limit) or 20,
193193- cursor = params.cursor,
194194- })
195195-end'
196196-197197-curl -X POST http://localhost:3000/admin/lexicons \
198198- -H "Authorization: Bearer $TOKEN" \
199199- -H "Content-Type: application/json" \
200200- -d "{
201201- \"lexicon_json\": $LEXICON,
202202- \"target_collection\": \"xyz.statusphere.status\",
203203- \"script\": \"$SCRIPT\"
204204- }"
205205-```
138138+4. Click **Save**
206139207140The endpoint now uses your custom logic. Filter by a specific user:
208141209142```sh
210210-curl "http://localhost:3000/xrpc/xyz.statusphere.listStatuses?did=did:plc:abc&limit=1"
143143+curl "http://localhost:3000/xrpc/xyz.statusphere.listStatuses?did=did:plc:abc&limit=1" \
144144+ -H "X-Client-Key: $CLIENT_KEY"
211145```
212146213147Fetch a single record by URI:
214148215149```sh
216216-curl "http://localhost:3000/xrpc/xyz.statusphere.listStatuses?uri=at://did:plc:abc/xyz.statusphere.status/3abc123"
150150+curl "http://localhost:3000/xrpc/xyz.statusphere.listStatuses?uri=at://did:plc:abc/xyz.statusphere.status/3abc123" \
151151+ -H "X-Client-Key: $CLIENT_KEY"
217152```
218153219154## Step 5: Add a procedure lexicon for setting status
220155221221-Add a write endpoint so users can set their status through your AppView. This creates a `POST /xrpc/xyz.statusphere.setStatus` endpoint that proxies writes to the user's PDS.
156156+Add a write endpoint so users can set their status through your AppView:
222157223223-The Lua script auto-fills `createdAt` and uses the authenticated user's DID:
158158+1. Go to **Lexicons > Add Lexicon > Local**
159159+2. Set the NSID to `xyz.statusphere.setStatus`
160160+3. Set the type to **Procedure**
161161+4. Set `target_collection` to `xyz.statusphere.status`
162162+5. A default Lua script is generated — replace it with:
224163225164```lua
226165function handle()
···233172end
234173```
235174236236-Upload the procedure lexicon with this script:
237237-238238-```sh
239239-LEXICON='{
240240- "lexicon": 1,
241241- "id": "xyz.statusphere.setStatus",
242242- "defs": {
243243- "main": {
244244- "type": "procedure",
245245- "input": { "encoding": "application/json" },
246246- "output": { "encoding": "application/json" }
247247- }
248248- }
249249-}'
175175+6. Click **Add**
250176251251-SCRIPT='function handle()
252252- local r = Record(collection, {
253253- status = input.status,
254254- createdAt = now(),
255255- })
256256- r:save()
257257- return { uri = r._uri, cid = r._cid }
258258-end'
259259-260260-curl -X POST http://localhost:3000/admin/lexicons \
261261- -H "Authorization: Bearer $TOKEN" \
262262- -H "Content-Type: application/json" \
263263- -d "{
264264- \"lexicon_json\": $LEXICON,
265265- \"target_collection\": \"xyz.statusphere.status\",
266266- \"script\": \"$SCRIPT\"
267267- }"
268268-```
177177+This creates a `POST /xrpc/xyz.statusphere.setStatus` endpoint that creates records on the user's PDS and indexes them locally.
269178270179## Step 6: Test the procedure endpoint
271180272272-Set a status (requires authentication):
181181+Set a status. This requires DPoP authentication — the [JavaScript SDK](../sdk/overview.md) handles this for you, but you can test with curl if you have a token:
273182274183```sh
275184curl -X POST http://localhost:3000/xrpc/xyz.statusphere.setStatus \
276276- -H "Authorization: Bearer $TOKEN" \
185185+ -H "X-Client-Key: $CLIENT_KEY" \
186186+ -H "Authorization: DPoP $TOKEN" \
187187+ -H "DPoP: $DPOP_PROOF" \
277188 -H "Content-Type: application/json" \
278189 -d '{ "status": "\ud83d\ude80" }'
279190```
···289200290201## What you've built
291202292292-With three lexicon uploads and a few lines of Lua, you have a complete Statusphere AppView:
203203+With three lexicons and a few lines of Lua, you have a complete Statusphere AppView:
293204294294-- **Real-time indexing** of `xyz.statusphere.status` records from the entire AT Protocol network
205205+- **Real-time indexing** of `xyz.statusphere.status` records from the entire atproto network
295206- **Historical backfill** of existing status records
296207- **A query endpoint** (`xyz.statusphere.listStatuses`) with filtering, pagination, and single-record lookups
297208- **A write endpoint** (`xyz.statusphere.setStatus`) that creates records on the user's PDS and indexes them locally
298209210210+Everything was done through the dashboard — no server restarts, no config files, no deploys. For automation and CI/CD, the same operations are available via the [admin API](../reference/admin-api.md).
211211+299212## Next steps
300213301214- [Lua Scripting](../guides/scripting.md): Explore the full Record and database APIs to build more complex queries
302215- [Lexicons](../guides/lexicons.md): Learn about network lexicons, the backfill flag, and target collections
303216- [XRPC API](../reference/xrpc-api.md): Understand how the generated endpoints behave
217217+- [Admin API](../reference/admin-api.md): Automate lexicon management via the API
304218- [Statusphere example app](https://github.com/bluesky-social/statusphere-example-app): See the full Statusphere frontend
305219- [ATProto Statusphere guide](https://atproto.com/guides/applications): Deep dive into how the app works at the protocol level