v0.1.0 · Verifiable identity · Dark forest content · Public proofs
typ headers for defense in depth.
Ed25519 dag-cbor CIDv1 JWS
typ: did:dfos:identity-op
did:dfos:* identity-op create
update delete
did (author DID, committed to by CID) and commits to a
documentCID. The baseDocumentCID tracks document edit
lineage. The protocol sees hashes, not content. typ: did:dfos:content-op
contentId content-op did
documentCID baseDocumentCID
createdAt wins.
5-minute future clock skew tolerance. typ: did:dfos:beacon
beacon merkleRoot SHA-256
kid DID in the JWS header tells you who signed. When it matches payload
did = author. When it differs = witness. No ambiguity from a single JWS
alone.
countersign kid !== did witness
sorted tree inclusion proofs set membership
$schema. Content-addressed
directly: CID(dagCborCanonicalEncode(contentObject)). Authorized access
only. The protocol proves they exist without revealing what they say.
post/v1 profile/v1 manifest/v1
| Component | Concern | JWS typ |
|---|---|---|
| Crypto core | Identity chains + content chains — Ed25519 signatures, JWS tokens, CID links |
identity-op
content-op
|
| Beacons | Signed merkle root announcements — periodic commitment over content sets | beacon |
| Countersignatures | Witness attestation — third-party signatures over operations and beacons |
content-op
beacon
|
| Merkle trees | SHA-256 binary trees over content IDs — inclusion proofs for beacon roots | n/a (pure SHA-256) |
The DID controller decides how much to reveal. Each level exposes more without requiring the next.
An observer at the leftmost level sees: a hash changed. At the second level: which contentIds exist. At the third: what they mean semantically. At the fourth: what they actually say. Each level requires increasing trust or authorization.
// JWS header { "alg": "EdDSA", "typ": "did:dfos:identity-op", "kid": "did:dfos:...#key_...", "cid": "bafyrei..." } // payload (create) { "version": 1, "type": "create", "authKeys": [MultikeyPublicKey[]], "assertKeys": [MultikeyPublicKey[]], "controllerKeys": [MultikeyPublicKey[]], "createdAt": "2026-03-07T..." }
// JWS header { "alg": "EdDSA", "typ": "did:dfos:content-op", "kid": "did:dfos:...#key_...", "cid": "bafyrei..." } // payload (create) — did committed to by CID { "version": 1, "type": "create", "did": "did:dfos:abc123", "documentCID": "bafyrei...", "baseDocumentCID": null, "createdAt": "2026-03-07T...", "note": null }
// JWS header { "alg": "EdDSA", "typ": "did:dfos:beacon", "kid": "did:dfos:...#key_...", "cid": "bafyrei..." } // payload — floating, no backlink { "version": 1, "type": "beacon", "did": "did:dfos:abc123", "merkleRoot": "7e80d478...28e", "createdAt": "2026-03-07T..." }
merkleRoot is hex SHA-256 (64 chars), not a CID. Signed by controller
key. Latest createdAt supersedes. 5-min future clock skew tolerance.
// content operation countersign { "typ": "did:dfos:content-op", "kid": "did:dfos:witness...#key_...", "cid": "bafyrei..." } // same payload → same CID, different signer // beacon countersign { "typ": "did:dfos:beacon", "kid": "did:dfos:witness...#key_...", "cid": "bafyrei..." } // same beacon payload → same CID
Works for both content operations and beacons. Same typ as the original.
Discrimination: kid DID vs payload did.
did rule
Every content operation and beacon payload includes a did field committed
to by the CID. Any JWS token for that CID is self-describing:
kid DID === payload.did →
Author / Controller
kid DID !== payload.did →
Countersignature (witness)
Decode, compare, done. No chain context needed. Works identically for content ops and beacons.
typ header discriminationdid:dfos:identity-op
— identity chain operations
did:dfos:content-op
— content chain ops + countersigns
did:dfos:beacon
— beacon announcements + countersigns
JWT
— device auth tokens (no CID)
All verifiers validate typ. Defense in depth — Zod schema
discrimination is the primary safety net.
// flat content object, committed to by CID { "$schema": "https://schemas.dfos.com/post/v1", "format": "short-post", "createdByDID": "did:dfos:...", "title": "Hello World", "body": "First post on the protocol." }
// semantic index — the navigation layer { "$schema": "https://schemas.dfos.com/manifest/v1", "entries": { "profile": "67t27rzc83v7c22n9t6z7c", "posts": "a4b8c2d3e5f6g7h8i9j0k1", "dark-publisher": "did:dfos:proxy_xyz" } }
Points to contentIds, DIDs, other manifests. The semantic graph is dark forest content — invisible without authorization.
Pure SHA-256 binary tree over content identifiers. No dag-cbor, no CIDs. The tree is a commitment scheme, not content-addressed data.
SHA-256(UTF-8(contentId)) → 32-byte leaf hash
SHA-256(left || right) → 32 bytes. Odd nodes promoted unpaired.
null (no beacon needed).
Inclusion proofs: sibling hashes along the path from leaf to root, plus direction (left/right) for each step. Verifier recomputes root from leaf + proof, compares to claimed root. 231 cross-language checks verify this construction in Go, Python, Rust, Swift, and TypeScript.
Four lookups. No global enumeration. No reverse indexes.
| Lookup | Returns | Visibility |
|---|---|---|
| did | Identity HEAD + full op chain (current key state) | public |
| did | Latest beacon (merkle root hash + witnesses) | public |
| contentId | Content HEAD + full op chain | public |
| operationCID | JWS token(s) — original + countersignatures | public |
| did → contentIds | Requires Merkle tree data (controller's choice to share) | controller's choice |
| documentCID | Document bytes | dark forest |
| contentId → signer DID |
Via did field in operation payload (committed to by CID) and
kid in JWS header
|
always visible |
Discovery is dark. Attribution is visible. Knowing a DID doesn't let you find its content. Knowing a contentId lets you find its signer. The asymmetry is the dark forest.
End-to-end dark forest content resolution. Every step independently verifiable. Proof from Alice, chain from Bob, document from a USB drive — verification is identical.
typ: did:dfos:beacon. Get inclusion proof for contentId against root.
typ headers,
signer DID matches payload did, timestamp ordering.
The signer DID on the relay can be a proxy. The connection to the real identity is buried in dark forest manifest content. The protocol doesn't distinguish real from proxy — they're all just DIDs.
Arbitrarily deep nesting. Each layer uses the same protocol primitives. The graph is invisible without manifest access at each level.
Self-asserted, self-signed, self-certifying. The protocol verifies everything from cryptographic primitives. No third party needed. Runs on a laptop with no network.
Corroboration that first-party assertions can't provide. The protocol is complete without validators. Validators add independent timestamps, availability proof, and equivocation detection.
Same CID, different JWS signer. Works for both
content operations (typ: content-op) and
beacons (typ: beacon). The payload contains
did (the author/controller). When JWS kid DID matches
payload did = author. When they differ = witness. Ed25519 is
deterministic: same key + same payload = same signature, enabling dedup by JWS token
string.
Boolean: "Is this vouched for?" Cheap. No metadata. Collected per-operation and per-beacon.
Validator maintains their own content chains with structured attestation documents. Rich metadata: timestamps, subject DID, checks performed. Auditable, queryable, historical.
Record: "Who witnessed what, when, with what checks?" The validator's own dark forest content. Same protocol primitives all the way down.
A validator might do both: countersign in real-time (tier 1) and maintain an attestation stream for audit (tier 2). The countersignature is the fast path. The attestation chain is the record.
HTTPS servers that store, serve, and sync protocol artifacts. Each relay defines its own
policy — what it serves, to whom, for which DIDs. The protocol package
(@metalabel/dfos-protocol) is a pure verification library — zero HTTP,
storage, or runtime opinions. The relay is a separate artifact.
// Proof substrate (always) Identity ops — DID resolution, key state Content ops — chain verification Root beacons — merkle root announcements JWS lookups — operation CID → signatures Witnesses — countersignatures per CID // Optional (operator policy) Merkle tree — full tree for enumeration Documents — dark forest content (authed) SSE stream — real-time operation feed
// Relay-to-relay reconciliation 1. Exchange root beacons for tracked DIDs 2. Compare roots — if equal, in sync 3. If different, exchange content ops to reconcile chain heads 4. MST (Merkle Search Tree) for efficient delta transfer // Phase 1: HTTPS + hardcoded peers // Phase 2: DNS discovery // Phase 3: Relay directory // Phase 4: libp2p (if needed)
Everything needed to verify without reading. Identity chains, content chain operations, root beacons, countersignatures. Who signed what, when, how many operations, which witnesses attested. The skeleton.
You can see that content exists.
The actual documents behind CID commitments. Posts, profiles, manifests, identity linkages. Application semantics. What things actually say. Who is really behind what.
You cannot see what it says.
The gap between these two is where sovereignty lives. The proof layer is infrastructure. The content layer is meaning. The protocol proves without revealing.
DFOS Protocol v0.1.0 · Ed25519 · dag-cbor · CIDv1 · SHA-256 Merkle · W3C DIDs · Self-certifying · Transport-agnostic
231 cross-language checks · TypeScript 145 · Python 48 · Go 13 · Rust 13 · Swift 12
protocol.dfos.com · github.com/metalabel/dfos
v0.1.0 architecture · 2026-03-20