Hey everyone! I’m excited to present this proposal for a standardized attestation and signature specification for ATProtocol. This specification builds upon recent proof of concept work and our production implementation of badges in Smoke Signal, addressing fundamental challenges in establishing trust and authenticity for third-party attestations within the ATProtocol network.
The core problem we’re solving here is straightforward but important: when records represent content or intentions involving third parties, those parties currently have no standardized way to signal the authenticity of these records. Whether we’re talking about membership confirmations, identity verification, awards and achievements, or any other form of recognition, we need a robust system for cryptographic attestation that integrates seamlessly with ATProtocol’s existing infrastructure.
Background and Motivation
Through our recent work implementing badges in Smoke Signal as both proof of concept and production features, we’ve encountered numerous scenarios where attestation is not just useful but essential. Consider the case of event RSVPs where an organizer needs to confirm acceptance, or badge systems where issuers need to verify that someone has genuinely earned an achievement. Without a standardized approach to attestation, each application ends up rolling its own solution, leading to fragmentation and incompatibility across the ecosystem.
The attestation lexicon I’m proposing represents a carefully designed set of types for cryptographically verified attestation that addresses these needs while maintaining flexibility for future use cases. This isn’t just about signatures – it’s about creating a foundation for trust that can span across different applications and services within the ATProtocol ecosystem.
Core Lexicon Components
The specification consists of two primary lexicon components that work together to provide comprehensive attestation capabilities. The first component is a general signatures block that serves as a container for referencing signature lexicons and structures that apply to any record or object. This takes the form of an array that can contain multiple signature types, allowing for complex attestation scenarios where multiple parties might need to sign or verify content.
{
"id": "community.lexicon.attestation.defs",
"defs": {
"signatures": {
"type": "array",
"items": {
"refs": [
"community.lexicon.attestation.signature",
"com.atproto.repo.strongRef"
],
"type": "union"
}
}
}
}
The second component is the signature record itself, which contains the actual cryptographic signature along with essential metadata. This record type establishes the minimum required fields that every signature must contain to ensure consistency and verifiability across the ecosystem.
{
"id": "community.lexicon.attestation.signature",
"defs": {
"main": {
"key": "tid",
"type": "record",
"record": {
"type": "object",
"required": [
"signature",
"issuer",
"issuedAt"
],
"properties": {
"signature": {
"type": "bytes",
"description": "The signature"
},
"issuer": {
"type": "string",
"format": "did",
"description": "The DID that issued the signature."
},
"issuedAt": {
"type": "string",
"format": "datetime",
"description": "A user-provided timestamp for when the signature was issued."
}
}
},
"description": "A common and base signature record."
}
}
}
Practical Use Cases
Let me walk you through some concrete examples of how this specification enables powerful attestation scenarios. These aren’t theoretical – they’re based on real needs we’ve encountered in building applications on ATProtocol.
Calendar RSVPs with Verified Acceptance
One of the most immediate applications is in event management, where organizers or ticketing services need to attest that an RSVP is valid and accepted. Using an authoritative record reference, the attestation can be dynamically managed – if a ticket is transferred or refunded, the attestation can be revoked simply by deleting or updating the referenced record. This provides a clean separation between the user’s intent to attend (the RSVP) and the organizer’s confirmation of that attendance.
{
"$type": "community.lexicon.calendar.rsvp",
"createdAt": "2025-07-14T19:36:02.830Z",
"signatures": [
{
"$type": "com.atproto.repo.strongRef",
"uri": "at://did:plc:lehcqqkwzcwvjvw66uthu5oq/community.lexicon.attestation.signature/01k2cvg2v11qxng6393pc07bgc",
"cid": "bafyreic6e..svfji",
}
],
"status": "community.lexicon.calendar.rsvp#going",
"subject": {
"cid": "bafyreic6ev7ulowb7il4egk7kr5vwfgw5nweyj5dhkzlkid5sf3aqsvfji",
"uri": "at://did:plc:lehcqqkwzcwvjvw66uthu5oq/community.lexicon.calendar.event/3ltl5aficno2m"
}
}
Badge Awards and Achievements
The badge system we’ve been developing at Smoke Signal demonstrates another compelling use case. When someone earns a badge, they can use unauthoritative signatures to “own” the badge along with cryptographic proof that they legitimately earned it. This creates a portable, verifiable credential that the user controls while maintaining the issuer’s authority over the attestation.
{
"$type": "community.lexicon.badge.award",
"badge": {
"$type": "com.atproto.repo.strongRef",
"cid": "bafyreibnfpriilyjmssycvlkcp46cmoscwon7okbfvhjmobggisinerj5e",
"uri": "at://did:plc:tgudj2fjm77pzkuawquqhsxm/community.lexicon.badge.definition/3ltwfsgx3vu2a"
},
"did": "did:plc:cbkjy5n7bk3ax2wplmtjofq2",
"issued": "2025-07-14T12:00:00.000Z",
"signatures": [
{
"$type": "community.lexicon.attestation.signature",
"issuedAt": "2025-08-11T14:51:38.458Z",
"issuer": "did:plc:tgudj2fjm77pzkuawquqhsxm",
"signature": {
"$bytes": "GPj75xyWCzRCKijHFwREryOl_nDW-kvyd1muzXRvxtfXOymslRhhX3588p5eQcmpW85ZH2aXnwAC3shzNuoC1Q"
}
}
]
}
Collective Content Endorsements
A particularly interesting application emerges in collaborative content creation. When a collective identity like a group or aggregate publishes an article, individual authors can attest to their contribution by signing the record. This creates a transparent chain of accountability and credit that’s cryptographically verifiable.
{
"$type": "tools.smokesignal.blahg.content.post",
"attachments": [],
"authors": [
"did:plc:tgudj2fjm77pzkuawquqhsxm"
],
"content": {
"$type": "blob",
"mimeType": "text/markdown",
"ref": {
"$link": "bafkreigcivolfvq6onzypxq7hvflrbwhtnmjxlwebkplvho25ryvus5nma"
},
"size": 17205
},
"langs": [
"en"
],
"publishedAt": "2025-08-07T19:45:00.000Z",
"signatures": [
{
"$type": "community.lexicon.attestation.signature",
"issuedAt": "2025-08-11T14:51:38.458Z",
"issuer": "did:plc:tgudj2fjm77pzkuawquqhsxm",
"signature": {
"$bytes": "GPj75xyWCzRCKijHFwREryOl_nDW-kvyd1muzXRvxtfXOymslRhhX3588p5eQcmpW85ZH2aXnwAC3shzNuoC1Q"
}
}
],
"title": "Dynamic Client Registration is the Missing Piece for Mobile and CLI Applications"
}
Extensibility Through Open Records
One of the key design decisions in this specification is the use of an “open” union array for the signatures field. This allows applications to implement their own signature record types while maintaining compatibility with the base specification. The only requirement is that custom signature types must contain the common signature fields we’ve defined as mandatory.
This extensibility is crucial for supporting domain-specific attestation needs without fragmenting the ecosystem. For example, we can create specialized signature types for RSVPs that include acceptance status and waitlist position information. The lexicon for such an extension might look like this:
{
"defs": {
"accepted": {
"description": "The RSVP was accepted.",
"type": "token"
},
"main": {
"key": "any",
"record": {
"properties": {
"acceptance": {
"default": "community.lexicon.calendar.acceptanceSignature#waitlisted",
"knownValues": [
"community.lexicon.calendar.acceptanceSignature#waitlisted",
"community.lexicon.calendar.acceptanceSignature#accepted",
"community.lexicon.calendar.acceptanceSignature#rejected"
],
"type": "string"
},
"issuedAt": { "format": "datetime", "type": "string" },
"issuer": { "format": "did", "type": "string" },
"position": { "type": "number" },
"signature": { "type": "bytes" }
},
"required": [ "signature", "issuer", "issuedAt", "acceptance", "position" ],
"type": "object"
},
"type": "record"
},
"rejected": {
"description": "The RSVP was rejected.",
"type": "token"
},
"waitlisted": {
"description": "The identity is waitlisted.",
"type": "token"
}
},
"id": "community.lexicon.calendar.acceptanceSignature",
"lexicon": 1
}
This extensibility enables rich scenarios like waitlisted RSVPs that can be upgraded to accepted status:
{
"$type": "community.lexicon.calendar.rsvp",
"createdAt": "2025-07-14T19:36:02.830Z",
"signatures": [
{
"$type": "community.lexicon.calendar.acceptanceSignature",
"acceptance": "community.lexicon.calendar.acceptanceSignature#waitlisted",
"issuedAt": "2025-08-11T14:51:38.458Z",
"issuer": "did:plc:tgudj2fjm77pzkuawquqhsxm",
"signature": {
"$bytes": "GPj75xyWCzRCKijHFwREryOl_nDW-kvyd1muzXRvxtfXOymslRhhX3588p5eQcmpW85ZH2aXnwAC3shzNuoC1Q"
}
}
],
"status": "community.lexicon.calendar.rsvp#going",
"subject": {
"cid": "bafyreic6ev7ulowb7il4egk7kr5vwfgw5nweyj5dhkzlkid5sf3aqsvfji",
"uri": "at://did:plc:lehcqqkwzcwvjvw66uthu5oq/community.lexicon.calendar.event/3ltl5aficno2m"
}
}
Or as an authoritative record that marks final acceptance:
{
"$type": "community.lexicon.calendar.acceptanceSignature",
"acceptance": "community.lexicon.calendar.acceptanceSignature#accepted",
"issuedAt": "2025-08-11T14:51:38.458Z",
"issuer": "did:plc:tgudj2fjm77pzkuawquqhsxm",
"signature": {
"$bytes": "GPj75xyWCz...uoC1Q"
}
}
Security Considerations and Required Fields
The specification mandates three essential fields for all signatures: the signature itself, the issuer DID, and the issuedAt timestamp. This isn’t arbitrary – these fields work together to prevent several classes of attacks and forgery attempts. The inclusion of the issuer and timestamp makes signed records significantly harder to forge because they bind the signature to a specific identity and time context.
The Critical Role of Repository and Collection Fields
Introducing the repository and collection fields makes a signed record significantly harder to forge. Without having the collection or repository context included in the signature, someone could take the content of the record and duplicate it to some other repository or collection/type and claim the signed record as their own.
For example, did:web:badactor.com could copy the following record from at://did:web:some-rando.lol/community.lexicon.calendar.rsvp/01k2cxz9a4scmn7mc5gtxp03jj:
{
"$type": "community.lexicon.calendar.rsvp",
"createdAt": "2025-07-14T19:36:02.830Z",
"signatures": [
{
"$type": "social.concerts.calendar.ticketedRsvp",
"issuedAt": "2025-08-01T10:00:00.000Z",
"issuer": "did:web:cool-band.social",
"signature": {
"$bytes": "GPj75xyWCzRCKijHFwREryOl_nDW-kvyd1muzXRvxtfXOymslRhhX3588p5eQcmpW85ZH2aXnwAC3shzNuoC1Q"
},
"ticket": "FOO-BAR-BAZ-1"
}
],
"status": "community.lexicon.calendar.rsvp#going",
"subject": {
"cid": "bafyreic6ev7ulowb7il4egk7kr5vwfgw5nweyj5dhkzlkid5sf3aqsvfji",
"uri": "at://did:web:cool-band.social/community.lexicon.calendar.event/01k2cy2s32rj4krxr4gv8yxnk1"
}
}
They could attempt to place this in their own repository at at://did:web:badactor.com/community.lexicon.calendar.rsvp/01k2cy7bcer19gqxstv4m1nyzq and show up to the concert with what appears to be a valid ticketed RSVP. By adding the repository (DID) and collection (type) to the signature computation, signatures cannot be cloned or relocated – they remain cryptographically bound to their original location.
Why Authoritative References Still Need Signatures
When examining authoritative signature record references, you might wonder why the signature payload is necessary at all. After all, the existence of the record itself seems to imply authenticity and authority. However, this is where the details become crucial.
Consider what would happen if we omitted the signature payload for authoritative references. Take a simplified badge award at at://did:web:the-awarded.com/community.lexicon.badge.award/1234tid5678:
{
"$type": "community.lexicon.badge.award",
"badge": "some-award",
"signatures": [
{
"$type": "com.atproto.repo.strongRef",
"cid": "bafyreic6e..svfji",
"uri": "at://did:web:the-issuer.com/community.lexicon.attestation.signature/2345tid6789"
}
]
}
With the remote signature record containing only metadata:
{
"$type": "community.lexicon.attestation.signature",
"issuedAt": "2025-08-11T14:51:38.458Z",
"issuer": "did:plc:tgudj2fjm77pzkuawquqhsxm"
}
Without the signature cryptographically binding to the content, the awarded identity could simply modify their record while keeping the same authoritative reference, changing "badge": "some-award" to "badge": "some-other-award" without detection.
You might think CID references could solve this, but they introduce an impossible circular dependency. The award would reference the signature’s CID, which must reference the award’s CID, but any update to either record changes its CID, requiring updates to the other, creating an infinite loop. Weak references without CIDs would allow undetected content changes, undermining the entire attestation system. The signature payload is therefore essential – it cryptographically binds the attestation to the specific content being attested, regardless of how the records reference each other.
This same principle prevents another attack vector. Without proper context binding, a malicious actor finding a legitimate RSVP record like this:
{
"$type": "community.lexicon.calendar.rsvp",
"createdAt": "2025-07-14T19:36:02.830Z",
"signatures": [
{
"$type": "social.concerts.calendar.ticketedRsvp",
"issuedAt": "2025-08-01T10:00:00.000Z",
"issuer": "did:web:cool-band.social",
"signature": {
"$bytes": "GPj75xyWCzRCKijHFwREryOl_nDW-kvyd1muzXRvxtfXOymslRhhX3588p5eQcmpW85ZH2aXnwAC3shzNuoC1Q"
},
"ticket": "FOO-BAR-BAZ-1"
}
],
"status": "community.lexicon.calendar.rsvp#going",
"subject": {
"cid": "bafyreic6ev7ulowb7il4egk7kr5vwfgw5nweyj5dhkzlkid5sf3aqsvfji",
"uri": "at://did:web:cool-band.social/community.lexicon.calendar.event/01k2cy2s32rj4krxr4gv8yxnk1"
}
}
Could attempt to copy it to their own repository. However, because the repository and collection context are included in the signature computation, the verification would fail immediately. The signature remains cryptographically bound to its original location, making such forgery attempts impossible.
Signature Creation Process
The process for creating signatures is designed to be deterministic and verifiable while maintaining flexibility for different signature schemes. The core workflow involves normalizing the record content, adding contextual information, and then applying cryptographic signing using the issuer’s verification methods.
The signature creation process follows these specific steps. First, we identify and normalize the subject record that needs to be signed. For instance, when signing a badge award record located at at://did:plc:cbkjy5n7bk3ax2wplmtjofq2/community.lexicon.badge.award/3ltwfsgx3vu2a, we start with the clean record representation:
{
"$type": "community.lexicon.badge.award",
"badge": {
"cid": "bafyreibnfpriilyjmssycvlkcp46cmoscwon7okbfvhjmobggisinerj5e",
"uri": "at://did:plc:tgudj2fjm77pzkuawquqhsxm/community.lexicon.badge.definition/3ltwfsgx3vu2a"
},
"did": "did:plc:cbkjy5n7bk3ax2wplmtjofq2",
"issued": "2025-07-14T12:00:00.000Z"
}
Next, we remove any existing signatures field to ensure we’re signing the core content without any prior attestations. Then we create a special $sig object that captures the signing context, including the signature type, repository, collection, issuer, and timestamp:
{
"$sig": {
"$type": "community.lexicon.attestation.signature",
"collection": "community.lexicon.badge.award",
"issuedAt": "2025-08-11T14:51:38.458Z",
"issuer": "did:web:resounding-gerbil.social",
"repository": "did:web:compatible-ghoul.social"
},
"$type": "community.lexicon.badge.award",
"badge": {
"cid": "bafyreibnf...erj5e",
"uri": "at://did:web:resounding-gerbil.social/community.lexicon.badge.definition/01k2cps2db4r59j52sbnfcr1c3"
},
"did": "did:web:compatible-ghoul.social",
"issued": "2025-08-01T12:00:00.000Z"
}
This structure is then serialized to the IPLD DAG-CBOR binary representation, which provides a canonical form for signing. The actual signing is performed using a verification method associated with the issuer’s DID, and the resulting signature is added to the record’s signatures field:
{
"$type": "community.lexicon.badge.award",
"badge": {
"cid": "bafyreibnf...erj5e",
"uri": "at://did:web:resounding-gerbil.social/community.lexicon.badge.definition/01k2cps2db4r59j52sbnfcr1c3"
},
"did": "did:web:compatible-ghoul.social",
"issued": "2025-08-01T12:00:00.000Z",
"signatures": [
{
"$type": "community.lexicon.attestation.signature",
"issuedAt": "2025-08-11T14:51:38.458Z",
"issuer": "did:web:resounding-gerbil.social",
"signature": {
"$bytes": "GPj75xyWCzRCKijHFwREryOl_nDW-kvyd1muzXRvxtfXOymslRhhX3588p5eQcmpW85ZH2aXnwAC3shzNuoC1Q"
}
}
]
}
Signature Verification Process
Verification is the complement to signature creation and must be equally robust while handling both authoritative and unauthoritative signature patterns. The verification process begins by fetching the record and retaining its repository and collection context from the AT-URI. This context is crucial for preventing the signature replay attacks I mentioned earlier.
The signature verification follows these specific steps:
-
Fetch the record and retain the
repositoryandcollectionfrom the AT-URI used to retrieve the record. -
Fail if there are no signature elements in the
signaturesfield. -
For each of the signature elements in the
signaturesfield, determine if the signature is valid.
Authoritative Signatures
The system supports two distinct verification patterns, each suited to different use cases. Authoritative signatures use record references like com.atproto.repo.strongRef, pointing to signature records stored separately from the signed content. This pattern is ideal when issuers need to maintain control over attestations, allowing them to revoke or update signatures by modifying or deleting the referenced records.
When the signature element is an authoritative record reference, the signature record will need to be retrieved:
-
Fail if the record cannot be retrieved.
-
(Optional) Fail if the retrieved record does not match the CID if provided.
Unauthoritative Signatures
Unauthoritative signatures, on the other hand, embed all signature data directly within the signed record. This pattern works well when issuers don’t need revocation capabilities or prefer not to maintain long-term storage of attestation records. The trade-off is clear: you gain simplicity and reduce storage requirements but lose the ability to centrally manage attestations after they’re issued.
Validation Process
Once the authoritative or unauthoritative record content for signature elements are retrieved, the verification process can continue:
-
Fail if the
issuerfield of the signature cannot be resolved to a DID document with at least one verification method. -
Fail if the
issuedAtfield of the signature cannot be resolved to a valid date/time value.
Create a comparison payload using the public key material of the verification:
-
Create a copy of the record.
-
Remove the
signaturesfield if it is present. -
Remove the
$sigfield if it is present. -
Set the
$sigfield to an empty object. -
Set the
repositoryfield of the$sigfield to the retained repository from the beginning of verification. -
Set the
collectionfield of the$sigfield to the retained collection from the beginning of verification. -
Add all of the fields and their values from the signature that is being validated. This excludes the
signaturefield and includes the$type,issuerandissuedAtfields in addition to any others present (that are notsignature). -
Serialize the structure to the IPLD DAG-CBOR binary representation.
-
Verify using a verification method associated with the issuer.
The actual validation process reconstructs the signed payload exactly as it was created during signature generation. This reconstruction ensures that any tampering with the record, its location, or its context will cause verification to fail, maintaining the integrity of the attestation system.
Tooling and Implementation
To support adoption of this specification, we’re providing reference implementations through command-line tools that demonstrate the signature creation and verification processes. The atproto-identity-key tool handles key generation for different curve types, while atproto-record-sign performs the actual signing operation. These tools aren’t just reference implementations – they’re production-ready utilities that developers can use immediately.
Full Example
Using the atproto-identity-key and atproto-record-sign tools from this repository, we can execute the complete process.
First, imagine a fake identity did:web:resounding-gerbil.social and generate a key:
$ atproto-identity-key generate p256
p256 private: did:key:z42tu9tD9TSMnxHtwaahBSjh1rb4MzhG6jFqJcZnRg5pKH5d
p256 public: did:key:zDnaeRn1mJo1wLfySU5grXUbydgqMZiqMQP95Z65Er7FScgjo
Second, imagine a badge definition: at://did:web:resounding-gerbil.social/community.lexicon.badge.definition/01k2cps2db4r59j52sbnfcr1c3
{
"$type": "community.lexicon.badge.definition",
"description": "Up Up And Away",
"name": "Off you go into the ATmosphere."
}
Third, imagine a fake identity did:web:compatible-ghoul.social to receive a badge award record:
{
"$type": "community.lexicon.badge.award",
"badge": {
"cid": "bafyreibnf...erj5e",
"uri": "at://did:web:resounding-gerbil.social/community.lexicon.badge.definition/01k2cps2db4r59j52sbnfcr1c3"
},
"did": "did:web:compatible-ghoul.social",
"issued": "2025-08-01T12:00:00.000Z"
}
Now sign the payload:
$ atproto-record-sign \
did:key:z42tu9tD9TSMnxHtwaahBSjh1rb4MzhG6jFqJcZnRg5pKH5d \
./up-up-and-away-award.json \
did:web:resounding-gerbil.social \
repository=did:web:compatible-ghoul.social \
collection=community.lexicon.badge.award
The result:
{
"$type": "community.lexicon.badge.award",
"badge": {
"cid": "bafyreibnf...erj5e",
"uri": "at://did:web:resounding-gerbil.social/community.lexicon.badge.definition/01k2cps2db4r59j52sbnfcr1c3"
},
"did": "did:web:compatible-ghoul.social",
"issued": "2025-08-01T12:00:00.000Z",
"signatures": [
{
"$type": "community.lexicon.attestation.signature",
"issuedAt": "2025-08-11T14:51:38.458Z",
"issuer": "did:web:resounding-gerbil.social",
"signature": {
"$bytes": "GPj75xyWCzRCKijHFwREryOl_nDW-kvyd1muzXRvxtfXOymslRhhX3588p5eQcmpW85ZH2aXnwAC3shzNuoC1Q"
}
}
]
}
These tools demonstrate that the specification is not just theoretical but immediately actionable. Developers can start implementing attestation in their applications today using these utilities as a foundation or reference for their own implementations.
Future Considerations and Community Input
This specification represents a significant step forward in bringing cryptographic attestation to the ATProtocol ecosystem, but it’s designed to be a starting point rather than a final destination. The extensibility mechanisms we’ve built in allow for evolution and adaptation as new use cases emerge. We’ve deliberately kept the core specification minimal while providing clear extension points for domain-specific needs.
I’m particularly interested in community feedback on several aspects of this proposal. The balance between authoritative and unauthoritative signatures offers flexibility, but I’d love to hear about use cases that might challenge this model. The required fields we’ve chosen are based on extensive real-world testing, but there may be scenarios we haven’t considered. The serialization format using IPLD DAG-CBOR aligns with ATProtocol’s existing infrastructure, but we’re open to discussing alternatives if there are compelling reasons.
Conclusion
This attestation specification addresses a fundamental need in the ATProtocol ecosystem: the ability to create cryptographically verifiable claims about records and their relationships. By providing a standardized approach to signatures and attestations, we’re enabling a new class of applications that require trust and verification without centralized authorities. The specification is practical, implemented, and ready for adoption, while remaining flexible enough to accommodate future innovations.
The examples I’ve shared – from event RSVPs to badge awards to collaborative content – are just the beginning. I envision this specification enabling everything from decentralized credential systems to complex multi-party agreements, all built on the solid foundation of ATProtocol. The combination of authoritative and unauthoritative signature patterns provides the flexibility needed for diverse use cases while maintaining the security properties essential for trustworthy attestation.
I’m excited to see what the community builds with this specification and look forward to your feedback, questions, and contributions. Let’s work together to make attestation a core capability of the ATProtocol ecosystem, enabling new forms of trust and collaboration across our decentralized network.