diff --git a/packages/docs/content/docs/api-reference/admin/admin-api.md b/packages/docs/content/docs/api-reference/admin/admin-api.md index dea2496..706bf14 100644 --- a/packages/docs/content/docs/api-reference/admin/admin-api.md +++ b/packages/docs/content/docs/api-reference/admin/admin-api.md @@ -2,13 +2,13 @@ title: "Overview" --- -The admin API lets you manage lexicons, monitor records, run backfill jobs, and control user access. All endpoints live under `/admin` and require authentication from a DID that exists in the `users` table, with the appropriate [permissions](../../guides/admin/permissions.md) for the endpoint being called. You can also manage all of this through the [web dashboard](../../getting-started/dashboard.md). +The admin API lets you manage lexicons, monitor records, run backfill jobs, and control user access. All endpoints live under `/admin` and require authentication from a DID that exists in the `users` table, with the appropriate [permissions](../../guides/permissions.md) for the endpoint being called. You can also manage all of this through the [web dashboard](../../getting-started/dashboard.md). ## Auth The admin API supports two authentication methods: -1. **API keys** — read/write tokens starting with `hv_`, passed as `Authorization: Bearer hv_...`. See the [API Keys guide](../../guides/admin/api-keys.md) for details. +1. **API keys** — read/write tokens starting with `hv_`, passed as `Authorization: Bearer hv_...`. See the [API Keys guide](../../guides/api-keys.md) for details. 2. **Service auth JWT** — atproto inter-service authentication via signed JWTs. In all cases the resolved DID is checked against the `users` table, and the user's permissions are loaded to authorize the request. @@ -58,7 +58,7 @@ AUTH="Authorization: Bearer $TOKEN" ## Permissions -Each admin API endpoint requires a specific permission. See the [Permissions guide](../../guides/admin/permissions.md) for the full list of permissions and templates. +Each admin API endpoint requires a specific permission. See the [Permissions guide](../../guides/permissions.md) for the full list of permissions and templates. | Endpoint | Required Permission | | ---------------------------------------- | -------------------------- | diff --git a/packages/docs/content/docs/api-reference/admin/api-clients.md b/packages/docs/content/docs/api-reference/admin/api-clients.md index 7dbbc74..bbc3ca7 100644 --- a/packages/docs/content/docs/api-reference/admin/api-clients.md +++ b/packages/docs/content/docs/api-reference/admin/api-clients.md @@ -6,7 +6,7 @@ API clients identify third-party applications that call HappyView's XRPC endpoin A single API client represents your application, not individual users. Create one client for your app and use the same client key across all instances. Users authenticate separately via OAuth — the client key identifies _your app_, not _who is using it_. -Each client has an `hvc_`-prefixed client key and an `hvs_`-prefixed client secret. The secret is only returned at creation and is sha256-hashed in the database. Server-to-server callers pass the secret as `X-Client-Secret`. Browser callers use the `Origin` header, which is matched against the client's `client_uri`. Mismatches currently log warnings rather than rejecting the request, but rate limiting applies either way. See [Authentication — XRPC](../../getting-started/authentication.md#xrpc-api-client-identification) for the client-side view, and the [API Keys guide](../../guides/admin/api-keys.md) for how admin API keys differ from API clients. +Each client has an `hvc_`-prefixed client key and an `hvs_`-prefixed client secret. The secret is only returned at creation and is sha256-hashed in the database. Server-to-server callers pass the secret as `X-Client-Secret`. Browser callers use the `Origin` header, which is matched against the client's `client_uri`. Mismatches currently log warnings rather than rejecting the request, but rate limiting applies either way. See [Authentication — XRPC](../../getting-started/authentication.md#xrpc-api-client-identification) for the client-side view, and the [API Keys guide](../../guides/api-keys.md) for how admin API keys differ from API clients. Third-party apps can also create, list, and delete their own API clients programmatically via the [XRPC API](../oauth/api-clients.md), without needing admin access. diff --git a/packages/docs/content/docs/api-reference/admin/api-keys.md b/packages/docs/content/docs/api-reference/admin/api-keys.md index b437637..d1044b7 100644 --- a/packages/docs/content/docs/api-reference/admin/api-keys.md +++ b/packages/docs/content/docs/api-reference/admin/api-keys.md @@ -2,7 +2,7 @@ title: "API Keys" --- -Manage API keys for programmatic access. See the [API Keys guide](../../guides/admin/api-keys.md) for usage details. +Manage API keys for programmatic access. See the [API Keys guide](../../guides/api-keys.md) for usage details. ```sh # All examples assume $TOKEN is an API key (hv_...) diff --git a/packages/docs/content/docs/api-reference/admin/backfill.md b/packages/docs/content/docs/api-reference/admin/backfill.md index 9b4d339..771101d 100644 --- a/packages/docs/content/docs/api-reference/admin/backfill.md +++ b/packages/docs/content/docs/api-reference/admin/backfill.md @@ -2,7 +2,7 @@ title: "Backfill" --- -Create and monitor historical backfill jobs. See the [Backfill guide](../../guides/indexing/backfill.md) for background. +Create and monitor historical backfill jobs. See the [Backfill guide](../../guides/backfill.md) for background. ```sh # All examples assume $TOKEN is an API key (hv_...) diff --git a/packages/docs/content/docs/api-reference/admin/events.md b/packages/docs/content/docs/api-reference/admin/events.md index d463647..86ac4f1 100644 --- a/packages/docs/content/docs/api-reference/admin/events.md +++ b/packages/docs/content/docs/api-reference/admin/events.md @@ -2,7 +2,7 @@ title: "Event Logs" --- -HappyView logs system events — lexicon changes, record operations, script errors, user actions, and more. See the [Event Logs guide](../../guides/admin/event-logs.md) for details on event types and retention. +HappyView logs system events — lexicon changes, record operations, script errors, user actions, and more. See the [Event Logs guide](../../guides/event-logs.md) for details on event types and retention. ```sh # All examples assume $TOKEN is an API key (hv_...) diff --git a/packages/docs/content/docs/api-reference/admin/labelers.md b/packages/docs/content/docs/api-reference/admin/labelers.md index d573ba6..5f95f0c 100644 --- a/packages/docs/content/docs/api-reference/admin/labelers.md +++ b/packages/docs/content/docs/api-reference/admin/labelers.md @@ -2,7 +2,7 @@ title: "Labelers" --- -Manage external labeler subscriptions. See the [Labelers guide](../../guides/features/labelers.md) for background. +Manage external labeler subscriptions. See the [Labelers guide](../../guides/labelers.md) for background. ```sh # All examples assume $TOKEN is an API key (hv_...) diff --git a/packages/docs/content/docs/api-reference/admin/lexicons.md b/packages/docs/content/docs/api-reference/admin/lexicons.md index c312407..f56b59a 100644 --- a/packages/docs/content/docs/api-reference/admin/lexicons.md +++ b/packages/docs/content/docs/api-reference/admin/lexicons.md @@ -2,7 +2,7 @@ title: "Lexicons" --- -Manage lexicons and network lexicons. See the [Lexicons guide](../../guides/indexing/lexicons.md) for background on how lexicons drive indexing and XRPC routing. +Manage lexicons and network lexicons. See the [Lexicons guide](../../guides/lexicons.md) for background on how lexicons drive indexing and XRPC routing. ```sh # All examples assume $TOKEN is an API key (hv_...) @@ -32,7 +32,7 @@ curl -X POST http://127.0.0.1:3000/admin/lexicons \ | `backfill` | boolean | no | Whether uploading triggers historical backfill (default `true`) | | `target_collection` | string | no | For query/procedure lexicons, the record collection they operate on | | `script` | string | no | Lua script for query/procedure endpoints | -| `index_hook` | string | no | [Index hook](../../guides/indexing/index-hooks.md) Lua script for record lexicons | +| `index_hook` | string | no | [Index hook](../../guides/index-hooks.md) Lua script for record lexicons | **Response**: `201 Created` (new) or `200 OK` (upsert) @@ -94,7 +94,7 @@ curl -X DELETE http://127.0.0.1:3000/admin/lexicons/xyz.statusphere.status -H "$ ## Network Lexicons -Network lexicons are fetched from the atproto network via DNS TXT resolution and kept updated via the Jetstream subscription. See [Lexicons - Network lexicons](../../guides/indexing/lexicons.md#network-lexicons) for background. +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. ### Add a network lexicon diff --git a/packages/docs/content/docs/api-reference/admin/plugins.md b/packages/docs/content/docs/api-reference/admin/plugins.md index fdead49..343450a 100644 --- a/packages/docs/content/docs/api-reference/admin/plugins.md +++ b/packages/docs/content/docs/api-reference/admin/plugins.md @@ -2,7 +2,7 @@ title: "Plugins" --- -Plugins extend HappyView with WebAssembly modules sourced from the [official plugin registry](../../guides/features/plugins.md) or any URL serving a `manifest.json`. Most endpoints take a plugin manifest URL and load (or reload) the plugin in place — no restart needed. Encrypted plugin secrets require `TOKEN_ENCRYPTION_KEY` to be configured. +Plugins extend HappyView with WebAssembly modules sourced from the [official plugin registry](../../guides/plugins.md) or any URL serving a `manifest.json`. Most endpoints take a plugin manifest URL and load (or reload) the plugin in place — no restart needed. Encrypted plugin secrets require `TOKEN_ENCRYPTION_KEY` to be configured. ```sh # All examples assume $TOKEN is an API key (hv_...) diff --git a/packages/docs/content/docs/api-reference/admin/users.md b/packages/docs/content/docs/api-reference/admin/users.md index 22aed31..383d8d8 100644 --- a/packages/docs/content/docs/api-reference/admin/users.md +++ b/packages/docs/content/docs/api-reference/admin/users.md @@ -2,7 +2,7 @@ title: "Users" --- -Manage admin users and their permissions. See the [Permissions guide](../../guides/admin/permissions.md) for available permissions and templates. +Manage admin users and their permissions. See the [Permissions guide](../../guides/permissions.md) for available permissions and templates. ```sh # All examples assume $TOKEN is an API key (hv_...) diff --git a/packages/docs/content/docs/api-reference/lua/atproto-api.md b/packages/docs/content/docs/api-reference/lua/atproto-api.md index b6e6c05..379be5f 100644 --- a/packages/docs/content/docs/api-reference/lua/atproto-api.md +++ b/packages/docs/content/docs/api-reference/lua/atproto-api.md @@ -2,7 +2,7 @@ title: "atproto API" --- -The `atproto` table provides atproto utility functions. Available in queries, procedures, and [index hooks](../../guides/indexing/index-hooks.md). +The `atproto` table provides atproto utility functions. Available in queries, procedures, and [index hooks](../../guides/index-hooks.md). ## atproto.resolve_service_endpoint diff --git a/packages/docs/content/docs/api-reference/lua/database-api.md b/packages/docs/content/docs/api-reference/lua/database-api.md index 6f80d67..64e4b4d 100644 --- a/packages/docs/content/docs/api-reference/lua/database-api.md +++ b/packages/docs/content/docs/api-reference/lua/database-api.md @@ -2,7 +2,7 @@ title: "Database API" --- -The `db` table provides access to the database. Available in queries, procedures, and [index hooks](../../guides/indexing/index-hooks.md). +The `db` table provides access to the database. Available in queries, procedures, and [index hooks](../../guides/index-hooks.md). ## db.query diff --git a/packages/docs/content/docs/api-reference/lua/http-api.md b/packages/docs/content/docs/api-reference/lua/http-api.md index b6b9aee..6b65477 100644 --- a/packages/docs/content/docs/api-reference/lua/http-api.md +++ b/packages/docs/content/docs/api-reference/lua/http-api.md @@ -2,7 +2,7 @@ title: "HTTP API" --- -The `http` table provides async HTTP client functions. Available in queries, procedures, and [index hooks](../../guides/indexing/index-hooks.md). +The `http` table provides async HTTP client functions. Available in queries, procedures, and [index hooks](../../guides/index-hooks.md). ## Methods diff --git a/packages/docs/content/docs/api-reference/lua/json-api.md b/packages/docs/content/docs/api-reference/lua/json-api.md index 67f65fa..794592f 100644 --- a/packages/docs/content/docs/api-reference/lua/json-api.md +++ b/packages/docs/content/docs/api-reference/lua/json-api.md @@ -2,7 +2,7 @@ title: "JSON API" --- -The `json` global provides JSON serialization and deserialization. Available in queries, procedures, and [index hooks](../../guides/indexing/index-hooks.md). +The `json` global provides JSON serialization and deserialization. Available in queries, procedures, and [index hooks](../../guides/index-hooks.md). ## json.encode diff --git a/packages/docs/content/docs/api-reference/lua/utility-globals.md b/packages/docs/content/docs/api-reference/lua/utility-globals.md index de1bb5a..d9fbde3 100644 --- a/packages/docs/content/docs/api-reference/lua/utility-globals.md +++ b/packages/docs/content/docs/api-reference/lua/utility-globals.md @@ -2,7 +2,7 @@ title: "Utility Globals" --- -Global functions available in queries, procedures, and [index hooks](../../guides/indexing/index-hooks.md). These don't belong to a specific API table — they're available at the top level of any Lua script. +Global functions available in queries, procedures, and [index hooks](../../guides/index-hooks.md). These don't belong to a specific API table — they're available at the top level of any Lua script. ## now diff --git a/packages/docs/content/docs/api-reference/lua/xrpc-lua-api.md b/packages/docs/content/docs/api-reference/lua/xrpc-lua-api.md index 8179a30..3db18b7 100644 --- a/packages/docs/content/docs/api-reference/lua/xrpc-lua-api.md +++ b/packages/docs/content/docs/api-reference/lua/xrpc-lua-api.md @@ -2,7 +2,7 @@ title: "XRPC Lua API" --- -The `xrpc` table provides cross-endpoint XRPC calls. Available in queries, procedures, and [index hooks](../../guides/indexing/index-hooks.md). +The `xrpc` table provides cross-endpoint XRPC calls. Available in queries, procedures, and [index hooks](../../guides/index-hooks.md). ## xrpc.query diff --git a/packages/docs/content/docs/api-reference/oauth/api-clients.md b/packages/docs/content/docs/api-reference/oauth/api-clients.md index 5d42f55..95cdd3d 100644 --- a/packages/docs/content/docs/api-reference/oauth/api-clients.md +++ b/packages/docs/content/docs/api-reference/oauth/api-clients.md @@ -4,7 +4,7 @@ title: "Third-Party API Clients" Third-party applications can manage their own API clients via the `dev.happyview.*` XRPC endpoints. A third-party client is always tied to exactly one parent — the admin-created top-level API client whose DPoP session made the request. Only one level of nesting is allowed; third-party clients cannot create further children. Each third-party client gets its own rate limit bucket with instance default settings. -All endpoints use [DPoP authentication](../../getting-started/authentication.md#authenticating-users-for-procedures). See the [admin API client docs](../admin/api-clients.md) for managing clients through the admin API, and the [API Clients guide](../../guides/features/api-clients.md) for how API clients work. +All endpoints use [DPoP authentication](../../getting-started/authentication.md#authenticating-users-for-procedures). See the [admin API client docs](../admin/api-clients.md) for managing clients through the admin API, and the [API Clients guide](../../guides/api-clients.md) for how API clients work. Only top-level API clients can call these endpoints. Third-party (child) clients receive `401 Unauthorized` or `403 Forbidden`. diff --git a/packages/docs/content/docs/api-reference/xrpc-api.md b/packages/docs/content/docs/api-reference/xrpc-api.md index 47fda88..d4c1adf 100644 --- a/packages/docs/content/docs/api-reference/xrpc-api.md +++ b/packages/docs/content/docs/api-reference/xrpc-api.md @@ -2,9 +2,9 @@ title: "XRPC API" --- -[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/indexing/lexicons.md): query lexicons become `GET /xrpc/{nsid}` routes, procedure lexicons become `POST /xrpc/{nsid}` routes. +[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. -If 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). +If a query or procedure lexicon has a [Lua script](../guides/lua-scripting.md) attached, the script handles the request. Otherwise, HappyView uses built-in default behavior (described below). ## Auth @@ -75,7 +75,7 @@ curl -X POST http://127.0.0.1:3000/xrpc/com.atproto.repo.uploadBlob \ ## Dynamic query endpoints -Query endpoints are generated from lexicons with `type: "query"`. Without a [Lua script](../guides/scripting.md), they support two built-in modes depending on whether a `uri` parameter is provided. +Query endpoints are generated from lexicons with `type: "query"`. Without a [Lua script](../guides/lua-scripting.md), they support two built-in modes depending on whether a `uri` parameter is provided. ### Single record @@ -139,7 +139,7 @@ The `cursor` field is an opaque string present only when more records exist. Pas ## Dynamic procedure endpoints -Procedure endpoints are generated from lexicons with `type: "procedure"`. Without a [Lua script](../guides/scripting.md), HappyView auto-detects create vs update based on whether the request body contains a `uri` field. +Procedure endpoints are generated from lexicons with `type: "procedure"`. Without a [Lua script](../guides/lua-scripting.md), HappyView auto-detects create vs update based on whether the request body contains a `uri` field. ### Create a record @@ -207,7 +207,7 @@ When a Lua script fails, the response is `500` with one of: - `{"error": "script execution failed"}`: syntax error, runtime error, or missing `handle()` function - `{"error": "script exceeded execution time limit"}`: the script hit the 1,000,000 instruction limit -The full error details are logged server-side but not exposed to the client. See [Lua Scripting - Debugging](../guides/scripting.md#debugging) for how to diagnose script issues. +The full error details are logged server-side but not exposed to the client. See [Lua Scripting - Debugging](../guides/lua-scripting.md#debugging) for how to diagnose script issues. ### PDS errors @@ -215,6 +215,6 @@ When a procedure proxies a write to the user's PDS and the PDS returns an error, ## Next steps -- [Lua Scripting](../guides/scripting.md): Override the default query and procedure behavior with custom logic -- [Lexicons](../guides/indexing/lexicons.md): Understand how lexicons generate these endpoints +- [Lua Scripting](../guides/lua-scripting.md): Override the default query and procedure behavior with custom logic +- [Lexicons](../guides/lexicons.md): Understand how lexicons generate these endpoints - [Admin API](admin/admin-api.md): Manage lexicons and monitor your instance diff --git a/packages/docs/content/docs/experimental/spaces/index.md b/packages/docs/content/docs/experimental/spaces/index.md index 688af69..e8456fb 100644 --- a/packages/docs/content/docs/experimental/spaces/index.md +++ b/packages/docs/content/docs/experimental/spaces/index.md @@ -39,7 +39,7 @@ When disabled, all `/xrpc/dev.happyview.space.*` endpoints return `501 Not Imple ## Endpoints -All space endpoints live under the `dev.happyview.space` namespace and require [DPoP authentication](../getting-started/authentication.md). +All space endpoints live under the `dev.happyview.space` namespace and require [DPoP authentication](../../getting-started/authentication.md). | Endpoint | Method | Description | | ---------------------------------------- | ------ | ------------------------------------- | @@ -97,8 +97,8 @@ HappyView mostly mirrors [Daniel Holmgren's `permissioned-data` branch](https:// ## Next steps -- [Managing Spaces](spaces/managing-spaces.md) — create, update, and delete spaces -- [Members](spaces/members.md) — manage membership and delegation -- [Records](spaces/records.md) — read and write permissioned data -- [Credentials](spaces/credentials.md) — cross-service authentication for spaces -- [Invites](spaces/invites.md) — invite-based membership +- [Managing Spaces](./managing-spaces.md) — create, update, and delete spaces +- [Members](./members.md) — manage membership and delegation +- [Records](./records.md) — read and write permissioned data +- [Credentials](./credentials.md) — cross-service authentication for spaces +- [Invites](./invites.md) — invite-based membership diff --git a/packages/docs/content/docs/getting-started/authentication.md b/packages/docs/content/docs/getting-started/authentication.md index 71a5844..affe8c1 100644 --- a/packages/docs/content/docs/getting-started/authentication.md +++ b/packages/docs/content/docs/getting-started/authentication.md @@ -5,7 +5,7 @@ title: "Authentication" HappyView has two distinct authentication surfaces: - **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). -- **Admin API** (`/admin/*`) — user-level authentication via admin API keys or service auth JWTs, gated by [permissions](../guides/admin/permissions.md). +- **Admin API** (`/admin/*`) — user-level authentication via admin API keys or service auth JWTs, gated by [permissions](../guides/permissions.md). ## Which endpoints require what? @@ -13,7 +13,7 @@ HappyView has two distinct authentication surfaces: | ---------------------------------- | ----------------------- | --------------------------------------------------------------------------------------------------- | | Queries (`GET /xrpc/{method}`) | `X-Client-Key` required | Optional — DPoP auth if the query needs to know who the user is | | Procedures (`POST /xrpc/{method}`) | `X-Client-Key` required | Required — DPoP auth so HappyView can proxy writes to the user's PDS | -| Admin API (`/admin/*`) | — | Required — admin API key or service auth JWT with the right [permissions](../guides/admin/permissions.md) | +| Admin API (`/admin/*`) | — | Required — admin API key or service auth JWT with the right [permissions](../guides/permissions.md) | | Health check (`GET /health`) | — | — | ## XRPC: API client identification @@ -94,7 +94,7 @@ Admin endpoints don't use API clients. They require a real HappyView user, ident ### Admin API key -For automation — CI/CD, monitoring, cron jobs — create an [admin API key](../guides/admin/api-keys.md) at **Settings > API Keys** or via `POST /admin/api-keys` and pass it as a bearer token: +For automation — CI/CD, monitoring, cron jobs — create an [admin API key](../guides/api-keys.md) at **Settings > API Keys** or via `POST /admin/api-keys` and pass it as a bearer token: ```sh export TOKEN="hv_your-api-key-here" @@ -121,7 +121,7 @@ As with the other methods, the resolved DID still has to exist in the HappyView On a fresh deployment, the `users` table is empty. The first authenticated request to any admin endpoint auto-bootstraps that user as the **super user** with all permissions granted. This includes logging in to the dashboard — the dashboard makes admin API calls on your behalf, so the first person to log in becomes the super user. -To add more users after that, use `POST /admin/users` or the [dashboard](dashboard.md). You can assign permissions individually or use a template (`viewer`, `operator`, `manager`, `full_access`). See [Admin API — Users](../reference/admin/users.md) for details. +To add more users after that, use `POST /admin/users` or the [dashboard](dashboard.md). You can assign permissions individually or use a template (`viewer`, `operator`, `manager`, `full_access`). See [Admin API — Users](../api-reference/admin/users.md) for details. ## Proxying procedures to the user's PDS @@ -318,7 +318,7 @@ This deletes the stored session and the associated DPoP key. ## Next steps - [JavaScript SDK](../sdk/overview.md) — authenticate and make XRPC calls from JavaScript -- [Permissions](../guides/admin/permissions.md) — full list of permissions and what each one grants -- [API Keys](../guides/admin/api-keys.md) — create scoped admin API keys for automation -- [Admin API — API Clients](../reference/admin/api-clients.md) — register API clients and configure rate limits -- [Third-Party API Clients](../reference/oauth/api-clients.md) — let third-party apps manage their own API clients programmatically +- [Permissions](../guides/permissions.md) — full list of permissions and what each one grants +- [API Keys](../guides/api-keys.md) — create scoped admin API keys for automation +- [Admin API — API Clients](../api-reference/admin/api-clients.md) — register API clients and configure rate limits +- [Third-Party API Clients](../api-reference/oauth/api-clients.md) — let third-party apps manage their own API clients programmatically diff --git a/packages/docs/content/docs/getting-started/configuration.md b/packages/docs/content/docs/getting-started/configuration.md index 41f60cf..5b40060 100644 --- a/packages/docs/content/docs/getting-started/configuration.md +++ b/packages/docs/content/docs/getting-started/configuration.md @@ -2,7 +2,7 @@ title: "Configuration" --- -HappyView is configured via environment variables. A `.env` file in the project root is loaded automatically on startup. See [Deployment](deployment/docker.md) for local setup or [Production Deployment](production-deployment.md) for production setup. +HappyView is configured via environment variables. A `.env` file in the project root is loaded automatically on startup. See [Deployment](deployment/docker.md) for local setup or [Production Deployment](deployment/production.md) for production setup. ## Environment variables @@ -11,19 +11,19 @@ HappyView is configured via environment variables. A `.env` file in the project | `DATABASE_URL` | yes | --- | Database connection string. SQLite (`sqlite://path/to/db?mode=rwc`) or Postgres (`postgres://user:pass@host/db`) | | `DATABASE_BACKEND` | no | auto-detected | Force `sqlite` or `postgres`. Auto-detected from `DATABASE_URL` scheme if not set | | `PUBLIC_URL` | yes | --- | Public-facing URL for HappyView (used for OAuth callbacks, e.g. `https://happyview.example.com`). **For local development, use `http://127.0.0.1:3000` — not `localhost`** (see note below). Do **not** include the base path — see `BASE_PATH` | -| `BASE_PATH` | no | _(none)_ | Subpath prefix for mounting HappyView behind a reverse proxy (e.g. `/hv`). Must start with `/` and have no trailing slash. When set, all routes are served under this prefix and the dashboard is accessible at `https://example.com/hv/`. See [Reverse proxy subpath](production-deployment.md#reverse-proxy-subpath) | +| `BASE_PATH` | no | _(none)_ | Subpath prefix for mounting HappyView behind a reverse proxy (e.g. `/hv`). Must start with `/` and have no trailing slash. When set, all routes are served under this prefix and the dashboard is accessible at `https://example.com/hv/`. See [Reverse proxy subpath](deployment/production.md#reverse-proxy-subpath) | | `SESSION_SECRET` | no | dev default | Secret key for signing session cookies (at least 64 characters). **Must be set in production** | | `HOST` | no | `0.0.0.0` | Bind host | | `PORT` | no | `3000` | Bind port | | `JETSTREAM_URL` | no | `wss://jetstream1.us-east.bsky.network` | Jetstream WebSocket URL for real-time record streaming | -| `RELAY_URL` | no | `https://bsky.network` | Relay URL for [backfill](../guides/indexing/backfill.md) repo discovery | +| `RELAY_URL` | no | `https://bsky.network` | Relay URL for [backfill](../guides/backfill.md) repo discovery | | `PLC_URL` | no | `https://plc.directory` | [PLC directory](https://github.com/did-method-plc/did-method-plc) URL for DID resolution | | `STATIC_DIR` | no | `./web/out` | Directory containing the built dashboard static assets | | `EVENT_LOG_RETENTION_DAYS` | no | `30` | Number of days to keep event logs before automatic cleanup. Set to `0` to disable cleanup | | `TOKEN_ENCRYPTION_KEY` | no | --- | Base64-encoded 32-byte key for encrypting stored OAuth tokens. **Strongly recommended in production** | | `DEFAULT_RATE_LIMIT_CAPACITY` | no | `100` | Default token bucket capacity used when registering a new API client | | `DEFAULT_RATE_LIMIT_REFILL_RATE` | no | `2.0` | Default token bucket refill rate (tokens/second) for new API clients | -| `ATTESTATION_PRIVATE_KEY` | no | auto-generated | Hex-encoded 32-byte secp256k1 private key for [attestation signing](../guides/features/attestation-signing.md). Auto-generated and persisted to database on first run | +| `ATTESTATION_PRIVATE_KEY` | no | auto-generated | Hex-encoded 32-byte secp256k1 private key for [attestation signing](../guides/attestation-signing.md). Auto-generated and persisted to database on first run | | `ATTESTATION_KEY_ID` | no | `did:web:{host}#attestation` | Key identifier included in attestation signatures. Derived from `PUBLIC_URL` by default | | `ATTESTATION_SIG_TYPE` | no | app-specific NSID | `$type` value used in attestation signature objects | | `RUST_LOG` | no | `happyview=debug,tower_http=debug` | Log filter (uses `tracing_subscriber::EnvFilter`) | @@ -70,4 +70,4 @@ SESSION_SECRET=change-me-in-production - [Authentication](authentication.md) — set up OAuth and admin users - [Dashboard](dashboard.md) — explore the admin dashboard -- [Production deployment](production-deployment.md) — deploy HappyView to production +- [Production deployment](deployment/production.md) — deploy HappyView to production diff --git a/packages/docs/content/docs/getting-started/dashboard.md b/packages/docs/content/docs/getting-started/dashboard.md index 82a3485..324a49b 100644 --- a/packages/docs/content/docs/getting-started/dashboard.md +++ b/packages/docs/content/docs/getting-started/dashboard.md @@ -2,7 +2,7 @@ title: "Dashboard" --- -HappyView ships with a web dashboard that provides a visual interface for everything the [admin API](../reference/admin/admin-api.md) offers. It runs as a separate Next.js application alongside the Rust backend and authenticates via atproto OAuth. +HappyView ships with a web dashboard that provides a visual interface for everything the [admin API](../api-reference/admin/admin-api.md) offers. It runs as a separate Next.js application alongside the Rust backend and authenticates via atproto OAuth. On 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. @@ -27,7 +27,7 @@ HappyView generates a default Lua script when you first set the type to query or Toggle **Enable backfill** to index historical records when uploading a record-type lexicon. -**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/indexing/lexicons.md#network-lexicons) for how resolution works. +**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. #### JSON editor @@ -41,7 +41,7 @@ The JSON editor provides real-time validation against the atproto Lexicon v1 sch The Lua editor provides context-aware code completions, including suggestions for the `Record`, `db`, `input`, and `params` APIs as well as Lua keywords, builtins, and standard library functions. It also has snippets for `if`, `for`, `function`, etc. -See [Lua Scripting](../guides/scripting.md) for the full runtime reference and examples. +See [Lua Scripting](../guides/lua-scripting.md) for the full runtime reference and examples. ### Records @@ -49,7 +49,7 @@ Navigate to **Records** to browse all indexed atproto records. Records are group ### Backfill -Navigate to **Backfill** to view and manage backfill jobs. You can start a new backfill for any record-type lexicon to import historical records from the network. The page shows job status, progress (repos processed / total), and record counts. See [Backfill](../guides/indexing/backfill.md) for how the process works. +Navigate to **Backfill** to view and manage backfill jobs. You can start a new backfill for any record-type lexicon to import historical records from the network. The page shows job status, progress (repos processed / total), and record counts. See [Backfill](../guides/backfill.md) for how the process works. ### Dead Letters @@ -59,11 +59,11 @@ Navigate to **Dead Letters** to view records that failed to index. Each entry sh ### Users -Navigate to **Users** to manage who can access the admin API and dashboard. You can add users by DID, assign permissions individually or via a template (`viewer`, `operator`, `manager`, `full_access`), and remove users. The super user is highlighted and has all permissions by default. See [Permissions](../guides/admin/permissions.md) for what each permission grants. +Navigate to **Users** to manage who can access the admin API and dashboard. You can add users by DID, assign permissions individually or via a template (`viewer`, `operator`, `manager`, `full_access`), and remove users. The super user is highlighted and has all permissions by default. See [Permissions](../guides/permissions.md) for what each permission grants. ### API Keys -Create and revoke admin API keys for automation. Each key is scoped to specific permissions and tied to the creating user. See [API Keys](../guides/admin/api-keys.md) for details. +Create and revoke admin API keys for automation. Each key is scoped to specific permissions and tied to the creating user. See [API Keys](../guides/api-keys.md) for details. ### API Clients @@ -73,11 +73,11 @@ Register and manage third-party API clients. Each client gets an `hvc_…` clien ### Plugins -Manage installed plugins and configure plugin secrets. Plugins extend HappyView with additional functionality. Plugin secrets are encrypted at rest when `TOKEN_ENCRYPTION_KEY` is configured. See [Plugins](../guides/features/plugins.md) for details. +Manage installed plugins and configure plugin secrets. Plugins extend HappyView with additional functionality. Plugin secrets are encrypted at rest when `TOKEN_ENCRYPTION_KEY` is configured. See [Plugins](../guides/plugins.md) for details. ### Labelers -Configure labeler subscriptions for content labeling. See [Labelers](../guides/features/labelers.md) for details. +Configure labeler subscriptions for content labeling. See [Labelers](../guides/labelers.md) for details. ## System @@ -87,7 +87,7 @@ Configure instance-level settings: application name, logo, terms of service URL, ### XRPC Proxy -Control which unrecognized XRPC methods are forwarded to their resolved authority. Choose from four modes: **Disabled** (block all proxy requests), **Open** (proxy everything — the default), **Allowlist** (only proxy NSIDs matching your patterns), or **Blocklist** (proxy everything except matching patterns). Allowlist and blocklist modes accept NSID patterns with trailing wildcards (e.g. `com.example.*`). Locally registered lexicons are always served regardless of this setting. See [XRPC Proxy](../reference/admin/xrpc-proxy.md) for the full API reference. +Control which unrecognized XRPC methods are forwarded to their resolved authority. Choose from four modes: **Disabled** (block all proxy requests), **Open** (proxy everything — the default), **Allowlist** (only proxy NSIDs matching your patterns), or **Blocklist** (proxy everything except matching patterns). Allowlist and blocklist modes accept NSID patterns with trailing wildcards (e.g. `com.example.*`). Locally registered lexicons are always served regardless of this setting. See [XRPC Proxy](../api-reference/admin/xrpc-proxy.md) for the full API reference. ### ENV Variables @@ -103,7 +103,7 @@ The **About** page shows the current HappyView version and instance configuratio ## Next steps -- [Lexicons](../guides/indexing/lexicons.md) — how lexicons drive HappyView's indexing and routing -- [Lua Scripting](../guides/scripting.md) — write custom query and procedure logic -- [Permissions](../guides/admin/permissions.md) — manage user access to admin features +- [Lexicons](../guides/lexicons.md) — how lexicons drive HappyView's indexing and routing +- [Lua Scripting](../guides/lua-scripting.md) — write custom query and procedure logic +- [Permissions](../guides/permissions.md) — manage user access to admin features - [Configuration](configuration.md) — full list of environment variables diff --git a/packages/docs/content/docs/getting-started/deployment/production.md b/packages/docs/content/docs/getting-started/deployment/production.md index e4bfa6c..edd589b 100644 --- a/packages/docs/content/docs/getting-started/deployment/production.md +++ b/packages/docs/content/docs/getting-started/deployment/production.md @@ -2,7 +2,7 @@ title: "Production" --- -This page covers what to change when taking a HappyView instance from local development to production. For setup instructions, see [Deployment](../getting-started/deployment/railway.md). This page assumes you already have a working deployment and focuses on hardening and operational concerns. +This page covers what to change when taking a HappyView instance from local development to production. For setup instructions, see [Deployment](railway.md). This page assumes you already have a working deployment and focuses on hardening and operational concerns. ## Session secret @@ -16,7 +16,7 @@ Never commit the secret to source control. Store it in your platform's secret ma ## Token encryption key -If you use [plugins](../guides/features/plugins.md) that require secrets (API keys, OAuth credentials), set `TOKEN_ENCRYPTION_KEY` to a base64-encoded 32-byte key. This encrypts plugin secrets at rest using AES-256-GCM: +If you use [plugins](../../guides/plugins.md) that require secrets (API keys, OAuth credentials), set `TOKEN_ENCRYPTION_KEY` to a base64-encoded 32-byte key. This encrypts plugin secrets at rest using AES-256-GCM: ```sh openssl rand -base64 32 @@ -90,13 +90,13 @@ SQLite is fine for small to medium instances and is the default. Switch to Postg - Larger-than-memory working sets - External tools that need direct read access to the records table -See the [database setup guide](../guides/database/database-setup.md) for configuration details and [Postgres → SQLite migration](../guides/database/postgres-to-sqlite-migration.md) if you're moving the other direction. Migrations run automatically on startup regardless of backend. +See the [database setup guide](../../guides/database/database-setup.md) for configuration details and [Postgres → SQLite migration](../../guides/database/postgres-to-sqlite-migration.md) if you're moving the other direction. Migrations run automatically on startup regardless of backend. ## Rate limits -HappyView has a per-client token-bucket rate limiter for XRPC endpoints. The defaults (set via `DEFAULT_RATE_LIMIT_CAPACITY` and `DEFAULT_RATE_LIMIT_REFILL_RATE`) apply to any [API client](../guides/admin/api-keys.md) that doesn't have per-client overrides. Raise the defaults cautiously — they exist so one misbehaving integrator can't saturate the server. +HappyView has a per-client token-bucket rate limiter for XRPC endpoints. The defaults (set via `DEFAULT_RATE_LIMIT_CAPACITY` and `DEFAULT_RATE_LIMIT_REFILL_RATE`) apply to any [API client](../../guides/api-keys.md) that doesn't have per-client overrides. Raise the defaults cautiously — they exist so one misbehaving integrator can't saturate the server. -Per-client overrides are set at client creation or via `PUT /admin/api-clients/{id}` (see [Admin API — API Clients](../reference/admin/api-clients.md)). +Per-client overrides are set at client creation or via `PUT /admin/api-clients/{id}` (see [Admin API — API Clients](../../api-reference/admin/api-clients.md)). ## Logging @@ -110,7 +110,7 @@ Structured logs go to stdout, so any platform that captures container stdout (Ra ## Event log retention -The admin [event log](../guides/admin/event-logs.md) is stored in the same database as records. `EVENT_LOG_RETENTION_DAYS` (default `30`) controls automatic cleanup. Set to `0` to keep events indefinitely — useful for compliance-sensitive deployments, but plan for database growth. +The admin [event log](../../guides/event-logs.md) is stored in the same database as records. `EVENT_LOG_RETENTION_DAYS` (default `30`) controls automatic cleanup. Set to `0` to keep events indefinitely — useful for compliance-sensitive deployments, but plan for database growth. ## Health checks @@ -123,10 +123,10 @@ For a deeper check, hit `GET /xrpc/com.atproto.server.describeServer` — this e - **SQLite**: back up the database file (e.g. `data/happyview.db`) plus its `-wal` and `-shm` sidecar files. Use `sqlite3 happyview.db ".backup '/path/backup.db'"` for a consistent snapshot while HappyView is running. - **Postgres**: standard `pg_dump` / managed-Postgres snapshots. -Most of what HappyView stores is derivable from the network — lost records can be re-indexed via [backfill](../guides/indexing/backfill.md). You can't recover from the network: user accounts and permissions, API keys, API clients, plugin secrets, and the Jetstream cursor. Prioritize those in your backup plan. +Most of what HappyView stores is derivable from the network — lost records can be re-indexed via [backfill](../../guides/backfill.md). You can't recover from the network: user accounts and permissions, API keys, API clients, plugin secrets, and the Jetstream cursor. Prioritize those in your backup plan. ## Next steps -- [Configuration](../getting-started/configuration.md) — full environment variable reference -- [Permissions](../guides/admin/permissions.md) — lock down admin access before exposing the dashboard publicly -- [Troubleshooting](../reference/troubleshooting.md) — diagnose issues with a running instance +- [Configuration](../configuration.md) — full environment variable reference +- [Permissions](../../guides/permissions.md) — lock down admin access before exposing the dashboard publicly +- [Troubleshooting](../../reference/troubleshooting.md) — diagnose issues with a running instance diff --git a/packages/docs/content/docs/getting-started/deployment/railway.md b/packages/docs/content/docs/getting-started/deployment/railway.md index d43d10b..7abf6d1 100644 --- a/packages/docs/content/docs/getting-started/deployment/railway.md +++ b/packages/docs/content/docs/getting-started/deployment/railway.md @@ -35,4 +35,4 @@ After deploying the template, you'll need to configure a few things before the s - [Configuration](../configuration.md) — full list of environment variables - [Dashboard](../dashboard.md) — manage lexicons, users, and plugins via the web UI -- [Production deployment](../production-deployment.md) — hardening checklist for production instances +- [Production deployment](production.md) — hardening checklist for production instances diff --git a/packages/docs/content/docs/getting-started/quickstart.md b/packages/docs/content/docs/getting-started/quickstart.md index c7e7da8..237edda 100644 --- a/packages/docs/content/docs/getting-started/quickstart.md +++ b/packages/docs/content/docs/getting-started/quickstart.md @@ -31,7 +31,7 @@ Lexicons tell HappyView what data to index and what endpoints to serve. The quic HappyView starts indexing records for that collection. A backfill job fetches historical records, and new records stream in via Jetstream. -You can also upload lexicons manually via the dashboard or the [admin API](../reference/admin/admin-api.md). See [Lexicons](../guides/indexing/lexicons.md) for the full details. +You can also upload lexicons manually via the dashboard or the [admin API](../api-reference/admin/admin-api.md). See [Lexicons](../guides/lexicons.md) for the full details. ## 4. Verify records are being indexed @@ -47,12 +47,12 @@ Without a Lua script, HappyView generates a default query endpoint that supports GET /xrpc/xyz.statusphere.listStatuses?limit=5 ``` -For custom query logic, attach a [Lua script](../guides/scripting.md). +For custom query logic, attach a [Lua script](../guides/lua-scripting.md). ## Next steps - [**Statusphere tutorial**](../tutorials/statusphere.md): full walkthrough building a complete AppView with record, query, and procedure lexicons -- [**Lexicons guide**](../guides/indexing/lexicons.md): target collections, backfill flag, network lexicons -- [**Lua Scripting**](../guides/scripting.md): custom query and procedure logic +- [**Lexicons guide**](../guides/lexicons.md): target collections, backfill flag, network lexicons +- [**Lua Scripting**](../guides/lua-scripting.md): custom query and procedure logic - [**Configuration**](configuration.md): environment variables and tuning - [**Authentication**](authentication.md): how OAuth works and how to get API tokens diff --git a/packages/docs/content/docs/guides/api-clients.md b/packages/docs/content/docs/guides/api-clients.md index 23a5a7f..c2901fc 100644 --- a/packages/docs/content/docs/guides/api-clients.md +++ b/packages/docs/content/docs/guides/api-clients.md @@ -4,7 +4,7 @@ title: "API Clients" API clients identify your application to a HappyView instance. Every XRPC request — even unauthenticated queries — must include a client key. This guide walks through creating a client, choosing between public and confidential types, and authenticating users. -For the admin CRUD endpoints, see the [API reference](../../reference/admin/api-clients.md). For the JavaScript SDK, see the [SDK docs](../../sdk/overview.md). +For the admin CRUD endpoints, see the [API reference](../api-reference/admin/api-clients.md). For the JavaScript SDK, see the [SDK docs](../sdk/overview.md). ## Concepts @@ -65,7 +65,7 @@ curl -X POST http://127.0.0.1:3000/admin/api-clients \ }' ``` -See the [API reference](../../reference/admin/api-clients.md#create-an-api-client) for all fields. +See the [API reference](../api-reference/admin/api-clients.md#create-an-api-client) for all fields. ## Using your client key @@ -93,7 +93,7 @@ curl 'https://happyview.example.com/xrpc/com.example.feed.getHot' \ ### Authenticated requests (user identity) -Procedures — and queries whose scripts need to know who the caller is — require a user's OAuth session. This uses [DPoP authentication](../../getting-started/authentication.md#dpop-key-provisioning-for-third-party-apps), where each request includes a cryptographic proof that the caller holds the right key. +Procedures — and queries whose scripts need to know who the caller is — require a user's OAuth session. This uses [DPoP authentication](../getting-started/authentication.md#dpop-key-provisioning-for-third-party-apps), where each request includes a cryptographic proof that the caller holds the right key. ```sh curl -X POST 'https://happyview.example.com/xrpc/com.example.createPost' \ @@ -137,7 +137,7 @@ if (result) { } ``` -For server-side Node.js apps, use the core [`@happyview/oauth-client`](../../sdk/oauth-client.md) package with a confidential client. For type-safe XRPC calls, pair either client with [`@happyview/lex-agent`](../../sdk/lex-agent.md). +For server-side Node.js apps, use the core [`@happyview/oauth-client`](../sdk/oauth-client.md) package with a confidential client. For type-safe XRPC calls, pair either client with [`@happyview/lex-agent`](../sdk/lex-agent.md). ### Manual DPoP flow @@ -344,7 +344,7 @@ Rate limit state is returned in response headers: | `RateLimit-Reset` | Unix timestamp when the bucket will be full | | `Retry-After` | Seconds to wait (only on `429` responses) | -Adjust per-client rate limits via the dashboard or the [admin API](../../reference/admin/api-clients.md#update-an-api-client). +Adjust per-client rate limits via the dashboard or the [admin API](../api-reference/admin/api-clients.md#update-an-api-client). ## Security notes @@ -355,7 +355,7 @@ Adjust per-client rate limits via the dashboard or the [admin API](../../referen ## Next steps -- [Authentication](../../getting-started/authentication.md) — full protocol details and security model -- [JavaScript SDK](../../sdk/overview.md) — get started with the SDK -- [Admin API — API Clients](../../reference/admin/api-clients.md) — CRUD endpoints -- [Permissions](../admin/permissions.md) — control who can manage API clients +- [Authentication](../getting-started/authentication.md) — full protocol details and security model +- [JavaScript SDK](../sdk/overview.md) — get started with the SDK +- [Admin API — API Clients](../api-reference/admin/api-clients.md) — CRUD endpoints +- [Permissions](./permissions.md) — control who can manage API clients diff --git a/packages/docs/content/docs/guides/api-keys.md b/packages/docs/content/docs/guides/api-keys.md index b21765e..427259e 100644 --- a/packages/docs/content/docs/guides/api-keys.md +++ b/packages/docs/content/docs/guides/api-keys.md @@ -33,7 +33,7 @@ curl http://127.0.0.1:3000/admin/lexicons \ -H "Authorization: Bearer hv_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4" ``` -This works for all [admin API](../../reference/admin/admin-api.md) endpoints that the key has permissions for. Unlike OAuth tokens which carry the user's full permissions, API keys are limited to the specific permissions assigned at creation time. +This works for all [admin API](../api-reference/admin/admin-api.md) endpoints that the key has permissions for. Unlike OAuth tokens which carry the user's full permissions, API keys are limited to the specific permissions assigned at creation time. ## Revoking a key @@ -57,6 +57,6 @@ The **Last Used** column in the API Keys table shows when each key was last used ## Next steps -- [Admin API reference](../../reference/admin/admin-api.md) — full endpoint documentation -- [Scripting](../scripting.md) — automate record processing with Lua scripts -- [Index hooks](../indexing/index-hooks.md) — push records to external services on write +- [Admin API reference](../api-reference/admin/admin-api.md) — full endpoint documentation +- [Scripting](./lua-scripting.md) — automate record processing with Lua scripts +- [Index hooks](./index-hooks.md) — push records to external services on write diff --git a/packages/docs/content/docs/guides/attestation-signing.md b/packages/docs/content/docs/guides/attestation-signing.md index 077f84f..16c81f8 100644 --- a/packages/docs/content/docs/guides/attestation-signing.md +++ b/packages/docs/content/docs/guides/attestation-signing.md @@ -39,7 +39,7 @@ If key loading fails for any reason, signing is disabled and `atproto.sign` / `a ## Using in Lua scripts -Available in queries, procedures, and index hooks via the [atproto API](../../reference/lua/atproto-api.md). +Available in queries, procedures, and index hooks via the [atproto API](../api-reference/lua/atproto-api.md). ### Signing a record @@ -106,6 +106,6 @@ Signatures are stored as objects in the record's `signatures` array: ## Next steps -- [atproto API reference](../../reference/lua/atproto-api.md#atprotosign) — `atproto.sign` and `atproto.verify_signature` parameter docs -- [Signed Record](../scripting/signed-record.md) — save a record with an attestation signature -- [Verify Signed Record](../scripting/signed-record-verify.md) — fetch a record and verify its signature +- [atproto API reference](../api-reference/lua/atproto-api.md#atprotosign) — `atproto.sign` and `atproto.verify_signature` parameter docs +- [Signed Record](../reference/script-examples/signed-record.md) — save a record with an attestation signature +- [Verify Signed Record](../reference/script-examples/signed-record-verify.md) — fetch a record and verify its signature diff --git a/packages/docs/content/docs/guides/backfill.md b/packages/docs/content/docs/guides/backfill.md index ba2b638..2414484 100644 --- a/packages/docs/content/docs/guides/backfill.md +++ b/packages/docs/content/docs/guides/backfill.md @@ -7,9 +7,9 @@ When you add a new record-type lexicon, HappyView starts indexing new records fr ## When backfill runs - **Automatically** when a record-type lexicon is uploaded with `backfill: true` (the default). See [Lexicons - Backfill flag](lexicons.md#backfill-flag). -- **Manually** via `POST /admin/backfill` or the [dashboard](../../getting-started/dashboard.md). You can scope a manual backfill to a specific collection, a specific DID, or both. +- **Manually** via `POST /admin/backfill` or the [dashboard](../getting-started/dashboard.md). You can scope a manual backfill to a specific collection, a specific DID, or both. -See the [admin API](../../reference/admin/backfill.md) for endpoint details. +See the [admin API](../api-reference/admin/backfill.md) for endpoint details. ## How it works @@ -36,4 +36,4 @@ Deleting records from HappyView (via the dashboard or API) only removes them fro ## Next steps - [Lexicons](lexicons.md#backfill-flag): Control whether lexicons trigger backfill on upload -- [Admin API — Backfill](../../reference/admin/backfill.md): Full reference for backfill endpoints +- [Admin API — Backfill](../api-reference/admin/backfill.md): Full reference for backfill endpoints diff --git a/packages/docs/content/docs/guides/database/database-setup.md b/packages/docs/content/docs/guides/database/database-setup.md index 9e13162..960dad1 100644 --- a/packages/docs/content/docs/guides/database/database-setup.md +++ b/packages/docs/content/docs/guides/database/database-setup.md @@ -76,5 +76,5 @@ If you are migrating existing Lua scripts from Postgres SQL syntax to SQLite syn - [SQLite → Postgres migration](sqlite-to-postgres-migration.md) — switch an existing instance from SQLite to Postgres - [Postgres → SQLite migration](postgres-to-sqlite-migration.md) — switch an existing instance from Postgres to SQLite -- [Lua scripting](../scripting.md) — write queries that target either backend +- [Lua scripting](../lua-scripting.md) — write queries that target either backend - [Configuration](../../getting-started/configuration.md) — `DATABASE_URL` and related variables diff --git a/packages/docs/content/docs/guides/database/postgres-to-sqlite-migration.md b/packages/docs/content/docs/guides/database/postgres-to-sqlite-migration.md index 0ce1a86..fdb1cb6 100644 --- a/packages/docs/content/docs/guides/database/postgres-to-sqlite-migration.md +++ b/packages/docs/content/docs/guides/database/postgres-to-sqlite-migration.md @@ -89,5 +89,5 @@ To switch back to Postgres, revert your `DATABASE_URL` to the Postgres connectio - [SQLite → Postgres migration](sqlite-to-postgres-migration.md) — migrate in the opposite direction - [Database setup](database-setup.md) — choose between SQLite and Postgres for new instances -- [Backfill](../indexing/backfill.md) — re-index records from the network after switching backends -- [Lua scripting](../scripting.md) — write SQL that works against either backend +- [Backfill](../backfill.md) — re-index records from the network after switching backends +- [Lua scripting](../lua-scripting.md) — write SQL that works against either backend diff --git a/packages/docs/content/docs/guides/database/sqlite-to-postgres-migration.md b/packages/docs/content/docs/guides/database/sqlite-to-postgres-migration.md index c9c6e46..2e78dd3 100644 --- a/packages/docs/content/docs/guides/database/sqlite-to-postgres-migration.md +++ b/packages/docs/content/docs/guides/database/sqlite-to-postgres-migration.md @@ -86,5 +86,5 @@ To switch back to SQLite, revert your `DATABASE_URL` to the SQLite connection st - [Postgres → SQLite migration](postgres-to-sqlite-migration.md) — migrate in the opposite direction - [Database setup](database-setup.md) — choose between SQLite and Postgres for new instances -- [Backfill](../indexing/backfill.md) — re-index records from the network after switching backends -- [Lua scripting](../scripting.md) — write SQL that works against either backend +- [Backfill](../backfill.md) — re-index records from the network after switching backends +- [Lua scripting](../lua-scripting.md) — write SQL that works against either backend diff --git a/packages/docs/content/docs/guides/developing-plugins.md b/packages/docs/content/docs/guides/developing-plugins.md index a0ca83b..bd8b4fc 100644 --- a/packages/docs/content/docs/guides/developing-plugins.md +++ b/packages/docs/content/docs/guides/developing-plugins.md @@ -103,5 +103,5 @@ Plugins can import these host functions: - [Official plugins repository](https://tangled.org/gamesgamesgamesgames.games/happyview-plugins) — ready-to-use plugins and the plugin SDK - [Plugins guide](plugins.md) — install and configure plugins -- [API Keys](../admin/api-keys.md) — authenticate programmatic access to admin endpoints -- [Permissions](../admin/permissions.md) — configure user access to plugin management +- [API Keys](./api-keys.md) — authenticate programmatic access to admin endpoints +- [Permissions](./permissions.md) — configure user access to plugin management diff --git a/packages/docs/content/docs/guides/event-logs.md b/packages/docs/content/docs/guides/event-logs.md index 848d5cf..363b09a 100644 --- a/packages/docs/content/docs/guides/event-logs.md +++ b/packages/docs/content/docs/guides/event-logs.md @@ -2,7 +2,7 @@ title: "Event Logs" --- -HappyView maintains an internal event log that records system activity — lexicon changes, record operations, Lua script executions and errors, user actions, API key events, backfill jobs, and Jetstream connectivity. Events are stored in the database and queryable via the [admin API](../../reference/admin/events.md). +HappyView maintains an internal event log that records system activity — lexicon changes, record operations, Lua script executions and errors, user actions, API key events, backfill jobs, and Jetstream connectivity. Events are stored in the database and queryable via the [admin API](../api-reference/admin/events.md). ## Event types @@ -16,7 +16,7 @@ Events follow a `category.action` naming convention. Each event has a severity l | `lexicon.updated` | info | Lexicon NSID | `revision`, `has_script`, `source` | | `lexicon.deleted` | info | Lexicon NSID | — | -Logged when lexicons are uploaded, updated, or deleted via the [admin API](../../reference/admin/lexicons.md). The `actor_did` is the user who performed the action. +Logged when lexicons are uploaded, updated, or deleted via the [admin API](../api-reference/admin/lexicons.md). The `actor_did` is the user who performed the action. ### Record events @@ -50,7 +50,7 @@ For query scripts (unauthenticated), `caller_did` and `input` are omitted from t | `user.permissions_updated` | info | User ID | `granted`, `revoked` | | `user.super_transferred` | warn | New super user ID | `from_user_id` | -The `user.bootstrapped` event is logged when the first user is auto-promoted to super user (see [Auth - Auto-bootstrap](../../reference/admin/admin-api.md#auth)). +The `user.bootstrapped` event is logged when the first user is auto-promoted to super user (see [Auth - Auto-bootstrap](../api-reference/admin/admin-api.md#auth)). ### Auth events @@ -81,7 +81,7 @@ Logged when a user attempts to access an endpoint they don't have permission for | `hook.executed` | info | Record AT URI | `lexicon_id` | | `hook.dead_lettered` | error | Record AT URI | `lexicon_id`, `error` | -Logged when [index hooks](../indexing/index-hooks.md) run. Dead-lettered events indicate a hook failed all retry attempts. You can manage dead letters from the **Data > Dead Letters** page in the dashboard — see [Dead Letters](#dead-letters) below. +Logged when [index hooks](./index-hooks.md) run. Dead-lettered events indicate a hook failed all retry attempts. You can manage dead letters from the **Data > Dead Letters** page in the dashboard — see [Dead Letters](#dead-letters) below. ### Backfill events @@ -91,7 +91,7 @@ Logged when [index hooks](../indexing/index-hooks.md) run. Dead-lettered events | `backfill.completed` | info | Collection NSID | `job_id`, `total_repos` | | `backfill.failed` | error | Collection NSID | `job_id`, `error` | -See [Backfill](../indexing/backfill.md) for background on backfill jobs. +See [Backfill](./backfill.md) for background on backfill jobs. ### Jetstream events @@ -120,7 +120,7 @@ curl "http://127.0.0.1:3000/admin/events?category=lexicon" -H "$AUTH" curl "http://127.0.0.1:3000/admin/events?limit=20&cursor=2026-03-01T11:59:00Z" -H "$AUTH" ``` -See the [Admin API reference](../../reference/admin/events.md#list-event-logs) for full parameter documentation. +See the [Admin API reference](../api-reference/admin/events.md#list-event-logs) for full parameter documentation. ## Retention @@ -128,7 +128,7 @@ Event logs are automatically cleaned up based on the `EVENT_LOG_RETENTION_DAYS` Set `EVENT_LOG_RETENTION_DAYS=0` to disable automatic cleanup and keep logs indefinitely. -See [Configuration](../../getting-started/configuration.md) for all environment variables. +See [Configuration](../getting-started/configuration.md) for all environment variables. ## Dead Letters @@ -144,6 +144,6 @@ Bulk actions are available for selected rows or all entries matching the current ## Next steps -- [Admin API — Event Logs](../../reference/admin/events.md) — full query parameters and response format +- [Admin API — Event Logs](../api-reference/admin/events.md) — full query parameters and response format - [Permissions](permissions.md) — control which users can read event logs -- [Troubleshooting](../../reference/troubleshooting.md) — using event logs to diagnose issues +- [Troubleshooting](../reference/troubleshooting.md) — using event logs to diagnose issues diff --git a/packages/docs/content/docs/guides/index-hooks.md b/packages/docs/content/docs/guides/index-hooks.md index 2749e53..1f20b68 100644 --- a/packages/docs/content/docs/guides/index-hooks.md +++ b/packages/docs/content/docs/guides/index-hooks.md @@ -4,11 +4,11 @@ title: "Index Hooks" Index hooks are Lua scripts that run whenever a record in a collection is created, updated, or deleted. They run **before** the record is indexed, giving you the ability to filter out unwanted records, transform record data before storage, or trigger side effects like syncing with external services. -Index hooks fire on **all** record events for the collection — including records created by HappyView procedure endpoints, not just events from the network. Unlike [query and procedure scripts](../scripting.md) that run in response to XRPC requests, index hooks are triggered by incoming Jetstream events (which include events caused by HappyView's own PDS writes). +Index hooks fire on **all** record events for the collection — including records created by HappyView procedure endpoints, not just events from the network. Unlike [query and procedure scripts](./lua-scripting.md) that run in response to XRPC requests, index hooks are triggered by incoming Jetstream events (which include events caused by HappyView's own PDS writes). ## Attaching a hook -Each record-type lexicon can have one index hook. You can add it through the [dashboard](../../getting-started/dashboard.md) (click "Add Index Hook" on any record lexicon's detail page) or via the [admin API](../../reference/admin/lexicons.md#upload--upsert-a-lexicon) by including the `index_hook` field when uploading a lexicon. +Each record-type lexicon can have one index hook. You can add it through the [dashboard](../getting-started/dashboard.md) (click "Add Index Hook" on any record lexicon's detail page) or via the [admin API](../api-reference/admin/lexicons.md#upload--upsert-a-lexicon) by including the `index_hook` field when uploading a lexicon. ## Script structure @@ -59,13 +59,13 @@ Index hooks do **not** have access to `caller_did`, `input`, `params`, `method`, Index hooks have access to: -- **[Database API](../../reference/lua/database-api.md)** — `db.query`, `db.get`, `db.search`, `db.backlinks`, `db.count`, `db.raw` -- **[HTTP API](../../reference/lua/http-api.md)** — `http.get`, `http.post`, `http.put`, `http.patch`, `http.delete`, `http.head` -- **[XRPC Lua API](../../reference/lua/xrpc-lua-api.md)** — `xrpc.query`, `xrpc.procedure` -- **[atproto API](../../reference/lua/atproto-api.md)** — `atproto.resolve_service_endpoint`, `atproto.get_labels`, `atproto.get_labels_batch` -- **[JSON API](../../reference/lua/json-api.md)** — `json.encode`, `json.decode` -- **[Utility globals](../scripting.md#utility-globals)** — `log()`, `now()`, `TID()`, `toarray()` -- **[Script variables](../../reference/admin/script-variables.md)** — `env` table with key-value pairs configured in the dashboard +- **[Database API](../api-reference/lua/database-api.md)** — `db.query`, `db.get`, `db.search`, `db.backlinks`, `db.count`, `db.raw` +- **[HTTP API](../api-reference/lua/http-api.md)** — `http.get`, `http.post`, `http.put`, `http.patch`, `http.delete`, `http.head` +- **[XRPC Lua API](../api-reference/lua/xrpc-lua-api.md)** — `xrpc.query`, `xrpc.procedure` +- **[atproto API](../api-reference/lua/atproto-api.md)** — `atproto.resolve_service_endpoint`, `atproto.get_labels`, `atproto.get_labels_batch` +- **[JSON API](../api-reference/lua/json-api.md)** — `json.encode`, `json.decode` +- **[Utility globals](./lua-scripting.md#utility-globals)** — `log()`, `now()`, `TID()`, `toarray()` +- **[Script variables](../api-reference/admin/script-variables.md)** — `env` table with key-value pairs configured in the dashboard ## Error handling and retries @@ -75,7 +75,7 @@ Index hooks are designed to be resilient: 2. If all retries are exhausted, the failed event is inserted into the `dead_letter_hooks` table for later inspection. 3. On failure the system **fails open** — the original record is stored as-is so indexing is not permanently blocked. -Failed hooks are logged as errors. Check the [event logs](../admin/event-logs.md) or query the `dead_letter_hooks` table directly to find and replay failures. +Failed hooks are logged as errors. Check the [event logs](./event-logs.md) or query the `dead_letter_hooks` table directly to find and replay failures. ### Performance considerations @@ -183,7 +183,7 @@ function handle() end ``` -See the full [Algolia sync reference](../scripting/algolia-sync.md) for more detail. +See the full [Algolia sync reference](../reference/script-examples/algolia-sync.md) for more detail. ### Sync to Meilisearch @@ -218,10 +218,10 @@ function handle() end ``` -See the full [Meilisearch sync reference](../scripting/meilisearch-sync.md) for more detail. +See the full [Meilisearch sync reference](../reference/script-examples/meilisearch-sync.md) for more detail. ## Next steps -- [Lua Scripting](../scripting.md): Full reference for the sandbox, APIs, and debugging +- [Lua Scripting](./lua-scripting.md): Full reference for the sandbox, APIs, and debugging - [Lexicons](lexicons.md): Understand how record, query, and procedure lexicons work together -- [Admin API — Lexicons](../../reference/admin/lexicons.md#upload--upsert-a-lexicon): Upload lexicons with index hooks via the API +- [Admin API — Lexicons](../api-reference/admin/lexicons.md#upload--upsert-a-lexicon): Upload lexicons with index hooks via the API diff --git a/packages/docs/content/docs/guides/labelers.md b/packages/docs/content/docs/guides/labelers.md index 2b554e6..0dcfc59 100644 --- a/packages/docs/content/docs/guides/labelers.md +++ b/packages/docs/content/docs/guides/labelers.md @@ -67,7 +67,7 @@ Labels appear in the **Labels** column on the Records page as color-coded badges Self-labels (applied by the record author) use an outline badge style to distinguish them from external labels. Hover over a badge to see the source labeler's DID. -Labels are also available in the records API response and in Lua scripts via the [`atproto.get_labels` and `atproto.get_labels_batch`](../../reference/lua/atproto-api.md#atprotoget_labels) functions. +Labels are also available in the records API response and in Lua scripts via the [`atproto.get_labels` and `atproto.get_labels_batch`](../api-reference/lua/atproto-api.md#atprotoget_labels) functions. ## Using labels in your AppView @@ -87,6 +87,6 @@ Labeler subscriptions give your AppView access to content moderation signals wit ## Next steps -- [Admin API — Labelers](../../reference/admin/labelers.md) — full endpoint documentation -- [atproto API](../../reference/lua/atproto-api.md) — access labels in Lua scripts with `get_labels` and `get_labels_batch` -- [Permissions](../admin/permissions.md) — manage user access to labeler operations +- [Admin API — Labelers](../api-reference/admin/labelers.md) — full endpoint documentation +- [atproto API](../api-reference/lua/atproto-api.md) — access labels in Lua scripts with `get_labels` and `get_labels_batch` +- [Permissions](./permissions.md) — manage user access to labeler operations diff --git a/packages/docs/content/docs/guides/lexicons.md b/packages/docs/content/docs/guides/lexicons.md index 9a34863..0e10ab7 100644 --- a/packages/docs/content/docs/guides/lexicons.md +++ b/packages/docs/content/docs/guides/lexicons.md @@ -4,7 +4,7 @@ title: "Lexicons" 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. -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). +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](../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). ## Supported lexicon types @@ -15,7 +15,7 @@ You don't write route handlers or database queries; you upload a lexicon and Hap | `procedure` | Registers a `POST /xrpc/{nsid}` endpoint that proxies writes to the user's PDS | | `definitions` | Stored but does not generate routes or subscriptions | -A typical setup has three lexicons working together: a **record** lexicon that defines the data and triggers indexing, a **query** lexicon that exposes a read endpoint, and a **procedure** lexicon that exposes a write endpoint. The [Statusphere tutorial](../../tutorials/statusphere.md) walks through this pattern end-to-end. +A typical setup has three lexicons working together: a **record** lexicon that defines the data and triggers indexing, a **query** lexicon that exposes a read endpoint, and a **procedure** lexicon that exposes a write endpoint. The [Statusphere tutorial](../tutorials/statusphere.md) walks through this pattern end-to-end. ## Target collection @@ -23,7 +23,7 @@ Query and procedure lexicons don't store data themselves. They operate on record For example, a query lexicon `xyz.statusphere.listStatuses` would set `target_collection` to `xyz.statusphere.status` to read from that record collection. -See the [admin API](../../reference/admin/lexicons.md#upload--upsert-a-lexicon) for how to set `target_collection` when uploading. +See the [admin API](../api-reference/admin/lexicons.md#upload--upsert-a-lexicon) for how to set `target_collection` when uploading. The `target_collection` is available in Lua scripts as the `collection` global, but it is not required if your endpoint uses a Lua script. @@ -91,7 +91,7 @@ When a client calls `/xrpc/{method}` and HappyView has a local lexicon for that A few things to note: - HappyView does **not** proxy to the reversed hostname directly. `foo.example.com` is only the DNS host for the TXT record — the actual XRPC request goes to whatever PDS endpoint the authority DID resolves to. -- Proxying applies equally to queries and procedures. For procedures, HappyView uses the caller's OAuth session to attach a DPoP-bound access token (see [Authentication](../../getting-started/authentication.md#proxying-procedures-to-the-users-pds)). +- Proxying applies equally to queries and procedures. For procedures, HappyView uses the caller's OAuth session to attach a DPoP-bound access token (see [Authentication](../getting-started/authentication.md#proxying-procedures-to-the-users-pds)). - If authority resolution fails — no TXT record, unresolvable DID, or the target PDS doesn't support the method — the client gets an error back. HappyView does not fall back to any other routing strategy. - Tracking a network lexicon does **not** make HappyView handle requests for that NSID locally. Network lexicons are only about indexing record collections and keeping the schema up to date. If a client calls a query NSID that you've tracked as a network lexicon but haven't uploaded a local query lexicon for, HappyView still proxies the request out — it won't query your local record table. To serve a method locally, upload a local query or procedure lexicon with a matching `target_collection`. @@ -99,8 +99,8 @@ In short: if you want to serve an XRPC method on your instance, you need a local ## Next steps -- [Lua Scripting](../scripting.md): Add custom query and procedure logic to your endpoints +- [Lua Scripting](./lua-scripting.md): Add custom query and procedure logic to your endpoints - [Index Hooks](index-hooks.md): Run Lua scripts when records are indexed from the network -- [XRPC API](../../reference/xrpc-api.md): Understand how the generated endpoints behave +- [XRPC API](../api-reference/xrpc-api.md): Understand how the generated endpoints behave - [Backfill](backfill.md): Learn how historical records are indexed -- [Admin API](../../reference/admin/admin-api.md): Full reference for lexicon management endpoints +- [Admin API](../api-reference/admin/admin-api.md): Full reference for lexicon management endpoints diff --git a/packages/docs/content/docs/guides/lua-scripting.md b/packages/docs/content/docs/guides/lua-scripting.md index e22128d..3b75c75 100644 --- a/packages/docs/content/docs/guides/lua-scripting.md +++ b/packages/docs/content/docs/guides/lua-scripting.md @@ -12,7 +12,7 @@ Without Lua scripts, HappyView's query endpoints return raw records and procedur Scripts are attached to query and procedure lexicons and run in a sandboxed Lua VM with access to the [Record API](#record-api), a [database API](#database-api), an [HTTP client API](#http-api), a [JSON API](#json-api), and a set of [context globals](#context-globals). -For scripts that react to record changes from the network (rather than XRPC requests), see [Index Hooks](indexing/index-hooks.md). +For scripts that react to record changes from the network (rather than XRPC requests), see [Index Hooks](index-hooks.md). ## Script structure @@ -37,7 +37,7 @@ The `os` module is replaced with a safe subset exposing only `os.time`, `os.date An instruction limit of 1,000,000 prevents infinite loops. Exceeding it terminates the script with an error. -See the [Standard Libraries](../reference/lua/standard-libraries.md) reference for the full list of available Lua modules and builtins. +See the [Standard Libraries](../api-reference/lua/standard-libraries.md) reference for the full list of available Lua modules and builtins. ## Context globals @@ -73,7 +73,7 @@ Available in both queries and procedures: | ---------------- | ------- | ------------------------------------------------------------------- | | `now()` | string | Current UTC timestamp in ISO 8601 format | | `log(message)` | — | Log a message (appears in server logs at debug level) | -| `TID()` | string | Generate a fresh atproto TID (13-character sortable identifier). Also provides conversion methods — see [Utility Globals reference](../reference/lua/utility-globals.md#tid). | +| `TID()` | string | Generate a fresh atproto TID (13-character sortable identifier). Also provides conversion methods — see [Utility Globals reference](../api-reference/lua/utility-globals.md#tid). | | `toarray(table)` | table | Mark a table as a JSON array for serialization (see [below](#toarray)) | ### toarray @@ -92,7 +92,7 @@ You don't need `toarray()` on results from `db.query`, `db.search`, `db.backlink 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. -See the full [Record API reference](../reference/lua/record-api.md) for constructor, static methods, instance methods, fields, schema validation, and save behavior. +See the full [Record API reference](../api-reference/lua/record-api.md) for constructor, static methods, instance methods, fields, schema validation, and save behavior. Quick example: @@ -108,7 +108,7 @@ end The `db` table provides access to the database. Available in both queries and procedures. -See the full [Database API reference](../reference/lua/database-api.md) for `db.query`, `db.get`, `db.search`, `db.backlinks`, `db.count`, and `db.raw`. +See the full [Database API reference](../api-reference/lua/database-api.md) for `db.query`, `db.get`, `db.search`, `db.backlinks`, `db.count`, and `db.raw`. Quick example: @@ -123,7 +123,7 @@ end The `http` table provides async HTTP client functions. Available in both queries and procedures. -See the full [HTTP API reference](../reference/lua/http-api.md) for all methods, options, and response format. +See the full [HTTP API reference](../api-reference/lua/http-api.md) for all methods, options, and response format. Quick example: @@ -136,7 +136,7 @@ local data = json.decode(resp.body) The `xrpc` table lets scripts call other XRPC endpoints — both local and proxied. Available in both queries and procedures. -See the full [XRPC Lua API reference](../reference/lua/xrpc-lua-api.md) for `xrpc.query` and `xrpc.procedure`. +See the full [XRPC Lua API reference](../api-reference/lua/xrpc-lua-api.md) for `xrpc.query` and `xrpc.procedure`. Quick example: @@ -149,13 +149,13 @@ local data = json.decode(resp.body) The `atproto` table provides atproto utility functions like DID resolution, label queries, and record signing. -See the full [atproto API reference](../reference/lua/atproto-api.md) for `atproto.resolve_service_endpoint`, `atproto.get_labels`, `atproto.get_labels_batch`, `atproto.sign`, and `atproto.verify_signature`. +See the full [atproto API reference](../api-reference/lua/atproto-api.md) for `atproto.resolve_service_endpoint`, `atproto.get_labels`, `atproto.get_labels_batch`, `atproto.sign`, and `atproto.verify_signature`. ## JSON API The `json` global provides JSON serialization and deserialization. -See the full [JSON API reference](../reference/lua/json-api.md) for `json.encode` and `json.decode`. +See the full [JSON API reference](../api-reference/lua/json-api.md) for `json.encode` and `json.decode`. ## Debugging @@ -195,28 +195,28 @@ The **full error message** is logged server-side at error level. Check the serve See the example script references for complete, ready-to-use scripts: **Queries:** -- [Get a record](scripting/get-record.md) — fetch a single record by AT URI -- [Paginated list](scripting/paginated-list.md) — list records with cursor-based pagination and DID filtering -- [List or fetch](scripting/list-or-fetch.md) — combined single-record lookup and paginated listing -- [Expanded query](scripting/expanded-query.md) — list statuses with user profiles in a single response -- [Verify signed record](scripting/signed-record-verify.md) — fetch a record and verify its attestation signature +- [Get a record](../reference/script-examples/get-record.md) — fetch a single record by AT URI +- [Paginated list](../reference/script-examples/paginated-list.md) — list records with cursor-based pagination and DID filtering +- [List or fetch](../reference/script-examples/list-or-fetch.md) — combined single-record lookup and paginated listing +- [Expanded query](../reference/script-examples/expanded-query.md) — list statuses with user profiles in a single response +- [Verify signed record](../reference/script-examples/signed-record-verify.md) — fetch a record and verify its attestation signature **Procedures:** -- [Create a record](scripting/create-record.md) — simple write that saves input as a record -- [Upsert a record](scripting/upsert-record.md) — create or update using a deterministic rkey -- [Update or delete](scripting/update-or-delete.md) — single endpoint handling create, update, and delete -- [Batch save](scripting/batch-save.md) — create multiple records in parallel with `Record.save_all()` -- [Sidecar records](scripting/sidecar-records.md) — create linked records across collections with a shared rkey -- [Cascading delete](scripting/cascading-delete.md) — delete a record and all related records -- [Complex mutations](scripting/complex-mutations.md) — load, transform, and save a record with multiple field changes -- [Signed record](scripting/signed-record.md) — save a record with an attestation signature +- [Create a record](../reference/script-examples/create-record.md) — simple write that saves input as a record +- [Upsert a record](../reference/script-examples/upsert-record.md) — create or update using a deterministic rkey +- [Update or delete](../reference/script-examples/update-or-delete.md) — single endpoint handling create, update, and delete +- [Batch save](../reference/script-examples/batch-save.md) — create multiple records in parallel with `Record.save_all()` +- [Sidecar records](../reference/script-examples/sidecar-records.md) — create linked records across collections with a shared rkey +- [Cascading delete](../reference/script-examples/cascading-delete.md) — delete a record and all related records +- [Complex mutations](../reference/script-examples/complex-mutations.md) — load, transform, and save a record with multiple field changes +- [Signed record](../reference/script-examples/signed-record.md) — save a record with an attestation signature **Index Hooks:** -- [Algolia sync](scripting/algolia-sync.md) — push records to an Algolia search index on create/update/delete +- [Algolia sync](../reference/script-examples/algolia-sync.md) — push records to an Algolia search index on create/update/delete ## Next steps -- [Index Hooks](indexing/index-hooks.md): React to record changes from the network in real time -- [Lexicons](indexing/lexicons.md): Understand how record, query, and procedure lexicons work together -- [XRPC API](../reference/xrpc-api.md): See how endpoints behave with and without Lua scripts +- [Index Hooks](index-hooks.md): React to record changes from the network in real time +- [Lexicons](lexicons.md): Understand how record, query, and procedure lexicons work together +- [XRPC API](../api-reference/xrpc-api.md): See how endpoints behave with and without Lua scripts - [Dashboard](../getting-started/dashboard.md#lua-editor): Use the web editor with context-aware completions diff --git a/packages/docs/content/docs/guides/permissions.md b/packages/docs/content/docs/guides/permissions.md index 42ea1cd..20e6105 100644 --- a/packages/docs/content/docs/guides/permissions.md +++ b/packages/docs/content/docs/guides/permissions.md @@ -134,10 +134,10 @@ Go to **Settings > Users** to view and manage user permissions. Click on a user - `PATCH /admin/users/{id}/permissions` — grant or revoke individual permissions - `POST /admin/users/transfer-super` — transfer super user status (super user only) -See the [Admin API — Users](../../reference/admin/users.md) for full details. +See the [Admin API — Users](../api-reference/admin/users.md) for full details. ## Next steps -- [Admin API reference](../../reference/admin/admin-api.md) — endpoint documentation with required permissions +- [Admin API reference](../api-reference/admin/admin-api.md) — endpoint documentation with required permissions - [API Keys](api-keys.md) — creating scoped API keys - [Event Logs](event-logs.md) — permission-denied events are logged for auditing diff --git a/packages/docs/content/docs/guides/plugins.md b/packages/docs/content/docs/guides/plugins.md index 4281c57..4c80899 100644 --- a/packages/docs/content/docs/guides/plugins.md +++ b/packages/docs/content/docs/guides/plugins.md @@ -77,5 +77,5 @@ These are only necessary if you can't configure variables via the dashboard. Das - [Developing Plugins](developing-plugins.md) — create your own plugins with the WASM plugin API - [Official plugins repository](https://tangled.org/gamesgamesgamesgames.games/happyview-plugins) — ready-to-use plugins for Steam, Xbox, itch.io, and more -- [API Keys](../admin/api-keys.md) — authenticate programmatic access to admin endpoints -- [Permissions](../admin/permissions.md) — configure user access to plugin management +- [API Keys](./api-keys.md) — authenticate programmatic access to admin endpoints +- [Permissions](./permissions.md) — configure user access to plugin management diff --git a/packages/docs/content/docs/guides/upgrading-to-v2.md b/packages/docs/content/docs/guides/upgrading-to-v2.md index 3abaffd..9d8e2f7 100644 --- a/packages/docs/content/docs/guides/upgrading-to-v2.md +++ b/packages/docs/content/docs/guides/upgrading-to-v2.md @@ -105,15 +105,15 @@ Clients should pass the `cursor` value from the response as a query parameter to v2 adds several new Lua APIs that you can optionally adopt: -- [`atproto.resolve_service_endpoint`](../reference/lua/atproto-api.md) — resolve a DID to its PDS endpoint -- [`atproto.get_labels`](../reference/lua/atproto-api.md) / [`atproto.get_labels_batch`](../reference/lua/atproto-api.md) — fetch content labels from subscribed labelers -- [`os.time`](../reference/lua/standard-libraries.md), `os.date`, `os.difftime`, `os.clock` — safe `os` subset +- [`atproto.resolve_service_endpoint`](../api-reference/lua/atproto-api.md) — resolve a DID to its PDS endpoint +- [`atproto.get_labels`](../api-reference/lua/atproto-api.md) / [`atproto.get_labels_batch`](../api-reference/lua/atproto-api.md) — fetch content labels from subscribed labelers +- [`os.time`](../api-reference/lua/standard-libraries.md), `os.date`, `os.difftime`, `os.clock` — safe `os` subset ## 5. Update API key prefixes v1 API keys used the `hv_` prefix. v2 keeps existing `hv_` keys working but new keys use the `hv_` prefix as well. No migration needed. -v2 also adds **API clients** for third-party OAuth apps, which use the `hvc_` prefix. These are separate from API keys — see the [API Clients guide](features/api-clients.md). +v2 also adds **API clients** for third-party OAuth apps, which use the `hvc_` prefix. These are separate from API keys — see the [API Clients guide](api-clients.md). ## 6. Update the dashboard URL @@ -136,7 +136,7 @@ v2 introduces granular user permissions. After upgrading: 2. Additional users are created with no permissions by default. 3. Assign permissions or use a template (Viewer, Operator, Manager, Full Access). -See the [Permissions guide](admin/permissions.md) for details. +See the [Permissions guide](permissions.md) for details. ## 8. Docker Compose (example) diff --git a/packages/docs/content/docs/index.md b/packages/docs/content/docs/index.md index 17c7e67..69bd6c6 100644 --- a/packages/docs/content/docs/index.md +++ b/packages/docs/content/docs/index.md @@ -2,21 +2,21 @@ title: "Introduction" --- -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/indexing/backfill.md), without writing a single line of server code. +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. Building 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. ## Features -- **Schema-driven endpoints:** Upload a [lexicon](guides/indexing/lexicons.md) and HappyView generates XRPC query and procedure routes, storage, and indexing from it — updatable at runtime with no restart. +- **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. -- **Network sync built in:** Real-time record streaming via [Jetstream](https://github.com/bluesky-social/jetstream), historical [backfill](guides/indexing/backfill.md) from each user's PDS, and atproto OAuth with DPoP-bound proxy writes back to the PDS. +- **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. -- **Customize with Lua, hooks, and plugins:** [Lua scripts](guides/scripting.md) for query and procedure logic, [index hooks](guides/indexing/index-hooks.md) that fire on every record change, WASM [plugins](guides/features/plugins.md) for external platform integration, and [labeler](guides/features/labelers.md) subscriptions for content moderation. +- **Customize with Lua, hooks, and plugins:** [Lua scripts](guides/lua-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. -- **Protocol-native:** Works with any PDS, resolves DIDs through the directory, and fetches [network lexicons](guides/indexing/lexicons.md#network-lexicons) via DNS authority resolution. +- **Protocol-native:** Works with any PDS, resolves DIDs through the directory, and fetches [network lexicons](guides/lexicons.md#network-lexicons) via DNS authority resolution. -- **Full admin surface:** Built-in [dashboard](getting-started/dashboard.md) and [admin API](reference/admin/admin-api.md) for managing lexicons, users, API keys, API clients, backfill jobs, and plugins. +- **Full admin surface:** Built-in [dashboard](getting-started/dashboard.md) and [admin API](api-reference/admin/admin-api.md) for managing lexicons, users, API keys, API clients, backfill jobs, and plugins. ## Design Principles @@ -31,9 +31,9 @@ Building an AppView from scratch means wiring up real-time event streams, record ## Next Steps - [Quickstart](getting-started/deployment/railway.md): Deploy HappyView on Railway or run it locally -- [Lexicons](guides/indexing/lexicons.md): Upload lexicon schemas and start indexing records -- [Lua Scripting](guides/scripting.md): Write custom query and procedure logic -- [Index Hooks](guides/indexing/index-hooks.md): React to record changes in real time -- [Labelers](guides/features/labelers.md): Subscribe to external labelers and manage content labels -- [Plugins](guides/features/plugins.md): Integrate with external platforms using WASM plugins -- [Event Logs](guides/admin/event-logs.md): Monitor system activity, debug script errors, and audit admin actions +- [Lexicons](guides/lexicons.md): Upload lexicon schemas and start indexing records +- [Lua Scripting](guides/lua-scripting.md): Write custom query and procedure logic +- [Index Hooks](guides/index-hooks.md): React to record changes in real time +- [Labelers](guides/labelers.md): Subscribe to external labelers and manage content labels +- [Plugins](guides/plugins.md): Integrate with external platforms using WASM plugins +- [Event Logs](guides/event-logs.md): Monitor system activity, debug script errors, and audit admin actions diff --git a/packages/docs/content/docs/reference/architecture.md b/packages/docs/content/docs/reference/architecture.md index 682e0a1..ab58e21 100644 --- a/packages/docs/content/docs/reference/architecture.md +++ b/packages/docs/content/docs/reference/architecture.md @@ -2,7 +2,7 @@ title: "Architecture" --- -Guide for contributors working on HappyView itself. For a user-facing overview, see the [Introduction](../README.md). +Guide for contributors working on HappyView itself. For a user-facing overview, see the [Introduction](../index.md). ## System overview @@ -33,7 +33,7 @@ graph LR Labeler["Labeler
WebSocket (out-of-band)"] -->|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](https://github.com/bluesky-social/jetstream); historical records are backfilled in-process by discovering repos via the relay's `listReposByCollection` and fetching records directly from each PDS. [Labelers](../guides/features/labelers.md) are external services that emit content labels over a direct WebSocket connection — they operate out-of-band, outside the relay/repo system. +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](https://github.com/bluesky-social/jetstream); historical records are backfilled in-process by discovering repos via the relay's `listReposByCollection` and fetching records directly from each PDS. [Labelers](../guides/labelers.md) are external services that emit content labels over a direct WebSocket connection — they operate out-of-band, outside the relay/repo system. ## Request flow diff --git a/packages/docs/content/docs/reference/glossary.md b/packages/docs/content/docs/reference/glossary.md index 8bf3d87..85aff68 100644 --- a/packages/docs/content/docs/reference/glossary.md +++ b/packages/docs/content/docs/reference/glossary.md @@ -14,7 +14,7 @@ Key terms used throughout the HappyView documentation. For a broader introductio **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. -**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/indexing/lexicons.md). +**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). **NSID** (Namespaced Identifier) — A reverse-DNS identifier for a lexicon (e.g. `xyz.statusphere.status`). The authority is everything except the last segment. @@ -24,23 +24,23 @@ Key terms used throughout the HappyView documentation. For a broader introductio **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`). -**Relay** — A network service that aggregates repository data from many PDSes. HappyView queries the relay during [backfill](../guides/indexing/backfill.md) to discover which repos contain records for a given collection, then fetches each repo's records directly from its PDS. +**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. **rkey** (Record Key) — The unique key for a record within a collection and repo. These are most commonly TIDs (timestamp-based) or NSIDs. **TID** (Timestamp Identifier) — A 13-character sortable identifier used as a record key. Generated from the current timestamp. -**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). +**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](../api-reference/xrpc-api.md). **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. ## HappyView-specific terms -**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/indexing/backfill.md). +**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). -**Network lexicon** — A lexicon fetched directly from the atproto network via DNS authority resolution, rather than uploaded manually. See [Lexicons - Network lexicons](../guides/indexing/lexicons.md#network-lexicons). +**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). -**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/admin/permissions.md). +**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). **Permission template** — A predefined set of permissions that can be applied when creating a user. Templates are: **Viewer** (read-only access), **Operator** (viewer + backfill and API key management), **Manager** (operator + lexicon and record management), and **Full Access** (all 20 permissions). diff --git a/packages/docs/content/docs/reference/script-examples/algolia-sync.md b/packages/docs/content/docs/reference/script-examples/algolia-sync.md index 3167180..1ae1949 100644 --- a/packages/docs/content/docs/reference/script-examples/algolia-sync.md +++ b/packages/docs/content/docs/reference/script-examples/algolia-sync.md @@ -39,7 +39,7 @@ end 1. On **create** or **update**: sends a `PUT` request to Algolia's index API with the record data, using the AT URI as the `objectID`. Algolia upserts the object — if it already exists, it's replaced. 2. On **delete**: sends a `DELETE` request to remove the object from the index by its AT URI. -The `json.encode()` function converts the Lua table into a JSON string for the request body. See [JSON API](../../reference/lua/json-api.md). +The `json.encode()` function converts the Lua table into a JSON string for the request body. See [JSON API](../../api-reference/lua/json-api.md). ## Configuration @@ -56,4 +56,4 @@ Replace the placeholder values: This hook keeps an external search index in sync with your indexed records in real time. Users searching through Algolia get results that reflect the latest state of the network without polling or scheduled jobs. -Combine this with a [query script](../../guides/scripting.md) that searches Algolia instead of the local database for a full-text search experience that goes beyond what `db.search` offers. +Combine this with a [query script](../../guides/lua-scripting.md) that searches Algolia instead of the local database for a full-text search experience that goes beyond what `db.search` offers. diff --git a/packages/docs/content/docs/reference/script-examples/batch-save.md b/packages/docs/content/docs/reference/script-examples/batch-save.md index 405056f..7a6afc3 100644 --- a/packages/docs/content/docs/reference/script-examples/batch-save.md +++ b/packages/docs/content/docs/reference/script-examples/batch-save.md @@ -25,8 +25,8 @@ end ## How it works -1. Iterate over `input.items` and create a [`Record`](../../reference/lua/record-api.md) instance for each item. -2. Call [`Record.save_all()`](../../reference/lua/record-api.md#static-methods) to save all records in parallel, rather than one at a time. +1. Iterate over `input.items` and create a [`Record`](../../api-reference/lua/record-api.md) instance for each item. +2. Call [`Record.save_all()`](../../api-reference/lua/record-api.md#static-methods) to save all records in parallel, rather than one at a time. 3. Collect the resulting AT URIs and return them. ## Usage diff --git a/packages/docs/content/docs/reference/script-examples/cascading-delete.md b/packages/docs/content/docs/reference/script-examples/cascading-delete.md index ca2ebc0..2ce753c 100644 --- a/packages/docs/content/docs/reference/script-examples/cascading-delete.md +++ b/packages/docs/content/docs/reference/script-examples/cascading-delete.md @@ -51,7 +51,7 @@ end 1. Load the primary record by URI. Return early if it doesn't exist. 2. Query for related records, in this example comments by the same user that reference the primary record's URI. -3. Load each related record with [`Record.load`](../../reference/lua/record-api.md#static-methods) to get a deletable `Record` instance. +3. Load each related record with [`Record.load`](../../api-reference/lua/record-api.md#static-methods) to get a deletable `Record` instance. 4. Delete everything. Each `r:delete()` removes the record from the user's PDS and the local index. ## Usage diff --git a/packages/docs/content/docs/reference/script-examples/complex-mutations.md b/packages/docs/content/docs/reference/script-examples/complex-mutations.md index 35208af..f21a529 100644 --- a/packages/docs/content/docs/reference/script-examples/complex-mutations.md +++ b/packages/docs/content/docs/reference/script-examples/complex-mutations.md @@ -58,12 +58,12 @@ end ## How it works -1. Load the existing record with [`Record.load`](../../reference/lua/record-api.md#static-methods). This gives you a mutable `Record` instance with all the current field values. +1. Load the existing record with [`Record.load`](../../api-reference/lua/record-api.md#static-methods). This gives you a mutable `Record` instance with all the current field values. 2. Apply transformations directly on the record's fields: - **Increment a counter**: use `or 0` to handle the field being `nil` on first access. - **Merge tags**: iterate over `input.tags`, skip duplicates already in `r.tags`, append new ones, then trim the list to 10. - **Normalize a string**: use `string.gsub` to trim whitespace. - - **Set a timestamp**: use [`now()`](../../guides/scripting.md#utility-globals) for UTC ISO 8601. + - **Set a timestamp**: use [`now()`](../../guides/lua-scripting.md#utility-globals) for UTC ISO 8601. 3. Call `r:save()`. Since `_uri` is set (from the load), this calls `putRecord` to update the record on the user's PDS. ## Usage diff --git a/packages/docs/content/docs/reference/script-examples/create-record.md b/packages/docs/content/docs/reference/script-examples/create-record.md index cd022d7..5939226 100644 --- a/packages/docs/content/docs/reference/script-examples/create-record.md +++ b/packages/docs/content/docs/reference/script-examples/create-record.md @@ -16,7 +16,7 @@ end ## How it works -1. Create a new [`Record`](../../reference/lua/record-api.md) instance from the target collection, populated with the fields from the request body. +1. Create a new [`Record`](../../api-reference/lua/record-api.md) instance from the target collection, populated with the fields from the request body. 2. Call `r:save()`, which creates the record on the caller's PDS and indexes it locally. 3. Return the AT URI and CID of the newly created record. diff --git a/packages/docs/content/docs/reference/script-examples/expanded-query.md b/packages/docs/content/docs/reference/script-examples/expanded-query.md index dfdb211..6f3b9b1 100644 --- a/packages/docs/content/docs/reference/script-examples/expanded-query.md +++ b/packages/docs/content/docs/reference/script-examples/expanded-query.md @@ -53,7 +53,7 @@ end 1. Query statuses from the target collection with pagination, same as a normal list query. 2. Extract the unique DIDs from the returned status URIs using `string.match`. 3. Build an AT URI for each DID's `app.bsky.actor.profile/self` record (this is where Bluesky profiles live). -4. Load all profiles in parallel with [`Record.load_all`](../../reference/lua/record-api.md#static-methods). Profiles that aren't indexed locally return `nil` and are skipped. +4. Load all profiles in parallel with [`Record.load_all`](../../api-reference/lua/record-api.md#static-methods). Profiles that aren't indexed locally return `nil` and are skipped. 5. Return statuses and profiles as separate keys, with the cursor from the status query. ## Usage diff --git a/packages/docs/content/docs/reference/script-examples/get-record.md b/packages/docs/content/docs/reference/script-examples/get-record.md index 838bd05..f037453 100644 --- a/packages/docs/content/docs/reference/script-examples/get-record.md +++ b/packages/docs/content/docs/reference/script-examples/get-record.md @@ -24,7 +24,7 @@ end ## How it works 1. Check that the `uri` query parameter is present. Return a structured error if missing. -2. Look up the record with [`db.get`](../../reference/lua/database-api.md#dbget), which returns the record table or `nil`. +2. Look up the record with [`db.get`](../../api-reference/lua/database-api.md#dbget), which returns the record table or `nil`. 3. Return the record wrapped in an object. ## Usage diff --git a/packages/docs/content/docs/reference/script-examples/list-or-fetch.md b/packages/docs/content/docs/reference/script-examples/list-or-fetch.md index 242280e..ae84da2 100644 --- a/packages/docs/content/docs/reference/script-examples/list-or-fetch.md +++ b/packages/docs/content/docs/reference/script-examples/list-or-fetch.md @@ -27,8 +27,8 @@ end ## How it works -1. If a `uri` query parameter is provided, fetch that single record with [`db.get`](../../reference/lua/database-api.md#dbget) and return it. If it doesn't exist, return a structured error (using `error()` would trigger a 500 response). -2. Otherwise, list records from the target collection using [`db.query`](../../reference/lua/database-api.md#dbquery), with optional filtering by `did` and cursor-based pagination. The `cursor` is an opaque string from a previous response — pass it through directly. Since `limit` arrives as a string, `tonumber()` converts it to a number. +1. If a `uri` query parameter is provided, fetch that single record with [`db.get`](../../api-reference/lua/database-api.md#dbget) and return it. If it doesn't exist, return a structured error (using `error()` would trigger a 500 response). +2. Otherwise, list records from the target collection using [`db.query`](../../api-reference/lua/database-api.md#dbquery), with optional filtering by `did` and cursor-based pagination. The `cursor` is an opaque string from a previous response — pass it through directly. Since `limit` arrives as a string, `tonumber()` converts it to a number. ## Usage diff --git a/packages/docs/content/docs/reference/script-examples/meilisearch-sync.md b/packages/docs/content/docs/reference/script-examples/meilisearch-sync.md index 177297c..fdb4a7b 100644 --- a/packages/docs/content/docs/reference/script-examples/meilisearch-sync.md +++ b/packages/docs/content/docs/reference/script-examples/meilisearch-sync.md @@ -40,11 +40,11 @@ end 1. On **create** or **update**: sends a `POST` request to Meilisearch's document API with the record data wrapped in an array. Meilisearch upserts by `id` — if a document with the same AT URI already exists, it's replaced. 2. On **delete**: sends a `DELETE` request to remove the document from the index by its AT URI. -The `toarray()` function ensures the table is encoded as a JSON array (Meilisearch expects an array of documents). See [JSON API](../../reference/lua/json-api.md). +The `toarray()` function ensures the table is encoded as a JSON array (Meilisearch expects an array of documents). See [JSON API](../../api-reference/lua/json-api.md). ## Configuration -This script uses [script variables](../../guides/scripting.md) instead of hardcoded values. Set these via the [admin API](../../reference/admin/admin-api.md) or dashboard: +This script uses [script variables](../../guides/lua-scripting.md) instead of hardcoded values. Set these via the [admin API](../../api-reference/admin/admin-api.md) or dashboard: | Variable | Value | | --------------------- | ------------------------------------------------------------------------------ | @@ -59,4 +59,4 @@ This hook keeps an external search index in sync with your indexed records in re Meilisearch is a good fit for self-hosted deployments — colocate it alongside HappyView (e.g. on the same Railway project) for sub-millisecond network latency. -Combine this with a [query script](../../guides/scripting.md) that searches Meilisearch instead of the local database for a full-text search experience that goes beyond what `db.search` offers. +Combine this with a [query script](../../guides/lua-scripting.md) that searches Meilisearch instead of the local database for a full-text search experience that goes beyond what `db.search` offers. diff --git a/packages/docs/content/docs/reference/script-examples/paginated-list.md b/packages/docs/content/docs/reference/script-examples/paginated-list.md index 7a281e7..4f98fcb 100644 --- a/packages/docs/content/docs/reference/script-examples/paginated-list.md +++ b/packages/docs/content/docs/reference/script-examples/paginated-list.md @@ -25,7 +25,7 @@ end ## How it works 1. Parse `limit` from the query string, defaulting to 20 and capping at 100. -2. Call [`db.query`](../../reference/lua/database-api.md#dbquery) with the target collection, optional DID filter, and cursor for pagination. +2. Call [`db.query`](../../api-reference/lua/database-api.md#dbquery) with the target collection, optional DID filter, and cursor for pagination. 3. Return the result directly. `db.query` returns `{ records = [...], cursor = "..." }` where `cursor` is an opaque string present when more records exist. ## Usage diff --git a/packages/docs/content/docs/reference/script-examples/sidecar-records.md b/packages/docs/content/docs/reference/script-examples/sidecar-records.md index 529da25..326d8b9 100644 --- a/packages/docs/content/docs/reference/script-examples/sidecar-records.md +++ b/packages/docs/content/docs/reference/script-examples/sidecar-records.md @@ -34,9 +34,9 @@ end ## How it works -1. Generate a single [`TID()`](../../guides/scripting.md#utility-globals) to use as the rkey for both records. +1. Generate a single [`TID()`](../../guides/lua-scripting.md#utility-globals) to use as the rkey for both records. 2. Create a `Record` for each collection and call `r:set_rkey()` with the shared rkey. -3. Save both records in parallel with [`Record.save_all()`](../../reference/lua/record-api.md#static-methods). +3. Save both records in parallel with [`Record.save_all()`](../../api-reference/lua/record-api.md#static-methods). 4. Return both URIs so the client knows the identity of each record. ## Usage diff --git a/packages/docs/content/docs/reference/script-examples/signed-record-verify.md b/packages/docs/content/docs/reference/script-examples/signed-record-verify.md index d42b361..9ac99bf 100644 --- a/packages/docs/content/docs/reference/script-examples/signed-record-verify.md +++ b/packages/docs/content/docs/reference/script-examples/signed-record-verify.md @@ -29,7 +29,7 @@ end ## How it works 1. Fetch the record by AT URI. -2. If a signature is present, rebuild the same field table that was signed and verify it with [`atproto.verify_signature()`](../../reference/lua/atproto-api.md#atprotoverify_signature). +2. If a signature is present, rebuild the same field table that was signed and verify it with [`atproto.verify_signature()`](../../api-reference/lua/atproto-api.md#atprotoverify_signature). 3. Return `verified = true` if the signature is valid, `false` if it's missing, invalid, or the signer isn't configured. ## Usage @@ -53,4 +53,4 @@ curl "http://127.0.0.1:3000/xrpc/xyz.example.getPost?uri=at://did:plc:abc/xyz.ex Pair this with the [Signed Record](signed-record.md) procedure to create a write-then-verify flow. The query re-derives the CID from the same fields that were originally signed, so any tampering between write and read is caught. -See [Attestation Signing](../features/attestation-signing.md) for setup and configuration. +See [Attestation Signing](../../guides/attestation-signing.md) for setup and configuration. diff --git a/packages/docs/content/docs/reference/script-examples/signed-record.md b/packages/docs/content/docs/reference/script-examples/signed-record.md index 46e8ab2..144c43c 100644 --- a/packages/docs/content/docs/reference/script-examples/signed-record.md +++ b/packages/docs/content/docs/reference/script-examples/signed-record.md @@ -26,7 +26,7 @@ end ## How it works 1. Create and save the record. -2. Sign the record fields with [`atproto.sign()`](../../reference/lua/atproto-api.md#atprotosign). The `nil` guard lets the script work without a signer configured. +2. Sign the record fields with [`atproto.sign()`](../../api-reference/lua/atproto-api.md#atprotosign). The `nil` guard lets the script work without a signer configured. 3. Return the signature alongside the URI. ## Usage @@ -55,4 +55,4 @@ curl -X POST http://127.0.0.1:3000/xrpc/xyz.example.createPost \ Attestation signatures let clients verify that a record was processed by your HappyView instance — useful for contributions, moderation decisions, or cross-instance data where provenance matters. The signature covers both the record content and the author's DID, so it can't be replayed across users or tampered with. -See [Attestation Signing](../features/attestation-signing.md) for setup and configuration, or [Verify Signed Record](signed-record-verify.md) for the read-side counterpart. +See [Attestation Signing](../../guides/attestation-signing.md) for setup and configuration, or [Verify Signed Record](signed-record-verify.md) for the read-side counterpart. diff --git a/packages/docs/content/docs/reference/script-examples/update-or-delete.md b/packages/docs/content/docs/reference/script-examples/update-or-delete.md index 40371ce..4a2882d 100644 --- a/packages/docs/content/docs/reference/script-examples/update-or-delete.md +++ b/packages/docs/content/docs/reference/script-examples/update-or-delete.md @@ -32,8 +32,8 @@ end ## How it works -1. If `input.delete` is truthy and `input.uri` is provided, load the record with [`Record.load`](../../reference/lua/record-api.md#static-methods) and delete it. -2. If only `input.uri` is provided, load the existing record with [`Record.load`](../../reference/lua/record-api.md#static-methods), update its fields, and save it back. Since `_uri` is already set, `r:save()` calls `putRecord` instead of `createRecord`. +1. If `input.delete` is truthy and `input.uri` is provided, load the record with [`Record.load`](../../api-reference/lua/record-api.md#static-methods) and delete it. +2. If only `input.uri` is provided, load the existing record with [`Record.load`](../../api-reference/lua/record-api.md#static-methods), update its fields, and save it back. Since `_uri` is already set, `r:save()` calls `putRecord` instead of `createRecord`. 3. If neither condition matches, create a new record from the input. ## Usage diff --git a/packages/docs/content/docs/reference/script-examples/upsert-record.md b/packages/docs/content/docs/reference/script-examples/upsert-record.md index f963e3e..1774302 100644 --- a/packages/docs/content/docs/reference/script-examples/upsert-record.md +++ b/packages/docs/content/docs/reference/script-examples/upsert-record.md @@ -34,8 +34,8 @@ end ## How it works -1. Use the client-provided `input.rkey` if present, otherwise generate a new [`TID()`](../../guides/scripting.md#utility-globals). This means omitting `rkey` always creates, while providing one enables updates. -2. Build the AT URI from the caller's DID, the target collection, and the rkey, then try to load it with [`Record.load`](../../reference/lua/record-api.md#static-methods). +1. Use the client-provided `input.rkey` if present, otherwise generate a new [`TID()`](../../guides/lua-scripting.md#utility-globals). This means omitting `rkey` always creates, while providing one enables updates. +2. Build the AT URI from the caller's DID, the target collection, and the rkey, then try to load it with [`Record.load`](../../api-reference/lua/record-api.md#static-methods). 3. If the record exists, update its fields and save. Since `_uri` is already set, `r:save()` calls `putRecord`. 4. If it doesn't exist, create a new record, set the rkey explicitly with `r:set_rkey()`, and save. This calls `createRecord` with the specified rkey. diff --git a/packages/docs/content/docs/reference/troubleshooting.md b/packages/docs/content/docs/reference/troubleshooting.md index d67c75c..0017e1f 100644 --- a/packages/docs/content/docs/reference/troubleshooting.md +++ b/packages/docs/content/docs/reference/troubleshooting.md @@ -20,9 +20,9 @@ Common issues and how to resolve them. **Causes**: -- The query lexicon is missing a `target_collection`. Without it, the query doesn't know which records to read. See [Lexicons - target_collection](../guides/indexing/lexicons.md#target-collection). +- The query lexicon is missing a `target_collection`. Without it, the query doesn't know which records to read. See [Lexicons - target_collection](../guides/lexicons.md#target-collection). - The record-type lexicon hasn't finished backfilling. Check backfill status with `GET /admin/backfill/status` or the dashboard. -- Records exist on the network but HappyView hasn't indexed them yet. Jetstream only delivers events from after the collection was added to the filter. Use [backfill](../guides/indexing/backfill.md) to import historical records. +- Records exist on the network but HappyView hasn't indexed them yet. Jetstream only delivers events from after the collection was added to the filter. Use [backfill](../guides/backfill.md) to import historical records. ## Procedure returns 401 Unauthorized @@ -50,7 +50,7 @@ Common issues and how to resolve them. **Causes**: -- Your user account doesn't have the specific permission required by the endpoint. Each endpoint requires a specific permission — see the [permissions table](admin/admin-api.md#permissions). +- Your user account doesn't have the specific permission required by the endpoint. Each endpoint requires a specific permission — see the [permissions table](../api-reference/admin/admin-api.md#permissions). - If using an API key, the key's effective permissions are the intersection of the key's permissions and your user permissions. A key can never have more access than the user who created it. - Only the super user can call `POST /admin/users/transfer-super`. This endpoint cannot be accessed with any permission — it requires super user status. @@ -62,9 +62,9 @@ Common issues and how to resolve them. 1. Check the server logs: the full error message is logged at error level but not exposed to the client. 2. Use `log("message")` in your script to trace execution. Output appears in server logs at debug level (requires `RUST_LOG` to include debug). -3. If you hit the execution limit, your script likely has an infinite loop or is processing too much data. See [Lua Scripting - Sandbox](../guides/scripting.md#sandbox). +3. If you hit the execution limit, your script likely has an infinite loop or is processing too much data. See [Lua Scripting - Sandbox](../guides/lua-scripting.md#sandbox). -See [Lua Scripting - Debugging](../guides/scripting.md#debugging) for more. +See [Lua Scripting - Debugging](../guides/lua-scripting.md#debugging) for more. ## Backfill job stuck in "pending" or "running" @@ -76,7 +76,7 @@ See [Lua Scripting - Debugging](../guides/scripting.md#debugging) for more. - The relay (`RELAY_URL`) may be unreachable or slow to respond. Check connectivity. - Individual PDS fetches can fail silently. The worker logs warnings and continues. Check server logs for details. -See [Backfill](../guides/indexing/backfill.md) for how the process works. +See [Backfill](../guides/backfill.md) for how the process works. ## Records not appearing in real time @@ -104,7 +104,7 @@ See [Backfill](../guides/indexing/backfill.md) for how the process works. **Causes**: -- `TOKEN_ENCRYPTION_KEY` is not set. Plugin secrets are encrypted at rest and cannot be read without this key. See [Plugins - Configuration](../guides/features/plugins.md#plugin-configuration). +- `TOKEN_ENCRYPTION_KEY` is not set. Plugin secrets are encrypted at rest and cannot be read without this key. See [Plugins - Configuration](../guides/plugins.md#plugin-configuration). - If `TOKEN_ENCRYPTION_KEY` changed since the secrets were saved, the existing encrypted values are unreadable. Re-enter the secrets via the dashboard or `PUT /admin/plugins/{id}/secrets`. - Environment variable secrets (`PLUGIN__`) are overridden by dashboard-configured secrets. If you've set both, the dashboard values take precedence. diff --git a/packages/docs/content/docs/sdk/oauth-client-browser/overview.md b/packages/docs/content/docs/sdk/oauth-client-browser/overview.md index 7df0a09..9ac8056 100644 --- a/packages/docs/content/docs/sdk/oauth-client-browser/overview.md +++ b/packages/docs/content/docs/sdk/oauth-client-browser/overview.md @@ -2,9 +2,9 @@ title: "Browser Client" --- -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. +The browser client handles the full OAuth redirect flow for browser apps authenticating with a HappyView instance. It wraps the [OAuth Client](../oauth-client/overview.md) with Web Crypto, localStorage, and atproto handle/DID resolution. -If 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. +If you're starting a new app, consider using [`@happyview/lex-agent`](../lex-agent/overview.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. ## Installation @@ -47,7 +47,7 @@ const client = new HappyViewBrowserClient({ ``` -The API client must be registered as a **public** client (no secret) with your app's origin in `allowed_origins`. See [Authentication — API clients](../getting-started/authentication.md#api-clients-confidential-vs-public). +The API client must be registered as a **public** client (no secret) with your app's origin in `allowed_origins`. See [Authentication — API clients](../../getting-started/authentication.md#api-clients-confidential-vs-public). ## Sign in diff --git a/packages/docs/content/docs/sdk/oauth-client-node/overview.md b/packages/docs/content/docs/sdk/oauth-client-node/overview.md index 8d5f9e6..abd7415 100644 --- a/packages/docs/content/docs/sdk/oauth-client-node/overview.md +++ b/packages/docs/content/docs/sdk/oauth-client-node/overview.md @@ -2,7 +2,7 @@ title: "Node Client" --- -Server-side OAuth client for authenticating with a HappyView instance using AT Protocol. Built on top of [`@happyview/oauth-client`](./oauth-client.md), matching the API surface of [`@atproto/oauth-client-node`](https://www.npmjs.com/package/@atproto/oauth-client-node). +Server-side OAuth client for authenticating with a HappyView instance using AT Protocol. Built on top of [`@happyview/oauth-client`](../oauth-client/overview.md), matching the API surface of [`@atproto/oauth-client-node`](https://www.npmjs.com/package/@atproto/oauth-client-node). ## Installation diff --git a/packages/docs/content/docs/sdk/oauth-client/overview.md b/packages/docs/content/docs/sdk/oauth-client/overview.md index f793540..d67da9a 100644 --- a/packages/docs/content/docs/sdk/oauth-client/overview.md +++ b/packages/docs/content/docs/sdk/oauth-client/overview.md @@ -4,7 +4,7 @@ title: "OAuth Client" The core OAuth client handles DPoP key provisioning, session registration, and session restoration against a HappyView instance. It's platform-agnostic — you provide a `CryptoAdapter` and optional `StorageAdapter` for your environment. -If you're building a browser app, use the [Browser Client](./oauth-client-browser.md) instead. It wraps this package with Web Crypto, localStorage, and a complete OAuth redirect flow. +If you're building a browser app, use the [Browser Client](../oauth-client-browser/overview.md) instead. It wraps this package with Web Crypto, localStorage, and a complete OAuth redirect flow. ## Installation @@ -26,11 +26,11 @@ const client = new HappyViewOAuthClient({ }); ``` -The `clientSecret` parameter makes this a **confidential client**. Omit it for public clients (browser apps), which use PKCE instead. See [Authentication — API clients](../getting-started/authentication.md#api-clients-confidential-vs-public) for details. +The `clientSecret` parameter makes this a **confidential client**. Omit it for public clients (browser apps), which use PKCE instead. See [Authentication — API clients](../../getting-started/authentication.md#api-clients-confidential-vs-public) for details. ## DPoP key provisioning -Request a DPoP keypair from the HappyView instance. This is the first step of the [DPoP key provisioning flow](../getting-started/authentication.md#dpop-key-provisioning-for-third-party-apps). +Request a DPoP keypair from the HappyView instance. This is the first step of the [DPoP key provisioning flow](../../getting-started/authentication.md#dpop-key-provisioning-for-third-party-apps). ```typescript const { provisionId, dpopKey, pkceVerifier } = diff --git a/packages/docs/content/docs/sdk/overview.md b/packages/docs/content/docs/sdk/overview.md index 8deddbb..4a4158b 100644 --- a/packages/docs/content/docs/sdk/overview.md +++ b/packages/docs/content/docs/sdk/overview.md @@ -15,7 +15,7 @@ HappyView provides JavaScript packages for building third-party apps that authen **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. -**Already using `@atproto/api`?** `HappyViewSession` works directly as a session manager for `@atproto/api`'s `Agent` — just pass it to `new Agent(session)`. See [Using with @atproto/api](./oauth-client-browser.md#using-with-atprotoapi). +**Already using `@atproto/api`?** `HappyViewSession` works directly as a session manager for `@atproto/api`'s `Agent` — just pass it to `new Agent(session)`. See [Using with @atproto/api](./oauth-client-browser/overview.md#using-with-atprotoapi). **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. @@ -69,8 +69,8 @@ const result = await lex.xrpc(myLexicons.com.example.getGame, { ## Next steps -- [Lex Agent](./lex-agent.md): type-safe XRPC with `@atproto/lex` -- [OAuth Client](./oauth-client.md): platform-agnostic core client -- [Browser Client](./oauth-client-browser.md): browser OAuth redirect flow -- [Node Client](./oauth-client-node.md): server-side OAuth flow +- [Lex Agent](./lex-agent/overview.md): type-safe XRPC with `@atproto/lex` +- [OAuth Client](./oauth-client/overview.md): platform-agnostic core client +- [Browser Client](./oauth-client-browser/overview.md): browser OAuth redirect flow +- [Node Client](./oauth-client-node/overview.md): server-side OAuth flow - [Authentication](../getting-started/authentication.md): full details on DPoP key provisioning and API client types diff --git a/packages/docs/content/docs/tutorials/statusphere.md b/packages/docs/content/docs/tutorials/statusphere.md index 26541ac..5a22434 100644 --- a/packages/docs/content/docs/tutorials/statusphere.md +++ b/packages/docs/content/docs/tutorials/statusphere.md @@ -23,7 +23,7 @@ For more background on how the app works, see the [ATProto Statusphere guide](ht ## Step 1: Add the record lexicon -First, tell HappyView to start indexing Statusphere records. Since `xyz.statusphere.status` is [published on the atproto network](../guides/indexing/lexicons.md#network-lexicons), you can add it directly from the dashboard: +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: 1. Go to **Lexicons > Add Lexicon > Network** 2. Enter `xyz.statusphere.status` @@ -34,7 +34,7 @@ First, tell HappyView to start indexing Statusphere records. Since `xyz.statusph HappyView now subscribes to `xyz.statusphere.status` via Jetstream and kicks off a backfill job to index historical records. -You can also add lexicons via the [admin API](../reference/admin/lexicons.md). This is useful for automation or CI/CD workflows: +You can also add lexicons via the [admin API](../api-reference/admin/lexicons.md). This is useful for automation or CI/CD workflows: ```sh curl -X POST http://127.0.0.1:3000/admin/lexicons \ @@ -75,7 +75,7 @@ Once the backfill starts, you should see records appearing in the dashboard: ## Step 3: Create an API client -Before you can call any XRPC endpoint, you need an [API client](../guides/features/api-clients.md). The client key identifies your application to HappyView and is required on every request. +Before you can call any XRPC endpoint, you need an [API client](../guides/api-clients.md). The client key identifies your application to HappyView and is required on every request. 1. Go to **Settings > API Clients > New client** 2. Set the **Name** to something like "Statusphere Dev" @@ -106,7 +106,7 @@ Now add a query endpoint to read the indexed data: } ``` -3. A [Lua script](../guides/scripting.md) editor appears automatically. Replace the default script with: +3. A [Lua script](../guides/lua-scripting.md) editor appears automatically. Replace the default script with: ```lua collection = "xyz.statusphere.status" @@ -241,14 +241,14 @@ With three lexicons and a few lines of Lua, you have a complete Statusphere AppV - **A query endpoint** (`xyz.statusphere.listStatuses`) with filtering, pagination, and single-record lookups - **A write endpoint** (`xyz.statusphere.setStatus`) that creates records on the user's PDS and indexes them locally -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/admin-api.md). +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](../api-reference/admin/admin-api.md). ## Next steps -- [API Clients](../guides/features/api-clients.md): Public vs. confidential clients, DPoP authentication, and rate limiting -- [Lua Scripting](../guides/scripting.md): Explore the full Record and database APIs to build more complex queries -- [Lexicons](../guides/indexing/lexicons.md): Learn about network lexicons, the backfill flag, and record collections -- [XRPC API](../reference/xrpc-api.md): Understand how the generated endpoints behave -- [Admin API](../reference/admin/admin-api.md): Automate lexicon management via the API +- [API Clients](../guides/api-clients.md): Public vs. confidential clients, DPoP authentication, and rate limiting +- [Lua Scripting](../guides/lua-scripting.md): Explore the full Record and database APIs to build more complex queries +- [Lexicons](../guides/lexicons.md): Learn about network lexicons, the backfill flag, and record collections +- [XRPC API](../api-reference/xrpc-api.md): Understand how the generated endpoints behave +- [Admin API](../api-reference/admin/admin-api.md): Automate lexicon management via the API - [Statusphere example app](https://github.com/bluesky-social/statusphere-example-app): See the full Statusphere frontend - [ATProto Statusphere guide](https://atproto.com/guides/applications): How the app works at the protocol level