SZDT Memos Specification
Memos are general-purpose metadata envelopes for annotating and cryptographically signing arbitrary CBOR data.
Memos take inspiration from the flexible memo format used in HTTP and email, with headers encoding arbitrary key-value metadata, followed by a body.
Memos provide a standardized way to:
- Annotate data with open-ended key-value metadata.
- Cryptographically sign data to create self-certifying records.
- Address data using cryptographic hashes for integrity verification.
- Version data using Git-like semantics.
Memo Structure
Memos are CBOR maps containing headers divided into two buckets: protected
and unprotected
.
An example of a basic memo:
{
"type": "szdt/memo", // always "szdt/memo"
"protected": {
"iss": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
"iat": 1640995200,
"src": h'c8d5e6f7a9b8c7d6e5f4g3h2i1j0k9l8m7n6o5p4q3r2s1t0u9v8w7x6y5z4a3b2c1',
"content-type": "text/plain"
},
"unprotected": {
"sig": h'5d2f8a0f3b4c7e8f9a1b2c3d4e5f6789abcdef01234567890abcdef0123456789abcdef01234567890abcdef0123456789abcdef01234567890abcdef'
}
}
- protected: A CBOR map of headers (key-value metadata). Protected headers are covered by cryptographic signatures when the memo is signed. Protected headers contain security-critical metadata like content hashes, timestamps, and issuer information.
- unprotected: A CBOR map of headers (key-value metadata). Unprotected headers are not covered by cryptographic signatures and may be freely modified. Unprotected headers contain auxiliary metadata, such as the signatures themselves, caching hints, or routing information.
This design allows intermediaries to add metadata (like caching information) without invalidating signatures, while critical metadata remains tamperproof.
Content Addressing
SZDT memos may point to other resources using content addresses (referencing data by cryptographic hash). SZDT content addresses are always Blake3 hashes, serialized in CBOR as byte strings.
Content addressing has a number of benefits:
- Decentralized: Content addresses allow us to refer to data based on what it is, not where it lives. Data referenced by a content address may live in multiple locations, and be retreived over multiple transports (HTTP, Iroh, etc).
- Zero-trust: Content addressed data may be retreived from untrusted locations, since the hash can be used to guarantee that the data you asked for is the data you got.
- Efficient: Content addresses allow for efficient storage and retrieval of data. Only the hash needs to be stored and transmitted, rather than the entire data payload. The same data can be referenced by multiple memos without duplicating the data itself.
Memo body
Like HTTP or email, memos are made up of a header part and a body part. Unlike HTTP and email, the body of a memo is not embedded directly into the memo structure itself. Instead, the body is referred to by a content address (Blake3 hash), stored in the src
field of the protected headers.
{
"type": "szdt/memo",
"unprotected": ...,
"protected": {
src: h'abcd1234...',
...
}
}
This design enables:
- Streaming verification: The signature over the headers may be verified immediately, while the body part is be streamed and verified incrementally via Blake3/Bao.
- Flexible delivery: Because the signature happens over the headers containing the hash of the body, the body part can be delivered in multiple ways. Memo and body can be bundled together into an SZDT sequence, or distributed independently.
Required and optional headers
Both protected and unprotected headers are CBOR maps containing open-ended key-value metadata.
Some header keys have predefined semantics. Some header keys are required, while others are optional. Optional headers must be omitted when not given a definite value. Authors must not serialize null values for unused headers.
Protected Headers
Required headers:
Field | Type | Description |
---|---|---|
iss |
String | Issuer DID (required for signed memos) |
iat |
Integer | Issued at timestamp (Unix seconds) |
src |
Bytes(32) | Blake3 hash of the referenced content |
Optional headers:
Field | Type | Description |
---|---|---|
nbf |
Integer | Not valid before timestamp (Unix seconds) |
exp |
Integer | Expiration timestamp (Unix seconds) |
prev |
Bytes(32) | Blake3 hash of previous version of this memo |
content-type |
String | MIME content type of referenced content |
iss-nickname |
String | The issuer's suggested nickname |
Unprotected Headers
Optional:
Field | Type | Description |
---|---|---|
sig |
Bytes | Ed25519 cryptographic signature over protected headers |
Custom Headers
Applications may define additional headers for application-specific use cases. Custom headers:
- Should use lowercase string keys
- Should prefer protected headers, unless the header needs to be modifiable
Compatibility with HTTP headers
Headers defined in the HTTP suite of specifications should be considered to have the same semantics as their HTTP counterparts. Custom headers must not be defined that conflict with HTTP header semantics.
Header Serialization Rules
- Optional headers with undefined values MUST be omitted from serialization
- Header maps MUST use deterministic CBOR encoding (CBOR/c profile)
- Header keys MUST be strings
- Header values may be any valid CBOR type
CBOR Encoding
All CBOR structures MUST use the deterministic CBOR/c ("CBOR Core") profile with definite-length encoding to ensure deterministic serialization for consistent hashing and signing.
Signatures
Memos support signing with an Ed25519 signatures to provide cryptographic proof of authenticity. Signatures are optional but recommended for zero-trust scenarios.
Signing Process
- Prepare protected headers: Ensure all required protected headers are present
- Encode headers: Serialize protected headers to CBOR using CBOR/c profile
- Hash headers: Compute the Blake3 hash of the serialized protected headers
- Sign hash: Generate Ed25519 signature over the Blake3 hash
- Add signature: Place signature in
sig
field of unprotected headers. Signature must be serialized as a CBOR byte string.
Verification Process
- Extract signature: Get signature from
sig
field in unprotected headers - Encode headers: Serialize protected headers to CBOR using CBOR/c profile
- Hash headers: Compute Blake3 hash of the serialized protected headers
- Verify signature: Validate Ed25519 signature over the hash using issuer's public key
- Verify timestamps: If
nbf
andexp
are present, check thatnbf
is not in the future andexp
is not in the past. A slush factor of 1000 milliseconds may be used to account for clock skew. - Verify content integrity: Verify content integrity using
src
hash, using the steps outlined below.
To verify content integrity:
- Read content: Obtain the content referenced by the memo
- Hash content: Compute Blake3 hash of the content
- Compare hashes: Verify computed hash matches
src
field in protected headers
For large content, implementors may use Blake3's streaming capabilities or Bao for incremental verification.
DIDs (Decentralized IDentifiers)
Actors in SZDT are identified with DIDs. DIDs provide a decentralized way to reference a public key.
Memo issuers (authors) are identified using a did:key
encoding the Ed25519 public key that may be used to verify the memo signature. The issuer DID is stored on the iss
field of the protected headers.
{
"iss": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"
}
DIDs are always serialized to their string representation.
Future versions of SZDT may support additional DID methods, such as did:web
.
Versioning and Updates
Memos can reference previous versions using the prev
field in protected headers:
{
"type": "szdt/memo",
"unprotected": {
"sig": h'9876fedc...'
},
"protected": {
"iss": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
"iat": 1640998800,
"prev": h'abcd1234...', // Blake3 hash of previous memo
"src": h'5678efab...', // Blake3 hash of current content
"content-type": "text/plain"
}
}
This creates a hash-linked chain of versions enabling Git-like version history.
Applications may choose to interpret this version history in a variety of ways, such as displaying a timeline of changes, or implementing branching workflows.
For example, to determine the most recent version, an application might implement last-write wins semantics using the following scheme:
- Choose a
iss
(issuer) to trust. Of the memos issued by that issuer...- Compare
iat
timestamps of versions. Newest wins.- If more than one memo has the newest
iat
timestamp, take the Blake3 hashes of the conflicting memos, and sort them in bytewise lexicographic order. The largest hash is the most recent version.
- If more than one memo has the newest
- Compare
Applications are also free to implement other versioning strategies, such as comparing the longest branch from a common ancestor, embedding CRDTs in the body, etc.
Usage Examples
Basic Signed Memo
{
"type": "szdt/memo",
"unprotected": {
"sig": h'5d2f8a0f3b4c7e8f9a1b2c3d4e5f6789abcdef01234567890abcdef0123456789abcdef01234567890abcdef0123456789abcdef01234567890abcdef'
},
"protected": {
"iss": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
"iat": 1640995200,
"src": h'c8d5e6f7a9b8c7d6e5f4g3h2i1j0k9l8m7n6o5p4q3r2s1t0u9v8w7x6y5z4a3b2c1',
"content-type": "text/plain"
}
}
Versioned Content Chain
// Version 1
{
"type": "szdt/memo",
"unprotected": {
"sig": h'signature1...'
},
"protected": {
"iss": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
"iat": 1640995200,
"src": h'content_hash_v1...',
"content-type": "application/json"
}
}
// Version 2 (references version 1)
{
"type": "szdt/memo",
"unprotected": {
"sig": h'signature2...'
},
"protected": {
"iss": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
"iat": 1640998800,
"prev": h'memo_hash_v1...',
"src": h'content_hash_v2...',
"content-type": "application/json"
}
}
References
- CBOR Core Profile
- Blake3 Specification
- Ed25519 Signature Scheme (RFC 8032)
- DID Key Method
- HTTP Semantics (RFC 9110)
Appendix
MIME Type
SZDT memos should use the MIME type:
application/vnd.szdt.memo+cbor