Maildown Conformance
Use this checklist to test a second Maildown implementation against the current reference behavior.
Versions
An implementation must recognize:
- plaintext message version:
maildown: 1 - signing prefix:
maildown-signature-v1 - envelope version:
maildownEnvelope: 2 - envelope algorithm:
maildown-envelope-v2 - payload algorithm:
aes-256-gcm - recipient key algorithm:
ml-kem-768
Unknown required versions or algorithms must fail closed.
Message Parser
The parser must:
- require a leading
---header delimiter - read header lines until the second
--- - parse
key: valueheader lines - parse bracketed lists such as
[athena, odysseus] - parse JSON-quoted strings in scalar or list values
- keep the body after the second delimiter
- trim trailing whitespace from parsed body
The parser should ignore blank header lines and comment lines that
start with #.
Renderer
The renderer must include these header fields when present:
maildown
messageId
ts
from
to
cc
bcc
subject
type
priority
thread
replyTo
files
role
sigAlg
sigKey
sig
pqSigAlg
pqSigKey
pqSig
The renderer should write:
---
<header>
---
<body>
Canonical Signing Payload
The signing payload must be:
maildown-signature-v1
<canonical-json>
The canonical JSON object must contain:
maildown
messageId
ts
from
to
cc
subject
type
priority
thread
replyTo
files
role
body
data
Rules:
- sort object keys lexicographically
- omit fields with
undefinedvalues - preserve array order
- encode primitives with JSON rules
- normalize
to,cc, andfilesto arrays - normalize missing
datatonull - normalize missing
maildownto1
bcc must be excluded from the signature version 1
signing payload. A closed-circle implementation must reject
accepted-delivery messages with any bcc value until a
future signature version defines signed recipient-private metadata.
Signature Verification
An implementation must verify:
- Ed25519 with SPKI DER public keys
- ML-DSA-65 with raw public keys
- key id matches an active public key for the sender
- key algorithm matches the signature algorithm
When requireHybridSignatures is true, both Ed25519 and
ML-DSA-65 signatures must verify.
Envelope Encryption
Envelope encryption must:
- generate a random 32-byte content key
- encrypt the rendered Markdown payload with AES-256-GCM
- generate a 12-byte nonce for each AES-GCM record
- store GCM tag separately as base64
- use ML-KEM-768 to wrap the content key for each recipient
- derive wrapping keys with HKDF-SHA-256
- use the envelope AAD for payload encryption
- use
key:<identity>:<keyId>as AAD for wrapped content keys
HKDF input:
ikm = ml_kem_shared_secret
salt = envelope_aad
info = "maildown-envelope-v2:<identity>:<keyId>:<base64_sha256_kemCipherText>"
len = 32
Envelope Decryption
Envelope decryption must:
- reject unsupported envelope versions
- select the recipient entry by local identity
- require recipient algorithm
ml-kem-768 - decapsulate with the local ML-KEM-768 secret key
- derive the same wrapping key
- decrypt the content key
- decrypt the payload
- parse and verify the plaintext message before trusting it
Policy
Closed-circle policy must reject a message when:
- sender is not a circle member
- recipient is not a circle member
- sender identity is missing or inactive
- sender lacks send permission for the recipient
- required signatures are missing or invalid
Reference Commands
Run the core conformance smoke:
cd tvl-maildown
npm test
Run the MCP smoke:
cd tvl-maildown-mcp
npm test
Run a deployed smoke:
cd tvl-maildown-mcp
MAILDOWN_LOGIN_PASSWORD=<password> npm run smoke:remote
Fixture Status
The repository does not yet include fixed conformance fixtures. The next useful step is to add:
- one signed plaintext message
- one envelope with one recipient
- one envelope with two recipients
- one policy rejection case
- one malformed envelope case