Attestation

@atiproto/atproto-attestation

Wraps a key pair and produces signature entries for a record's signatures[] array. By default emits inline network.attested.signature entries; with an agent it writes proof records to that agent's PDS and emits strongRefs.

Constructor

typescript
new Attestation(options: AttestationOptions)
OptionTypeDescription
privateKeystring | KeyDataMultibase string (e.g. z…) or a raw KeyData object. Supports p256, k256, p384, ed25519.
publicKey?stringdid:key:…. Derived from privateKey when omitted (P-384 must supply this explicitly).
role?stringDefault role to embed on every signature (e.g. "appview", "pos", "broker").
issuer?stringDefault issuer DID. Travels with the signature; useful when key does not directly identify the issuer.
signatureType?string$type of the inline signature record. Default network.attested.signature.
proofType?stringCollection NSID used for proof records when an agent is set. Default network.attested.proof.
agent?AttestationAgentXRPC-shaped client with did and call(nsid, params, data). When supplied, signatures become strongRefs pointing at proof records the agent writes.

sign

typescript
sign(input: SignInput): Promise<SignatureEntry>
interface SignInput {
record: RecordMap; // must include a $type
repository: string; // DID that owns the record
fields?: readonly string[]; // record keys to include in the canonical payload
signatureType?: string; // overrides the constructor default
metadata?: SignatureMetadata; // issuer, issuedAt, role, status, ...extras
}

Returns an inline InlineAttestation when no agent was configured. When an agent is configured, the proof record is written to its PDS and a RemoteAttestation (a strongRef) is returned instead. The signed CID is identical between the two paths only when the metadata's $type matches. By design, inline and remote attestations are domain-separated.

signAndAppend

typescript
signAndAppend<T extends RecordMap>(
input: SignInput & { record: T }
): Promise<T & { signatures: unknown[] }>

Convenience helper that signs and returns a copy of the record with the new entry merged into signatures[]. If the array already contains an entry by this attestation, that entry is replaced in place rather than appended.

Match rule: inline entries match if entry.key === this.publicKey. Remote entries match if the strongRef's authority DID equals the configured agent's did. Old proof records are intentionally left in the agent's repo so that prior strongRefs continue to resolve.

findOwnIndex

typescript
findOwnIndex(args: { signatures: readonly unknown[] }): number

Returns the index of an existing signature on signatures[] that was issued by this attestation, or -1 when none match. Used internally by signAndAppend; exposed for callers who want to inspect or manipulate signatures manually.

Inline example

typescript
import { Attestation } from "@atiproto/atproto-attestation";
const att = new Attestation({
privateKey: "z3u2en7t5LRb4G…",
role: "appview",
});
const entry = await att.sign({
record: { $type: "com.atiproto.cart", items: [], currency: "USD", status: "open" },
repository: "did:plc:recipient",
fields: ["items", "currency", "status"],
});
// entry = {
// $type: "network.attested.signature",
// key: "did:key:zDnae…",
// cid: "bafyrei…",
// signature: Uint8Array(64),
// role: "appview",
// }

Agent-backed example

Supply any object with a did and a call(nsid, params, data) method (e.g. an @atproto/api Agent or an @atproto/xrpc XrpcClient).

typescript
import { Attestation } from "@atiproto/atproto-attestation";
import { AtpAgent } from "@atproto/api";
const pdsAgent = new AtpAgent({ service: "https://my-pds.example" });
await pdsAgent.login({ identifier: "appview.bsky.team", password });
const att = new Attestation({
privateKey: process.env.APPVIEW_SIGNING_KEY!,
role: "appview",
issuer: pdsAgent.did,
agent: pdsAgent, // writes proof records here
});
const entry = await att.sign({
record: cartRecord,
repository: "did:plc:recipient",
fields: ["items", "currency", "status"],
});
// entry = {
// $type: "com.atproto.repo.strongRef",
// uri: "at://did:plc:appview/network.attested.proof/3lab…",
// cid: "bafyrei…",
// }