Signatures
app.certified.signature.defs · app.certified.signature.proof
Overview#
Every AT Protocol repository signs its commits, so a record fetched from a repository is already attributed to that account. The signatures property adds something different: a signature on a record's contents from someone other than the repository owner. Almost every Hypercerts and Certified record has this optional property, and it references app.certified.signature.defs#list.
A signature list can hold two kinds of entries:
- An inline signature (
app.certified.signature.defs#inline) sits inside the record. It holds signature bytes and a reference to the signing key in a DID document. This suits a party that produces the record, such as a platform writing to a user's repository on their behalf, which can sign with its own key to show it created the record. - A remote attestation is a strong reference to an
app.certified.signature.proofrecord. The attesting party, such as a reviewer or certifier, publishes the proof record in its own repository. It holds the CID of the attested content and no signature of its own: its authenticity comes from being published in the attestor's repository.
Unsigned records are the normal case. For how signatures fit alongside other trust signals, see Trust and Recognition in the Guide.
How it's used#
- A platform marks records it produced. When an app writes records into a user's repository, the commit is attributed to the user. A platform can add an inline signature made with its own key, so a reader can check which platform produced the record.
- A third party attests to a record. An auditor computes the CID of a project's activity and publishes a proof record naming it, with a
noteexplaining what they checked. The attestor needs no write access to the project's repository. - The author surfaces the attestation. The record's author adds a strong reference to the proof record in the record's
signaturesarray, so readers of the record can find it. - Readers verify. An app checks each entry independently and shows which signers or attestors it could verify.
Both forms sign or name the same input: the CID of the record with the signatures field removed and a temporary $sig object inserted, carrying a $type and the DID of the repository that holds the record. Because signatures is left out, entries can be added or removed without invalidating the others.
Signature definitions schema#
These definitions describe the shape of the signatures property that other records reference.
Lexicon version: 1.4.1 · View the schema
Definitions#
list
Reusable array of cryptographic signatures attesting to a record's content. Open union of inline signatures and strong references to remote attestation proof records.
Type: array of union: inline, strongRef.
inline
Inline attestation signature embedded directly in a record. Conforms to the ATProtocol Attestation Specification: the signed input is the 36-byte CIDv1 (dag-cbor + SHA-256) of the record with the signatures field removed and a temporary $sig metadata object (containing $type and the housing repository DID) inserted before canonical DAG-CBOR encoding. ECDSA with the low-S variant per BIP-0062 is required; the curve (P-256 or K-256) is determined by the multicodec prefix of the verification method's publicKeyMultibase.
| Property | Type | Required | Description |
|---|---|---|---|
signature | bytes | Yes | ECDSA signature bytes (raw r,s) over the 36-byte CID of the record. Low-S variant per BIP-0062 is mandatory. |
key | string | Yes | Full DID verification method reference (format: did:{method}:{identifier}#{fragment}). Identifies the signer and the specific key used; the key's multicodec prefix determines the signing curve. maxLength: 512 |
Signature proof schema#
A proof record lives in the attestor's repository and holds the CID of the attested content.
Record key: tid · Lexicon version: 1.4.1 · View the schema
Required properties#
| Property | Type | Description |
|---|---|---|
cid | string (cid) | CID of the attested content, computed per the ATProtocol Attestation Specification: encode the record with the signatures field removed and a temporary $sig object inserted (containing $type and the housing repository DID) as canonical DAG-CBOR, then SHA-256 hash to produce a 36-byte CIDv1. |
Optional properties#
| Property | Type | Description |
|---|---|---|
note | string | Optional note explaining the attestation purpose or context. maxLength: 500 |
createdAt | string (datetime) | Client-declared timestamp when this proof was created. |
Example#
An auditor's proof record, published in the auditor's repository:
{
"$type": "app.certified.signature.proof",
"cid": "bafyreif5dqzwxmvoj6l3aqsgyb4tbwnrhy2cfmj7k3u4vxe6pdzlh2q7ne",
"note": "Reviewed against the site visit on 2026-09-10. Installed capacity matches the activity description.",
"createdAt": "2026-09-15T13:40:00.000Z"
}
An activity carrying an inline signature from the platform that produced it and a reference to an auditor's proof record. In JSON, the bytes value is an object with the base64-encoded bytes under $bytes. The signature value is illustrative.
{
"$type": "org.hypercerts.claim.activity",
"title": "Solar array installation, phase 1",
"shortDescription": "Installation of a 40 kW community-owned solar array on the village hall roof.",
"createdAt": "2026-07-02T09:15:00.000Z",
"signatures": [
{
"$type": "app.certified.signature.defs#inline",
"signature": {
"$bytes": "j9gArE9UNRRCTap+fRYmjjf0lkSjuiORjp70W/fSNfF4QWrutXFlSM+IjWQBIo6UNm7XDMjWydC99d8T4o6moA"
},
"key": "did:web:grants.example.org#attestation"
},
{
"$type": "com.atproto.repo.strongRef",
"uri": "at://did:plc:ragtjsm2j2vknwkz3zp4oxrd/app.certified.signature.proof/3lxd7ka2mvs2e",
"cid": "bafyreie5737gdxlw5i64vzichcalba3z2v5n6icifvx5xytvske7mr3hpm"
}
]
}
The strong reference carries the proof record's own URI and CID. The cid inside the proof record is the CID computed for the activity.
Rules and best practices#
- A signature proves key control, not truth. A verified signature shows that the holder of a key signed this content in this repository. It doesn't say who the signer is in the real world, what they meant by signing, or whether the content is accurate. Don't present a signature as a review, audit, or approval unless the signer's own statement (such as a proof
note) or another record says so. - Verify before you trust. Schema validation accepts any bytes and any key string. To verify an inline signature, resolve
keyas a DID URL, find the verification method with that exact identifier, read the curve (P-256 or K-256) from itspublicKeyMultibaseprefix, rebuild the signed input from the record as fetched, and check the ECDSA signature (raw r and s, low-S form). - Use a full key reference. Set
keyto a DID plus a fragment, such asdid:web:grants.example.org#attestation, so it identifies one key. A bare DID doesn't say which key signed. - Sign with a stable, resolvable key. Reuse a long-lived key that stays in your published DID document. A signature from a key readers can't resolve can't be verified.
- Don't sign with the repository's own key. The repository commit already attributes the record to its owner. Inline signatures are useful when the signer is someone else.
- Never store
$sig. The$sigobject exists only while computing the signed input. For a record written on a user's behalf, it carries the DID of the repository holding the record (the user's), not the signer's. - Check what a strong reference points to. Treat a strong-reference entry as an attestation only when it resolves to an
app.certified.signature.proofrecord whose CID matches the reference and whosecidmatches the CID you computed for the record. - Write a new proof instead of editing one. A proof record is referenced by its CID, so editing it, even just the
note, breaks every reference to it. To attest to changed content, publish a new proof. - The list shows what the author chose to show. Only the record's author can add entries, and a proof record has no link back to the record it attests. An attestation can exist without appearing in the list, and an author can remove entries.
- Don't reject records over signatures. Unsigned records are normal. If an entry can't be verified or resolved, ignore that entry and keep the record.
Related#
- EVM Link: a wallet-ownership proof, distinct from record signatures.
- Acknowledgement and Evaluation: records for stating acceptance or assessment explicitly.
- Activity Claim: a typical signed record.
- Shared Definitions: other definitions shared across Certified records.
- Guide: Trust and Recognition, Records That Change Over Time.