A lexicon-driven AppView for ATProto. happyview.dev
backfill firehose jetstream atproto appview oauth lexicon
8
fork

Configure Feed

Select the types of activity you want to include in your feed.

docs: add missing docs for Lua APIs

Trezy 077c74bc 8f9a4bac

+450 -31
+3
packages/docs/docs/getting-started/configuration.md
··· 20 20 | `TOKEN_ENCRYPTION_KEY` | no | --- | Base64-encoded 32-byte key for encrypting stored OAuth tokens. **Strongly recommended in production** | 21 21 | `DEFAULT_RATE_LIMIT_CAPACITY` | no | `100` | Default token bucket capacity used when registering a new API client | 22 22 | `DEFAULT_RATE_LIMIT_REFILL_RATE` | no | `2.0` | Default token bucket refill rate (tokens/second) for new API clients | 23 + | `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 | 24 + | `ATTESTATION_KEY_ID` | no | `did:web:{host}#attestation` | Key identifier included in attestation signatures. Derived from `PUBLIC_URL` by default | 25 + | `ATTESTATION_SIG_TYPE` | no | app-specific NSID | `$type` value used in attestation signature objects | 23 26 | `RUST_LOG` | no | `happyview=debug,tower_http=debug` | Log filter (uses `tracing_subscriber::EnvFilter`) | 24 27 | `APP_NAME` | no | --- | Application name shown on OAuth authorization screens. Overridden by database setting if set via admin API | 25 28 | `LOGO_URI` | no | --- | URL to application logo for OAuth screens. Overridden by database setting or logo upload |
+109
packages/docs/docs/guides/features/attestation-signing.md
··· 1 + # Attestation Signing 2 + 3 + HappyView can sign records with an ECDSA (secp256k1) keypair so their origin can be verified later. Lua scripts call `atproto.sign()` to attach an inline signature to a record and `atproto.verify_signature()` to check one. HappyView's implementation follows the [atproto attestation spec](https://tangled.org/strings/did:plc:cbkjy5n7bk3ax2wplmtjofq2/3m3fy2xuahc22). 4 + 5 + ## How it works 6 + 7 + 1. HappyView loads or generates a secp256k1 keypair on startup 8 + 2. `atproto.sign(record)` encodes the record to DAG-CBOR, computes its CID, and signs the CID with the private key 9 + 3. The signature is added to the record's `signatures` array as an inline object 10 + 4. `atproto.verify_signature(record, sig, repo_did)` recomputes the CID and verifies the signature 11 + 12 + The repo DID is included in the signed data — a signature for one user's record can't be replayed against another's. Any modification to the record invalidates the signature. 13 + 14 + ## Setup 15 + 16 + Attestation signing is enabled by default — HappyView generates a keypair on first startup and persists it to the `instance_settings` database table. No configuration is required. 17 + 18 + To use an explicit key instead, set the `ATTESTATION_PRIVATE_KEY` environment variable: 19 + 20 + | Variable | Required | Default | Description | 21 + |----------|----------|---------|-------------| 22 + | `ATTESTATION_PRIVATE_KEY` | no | auto-generated | Hex-encoded 32-byte secp256k1 private key | 23 + | `ATTESTATION_KEY_ID` | no | `did:web:{host}#attestation` | Key identifier included in signatures. Derived from `PUBLIC_URL` by default | 24 + | `ATTESTATION_SIG_TYPE` | no | app-specific NSID | The `$type` value used in signature objects | 25 + 26 + The key ID defaults to a `did:web` derived from your `PUBLIC_URL`. For example, `PUBLIC_URL=https://happyview.example.com` produces a key ID of `did:web:happyview.example.com#attestation`. 27 + 28 + ### Priority order 29 + 30 + HappyView checks for signing configuration in this order: 31 + 32 + 1. **Environment variables** — if `ATTESTATION_PRIVATE_KEY` is set, it's used 33 + 2. **Database** — if previously generated keys exist in `instance_settings`, they're loaded 34 + 3. **Auto-generation** — a new key is generated and persisted to the database 35 + 36 + If key loading fails for any reason, signing is disabled and `atproto.sign` / `atproto.verify_signature` will be `nil` in Lua scripts. 37 + 38 + ## Using in Lua scripts 39 + 40 + Available in queries, procedures, and index hooks via the [atproto API](../../reference/lua/atproto-api.md). 41 + 42 + ### Signing a record 43 + 44 + ```lua 45 + function handle() 46 + local r = Record(collection, input) 47 + r:save() 48 + 49 + local sig = atproto.sign({ text = input.text, createdAt = input.createdAt }) 50 + return { uri = r._uri, cid = r._cid, signature = sig } 51 + end 52 + ``` 53 + 54 + The returned signature object: 55 + 56 + ```json 57 + { 58 + "$type": "your.app.attestation", 59 + "key": "did:web:happyview.example.com#attestation", 60 + "signature": { 61 + "$bytes": "base64-encoded-signature" 62 + } 63 + } 64 + ``` 65 + 66 + ### Verifying a signature 67 + 68 + ```lua 69 + function handle() 70 + local record = db.get(params.uri) 71 + if not record then 72 + return { error = "not found" } 73 + end 74 + 75 + local sig = record.signatures and record.signatures[1] 76 + if not sig then 77 + return { record = record, verified = false } 78 + end 79 + 80 + local valid = atproto.verify_signature(record, sig, record.did) 81 + return { record = record, verified = valid } 82 + end 83 + ``` 84 + 85 + ### Checking availability 86 + 87 + Both functions are `nil` when no signer is configured: 88 + 89 + ```lua 90 + if atproto.sign then 91 + record.signature = atproto.sign(record) 92 + end 93 + ``` 94 + 95 + ## Signature format 96 + 97 + Signatures are stored as objects in the record's `signatures` array: 98 + 99 + | Field | Type | Description | 100 + | ----------- | ------ | ------------------------------------ | 101 + | `$type` | string | Signature type NSID | 102 + | `key` | string | Key identifier (DID with fragment) | 103 + | `signature` | table | Contains `$bytes` (base64-encoded) | 104 + 105 + ## Next steps 106 + 107 + - [atproto API reference](../../reference/lua/atproto-api.md#atprotosign) — `atproto.sign` and `atproto.verify_signature` parameter docs 108 + - [Signed Record](../scripting/signed-record.md) — save a record with an attestation signature 109 + - [Verify Signed Record](../scripting/signed-record-verify.md) — fetch a record and verify its signature
+3
packages/docs/docs/guides/indexing/index-hooks.md
··· 59 59 60 60 - **[Database API](../../reference/lua/database-api.md)** — `db.query`, `db.get`, `db.search`, `db.backlinks`, `db.count`, `db.raw` 61 61 - **[HTTP API](../../reference/lua/http-api.md)** — `http.get`, `http.post`, `http.put`, `http.patch`, `http.delete`, `http.head` 62 + - **[XRPC Lua API](../../reference/lua/xrpc-lua-api.md)** — `xrpc.query`, `xrpc.procedure` 63 + - **[atproto API](../../reference/lua/atproto-api.md)** — `atproto.resolve_service_endpoint`, `atproto.get_labels`, `atproto.get_labels_batch` 62 64 - **[JSON API](../../reference/lua/json-api.md)** — `json.encode`, `json.decode` 63 65 - **[Utility globals](../scripting.md#utility-globals)** — `log()`, `now()`, `TID()`, `toarray()` 66 + - **[Script variables](../../reference/admin/script-variables.md)** — `env` table with key-value pairs configured in the dashboard 64 67 65 68 ## Error handling and retries 66 69
+33 -15
packages/docs/docs/guides/scripting.md
··· 43 43 44 44 ### Procedure globals 45 45 46 - | Global | Type | Description | 47 - | ------------ | ------ | ------------------------------------------------------- | 48 - | `method` | string | The XRPC method name (e.g. `xyz.statusphere.setStatus`) | 49 - | `input` | table | Parsed JSON request body | 50 - | `caller_did` | string | DID of the authenticated user | 51 - | `collection` | string | Target collection NSID | 46 + | Global | Type | Description | 47 + | -------------- | ------- | ------------------------------------------------------- | 48 + | `method` | string | The XRPC method name (e.g. `xyz.statusphere.setStatus`) | 49 + | `input` | table | Parsed JSON request body | 50 + | `params` | table | Query string parameters | 51 + | `caller_did` | string | DID of the authenticated user | 52 + | `collection` | string | Target collection NSID | 53 + | `delegate_did` | string? | DID of the delegated account, if using write delegation | 54 + | `env` | table | Script variables configured in the dashboard | 52 55 53 56 ### Query globals 54 57 55 - | Global | Type | Description | 56 - | ------------ | ------ | ------------------------------------------------ | 57 - | `method` | string | The XRPC method name | 58 - | `params` | table | Query string parameters (all values are strings) | 59 - | `collection` | string | Target collection NSID | 60 - 61 - Queries are unauthenticated: there is no `caller_did` or `input`. 58 + | Global | Type | Description | 59 + | ------------ | ------- | ------------------------------------------------ | 60 + | `method` | string | The XRPC method name | 61 + | `params` | table | Query string parameters (all values are strings) | 62 + | `collection` | string | Target collection NSID | 63 + | `caller_did` | string? | DID of the authenticated user (nil if unauthenticated) | 64 + | `env` | table | Script variables configured in the dashboard | 62 65 63 66 ## Utility globals 64 67 ··· 127 130 local data = json.decode(resp.body) 128 131 ``` 129 132 133 + ## XRPC Lua API 134 + 135 + The `xrpc` table lets scripts call other XRPC endpoints — both local and proxied. Available in both queries and procedures. 136 + 137 + See the full [XRPC Lua API reference](../reference/lua/xrpc-lua-api.md) for `xrpc.query` and `xrpc.procedure`. 138 + 139 + Quick example: 140 + 141 + ```lua 142 + local resp = xrpc.query("xyz.statusphere.listStatuses", { limit = 5 }) 143 + local data = json.decode(resp.body) 144 + ``` 145 + 130 146 ## atproto API 131 147 132 - The `atproto` table provides atproto utility functions like DID resolution and label queries. 148 + The `atproto` table provides atproto utility functions like DID resolution, label queries, and record signing. 133 149 134 - See the full [atproto API reference](../reference/lua/atproto-api.md) for `atproto.resolve_service_endpoint`, `atproto.get_labels`, and `atproto.get_labels_batch`. 150 + 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`. 135 151 136 152 ## JSON API 137 153 ··· 181 197 - [Paginated list](scripting/paginated-list.md) — list records with cursor-based pagination and DID filtering 182 198 - [List or fetch](scripting/list-or-fetch.md) — combined single-record lookup and paginated listing 183 199 - [Expanded query](scripting/expanded-query.md) — list statuses with user profiles in a single response 200 + - [Verify signed record](scripting/signed-record-verify.md) — fetch a record and verify its attestation signature 184 201 185 202 **Procedures:** 186 203 - [Create a record](scripting/create-record.md) — simple write that saves input as a record ··· 190 207 - [Sidecar records](scripting/sidecar-records.md) — create linked records across collections with a shared rkey 191 208 - [Cascading delete](scripting/cascading-delete.md) — delete a record and all related records 192 209 - [Complex mutations](scripting/complex-mutations.md) — load, transform, and save a record with multiple field changes 210 + - [Signed record](scripting/signed-record.md) — save a record with an attestation signature 193 211 194 212 **Index Hooks:** 195 213 - [Algolia sync](scripting/algolia-sync.md) — push records to an Algolia search index on create/update/delete
+54
packages/docs/docs/guides/scripting/signed-record-verify.md
··· 1 + # Query: Verify Signed Record 2 + 3 + Fetch a record and verify its attestation signature. 4 + 5 + **Lexicon type:** query 6 + 7 + ```lua 8 + function handle() 9 + local record = db.get(params.uri) 10 + if not record then 11 + return { error = "not found" } 12 + end 13 + 14 + local verified = false 15 + if atproto.verify_signature and record.signature then 16 + verified = atproto.verify_signature( 17 + { text = record.text, createdAt = record.createdAt }, 18 + record.signature, 19 + params.did 20 + ) 21 + end 22 + 23 + return { record = record, verified = verified } 24 + end 25 + ``` 26 + 27 + ## How it works 28 + 29 + 1. Fetch the record by AT URI. 30 + 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). 31 + 3. Return `verified = true` if the signature is valid, `false` if it's missing, invalid, or the signer isn't configured. 32 + 33 + ## Usage 34 + 35 + ```sh 36 + curl "http://127.0.0.1:3000/xrpc/xyz.example.getPost?uri=at://did:plc:abc/xyz.example.post/3abc123&did=did:plc:abc" 37 + ``` 38 + 39 + ```json 40 + { 41 + "record": { 42 + "uri": "at://did:plc:abc/xyz.example.post/3abc123", 43 + "text": "Hello world", 44 + "createdAt": "2026-04-30T12:00:00Z" 45 + }, 46 + "verified": true 47 + } 48 + ``` 49 + 50 + ## Use case 51 + 52 + 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. 53 + 54 + See [Attestation Signing](../features/attestation-signing.md) for setup and configuration.
+56
packages/docs/docs/guides/scripting/signed-record.md
··· 1 + # Procedure: Signed Record 2 + 3 + Save a record with an attestation signature attached. 4 + 5 + **Lexicon type:** procedure 6 + 7 + ```lua 8 + function handle() 9 + local r = Record(collection, { 10 + text = input.text, 11 + createdAt = now(), 12 + }) 13 + r:save() 14 + 15 + local sig = nil 16 + if atproto.sign then 17 + sig = atproto.sign({ text = input.text, createdAt = r.createdAt }) 18 + end 19 + 20 + return { uri = r._uri, cid = r._cid, signature = sig } 21 + end 22 + ``` 23 + 24 + ## How it works 25 + 26 + 1. Create and save the record. 27 + 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. 28 + 3. Return the signature alongside the URI. 29 + 30 + ## Usage 31 + 32 + ```sh 33 + curl -X POST http://127.0.0.1:3000/xrpc/xyz.example.createPost \ 34 + -H "X-Client-Key: $CLIENT_KEY" \ 35 + -H "Authorization: Bearer $TOKEN" \ 36 + -H "Content-Type: application/json" \ 37 + -d '{ "text": "Hello world" }' 38 + ``` 39 + 40 + ```json 41 + { 42 + "uri": "at://did:plc:abc/xyz.example.post/3abc123", 43 + "cid": "bafyrei...", 44 + "signature": { 45 + "$type": "your.app.attestation", 46 + "key": "did:web:happyview.example.com#attestation", 47 + "signature": { "$bytes": "..." } 48 + } 49 + } 50 + ``` 51 + 52 + ## Use case 53 + 54 + 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. 55 + 56 + See [Attestation Signing](../features/attestation-signing.md) for setup and configuration, or [Verify Signed Record](signed-record-verify.md) for the read-side counterpart.
+77 -15
packages/docs/docs/reference/lua/atproto-api.md
··· 10 10 11 11 Resolves a DID to its atproto service endpoint URL by fetching the DID document. Supports both `did:plc:*` (via the PLC directory) and `did:web:*` (via `.well-known/did.json`). 12 12 13 - | Parameter | Type | Description | 14 - | --------- | ------ | ------------------------ | 15 - | `did` | string | The DID to resolve | 13 + | Parameter | Type | Description | 14 + | --------- | ------ | ------------------ | 15 + | `did` | string | The DID to resolve | 16 16 17 17 **Returns:** The service endpoint URL as a string, or `nil` if resolution fails (DID not found, no PDS service in document, network error). 18 18 ··· 49 49 50 50 Returns an array of labels for a single AT URI. Merges external labels (from subscribed labelers) with self-labels (from the record's `labels.values[]` field). 51 51 52 - | Parameter | Type | Description | 53 - | --------- | ------ | ------------------------------ | 54 - | `uri` | string | AT URI of the record to query | 52 + | Parameter | Type | Description | 53 + | --------- | ------ | ----------------------------- | 54 + | `uri` | string | AT URI of the record to query | 55 55 56 56 Each label in the array is a table with: 57 57 58 - | Field | Type | Description | 59 - | ----- | ------ | ---------------------------------------- | 60 - | `src` | string | DID of the labeler (or record author) | 61 - | `uri` | string | AT URI this label applies to | 62 - | `val` | string | Label value (e.g. "nsfw", "!hide") | 63 - | `cts` | string | Timestamp when the label was created | 58 + | Field | Type | Description | 59 + | ----- | ------ | ------------------------------------- | 60 + | `src` | string | DID of the labeler (or record author) | 61 + | `uri` | string | AT URI this label applies to | 62 + | `val` | string | Label value (e.g. "nsfw", "!hide") | 63 + | `cts` | string | Timestamp when the label was created | 64 64 65 65 Expired labels are automatically filtered out. Returns an empty array if no labels exist. 66 66 ··· 72 72 73 73 Batch version of `get_labels`. Takes an array of AT URIs and returns a table keyed by URI, where each value is an array of labels. 74 74 75 - | Parameter | Type | Description | 76 - | --------- | ----- | ------------------------ | 77 - | `uris` | table | Array of AT URI strings | 75 + | Parameter | Type | Description | 76 + | --------- | ----- | ----------------------- | 77 + | `uris` | table | Array of AT URI strings | 78 78 79 79 **Returns:** A table keyed by URI. Each value is an array of label tables (same shape as `get_labels`). URIs with no labels have an empty array. 80 80 ··· 105 105 end 106 106 end 107 107 ``` 108 + 109 + ## atproto.sign 110 + 111 + ```lua 112 + local sig = atproto.sign(record) 113 + ``` 114 + 115 + Signs a record and returns the inline signature object. Only available when an attestation signer is configured — if no signer is configured, `atproto.sign` is `nil`. 116 + 117 + | Parameter | Type | Description | 118 + | --------- | ----- | ----------------------- | 119 + | `record` | table | The record data to sign | 120 + 121 + **Returns:** A signature table with: 122 + 123 + | Field | Type | Description | 124 + | ----------- | ------ | --------------------------------------------------- | 125 + | `key` | string | The signing key ID (e.g. `did:web:example#signing`) | 126 + | `signature` | table | Contains `$bytes` with the signature | 127 + 128 + ### Examples 129 + 130 + ```lua 131 + -- Sign a record before returning it 132 + local record = { contributionType = "correction", changes = { name = "Test" } } 133 + local sig = atproto.sign(record) 134 + record.signature = sig 135 + return record 136 + 137 + -- Check if signing is available 138 + if atproto.sign then 139 + local sig = atproto.sign(record) 140 + end 141 + ``` 142 + 143 + ## atproto.verify_signature 144 + 145 + ```lua 146 + local valid = atproto.verify_signature(record, signature, repo_did) 147 + ``` 148 + 149 + Verifies that an inline signature was produced by this HappyView instance. Only available when an attestation signer is configured — if no signer is configured, `atproto.verify_signature` is `nil`. 150 + 151 + | Parameter | Type | Description | 152 + | ----------- | ------ | ------------------------------------------ | 153 + | `record` | table | The record data | 154 + | `signature` | table | The signature object from `atproto.sign()` | 155 + | `repo_did` | string | The repo DID | 156 + 157 + **Returns:** `true` if the signature is valid, `false` otherwise. Returns `false` on failure rather than raising an error. 158 + 159 + ### Examples 160 + 161 + ```lua 162 + -- Verify a signature roundtrip 163 + local record = { contributionType = "correction", changes = { name = "Test" } } 164 + local sig = atproto.sign(record) 165 + local valid = atproto.verify_signature(record, sig, caller_did) 166 + if not valid then 167 + return { error = "signature verification failed" } 168 + end 169 + ```
+19 -1
packages/docs/docs/reference/lua/database-api.md
··· 95 95 96 96 ### SQL dialect 97 97 98 - Write SQL in **SQLite syntax** — HappyView translates it to Postgres at runtime if you're using Postgres. See [Database Setup](../../guides/database/database-setup.md) for details on what gets translated. If you need database-specific SQL that can't be translated, check `db.is_postgres()` at runtime. 98 + Write SQL in **SQLite syntax** — HappyView translates it to Postgres at runtime if you're using Postgres. See [Database Setup](../../guides/database/database-setup.md) for details on what gets translated. If you need database-specific SQL that can't be translated, check `db.backend()` at runtime. 99 99 100 100 ### Column type mapping 101 101 ··· 108 108 | `TEXT` (JSON) | `JSON`, `JSONB` | table | 109 109 | `TEXT` (ISO 8601) | `TIMESTAMPTZ` | string (ISO 8601) | 110 110 | Other | Other | string (fallback) | 111 + 112 + ## db.backend 113 + 114 + ```lua 115 + local backend = db.backend() 116 + -- "sqlite" or "postgres" 117 + ``` 118 + 119 + Returns `"sqlite"` or `"postgres"`. Useful when you need database-specific SQL that can't be automatically translated. 120 + 121 + ```lua 122 + if db.backend() == "postgres" then 123 + db.raw("SELECT * FROM records WHERE record @> $1::jsonb", { json.encode({ status = "active" }) }) 124 + else 125 + -- SQLite fallback 126 + db.raw("SELECT * FROM records WHERE json_extract(record, '$.status') = $1", { "active" }) 127 + end 128 + ```
+76
packages/docs/docs/reference/lua/xrpc-lua-api.md
··· 1 + # XRPC Lua API 2 + 3 + The `xrpc` table provides cross-endpoint XRPC calls. Available in queries, procedures, and [index hooks](../../guides/indexing/index-hooks.md). 4 + 5 + ## xrpc.query 6 + 7 + ```lua 8 + local resp = xrpc.query("xyz.statusphere.listStatuses", { -- required: XRPC method name 9 + limit = 10, -- optional: query parameters 10 + }) 11 + ``` 12 + 13 + Calls an XRPC query. If the method matches a locally registered query lexicon, it runs locally. Otherwise, the request is proxied to the NSID's authority. 14 + 15 + **Returns:** A table with: 16 + 17 + | Field | Type | Description | 18 + | -------- | ------- | -------------------- | 19 + | `status` | integer | HTTP status code | 20 + | `body` | string | Response body (JSON) | 21 + 22 + The body is a raw JSON string — use `json.decode(resp.body)` to parse it. 23 + 24 + ### Examples 25 + 26 + ```lua 27 + -- Call a local query endpoint 28 + local resp = xrpc.query("xyz.statusphere.listStatuses", { limit = 5 }) 29 + local data = json.decode(resp.body) 30 + for _, record in ipairs(data.records) do 31 + log(record.uri) 32 + end 33 + 34 + -- Call without parameters 35 + local resp = xrpc.query("com.example.getConfig") 36 + 37 + -- Proxy to a remote XRPC endpoint 38 + local resp = xrpc.query("app.bsky.feed.getAuthorFeed", { 39 + actor = "did:plc:abc123", 40 + limit = 10, 41 + }) 42 + ``` 43 + 44 + ## xrpc.procedure 45 + 46 + ```lua 47 + local resp = xrpc.procedure( 48 + "xyz.statusphere.setStatus", -- required: XRPC method name 49 + { status = "hello" }, -- required: request body 50 + { someParam = "value" } -- optional: query parameters 51 + ) 52 + ``` 53 + 54 + Calls an XRPC procedure using the current request's `caller_did` for authentication. If the method matches a locally registered procedure lexicon, it runs locally. Otherwise, the request is proxied. 55 + 56 + Requires a `caller_did` — raises an error without one. 57 + 58 + **Returns:** A table with the same shape as `xrpc.query` responses (`status` and `body`). 59 + 60 + ### Examples 61 + 62 + ```lua 63 + -- Call a local procedure 64 + local resp = xrpc.procedure("xyz.statusphere.setStatus", { 65 + status = "hello", 66 + createdAt = now(), 67 + }) 68 + 69 + if resp.status ~= 200 then 70 + return { error = "failed: " .. resp.body } 71 + end 72 + 73 + -- Parse the response 74 + local result = json.decode(resp.body) 75 + return { uri = result.uri } 76 + ```
+20
packages/docs/sidebars.ts
··· 90 90 }, 91 91 { 92 92 type: "doc", 93 + id: "guides/features/attestation-signing", 94 + label: "Attestation Signing", 95 + }, 96 + { 97 + type: "doc", 93 98 id: "guides/features/labelers", 94 99 label: "Labelers", 95 100 }, ··· 193 198 type: "doc", 194 199 id: "guides/scripting/complex-mutations", 195 200 label: "Complex Mutations", 201 + }, 202 + { 203 + type: "doc", 204 + id: "guides/scripting/signed-record", 205 + label: "Signed Record", 206 + }, 207 + { 208 + type: "doc", 209 + id: "guides/scripting/signed-record-verify", 210 + label: "Verify Signed Record", 196 211 }, 197 212 { 198 213 type: "doc", ··· 392 407 type: "doc", 393 408 id: "reference/lua/http-api", 394 409 label: "HTTP API", 410 + }, 411 + { 412 + type: "doc", 413 + id: "reference/lua/xrpc-lua-api", 414 + label: "XRPC Lua API", 395 415 }, 396 416 { 397 417 type: "doc",