Wire format
Every DMP record fits one 255-byte DNS TXT string by design. No multi-string splitting needed in the common path — records are sized so the binary body plus signature plus base64 plus protocol prefix lands under 255 bytes.
Chunks
chunk-<NNNN>-<msg_key12>.<mesh_domain> IN TXT "v=dmp1;t=chunk;d=<b64>"
<NNNN>— zero-padded 4-digit chunk index.<msg_key12>— first 12 hex chars ofsha256(msg_id + recipient_id + sender_spk). Senders and recipients derive the same path without the recipient needing to know the sender’s X25519 pubkey up front.
Each chunk carries one erasure share plus per-chunk Reed-Solomon parity:
| Bytes | Meaning |
|---|---|
| 8 | SHA-256 prefix checksum over the decoded data block |
| 128 | Data block (DATA_PER_CHUNK) |
| 32 | Reed-Solomon parity (RS_SYMBOLS) over the data block |
Recipient: RS-decode first, then verify checksum. RS repairs up to 16 byte-errors per chunk inside the received data.
Slot manifests
slot-<N>.mb-<recipient_hash12>.<mesh_domain>
IN TXT "v=dmp1;t=manifest;d=<b64(body || sig)>"
<N>∈ 0..9 — there are 10 slots per recipient.<recipient_hash12>—sha256(recipient_user_id)[:12]. Recipient’suser_idis itselfsha256(recipient_x25519_pub).
Binary body = 108 bytes:
| Offset | Size | Field | Notes |
|---|---|---|---|
| 0 | 16 | msg_id |
UUIDv4 bytes |
| 16 | 32 | sender_spk |
Ed25519 signing pubkey |
| 48 | 32 | recipient_id |
sha256(recipient_x25519_pub) |
| 80 | 4 | total_chunks |
n; capped at MAX_TOTAL_CHUNKS = 1024 |
| 84 | 4 | data_chunks |
k; erasure threshold |
| 88 | 4 | prekey_id |
0 = long-term X25519 key (no FS) |
| 92 | 8 | ts |
Unix seconds — publication time |
| 100 | 8 | exp |
Unix seconds — drop after |
Followed by a 64-byte Ed25519 signature over body. Total wire:
108 + 64 = 172 bytes → 232 base64 chars → 252 total prefix+b64.
Append semantics — mailbox slots hold multiple manifests at once; the receive path iterates all of them at each slot.
Identity records
Hashed form (shared mesh domain)
id-<sha256(username)[:16]>.<mesh_domain>
IN TXT "v=dmp1;t=identity;d=<b64(body || sig)>"
Zone-anchored form
dmp.<identity_domain>
IN TXT "v=dmp1;t=identity;d=<b64(body || sig)>"
Body (variable length, capped):
| Field | Size | Required |
|---|---|---|
username_len |
1 byte | yes |
username |
up to 64 utf-8 bytes | yes |
x25519_pk |
32 bytes | yes |
ed25519_spk |
32 bytes | yes |
ts |
8 bytes | yes |
versions_len |
1 byte | optional (see below) |
versions |
versions_len × 1 byte |
optional |
The optional versions suffix advertises the protocol versions the
publishing client supports as a receiver. Each byte is a single
protocol version number; the writer sorts and deduplicates them so
the encoding is deterministic.
Compatibility rules:
- Suffix absent ⇒ treat as
versions = [1]. Records published before this field existed continue to parse and are correctly interpreted as v1-only. A new client that publishesversions = [1]MUST also omit the suffix entirely so the bytes are bit-identical to the pre-versions historical form (preserves cached body hashes). - Suffix present ⇒
versions_len ≥ 1and the encoded list MUST be sorted and unique. Non-canonical encodings reject.
Senders consult the recipient’s versions field before emitting
any wire feature beyond v1 (e.g. the DMPv2 plaintext envelope
below). If a recipient hasn’t published v2 yet, the sender stays
on v1 — automatic, safe rollout without a config flag.
Publishing v2 capability: the CLI defaults to publishing v1-only identity records so pre-this-release clients can still parse them (their strict length check rejects any trailing suffix). To opt in once your peers have updated, run:
dnsmesh identity publish --advertise-v2
dnsmesh identity rotate --advertise-v2 [...]
Once a peer’s record advertises v2, every upgraded sender starts
emitting the DMPv2 envelope to them, populating a verified
sender_label on first-contact messages.
Signed by the identity’s Ed25519 key. The fetcher verifies the
signature against the embedded ed25519_spk (the record is
self-authenticating).
In zone-anchored form the fetcher also requires
record.username == address.user so a zone owner can’t publish a
body naming someone else under their zone.
Prekeys
prekeys.id-<sha256(username)[:12]>.<mesh_domain>
IN TXT "v=dmp1;t=prekey;d=<b64(body || sig)>" (one RRset, many values)
Body = 44 bytes:
| Offset | Size | Field |
|---|---|---|
| 0 | 4 | prekey_id |
| 4 | 32 | x25519_pub |
| 36 | 8 | exp |
Followed by the 64-byte Ed25519 signature. Total wire: 108 + prefix = 162 chars.
Many prekey records share the same DNS name (RRset semantics); the sender gets all of them in one query and picks a random verified one.
Magic prefixes
Every DMP TXT value starts with v=dmp1;t=<type>; so a DMP-aware
resolver can filter on prefix. Types seen on the wire:
t= |
Meaning |
|---|---|
chunk |
Erasure share of an encrypted message |
manifest |
Signed slot manifest |
identity |
Signed identity record |
prekey |
Signed one-time prekey |
See Spec overview
for the full registry including cluster and bootstrap, and
Wire encoding conventions
for the cross-record prefix / base64 / signature rules.
Related reading
- Spec overview — cross-cutting invariants every record on this page respects.
- DNS name routing — owner-name helpers for chunks, manifests, identities, and prekeys.
- End-to-end flows — how these records are produced and consumed during send / receive / identity publish.
- Threat model — what the signatures on these records defend against, and what they don’t.
- Cluster manifest and Bootstrap record — the two signed-record types introduced in M2 + M3 that live alongside the records on this page.