signedRecord

tcmts.atproto.signedRecord · #tuwnsvau · term · runs anywhere

A record at did/collection/rkey, as its author's repository signs it: fetched from their PDS with com.atproto.sync.getRecord, its blocks checked against their CIDs and its commit against the DID's key. A DID, document or PDS tcm won't ask (guardedFetch, pdsOf), or a proof too big to be one, is NotTheirs; a record the repository doesn't hold is NoSuchRecord.

import { cappedBytes, guardedFetch, NoSuchRecord, NotTheirs, Refused, signingKey } from "#codebase/tcmts/atproto";
import { verifyRecord } from "@atcute/repo";

export async function signedRecord(
  did: string,
  collection: string,
  rkey: string,
): Promise<{ cid: string; record: unknown }> {
  // (a DID, a document or a PDS tcm won't ask, as it never will: not theirs, rather than tried again)
  const theirs = <T>(p: Promise<T>) =>
    p.catch((e: unknown) => {
      throw e instanceof Refused ? new NotTheirs(e.message) : e;
    });
  const { pds, key } = await theirs(signingKey(did));
  const q = new URLSearchParams({ did, collection, rkey });
  const res = await theirs(
    guardedFetch(`${pds}/xrpc/com.atproto.sync.getRecord?${q}`, `${did}'s PDS`),
  );
  if (res.status === 400 || res.status === 404)
    throw new NoSuchRecord(`${did}'s repository has no ${collection}/${rkey}`);
  if (!res.ok) throw new Error(`${pds}: getRecord ${res.status}`);
  // (one record's proof: a commit, the tree's path to it and the record, a few KB)
  const carBytes = await theirs(cappedBytes(res, 2 * 1024 * 1024, `${did}'s PDS`));
  try {
    return await verifyRecord({
      did: did as `did:plc:${string}`,
      collection,
      rkey,
      publicKey: key,
      carBytes,
    });
  } catch (e) {
    throw new NotTheirs(
      `the record doesn't check against ${did}'s signed repository: ${(e as Error).message}`,
    );
  }
}

uses

used by