@metalabel/dfos-protocol 0.0.2 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CONTENT-MODEL.md +64 -23
- package/DID-METHOD.md +7 -8
- package/PROTOCOL.md +194 -123
- package/README.md +9 -11
- package/dist/chain/index.d.ts +97 -5
- package/dist/chain/index.js +15 -3
- package/dist/{chunk-LWC4PWGZ.js → chunk-ASGEXSVT.js} +177 -7
- package/dist/chunk-E5CFQG2B.js +99 -0
- package/dist/index.d.ts +2 -4
- package/dist/index.js +28 -34
- package/dist/merkle/index.d.ts +45 -0
- package/dist/merkle/index.js +14 -0
- package/examples/content-delete.json +5 -4
- package/examples/content-lifecycle.json +9 -7
- package/package.json +6 -9
- package/schemas/manifest.v1.json +29 -0
- package/schemas/post.v1.json +5 -0
- package/schemas/profile.v1.json +5 -0
- package/REGISTRY-API.md +0 -242
- package/dist/chunk-4MMWM2PC.js +0 -311
- package/dist/registry/index.d.ts +0 -143
- package/dist/registry/index.js +0 -34
- package/openapi.yaml +0 -376
- package/schemas/document-envelope.v1.json +0 -37
package/PROTOCOL.md
CHANGED
|
@@ -10,34 +10,26 @@ This spec is under active review. Discuss it in the [clear.txt](https://clear.df
|
|
|
10
10
|
|
|
11
11
|
## Philosophy
|
|
12
12
|
|
|
13
|
-
DFOS is a dark forest operating system. Content lives in private spaces — visible only to members, governed by the communities that create it. The
|
|
13
|
+
DFOS is a dark forest operating system. Content lives in private spaces — visible only to members, governed by the communities that create it. The cryptographic proof layer is public: signed chains of commitments that anyone can independently verify with a public key and any standard EdDSA library.
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
Two chain types — identity and content — use the same mechanics: Ed25519 signatures, JWS compact tokens, content-addressed CIDs. The protocol knows about keys and document hashes. It doesn't know about posts, profiles, or any application concept. Document semantics are application layer — free to evolve without protocol changes.
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
The protocol makes this verification radically simple. Two chain types — identity and content — using the same mechanics: Ed25519 signatures, JWS compact tokens, content-addressed CIDs. The protocol is deliberately minimal. It knows about keys and document hashes. It doesn't know about posts, profiles, or any application concept. Document semantics are entirely application layer — free to evolve without protocol changes.
|
|
20
|
-
|
|
21
|
-
This means the protocol is not coupled to DFOS. Any system could implement the same identity and content chain primitives — a fork, an alternative client, a completely independent platform — and produce interoperable, cross-verifiable proofs. An identity created on one system can sign content on another. A proof chain started here can be extended there. The protocol is a shared substrate, not a product feature. DFOS is one application built on it. There could be others.
|
|
22
|
-
|
|
23
|
-
The result: a signed content ledger that any standard EdDSA library can verify, in any language, without DFOS-specific dependencies. The dark forest has public roots.
|
|
24
|
-
|
|
25
|
-
---
|
|
26
|
-
|
|
27
|
-
All artifacts in this document are deterministic and reproducible from fixed seeds. An independent implementer can verify every value using standard Ed25519 + dag-cbor libraries.
|
|
17
|
+
The protocol is not coupled to the DFOS platform. Any system implementing the same chain primitives produces interoperable, cross-verifiable proofs. An identity created on one system can sign content on another.
|
|
28
18
|
|
|
29
19
|
---
|
|
30
20
|
|
|
31
21
|
## Protocol Overview
|
|
32
22
|
|
|
33
|
-
The DFOS protocol has
|
|
23
|
+
The DFOS protocol has four components:
|
|
34
24
|
|
|
35
|
-
|
|
|
25
|
+
| Component | Concern |
|
|
36
26
|
| --------------------- | ---------------------------------------------------------------------------- |
|
|
37
27
|
| **Crypto core** | Identity chains + content chains — Ed25519 signatures, JWS tokens, CID links |
|
|
38
|
-
| **
|
|
28
|
+
| **Beacons** | Signed merkle root announcements — periodic commitment over content sets |
|
|
29
|
+
| **Countersignatures** | Witness attestation — third-party signatures over existing chain operations |
|
|
30
|
+
| **Merkle trees** | SHA-256 binary trees over content IDs — inclusion proofs for beacon roots |
|
|
39
31
|
|
|
40
|
-
The crypto core is the trust boundary — everything below it is cryptographically verified.
|
|
32
|
+
The crypto core is the trust boundary — everything below it is cryptographically verified. Documents are flat content objects, content-addressed directly: `documentCID = CID(dagCborCanonicalEncode(contentObject))`. What goes inside the content object is application-defined — see the [DFOS Content Model](https://protocol.dfos.com/content-model) for the standard schema library.
|
|
41
33
|
|
|
42
34
|
### Crypto Core: Two Chain Types
|
|
43
35
|
|
|
@@ -49,47 +41,21 @@ The crypto core is the trust boundary — everything below it is cryptographical
|
|
|
49
41
|
| JWS typ | `did:dfos:identity-op` | `did:dfos:content-op` |
|
|
50
42
|
| Self-sovereign | Yes (signs own operations) | No (signed by external identity) |
|
|
51
43
|
|
|
52
|
-
Both chains are signed linked lists of state commitments. Identity chains embed their state (key sets). Content chains reference their state via `documentCID` — a content-addressed pointer to a
|
|
53
|
-
|
|
54
|
-
### Document Envelope
|
|
55
|
-
|
|
56
|
-
Every document committed to by a content chain uses a standard envelope, defined by JSON Schema at [`schemas/document-envelope.v1.json`](schemas/document-envelope.v1.json) (`https://schemas.dfos.com/document-envelope/v1`):
|
|
57
|
-
|
|
58
|
-
```json
|
|
59
|
-
{
|
|
60
|
-
"content": { "$schema": "https://schemas.dfos.com/post/v1", ... },
|
|
61
|
-
"baseDocumentCID": null,
|
|
62
|
-
"createdByDID": "did:dfos:...",
|
|
63
|
-
"createdAt": "2026-03-07T00:02:00.000Z"
|
|
64
|
-
}
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
| Field | Type | Description |
|
|
68
|
-
| ----------------- | ------------ | --------------------------------------------------------------------------- |
|
|
69
|
-
| `content` | object | Application-defined content — must include `$schema` URI, opaque to chains |
|
|
70
|
-
| `baseDocumentCID` | string\|null | Optional CID of a prior document version. Semantics are application-defined |
|
|
71
|
-
| `createdByDID` | string | DID of the identity that created this document version |
|
|
72
|
-
| `createdAt` | ISO 8601 | When this document version was created |
|
|
73
|
-
|
|
74
|
-
The `documentCID` in a content chain operation is `CID(dagCborEncode(envelope))`. The envelope provides attribution at the protocol level. The `content` object must include a `$schema` property identifying its content type — this makes every document self-describing and its schema cryptographically committed via the CID.
|
|
44
|
+
Both chains are signed linked lists of state commitments. Identity chains embed their state (key sets). Content chains reference their state via `documentCID` — a content-addressed pointer to a flat content object.
|
|
75
45
|
|
|
76
46
|
### Addressing
|
|
77
47
|
|
|
78
|
-
Three
|
|
79
|
-
|
|
80
|
-
| Thing | Form | Example |
|
|
81
|
-
| ---------------------- | -------------------------- | --------------------------------- |
|
|
82
|
-
| Operation or document | CID (dag-cbor + SHA-256) | See below |
|
|
83
|
-
| Entity (content chain) | `<hash>` (bare, no prefix) | `67t27rzc83v7c22n9t6z7c` |
|
|
84
|
-
| Identity (key chain) | `did:dfos:<hash>` | `did:dfos:e3vvtck42d4eacdnzvtrn6` |
|
|
48
|
+
Three addressing modes, self-describing by format:
|
|
85
49
|
|
|
86
|
-
Example
|
|
50
|
+
| Thing | Form | Example |
|
|
51
|
+
| --------------------- | ------------------------ | --------------------------------- |
|
|
52
|
+
| Operation or document | CID (dag-cbor + SHA-256) | `bafyrei...` (base32lower) |
|
|
53
|
+
| Content chain | contentId (22-char hash) | `a82z92a3hndk6c97thcrn8` |
|
|
54
|
+
| Identity chain | DID | `did:dfos:e3vvtck42d4eacdnzvtrn6` |
|
|
87
55
|
|
|
88
|
-
|
|
89
|
-
bafyreibanjpgcqffcfhr4sptzjfthh5szohhbo5tjfulemkw7uhden5uqy
|
|
90
|
-
```
|
|
56
|
+
CIDs are specific immutable artifacts — a pointer to an exact operation or document. Content IDs are living content chain entities — the 22-char bare hash derived from the genesis CID. DIDs are living identity chain entities.
|
|
91
57
|
|
|
92
|
-
Operations and documents are CIDs — standard IPLD content addresses.
|
|
58
|
+
Operations and documents are CIDs — standard IPLD content addresses. Content chains and identity chains use derived identifiers — `customAlpha(SHA-256(genesis CID bytes))`. Same derivation for both. Identity chains prepend `did:dfos:` (W3C DID spec). Content identifiers are bare — just the 22-char hash, no prefix.
|
|
93
59
|
|
|
94
60
|
Application code may add prefixes for routing (e.g., `post_xxxx`) — these are strippable semantic sugar, not part of the protocol identifier.
|
|
95
61
|
|
|
@@ -99,17 +65,20 @@ Application code may add prefixes for routing (e.g., `post_xxxx`) — these are
|
|
|
99
65
|
|
|
100
66
|
### Commitment Scheme
|
|
101
67
|
|
|
102
|
-
|
|
68
|
+
Both operations and documents are content-addressed via **CID** (`dagCborCanonicalEncode(payload)` → SHA-256 → CIDv1). Operations are additionally signed via **JWS**.
|
|
103
69
|
|
|
104
|
-
|
|
70
|
+
| Representation | Encoding | Purpose |
|
|
71
|
+
| -------------- | -------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
|
|
72
|
+
| CID | `dagCborCanonicalEncode(payload)` → SHA-256 → CIDv1 | Deterministic content addressing for operations and documents |
|
|
73
|
+
| JWS | `base64url(JSON.stringify(header))` + `.` + `base64url(JSON.stringify(payload))` → EdDSA signature covers both | Signature verification for operations |
|
|
105
74
|
|
|
106
|
-
|
|
75
|
+
CID uses [dag-cbor canonical encoding](https://ipld.io/specs/codecs/dag-cbor/spec/) for determinism — given the same logical payload, the CID MUST be identical regardless of implementation language or platform. JWS uses standard JSON for library interoperability. The dag-cbor hex test vectors in this document allow byte-level verification.
|
|
107
76
|
|
|
108
77
|
### Chain Validity
|
|
109
78
|
|
|
110
79
|
A valid chain is a **linear sequence** of operations. Each operation (after genesis) links to its predecessor via `previousOperationCID`. The chain provides structural ordering independent of timestamps.
|
|
111
80
|
|
|
112
|
-
**Forks are invalid at the protocol level.** Two operations referencing the same `previousOperationCID` constitute a fork. The protocol does not define fork resolution — this is application-defined
|
|
81
|
+
**Forks are invalid at the protocol level.** Two operations referencing the same `previousOperationCID` constitute a fork. The protocol does not define fork resolution — this is application-defined (e.g., longest chain, first-seen, advisory locks).
|
|
113
82
|
|
|
114
83
|
**Timestamp ordering**: `createdAt` SHOULD be strictly increasing within a chain. Implementations SHOULD reject operations with non-increasing timestamps as a sanity check against replayed or mis-ordered operations. However, the chain link (CID reference) is the authoritative ordering mechanism, not the timestamp. Implementations MAY relax timestamp ordering in constrained environments where clock synchronization is impractical.
|
|
115
84
|
|
|
@@ -121,23 +90,24 @@ This is a self-sovereign invariant: the identity chain defines its own valid sig
|
|
|
121
90
|
|
|
122
91
|
### Content Chain Signer Model
|
|
123
92
|
|
|
124
|
-
Content chain verification requires a **valid EdDSA signature**
|
|
93
|
+
Content chain verification requires a **valid EdDSA signature** and delegates key resolution to the caller. The `kid` in each operation's JWS header is a DID URL (`did:dfos:<id>#<keyId>`). The verifier calls `resolveKey(kid)` to obtain the raw Ed25519 public key bytes for that key on that identity. How the resolver obtains and validates the identity's key state is application-defined.
|
|
125
94
|
|
|
126
|
-
|
|
95
|
+
Identity chains are self-sovereign — they define their own valid signers via `controllerKeys`. Content chains are externally signed — a content chain with operations signed by multiple different identities is valid at the protocol level, as long as each signature verifies against the resolved key.
|
|
127
96
|
|
|
128
|
-
|
|
97
|
+
**Signer-payload consistency**: The `kid` DID in the JWS header MUST match the `did` field in the content operation payload. This enables discrimination between author operations and countersignatures — if the kid DID differs from the payload `did`, it is a countersignature (witness attestation), not a chain operation.
|
|
129
98
|
|
|
130
99
|
**What the protocol enforces:**
|
|
131
100
|
|
|
132
101
|
- The EdDSA signature on each operation is valid against the key returned by `resolveKey(kid)`
|
|
133
102
|
- Chain integrity (CID links, timestamp ordering, terminal state)
|
|
103
|
+
- The `kid` DID matches the payload `did` for chain operations
|
|
134
104
|
|
|
135
105
|
**What the protocol does NOT enforce (application concerns):**
|
|
136
106
|
|
|
137
107
|
- Which identities are authorized to sign operations on a given chain
|
|
138
108
|
- Which key role (auth, assert, controller) the signing key must have
|
|
139
109
|
- Whether a chain must have a single signer or may have multiple signers
|
|
140
|
-
- Ownership or attribution semantics between signers and
|
|
110
|
+
- Ownership or attribution semantics between signers and content chains
|
|
141
111
|
|
|
142
112
|
### Terminal States and Special Operations
|
|
143
113
|
|
|
@@ -145,18 +115,28 @@ This is a deliberate asymmetry with identity chains. Identity chains are self-so
|
|
|
145
115
|
|
|
146
116
|
**Controller key requirement:** `update` operations on identity chains MUST include at least one controller key. If decommissioning is intended, `delete` is the correct terminal operation.
|
|
147
117
|
|
|
148
|
-
**Content-null:** An `update` on a content chain with `documentCID: null` means the
|
|
118
|
+
**Content-null:** An `update` on a content chain with `documentCID: null` means the content exists but its document is cleared. The chain continues — a subsequent update can set content again.
|
|
149
119
|
|
|
150
120
|
### `typ` Header
|
|
151
121
|
|
|
152
|
-
The JWS `typ` header
|
|
122
|
+
The JWS `typ` header uses protocol-specific values (not IANA media types):
|
|
123
|
+
|
|
124
|
+
| `typ` value | Usage |
|
|
125
|
+
| ---------------------- | ------------------------- |
|
|
126
|
+
| `did:dfos:identity-op` | Identity chain operations |
|
|
127
|
+
| `did:dfos:content-op` | Content chain operations |
|
|
128
|
+
| `did:dfos:beacon` | Beacon announcements |
|
|
129
|
+
| `JWT` | Device auth tokens |
|
|
130
|
+
|
|
131
|
+
These are non-standard per JOSE convention, documented intentionally. The `typ` header aids routing but is not security-critical. Implementations SHOULD validate it but MUST NOT rely on it for security decisions.
|
|
153
132
|
|
|
154
133
|
### Operation Field Limits
|
|
155
134
|
|
|
156
|
-
The protocol defines maximum sizes for all operation fields
|
|
135
|
+
The protocol defines maximum sizes for all operation fields as abuse-prevention ceilings. Implementations MUST reject operations that exceed these bounds. Implementations MAY impose stricter limits.
|
|
157
136
|
|
|
158
137
|
| Field | Max | Rationale |
|
|
159
138
|
| -------------------------------------------- | --------- | -------------------------------------- |
|
|
139
|
+
| `did` | 256 chars | ~8× typical `did:dfos:` (~31 chars) |
|
|
160
140
|
| `key.id` | 64 chars | ~3× typical key ID (`key_` + 22 chars) |
|
|
161
141
|
| `key.publicKeyMultibase` | 128 chars | ~2× Ed25519 multikey (~50 chars) |
|
|
162
142
|
| `authKeys` / `assertKeys` / `controllerKeys` | 16 items | Generous for key rotation |
|
|
@@ -310,19 +290,24 @@ DID: did:dfos:e3vvtck42d4eacdnzvtrn6
|
|
|
310
290
|
```typescript
|
|
311
291
|
// Genesis — starts the content chain, commits initial document
|
|
312
292
|
{ version: 1, type: "create",
|
|
313
|
-
|
|
293
|
+
did: string, // author DID, committed to by CID
|
|
294
|
+
documentCID: string, // CID of flat content object
|
|
295
|
+
baseDocumentCID: string | null, // edit lineage — CID of prior document version
|
|
314
296
|
createdAt: string,
|
|
315
297
|
note: string | null }
|
|
316
298
|
|
|
317
299
|
// Content change (null documentCID = clear content)
|
|
318
300
|
{ version: 1, type: "update",
|
|
301
|
+
did: string, // author DID
|
|
319
302
|
previousOperationCID: string,
|
|
320
303
|
documentCID: string | null,
|
|
304
|
+
baseDocumentCID: string | null,
|
|
321
305
|
createdAt: string,
|
|
322
306
|
note: string | null }
|
|
323
307
|
|
|
324
|
-
// Permanent
|
|
308
|
+
// Permanent destruction
|
|
325
309
|
{ version: 1, type: "delete",
|
|
310
|
+
did: string, // author DID
|
|
326
311
|
previousOperationCID: string,
|
|
327
312
|
createdAt: string,
|
|
328
313
|
note: string | null }
|
|
@@ -379,11 +364,7 @@ Every operation JWS (identity-op and content-op) includes a `cid` field in the p
|
|
|
379
364
|
- `header.cid` is missing
|
|
380
365
|
- `header.cid` does not match the derived CID
|
|
381
366
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
- **Pre-verification routing**: The operation CID can be read from the header without parsing the payload or running dag-cbor encoding
|
|
385
|
-
- **Cross-implementation consistency**: A CID mismatch between header and derived value immediately surfaces dag-cbor encoding disagreements across implementations
|
|
386
|
-
- **Self-documenting tokens**: Each JWS token declares its content-addressed identity
|
|
367
|
+
A CID mismatch between header and derived value immediately surfaces dag-cbor encoding disagreements across implementations.
|
|
387
368
|
|
|
388
369
|
Note: JWT tokens (device auth) do NOT include a `cid` header — this field is specific to operation JWS tokens.
|
|
389
370
|
|
|
@@ -405,6 +386,90 @@ Where `idEncode` is the 19-char alphabet encoding described above.
|
|
|
405
386
|
|
|
406
387
|
---
|
|
407
388
|
|
|
389
|
+
## Beacons
|
|
390
|
+
|
|
391
|
+
A beacon is a signed announcement of a merkle root — a periodic commitment over a set of content IDs. Beacons are floating signed artifacts, not chained. They provide a compact, verifiable snapshot of an identity's content set at a point in time.
|
|
392
|
+
|
|
393
|
+
### Beacon Payload
|
|
394
|
+
|
|
395
|
+
```json
|
|
396
|
+
{
|
|
397
|
+
"version": 1,
|
|
398
|
+
"type": "beacon",
|
|
399
|
+
"did": "did:dfos:e3vvtck42d4eacdnzvtrn6",
|
|
400
|
+
"merkleRoot": "a3f8b2c1d4e5f6071829304a5b6c7d8e9f0a1b2c3d4e5f6071829304a5b6c7d8",
|
|
401
|
+
"createdAt": "2026-03-07T00:04:00.000Z"
|
|
402
|
+
}
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
| Field | Type | Description |
|
|
406
|
+
| ------------ | ------ | ------------------------------------------------------- |
|
|
407
|
+
| `version` | 1 | Protocol version |
|
|
408
|
+
| `type` | string | Literal `"beacon"` |
|
|
409
|
+
| `did` | string | DID of the identity publishing the beacon |
|
|
410
|
+
| `merkleRoot` | string | Hex-encoded SHA-256 root (64 chars, `/^[0-9a-f]{64}$/`) |
|
|
411
|
+
| `createdAt` | string | ISO 8601 timestamp |
|
|
412
|
+
|
|
413
|
+
### Beacon JWS Header
|
|
414
|
+
|
|
415
|
+
```json
|
|
416
|
+
{
|
|
417
|
+
"alg": "EdDSA",
|
|
418
|
+
"typ": "did:dfos:beacon",
|
|
419
|
+
"kid": "did:dfos:e3vvtck42d4eacdnzvtrn6#key_ez9a874tckr3dv933d3ckd",
|
|
420
|
+
"cid": "bafyrei..."
|
|
421
|
+
}
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
### Beacon Semantics
|
|
425
|
+
|
|
426
|
+
Beacons are not chained — there is no `previousOperationCID`. For a given DID, the latest beacon with a strictly-greater `createdAt` timestamp wins. Beacons replace, not accumulate.
|
|
427
|
+
|
|
428
|
+
**Clock skew tolerance**: Implementations MUST reject beacons with a `createdAt` more than 5 minutes in the future relative to the verifier's clock. This prevents pre-dating attacks while accommodating reasonable clock drift.
|
|
429
|
+
|
|
430
|
+
**merkleRoot**: A hex-encoded SHA-256 hash (64 characters). This is a commitment, not a CID — it uses raw SHA-256, not dag-cbor encoding. See the Merkle Tree section below for construction. An empty content set produces a `null` merkle root (no beacon needed).
|
|
431
|
+
|
|
432
|
+
---
|
|
433
|
+
|
|
434
|
+
## Merkle Trees
|
|
435
|
+
|
|
436
|
+
Beacons commit to a set of content IDs via a pure SHA-256 binary Merkle tree. The tree has no dag-cbor dependency — it uses only SHA-256 over raw bytes.
|
|
437
|
+
|
|
438
|
+
### Construction
|
|
439
|
+
|
|
440
|
+
1. **Collect** all content IDs (22-char bare hashes) in the set
|
|
441
|
+
2. **Sort** content IDs lexicographically (UTF-8 byte order)
|
|
442
|
+
3. **Hash leaves**: for each content ID, `SHA-256(UTF-8(contentId))` → 32-byte leaf hash
|
|
443
|
+
4. **Build tree**: recursively pair adjacent hashes. For each pair, `SHA-256(left || right)` → 32 bytes. If a level has an odd number of nodes, the last node is promoted to the next level unpaired.
|
|
444
|
+
5. **Root**: the final 32-byte hash, hex-encoded to a 64-character string
|
|
445
|
+
|
|
446
|
+
An empty set of content IDs produces a `null` root. A single content ID produces a root equal to `hex(SHA-256(UTF-8(contentId)))`.
|
|
447
|
+
|
|
448
|
+
### Inclusion Proofs
|
|
449
|
+
|
|
450
|
+
A Merkle inclusion proof demonstrates that a specific content ID is part of the committed set without revealing the full set. The proof consists of sibling hashes along the path from leaf to root, plus a direction (left/right) for each step.
|
|
451
|
+
|
|
452
|
+
---
|
|
453
|
+
|
|
454
|
+
## Countersignatures
|
|
455
|
+
|
|
456
|
+
A countersignature is a witness attestation — a third-party identity signing the same CID-committed bytes as an existing chain operation. Countersignatures use the same JWS format and `typ` (`did:dfos:content-op`) as the original operation.
|
|
457
|
+
|
|
458
|
+
### Discrimination Rule
|
|
459
|
+
|
|
460
|
+
The protocol distinguishes author operations from countersignatures by comparing the `kid` DID in the JWS header to the `did` field in the operation payload:
|
|
461
|
+
|
|
462
|
+
- **`kid` DID === payload `did`** → author operation (chain operation)
|
|
463
|
+
- **`kid` DID !== payload `did`** → witness countersignature
|
|
464
|
+
|
|
465
|
+
### Semantics
|
|
466
|
+
|
|
467
|
+
A countersignature proves that a witness identity has seen and attested to a specific operation. The witness signs the exact same payload (same CID), but with their own key. The countersignature's JWS header will contain the witness's `kid` (their DID URL), while the payload's `did` field remains the original author's DID.
|
|
468
|
+
|
|
469
|
+
Countersignatures are not part of the chain — they do not have `previousOperationCID` links and do not affect chain state. They are auxiliary attestations stored alongside chain operations.
|
|
470
|
+
|
|
471
|
+
---
|
|
472
|
+
|
|
408
473
|
## Verification
|
|
409
474
|
|
|
410
475
|
### Identity Chain
|
|
@@ -427,15 +492,16 @@ Where `idEncode` is the 19-char alphabet encoding described above.
|
|
|
427
492
|
2. First op must be `type: "create"`
|
|
428
493
|
3. For each subsequent op: verify `previousOperationCID` matches, verify `createdAt` increasing
|
|
429
494
|
4. Derive the operation CID via dag-cbor canonical encoding. Verify `header.cid` matches the derived CID.
|
|
430
|
-
5.
|
|
431
|
-
6.
|
|
432
|
-
7.
|
|
495
|
+
5. Verify the `kid` DID matches the payload `did` field (mismatches indicate a countersignature, not a chain operation)
|
|
496
|
+
6. Resolve `kid` via external key resolver (caller provides)
|
|
497
|
+
7. Verify EdDSA JWS signature
|
|
498
|
+
8. Apply state change (set document, clear, or delete)
|
|
433
499
|
|
|
434
500
|
---
|
|
435
501
|
|
|
436
502
|
## Deterministic Reference Artifacts
|
|
437
503
|
|
|
438
|
-
All
|
|
504
|
+
All artifacts below are deterministic and reproducible from fixed seeds. An independent implementer can verify every value using standard Ed25519 + dag-cbor libraries. Private keys are derived from `SHA-256(UTF8("dfos-protocol-reference-key-N"))`.
|
|
439
505
|
|
|
440
506
|
### Key 1 (Genesis Controller)
|
|
441
507
|
|
|
@@ -510,7 +576,7 @@ JWS Signature (hex):
|
|
|
510
576
|
JWS Token:
|
|
511
577
|
|
|
512
578
|
```
|
|
513
|
-
eyJhbGciOiJFZERTQSIsInR5cCI6ImRpZDpkZm9zOmlkZW50aXR5LW9wIiwia2lkIjoia2V5X3I5ZXYzNGZ2YzIzejk5OXZlYWFmdDgiLCJjaWQiOiJiYWZ5cmVpYmFuanBnY3FmZmNmaHI0c3B0empmdGhoNXN6b2hoYm81dGpmdWxlbWt3N3VoZGVuNXVxeSJ9.
|
|
579
|
+
eyJhbGciOiJFZERTQSIsInR5cCI6ImRpZDpkZm9zOmlkZW50aXR5LW9wIiwia2lkIjoia2V5X3I5ZXYzNGZ2YzIzejk5OXZlYWFmdDgiLCJjaWQiOiJiYWZ5cmVpYmFuanBnY3FmZmNmaHI0c3B0empmdGhoNXN6b2hoYm81dGpmdWxlbWt3N3VoZGVuNXVxeSJ9.eyJ2ZXJzaW9uIjoxLCJ0eXBlIjoiY3JlYXRlIiwiYXV0aEtleXMiOlt7ImlkIjoia2V5X3I5ZXYzNGZ2YzIzejk5OXZlYWFmdDgiLCJ0eXBlIjoiTXVsdGlrZXkiLCJwdWJsaWNLZXlNdWx0aWJhc2UiOiJ6Nk1rcnpMTU53b0pTVjRQM1ljY1djYnRrOHZkOUx0Z01LbkxlYURMVXFMdUFTamIifV0sImFzc2VydEtleXMiOlt7ImlkIjoia2V5X3I5ZXYzNGZ2YzIzejk5OXZlYWFmdDgiLCJ0eXBlIjoiTXVsdGlrZXkiLCJwdWJsaWNLZXlNdWx0aWJhc2UiOiJ6Nk1rcnpMTU53b0pTVjRQM1ljY1djYnRrOHZkOUx0Z01LbkxlYURMVXFMdUFTamIifV0sImNvbnRyb2xsZXJLZXlzIjpbeyJpZCI6ImtleV9yOWV2MzRmdmMyM3o5OTF2ZWFhZnQ4IiwidHlwZSI6Ik11bHRpa2V5IiwicHVibGljS2V5TXVsdGliYXNlIjoiejZNa3J6TE1Od29KU1Y0UDNZY2NXY2J0azh2ZDlMdGdNS25MZWFETFVxTHVBU2piIn1dLCJjcmVhdGVkQXQiOiIyMDI2LTAzLTA3VDAwOjAwOjAwLjAwMFoifQ.EDryDK1uvtix-17cHun9t6MacFIx2rMmMF1QLzfD5TFlSsOvMcue97pCgGn3CXeLVFtVxgpCoh0kGSXioKKzAw
|
|
514
580
|
```
|
|
515
581
|
|
|
516
582
|
Operation CID:
|
|
@@ -588,26 +654,22 @@ Post-rotation: DID unchanged (`did:dfos:e3vvtck42d4eacdnzvtrn6`), controller rot
|
|
|
588
654
|
|
|
589
655
|
### Content Chain: Document + Create
|
|
590
656
|
|
|
591
|
-
Document (
|
|
657
|
+
Document (flat content object):
|
|
592
658
|
|
|
593
659
|
```json
|
|
594
660
|
{
|
|
595
|
-
"
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
},
|
|
601
|
-
"baseDocumentCID": null,
|
|
602
|
-
"createdByDID": "did:dfos:e3vvtck42d4eacdnzvtrn6",
|
|
603
|
-
"createdAt": "2026-03-07T00:02:00.000Z"
|
|
661
|
+
"$schema": "https://schemas.dfos.com/post/v1",
|
|
662
|
+
"format": "short-post",
|
|
663
|
+
"title": "Hello World",
|
|
664
|
+
"body": "First post on the protocol.",
|
|
665
|
+
"createdByDID": "did:dfos:e3vvtck42d4eacdnzvtrn6"
|
|
604
666
|
}
|
|
605
667
|
```
|
|
606
668
|
|
|
607
669
|
Document CID:
|
|
608
670
|
|
|
609
671
|
```
|
|
610
|
-
|
|
672
|
+
bafyreihzwuoupfg3dxip6xmgzmxsywyii2jeoxxzbgx3zxm2in7knoi3g4
|
|
611
673
|
```
|
|
612
674
|
|
|
613
675
|
Content Create JWS Header:
|
|
@@ -617,7 +679,7 @@ Content Create JWS Header:
|
|
|
617
679
|
"alg": "EdDSA",
|
|
618
680
|
"typ": "did:dfos:content-op",
|
|
619
681
|
"kid": "did:dfos:e3vvtck42d4eacdnzvtrn6#key_ez9a874tckr3dv933d3ckd",
|
|
620
|
-
"cid": "
|
|
682
|
+
"cid": "bafyreiaedhjq64aajpwociahl5w37j6uoxr5mojoq5dnah6fpvxr5d4lxu"
|
|
621
683
|
}
|
|
622
684
|
```
|
|
623
685
|
|
|
@@ -627,7 +689,9 @@ Content Create Payload:
|
|
|
627
689
|
{
|
|
628
690
|
"version": 1,
|
|
629
691
|
"type": "create",
|
|
630
|
-
"
|
|
692
|
+
"did": "did:dfos:e3vvtck42d4eacdnzvtrn6",
|
|
693
|
+
"documentCID": "bafyreihzwuoupfg3dxip6xmgzmxsywyii2jeoxxzbgx3zxm2in7knoi3g4",
|
|
694
|
+
"baseDocumentCID": null,
|
|
631
695
|
"createdAt": "2026-03-07T00:02:00.000Z",
|
|
632
696
|
"note": null
|
|
633
697
|
}
|
|
@@ -636,19 +700,19 @@ Content Create Payload:
|
|
|
636
700
|
Content Create JWS Signature (hex):
|
|
637
701
|
|
|
638
702
|
```
|
|
639
|
-
|
|
703
|
+
46feaf973e4c7ebc2a0d4ad25481ace197de05b91051205c5e1c7067a85fb9d4abe4cc61625d3c853a8b0ce0345b534c8cdd07b34216f635d3c0bc0fd5d30306
|
|
640
704
|
```
|
|
641
705
|
|
|
642
706
|
Content Create JWS Token:
|
|
643
707
|
|
|
644
708
|
```
|
|
645
|
-
|
|
709
|
+
eyJhbGciOiJFZERTQSIsInR5cCI6ImRpZDpkZm9zOmNvbnRlbnQtb3AiLCJraWQiOiJkaWQ6ZGZvczplM3Z2dGNrNDJkNGVhY2RuenZ0cm42I2tleV9lejlhODc0dGNrcjNkdjkzM2QzY2tkIiwiY2lkIjoiYmFmeXJlaWFlZGhqcTY0YWFqcHdvY2lhaGw1dzM3ajZ1b3hyNW1vam9xNWRuYWg2ZnB2eHI1ZDRseHUifQ.eyJ2ZXJzaW9uIjoxLCJ0eXBlIjoiY3JlYXRlIiwiZGlkIjoiZGlkOmRmb3M6ZTN2dnRjazQyZDRlYWNkbnp2dHJuNiIsImRvY3VtZW50Q0lEIjoiYmFmeXJlaWh6d3VvdXBmZzNkeGlwNnhtZ3pteHN5d3lpaTJqZW94eHpiZ3gzenhtMmluN2tub2kzZzQiLCJiYXNlRG9jdW1lbnRDSUQiOm51bGwsImNyZWF0ZWRBdCI6IjIwMjYtMDMtMDdUMDA6MDI6MDAuMDAwWiIsIm5vdGUiOm51bGx9.Rv6vlz5MfrwqDUrSVIGs4ZfeBbkQUSBcXhxwZ6hfudSr5MxhYl08hTqLDOA0W1NMjN0Hs0IW9jXTwLwP1dMDBg
|
|
646
710
|
```
|
|
647
711
|
|
|
648
712
|
Content Operation CID:
|
|
649
713
|
|
|
650
714
|
```
|
|
651
|
-
|
|
715
|
+
bafyreiaedhjq64aajpwociahl5w37j6uoxr5mojoq5dnah6fpvxr5d4lxu
|
|
652
716
|
```
|
|
653
717
|
|
|
654
718
|
### Content Chain: Update
|
|
@@ -659,39 +723,45 @@ Content Update Payload:
|
|
|
659
723
|
{
|
|
660
724
|
"version": 1,
|
|
661
725
|
"type": "update",
|
|
662
|
-
"
|
|
663
|
-
"
|
|
726
|
+
"did": "did:dfos:e3vvtck42d4eacdnzvtrn6",
|
|
727
|
+
"previousOperationCID": "bafyreiaedhjq64aajpwociahl5w37j6uoxr5mojoq5dnah6fpvxr5d4lxu",
|
|
728
|
+
"documentCID": "bafyreidh7e36cvwy3uw5ypitcqk7uoktbkkkj7e6hxhky4o75rxn7kxilu",
|
|
729
|
+
"baseDocumentCID": "bafyreihzwuoupfg3dxip6xmgzmxsywyii2jeoxxzbgx3zxm2in7knoi3g4",
|
|
664
730
|
"createdAt": "2026-03-07T00:03:00.000Z",
|
|
665
731
|
"note": "edited title and body"
|
|
666
732
|
}
|
|
667
733
|
```
|
|
668
734
|
|
|
669
|
-
Updated document:
|
|
735
|
+
Updated document (flat content object):
|
|
670
736
|
|
|
671
737
|
```json
|
|
672
738
|
{
|
|
673
|
-
"
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
},
|
|
679
|
-
"baseDocumentCID": "bafyreifpvwuarml62sfogdpi2vlltvg2ev6o4xtw74zfud7cpkg7426zne",
|
|
680
|
-
"createdByDID": "did:dfos:e3vvtck42d4eacdnzvtrn6",
|
|
681
|
-
"createdAt": "2026-03-07T00:03:00.000Z"
|
|
739
|
+
"$schema": "https://schemas.dfos.com/post/v1",
|
|
740
|
+
"format": "short-post",
|
|
741
|
+
"title": "Hello World (edited)",
|
|
742
|
+
"body": "Updated content.",
|
|
743
|
+
"createdByDID": "did:dfos:e3vvtck42d4eacdnzvtrn6"
|
|
682
744
|
}
|
|
683
745
|
```
|
|
684
746
|
|
|
685
747
|
Document CID (edited):
|
|
686
748
|
|
|
687
749
|
```
|
|
688
|
-
|
|
750
|
+
bafyreidh7e36cvwy3uw5ypitcqk7uoktbkkkj7e6hxhky4o75rxn7kxilu
|
|
689
751
|
```
|
|
690
752
|
|
|
691
753
|
Content Update CID:
|
|
692
754
|
|
|
693
755
|
```
|
|
694
|
-
|
|
756
|
+
bafyreih6e5cbjitpozhzhgmfktmiohmxyn3ucwhqd3mjixizvwmlhv7hm4
|
|
757
|
+
```
|
|
758
|
+
|
|
759
|
+
### Content Chain Verified State
|
|
760
|
+
|
|
761
|
+
```
|
|
762
|
+
Content ID: a82z92a3hndk6c97thcrn8
|
|
763
|
+
Genesis CID: bafyreiaedhjq64aajpwociahl5w37j6uoxr5mojoq5dnah6fpvxr5d4lxu
|
|
764
|
+
Head CID: bafyreih6e5cbjitpozhzhgmfktmiohmxyn3ucwhqd3mjixizvwmlhv7hm4
|
|
695
765
|
```
|
|
696
766
|
|
|
697
767
|
---
|
|
@@ -707,7 +777,7 @@ Given the artifacts above, verify:
|
|
|
707
777
|
→ ba421e272fad4f941c221e47f87d9253bdc04f7d4ad2625ae667ab9f0688ce32
|
|
708
778
|
```
|
|
709
779
|
|
|
710
|
-
2. **Genesis JWS verify**: split token on `.`, take first two segments as signing input (UTF-8 bytes), base64url-decode third segment as 64-byte signature, `ed25519.verify(signature, signingInputBytes, publicKey)` → true.
|
|
780
|
+
2. **Genesis JWS verify**: split token on `.`, take first two segments as signing input (UTF-8 bytes), base64url-decode third segment as 64-byte signature, `ed25519.verify(signature, signingInputBytes, publicKey)` → true. The header contains `cid` alongside `alg`, `typ`, and `kid`.
|
|
711
781
|
|
|
712
782
|
3. **Genesis CID**: base64url-decode JWS payload → parse JSON → dag-cbor canonical encode → SHA-256 → CIDv1 → should be:
|
|
713
783
|
|
|
@@ -731,21 +801,23 @@ Given the artifacts above, verify:
|
|
|
731
801
|
did:dfos:e3vvtck42d4eacdnzvtrn6#key_ez9a874tckr3dv933d3ckd
|
|
732
802
|
```
|
|
733
803
|
|
|
734
|
-
8. **Document CID**: dag-cbor canonical encode the
|
|
804
|
+
8. **Document CID**: dag-cbor canonical encode the flat content object → SHA-256 → CIDv1 → should be:
|
|
735
805
|
|
|
736
806
|
```
|
|
737
|
-
|
|
807
|
+
bafyreihzwuoupfg3dxip6xmgzmxsywyii2jeoxxzbgx3zxm2in7knoi3g4
|
|
738
808
|
```
|
|
739
809
|
|
|
740
|
-
9. **Content
|
|
810
|
+
9. **Content operation `did` field**: verify the `did` field in each content operation matches the `kid` DID in the JWS header
|
|
741
811
|
|
|
742
|
-
10. **
|
|
812
|
+
10. **Content chain integrity**: update's `previousOperationCID` matches create's operation CID
|
|
813
|
+
|
|
814
|
+
11. **Chain completeness**: all operation CIDs, DID derivation, key rotation, and content chain linkage verified end-to-end.
|
|
743
815
|
|
|
744
816
|
---
|
|
745
817
|
|
|
746
818
|
## Source and Verification
|
|
747
819
|
|
|
748
|
-
All source lives in [`packages/dfos-protocol/`](https://github.com/metalabel/dfos/tree/main/packages/dfos-protocol) — self-contained, zero monorepo dependencies.
|
|
820
|
+
All source lives in [`packages/dfos-protocol/`](https://github.com/metalabel/dfos/tree/main/packages/dfos-protocol) — self-contained, zero monorepo dependencies. 235 checks across 5 languages.
|
|
749
821
|
|
|
750
822
|
- [`crypto/ed25519`](https://github.com/metalabel/dfos/blob/main/packages/dfos-protocol/src/crypto/ed25519.ts) — `createNewEd25519Keypair`, `importEd25519Keypair`, `signPayloadEd25519`, `isValidEd25519Signature`
|
|
751
823
|
- [`crypto/jws`](https://github.com/metalabel/dfos/blob/main/packages/dfos-protocol/src/crypto/jws.ts) — `createJws`, `verifyJws`, `decodeJwsUnsafe`
|
|
@@ -756,23 +828,26 @@ All source lives in [`packages/dfos-protocol/`](https://github.com/metalabel/dfo
|
|
|
756
828
|
- [`chain/schemas`](https://github.com/metalabel/dfos/blob/main/packages/dfos-protocol/src/chain/schemas.ts) — `IdentityOperation`, `ContentOperation`, `MultikeyPublicKey`, `VerifiedIdentity`
|
|
757
829
|
- [`chain/identity-chain`](https://github.com/metalabel/dfos/blob/main/packages/dfos-protocol/src/chain/identity-chain.ts) — `signIdentityOperation`, `verifyIdentityChain`
|
|
758
830
|
- [`chain/content-chain`](https://github.com/metalabel/dfos/blob/main/packages/dfos-protocol/src/chain/content-chain.ts) — `signContentOperation`, `verifyContentChain`
|
|
759
|
-
- [`chain/derivation`](https://github.com/metalabel/dfos/blob/main/packages/dfos-protocol/src/chain/derivation.ts) — `deriveChainIdentifier`
|
|
831
|
+
- [`chain/derivation`](https://github.com/metalabel/dfos/blob/main/packages/dfos-protocol/src/chain/derivation.ts) — `deriveChainIdentifier`, `deriveContentId`
|
|
832
|
+
- [`chain/beacon`](https://github.com/metalabel/dfos/blob/main/packages/dfos-protocol/src/chain/beacon.ts) — `signBeacon`, `verifyBeacon`
|
|
833
|
+
- [`chain/countersign`](https://github.com/metalabel/dfos/blob/main/packages/dfos-protocol/src/chain/countersign.ts) — `signCountersignature`, `verifyCountersignature`
|
|
834
|
+
- [`merkle/tree`](https://github.com/metalabel/dfos/blob/main/packages/dfos-protocol/src/merkle/tree.ts) — `buildMerkleTree`, `hashLeaf`
|
|
835
|
+
- [`merkle/proof`](https://github.com/metalabel/dfos/blob/main/packages/dfos-protocol/src/merkle/proof.ts) — `generateMerkleProof`, `verifyMerkleProof`
|
|
760
836
|
|
|
761
837
|
### Related Specifications
|
|
762
838
|
|
|
763
839
|
- [DID Method: `did:dfos`](https://protocol.dfos.com/did-method) — W3C DID method specification for identity chains
|
|
764
|
-
- [Content Model](https://protocol.dfos.com/content-model) — Standard content schemas (post, profile) for
|
|
765
|
-
- [Registry API](https://protocol.dfos.com/registry-api) — HTTP API for chain storage and resolution
|
|
840
|
+
- [Content Model](https://protocol.dfos.com/content-model) — Standard content schemas (post, profile) for document content objects
|
|
766
841
|
|
|
767
842
|
### Cross-Language Verification
|
|
768
843
|
|
|
769
844
|
| Language | Tests | Source |
|
|
770
845
|
| ---------- | ----- | ---------------------------------------------------------------------------------------------------- |
|
|
771
|
-
| TypeScript |
|
|
772
|
-
| Python |
|
|
773
|
-
| Go |
|
|
774
|
-
| Rust |
|
|
775
|
-
| Swift |
|
|
846
|
+
| TypeScript | 149 | [`tests/`](https://github.com/metalabel/dfos/tree/main/packages/dfos-protocol/tests) |
|
|
847
|
+
| Python | 48 | [`verify/python/`](https://github.com/metalabel/dfos/tree/main/packages/dfos-protocol/verify/python) |
|
|
848
|
+
| Go | 13 | [`verify/go/`](https://github.com/metalabel/dfos/tree/main/packages/dfos-protocol/verify/go) |
|
|
849
|
+
| Rust | 13 | [`verify/rust/`](https://github.com/metalabel/dfos/tree/main/packages/dfos-protocol/verify/rust) |
|
|
850
|
+
| Swift | 12 | [`verify/swift/`](https://github.com/metalabel/dfos/tree/main/packages/dfos-protocol/verify/swift) |
|
|
776
851
|
|
|
777
852
|
---
|
|
778
853
|
|
|
@@ -780,7 +855,3 @@ All source lives in [`packages/dfos-protocol/`](https://github.com/metalabel/dfo
|
|
|
780
855
|
|
|
781
856
|
- **Vinny Bellavia** — [stcisgood.com](https://stcisgood.com)
|
|
782
857
|
- **Allison Clift-Jennings** — [Jura Labs](https://juralabs.com)
|
|
783
|
-
|
|
784
|
-
---
|
|
785
|
-
|
|
786
|
-
Yancey · Ilya · Brandon · Lena
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @metalabel/dfos-protocol
|
|
2
2
|
|
|
3
|
-
Ed25519 signed
|
|
3
|
+
Cryptographic identity and content proof — Ed25519 signed chains, content-addressed CIDs, W3C DIDs. The protocol knows about keys and document hashes. It doesn't know about posts, profiles, or any application concept.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -15,17 +15,17 @@ npm install @metalabel/dfos-protocol
|
|
|
15
15
|
import { verifyContentChain, verifyIdentityChain } from '@metalabel/dfos-protocol/chain';
|
|
16
16
|
// Crypto primitives
|
|
17
17
|
import { createJws, dagCborCanonicalEncode, verifyJws } from '@metalabel/dfos-protocol/crypto';
|
|
18
|
-
//
|
|
19
|
-
import {
|
|
18
|
+
// Merkle trees
|
|
19
|
+
import { buildMerkleTree, verifyMerkleProof } from '@metalabel/dfos-protocol/merkle';
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
## Subpath Exports
|
|
23
23
|
|
|
24
|
-
| Export
|
|
25
|
-
|
|
|
26
|
-
| `@metalabel/dfos-protocol/chain`
|
|
27
|
-
| `@metalabel/dfos-protocol/crypto`
|
|
28
|
-
| `@metalabel/dfos-protocol/
|
|
24
|
+
| Export | Description |
|
|
25
|
+
| --------------------------------- | ----------------------------------------------------------------------- |
|
|
26
|
+
| `@metalabel/dfos-protocol/chain` | Identity and content chain signing, verification, beacons, countersigns |
|
|
27
|
+
| `@metalabel/dfos-protocol/crypto` | Ed25519, JWS, JWT, dag-cbor, base64url, ID generation |
|
|
28
|
+
| `@metalabel/dfos-protocol/merkle` | SHA-256 binary merkle tree, inclusion proofs |
|
|
29
29
|
|
|
30
30
|
## Specifications
|
|
31
31
|
|
|
@@ -33,9 +33,7 @@ import { createRegistryServer } from '@metalabel/dfos-protocol/registry';
|
|
|
33
33
|
| -------------------------------------- | -------------------------------------------------------------- |
|
|
34
34
|
| [PROTOCOL.md](./PROTOCOL.md) | Core protocol — chains, signatures, verification, test vectors |
|
|
35
35
|
| [DID-METHOD.md](./DID-METHOD.md) | W3C DID method specification for `did:dfos` |
|
|
36
|
-
| [CONTENT-MODEL.md](./CONTENT-MODEL.md) | Standard content schemas (post, profile,
|
|
37
|
-
| [REGISTRY-API.md](./REGISTRY-API.md) | HTTP API for chain storage and resolution |
|
|
38
|
-
| [openapi.yaml](./openapi.yaml) | OpenAPI 3.1 machine-readable registry spec |
|
|
36
|
+
| [CONTENT-MODEL.md](./CONTENT-MODEL.md) | Standard content schemas (post, profile, manifest) |
|
|
39
37
|
|
|
40
38
|
## Examples
|
|
41
39
|
|