- Guides
- Recipes
Signed record
Save a record with an attestation signature attached.
Trigger: xrpc.procedure:<nsid>. Needs: happyview-record, happyview-atproto.
local time = require("internal.time")local record = require("happyview.record")local atproto = require("happyview.atproto")
function handle(input, ctx) local body = { text = input.text, createdAt = time.to_iso8601(time.now()) }
local ok, sig = pcall(atproto.sign, body) if ok then body.signature = sig end
local ref = record.create(ctx.collection, body) return { uri = ref.uri, cid = ref.cid, signature = body.signature }endimport time from "internal.time";import record from "happyview.record";import atproto from "happyview.atproto";
export default async function handle(input, ctx) { const body = { text: input.text, createdAt: time.to_iso8601(time.now()) };
try { body.signature = await atproto.sign(body); } catch { // No signature: the record is saved without one. }
const ref = await record.create(ctx.collection, body); return { uri: ref.uri, cid: ref.cid, signature: body.signature };}import time from "internal.time";import record from "happyview.record";import atproto from "happyview.atproto";
export default async function handle(input: { text: string }, ctx: { collection: string }) { const body: Record<string, unknown> = { text: input.text, createdAt: time.to_iso8601(time.now()), };
try { body.signature = await atproto.sign(body); } catch { // No signature: the record is saved without one. }
const ref = await record.create(ctx.collection, body); return { uri: ref.uri, cid: ref.cid, signature: body.signature };}How it works
Section titled “How it works”- Build the record body.
- Sign it with
signonhappyview.atproto. Thepcall(Lua) ortry/catch(JavaScript and TypeScript) lets the script work without a signer configured, wheresignraisesNO_SIGNER. Signing also needs a caller, which a procedure always has. - Attach the signature to the body as
body.signature, then save the record withrecord.create. Declaresignaturein the record lexicon, so apps reading the record know the field exists. - Return the signature alongside the URI.
The signature’s $type defaults to games.gamesgamesgamesgames.attestation. Set ATTESTATION_SIG_TYPE to use another type.
The DPoP proof is signed per request; an app built on the HappyView SDK gets this from its session.
const response = await fetch("http://127.0.0.1:3000/xrpc/xyz.example.createPost", { method: "POST", headers: { "X-Client-Key": CLIENT_KEY, Authorization: `DPoP ${ACCESS_TOKEN}`, DPoP: DPOP_PROOF, "Content-Type": "application/json", }, body: JSON.stringify({ text: "Hello world" }),});const data = await response.json();const response = await fetch("http://127.0.0.1:3000/xrpc/xyz.example.createPost", { method: "POST", headers: { "X-Client-Key": CLIENT_KEY, Authorization: `DPoP ${ACCESS_TOKEN}`, DPoP: DPOP_PROOF, "Content-Type": "application/json", }, body: JSON.stringify({ text: "Hello world" }),});const data = await response.json();let response = client .post("http://127.0.0.1:3000/xrpc/xyz.example.createPost") .header("X-Client-Key", client_key) .header("Authorization", format!("DPoP {access_token}")) .header("DPoP", dpop_proof) .json(&serde_json::json!({ "text": "Hello world" })) .send() .await?;body := `{ "text": "Hello world" }`req, _ := http.NewRequest("POST", "http://127.0.0.1:3000/xrpc/xyz.example.createPost", bytes.NewBufferString(body))req.Header.Set("X-Client-Key", clientKey)req.Header.Set("Authorization", "DPoP "+accessToken)req.Header.Set("DPoP", dpopProof)req.Header.Set("Content-Type", "application/json")resp, err := http.DefaultClient.Do(req)curl -X POST http://127.0.0.1:3000/xrpc/xyz.example.createPost \ -H "X-Client-Key: $CLIENT_KEY" \ -H "Authorization: DPoP $ACCESS_TOKEN" \ -H "DPoP: $DPOP_PROOF" \ -H "Content-Type: application/json" \ -d '{ "text": "Hello world" }'{ "uri": "at://did:plc:abc/xyz.example.post/3abc123", "cid": "bafyrei...", "signature": { "$type": "games.gamesgamesgamesgames.attestation", "key": "did:web:happyview.example.com#attestation", "signature": { "$bytes": "..." } }}Use case
Section titled “Use case”An attestation signature lets an app verify that a record was processed by your instance, for contributions, moderation decisions or data shared between instances. The signature covers both the record content and the author’s DID, so it can’t be replayed across users, and changing the record invalidates it.
See Attestation signing for setup and configuration, or Verify a signed record for the read side.
© HappyProto

