@metalabel/dfos-protocol 0.0.1 → 0.0.3

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/PROTOCOL.md CHANGED
@@ -1,45 +1,33 @@
1
- # DFOS Protocol: Complete Reference
1
+ # DFOS Protocol
2
2
 
3
- - **Date**: 2026-03-10
4
- - **Status**: Implemented and tested — TypeScript + Go + Python + Rust + Swift cross-language verification. This spec is currently under review. Discuss the DFOS protocol in the [clear.txt](https://clear.dfos.com) space.
5
- - **Source**: `packages/dfos-protocol` (self-contained, zero monorepo dependencies, OSS-ready)
6
- - **Gist**: https://gist.github.com/bvalosek/ed4c96fd4b841302de544ffaee871648 (synced from this file)
3
+ Verifiable identity and content chains — Ed25519 signatures, content-addressed CIDs, W3C DIDs. Cross-language verification in TypeScript, Go, Python, Rust, and Swift.
7
4
 
8
- ---
9
-
10
- ## Philosophy
11
-
12
- DFOS is a dark forest operating system. Content lives in private spaces — visible only to members, governed by the communities that create it. The forest floor is dark by default.
13
-
14
- But the cryptographic proof layer is public and verifiable. Every piece of content, every identity, every edit has a signed chain of commitments that anyone can independently verify. You don't need to trust the platform. You don't need access to the database. You need a public key and a chain of JWS tokens.
15
-
16
- If you have content — from the official app, from an API export, from a screenshot someone sent you, from a pirate mirror, from anywhere — you can verify it's authentic. Hash the content, check the CID, walk the chain, verify the signature. The content is dark; the proof is light.
5
+ This spec is under active review. Discuss it in the [clear.txt](https://clear.dfos.com) space on DFOS.
17
6
 
18
- 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.
7
+ [Source](https://github.com/metalabel/dfos/tree/main/packages/dfos-protocol) · [npm](https://www.npmjs.com/package/@metalabel/dfos-protocol) · [Gist](https://gist.github.com/bvalosek/ed4c96fd4b841302de544ffaee871648)
19
8
 
20
- 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.
9
+ ---
21
10
 
22
- 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.
11
+ ## Philosophy
23
12
 
24
- ---
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.
25
14
 
26
- This document is a complete, self-contained reference for the DFOS protocol. All artifacts are deterministic and reproducible from fixed seeds. An independent implementer can verify every value using standard Ed25519 + dag-cbor libraries.
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.
27
16
 
28
- **To regenerate**: `pnpm --filter @metalabel/dfos-protocol exec vitest run tests/protocol-reference.spec.ts`
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.
29
18
 
30
19
  ---
31
20
 
32
21
  ## Protocol Overview
33
22
 
34
- The DFOS protocol has three layers:
23
+ The DFOS protocol has two layers:
35
24
 
36
25
  | Layer | Concern |
37
26
  | --------------------- | ---------------------------------------------------------------------------- |
38
27
  | **Crypto core** | Identity chains + content chains — Ed25519 signatures, JWS tokens, CID links |
39
28
  | **Document envelope** | Standard wrapper: `content` + `baseDocumentCID` + `createdByDID` + timestamp |
40
- | **Content schemas** | JSON Schema definitions for what goes inside `content` (post, profile, etc.) |
41
29
 
42
- The crypto core is the trust boundary — everything below it is cryptographically verified. The document envelope provides structural metadata (attribution, edit lineage, timestamps). Content schemas define the application-level semantics.
30
+ The crypto core is the trust boundary — everything below it is cryptographically verified. The document envelope provides structural metadata (attribution, edit lineage, timestamps). What goes inside the envelope's `content` field is application-defined — see the [DFOS Content Model](https://protocol.dfos.com/content-model) for the standard schema library.
43
31
 
44
32
  ### Crypto Core: Two Chain Types
45
33
 
@@ -60,7 +48,7 @@ Every document committed to by a content chain uses a standard envelope, defined
60
48
  ```json
61
49
  {
62
50
  "content": { "$schema": "https://schemas.dfos.com/post/v1", ... },
63
- "baseDocumentCID": "bafyrei..." | null,
51
+ "baseDocumentCID": null,
64
52
  "createdByDID": "did:dfos:...",
65
53
  "createdAt": "2026-03-07T00:02:00.000Z"
66
54
  }
@@ -69,27 +57,29 @@ Every document committed to by a content chain uses a standard envelope, defined
69
57
  | Field | Type | Description |
70
58
  | ----------------- | ------------ | --------------------------------------------------------------------------- |
71
59
  | `content` | object | Application-defined content — must include `$schema` URI, opaque to chains |
72
- | `baseDocumentCID` | string\|null | CID of the previous document version (edit lineage). Null for first version |
60
+ | `baseDocumentCID` | string\|null | Optional CID of a prior document version. Semantics are application-defined |
73
61
  | `createdByDID` | string | DID of the identity that created this document version |
74
62
  | `createdAt` | ISO 8601 | When this document version was created |
75
63
 
76
- The `documentCID` in a content chain operation is `CID(dagCborEncode(envelope))`. The envelope provides attribution and edit history at the protocol level. The `content` field is where application-defined JSON Schema types live. 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.
77
-
78
- ### Content Schemas
79
-
80
- The `content` field inside the document envelope is validated by JSON Schema. The protocol ships a standard library of schemas (post, profile) — see [Standard Document Schemas](#standard-document-schemas). These are conventions, not requirements. Any implementation can define custom schemas.
64
+ 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.
81
65
 
82
66
  ### Addressing
83
67
 
84
68
  Three canonical representations:
85
69
 
86
- | Thing | Form | Example |
87
- | ---------------------- | -------------------------- | ------------------------------------------------------------- |
88
- | Operation or document | CID (dag-cbor + SHA-256) | `bafyreibanjpgcqffcfhr4sptzjfthh5szohhbo5tjfulemkw7uhden5uqy` |
89
- | Entity (content chain) | `<hash>` (bare, no prefix) | `67t27rzc83v7c22n9t6z7c` |
90
- | Identity (key chain) | `did:dfos:<hash>` | `did:dfos:e3vvtck42d4eacdnzvtrn6` |
70
+ | Thing | Form | Example |
71
+ | --------------------- | -------------------------- | --------------------------------- |
72
+ | Operation or document | CID (dag-cbor + SHA-256) | See below |
73
+ | Content chain | `<hash>` (bare, no prefix) | `67t27rzc83v7c22n9t6z7c` |
74
+ | Identity chain | `did:dfos:<hash>` | `did:dfos:e3vvtck42d4eacdnzvtrn6` |
91
75
 
92
- Operations and documents are CIDs — standard IPLD content addresses. Entities and identities are derived identifiers — `customAlpha(SHA-256(genesis CID bytes))`. Same derivation for both. Identity chains prepend `did:dfos:` (W3C DID spec). Entity identifiers are bare — just the 22-char hash, no prefix.
76
+ Example CID:
77
+
78
+ ```
79
+ bafyreibanjpgcqffcfhr4sptzjfthh5szohhbo5tjfulemkw7uhden5uqy
80
+ ```
81
+
82
+ 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
83
 
94
84
  Application code may add prefixes for routing (e.g., `post_xxxx`) — these are strippable semantic sugar, not part of the protocol identifier.
95
85
 
@@ -99,17 +89,20 @@ Application code may add prefixes for routing (e.g., `post_xxxx`) — these are
99
89
 
100
90
  ### Commitment Scheme
101
91
 
102
- The protocol requires a **deterministic payload commitment**: given the same logical operation, the commitment (CID) MUST be identical regardless of implementation language or platform. The commitment scheme is **dag-cbor canonical encoding + SHA-256 + CIDv1**. This is not a recommendation — it is the protocol.
92
+ Both operations and documents are content-addressed via **CID** (`dagCborCanonicalEncode(payload)` SHA-256 CIDv1). Operations are additionally signed via **JWS**.
103
93
 
104
- Implementations MUST use dag-cbor canonical encoding as defined by the [IPLD dag-cbor codec specification](https://ipld.io/specs/codecs/dag-cbor/spec/). Raw JSON serialization, pretty-printed JSON, or any non-canonical encoding MUST NOT be used for CID derivation. The dag-cbor hex test vectors in this document allow byte-level verification of any implementation's canonical encoding.
94
+ | Representation | Encoding | Purpose |
95
+ | -------------- | -------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
96
+ | CID | `dagCborCanonicalEncode(payload)` → SHA-256 → CIDv1 | Deterministic content addressing for operations and documents |
97
+ | JWS | `base64url(JSON.stringify(header))` + `.` + `base64url(JSON.stringify(payload))` → EdDSA signature covers both | Signature verification for operations |
105
98
 
106
- **JWS signing vs CID derivation are intentionally different representations of the same payload.** JWS signs `base64url(JSON.stringify(payload))` the UTF-8 bytes of the JSON serialization. CID commits to `dagCborCanonicalEncode(payload)` the dag-cbor canonical encoding of the parsed object. These produce different bytes from the same logical data. This is by design: JWS uses standard JSON for maximum interoperability with existing JWS libraries, while CID uses dag-cbor for deterministic content addressing.
99
+ 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
100
 
108
101
  ### Chain Validity
109
102
 
110
103
  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
104
 
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. In DFOS's custodial model, forks are prevented by database-level advisory locks. A non-custodial implementation would need its own fork resolution strategy (e.g., longest chain, first-seen, application-specified preference).
105
+ **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
106
 
114
107
  **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
108
 
@@ -121,11 +114,9 @@ This is a self-sovereign invariant: the identity chain defines its own valid sig
121
114
 
122
115
  ### Content Chain Signer Model
123
116
 
124
- Content chain verification requires a **valid EdDSA signature** nothing more. The protocol does not define which identities may sign operations on a content chain, does not track or enforce key roles, and does not restrict a chain to a single signer.
125
-
126
- The signing key is resolved via the `kid` (DID URL), which references a key on an external identity. The content chain verifier delegates key resolution to the caller via a `resolveKey` callback — the protocol does not prescribe how to look up an identity's current key state.
117
+ 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.
127
118
 
128
- This is a deliberate asymmetry with identity chains. Identity chains are self-sovereign — they define their own valid signers internally. Content chains are externally signed — the signing authority model is entirely an application concern, delegated through `resolveKey`. A content chain with operations signed by multiple different identities is valid at the protocol level, as long as each operation's signature verifies against the resolved key.
119
+ 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.
129
120
 
130
121
  **What the protocol enforces:**
131
122
 
@@ -137,37 +128,23 @@ This is a deliberate asymmetry with identity chains. Identity chains are self-so
137
128
  - Which identities are authorized to sign operations on a given chain
138
129
  - Which key role (auth, assert, controller) the signing key must have
139
130
  - Whether a chain must have a single signer or may have multiple signers
140
- - Ownership or attribution semantics between signers and entities
141
-
142
- ### Terminal States
131
+ - Ownership or attribution semantics between signers and content chains
143
132
 
144
- **`delete` is the only terminal state.** No valid operations may follow a delete in either chain type. An implementation MUST reject any operation that appears after a delete.
133
+ ### Terminal States and Special Operations
145
134
 
146
- `delete` is a terminal marker that prevents future operations on the chain but does NOT remove data. The complete chain including all prior operations and their signatures — MUST remain intact for verification. Any party holding the chain can still walk it, verify every signature, and confirm the history up to and including the delete. Data removal (e.g., purging content from storage) is an application-layer concern, not a protocol operation.
135
+ **`delete` is the only terminal state.** No valid operations may follow a delete. An implementation MUST reject any operation after a delete. Delete prevents future operations but does NOT remove data the complete chain remains intact for verification. Data removal is an application concern.
147
136
 
148
- ### Controller Key Requirement
137
+ **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.
149
138
 
150
- `update` operations on identity chains MUST include at least one controller key. Validation MUST reject any `update` with an empty `controllerKeys` array. This ensures that an identity always has a path forward if decommissioning is intended, `delete` is the correct terminal operation.
151
-
152
- ### Content-Null Semantics
153
-
154
- An `update` operation on a content chain with `documentCID: null` means **the entity exists but its current content is cleared**. This is not a delete — the chain continues, and a subsequent update can set content again. Think of it as "unpublish" rather than "destroy."
139
+ **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.
155
140
 
156
141
  ### `typ` Header
157
142
 
158
- The JWS `typ` header (`did:dfos:identity-op`, `did:dfos:content-op`) is advisory — it aids routing and dispatch but is not a security-critical field. Verification checks the signature and chain integrity, not the `typ` value. Implementations SHOULD validate `typ` for correctness but MUST NOT rely on it for security decisions.
159
-
160
- ### JWT `kid` vs Operation `kid`
161
-
162
- JWT tokens (for device auth, MCP sessions, etc.) use `kid` as a simple key identifier for lookup — e.g., `key_ez9a874tckr3dv933d3ckd`. This does NOT follow the same DID URL convention used in operation JWS headers. Operation `kid` uses bare key ID for identity genesis and DID URL (`did:dfos:xxx#key_id`) for everything else. JWT `kid` is always a bare key ID — the JWT's `sub` claim carries the DID separately.
163
-
164
- ### ID Modulo Bias
165
-
166
- The ID encoding uses `byte % 19` where each byte ranges 0-255. Since 256 is not evenly divisible by 19, values 0-8 (alphabet positions) appear with probability ~5.26% while values 9-18 appear with probability ~5.22%. This is a ~0.3% bias — not security-relevant for identifiers but acknowledged here for completeness. A rejection-sampling approach (retry if `byte >= 247`) would eliminate the bias entirely.
143
+ The JWS `typ` header (`did:dfos:identity-op`, `did:dfos:content-op`) aids routing but is not security-critical. Implementations SHOULD validate it but MUST NOT rely on it for security decisions.
167
144
 
168
145
  ### Operation Field Limits
169
146
 
170
- The protocol defines maximum sizes for all operation fields. These are abuse-prevention ceilings — deliberately loose, not tight validation. Implementations MUST reject operations that exceed these bounds. Implementations MAY impose stricter limits.
147
+ 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.
171
148
 
172
149
  | Field | Max | Rationale |
173
150
  | -------------------------------------------- | --------- | -------------------------------------- |
@@ -188,84 +165,6 @@ The protocol does NOT limit:
188
165
 
189
166
  ---
190
167
 
191
- ## Standard Document Schemas
192
-
193
- The crypto core commits to `documentCID` values without inspecting their contents. The document envelope provides structural metadata. The **content** inside the envelope is where JSON Schema validation applies.
194
-
195
- The protocol ships a standard library of content schemas as JSON Schema (draft 2020-12) definitions. These are not required — any implementation can define its own content types. They are provided as a starting point for content built on the DFOS protocol, and they are what DFOS uses internally.
196
-
197
- ### Schema Convention
198
-
199
- Documents declare their type via a `$schema` field pointing to a schema URI:
200
-
201
- ```json
202
- {
203
- "$schema": "https://schemas.dfos.com/post/v1",
204
- "format": "short-post",
205
- "body": "Hello world."
206
- }
207
- ```
208
-
209
- Because the `$schema` field is part of the document, it is behind the `documentCID` — cryptographically committed in the content chain. Any verifier can resolve the document, read `$schema`, and validate against the schema.
210
-
211
- ### Schema Evolution
212
-
213
- Schemas are versioned via the URI path (`/post/v1`, `/post/v2`). Evolution rules:
214
-
215
- - **Strictly additive within a version** — new optional fields can be added to an existing version at any time without breaking existing documents
216
- - **Breaking changes require a new version** — removing fields, changing types, or adding new required fields means a new version URI
217
- - **Implementations declare which versions they understand** — a registry or application can accept `post/v1` and `post/v2` simultaneously, or only `post/v1`
218
-
219
- ### Standard Schemas
220
-
221
- Schema files live in `schemas/` in the protocol package. Each is a standalone JSON Schema (draft 2020-12).
222
-
223
- #### Post (`https://schemas.dfos.com/post/v1`)
224
-
225
- The primary content type. Covers short posts, long-form posts, comments, and replies via the `format` discriminator.
226
-
227
- | Field | Type | Required | Description |
228
- | ------------- | -------- | -------- | ---------------------------------------------------------------------------------- |
229
- | `$schema` | string | yes | `"https://schemas.dfos.com/post/v1"` |
230
- | `format` | enum | yes | `"short-post"`, `"long-post"`, `"comment"`, `"reply"` — immutable, set at creation |
231
- | `title` | string | no | Post title (typically for long-post format) |
232
- | `body` | string | no | Post body content |
233
- | `cover` | media | no | Cover image |
234
- | `attachments` | media[] | no | Attached media objects |
235
- | `topics` | string[] | no | Topic names (stored as names for portability) |
236
-
237
- #### Profile (`https://schemas.dfos.com/profile/v1`)
238
-
239
- The displayable identity for any agent, person, group, or space.
240
-
241
- | Field | Type | Required | Description |
242
- | ------------- | ------ | -------- | --------------------------------------- |
243
- | `$schema` | string | yes | `"https://schemas.dfos.com/profile/v1"` |
244
- | `name` | string | no | Display name |
245
- | `description` | string | no | Short bio or description |
246
- | `avatar` | media | no | Avatar image |
247
- | `banner` | media | no | Banner image |
248
- | `background` | media | no | Background image |
249
-
250
- ### Media Object
251
-
252
- Several schemas reference media objects. The standard representation:
253
-
254
- ```json
255
- {
256
- "id": "media_abc123",
257
- "uri": "https://cdn.example.com/media/abc123.jpg"
258
- }
259
- ```
260
-
261
- `id` is required (opaque identifier). `uri` is optional.
262
-
263
- ### Custom Schemas
264
-
265
- Any implementation can define custom document schemas following the same pattern — a JSON Schema with a `$schema` const field pointing to a unique URI. The protocol will commit to the document via CID regardless of what's inside. The standard schemas are conventions, not constraints.
266
-
267
- ---
268
-
269
168
  ## Standards and Dependencies
270
169
 
271
170
  | Component | Standard / Library |
@@ -275,7 +174,6 @@ Any implementation can define custom document schemas following the same pattern
275
174
  | Key encoding | W3C Multikey (multicodec `0xed01` + base58btc multibase) |
276
175
  | Signed envelopes | JWS Compact Serialization (RFC 7515) with `alg: "EdDSA"` |
277
176
  | Content addressing | CIDv1 with dag-cbor codec (`0x71`) + SHA-256 multihash (`0x12`) |
278
- | Auth tokens | JWT (RFC 7519) with `alg: "EdDSA"` |
279
177
  | ID encoding | SHA-256 → custom 19-char alphabet, 22 characters |
280
178
 
281
179
  ### ID Alphabet
@@ -286,7 +184,7 @@ Length: 22 characters
286
184
  Entropy: ~93.4 bits (19^22)
287
185
  ```
288
186
 
289
- Process: `SHA-256(input) → for each of first 22 bytes: alphabet[byte % 19]`
187
+ Process: `SHA-256(input) → for each of first 22 bytes: alphabet[byte % 19]`. The modulo introduces a ~0.3% bias (256 is not evenly divisible by 19) — not security-relevant for identifiers.
290
188
 
291
189
  DIDs: `did:dfos:` + 22-char ID derived from `SHA-256(genesis CID raw bytes)`
292
190
  Key IDs: `key_` + 22-char ID. Convention: derive from public key hash (`key_` + `customAlpha(SHA-256(publicKey))`), making key IDs deterministic and verifiable. Not a protocol requirement — key IDs can be any string.
@@ -414,7 +312,7 @@ DID: did:dfos:e3vvtck42d4eacdnzvtrn6
414
312
  createdAt: string,
415
313
  note: string | null }
416
314
 
417
- // Permanent entity destruction
315
+ // Permanent destruction
418
316
  { version: 1, type: "delete",
419
317
  previousOperationCID: string,
420
318
  createdAt: string,
@@ -443,11 +341,18 @@ token = signingInput + "." + base64url(signature)
443
341
 
444
342
  ### kid Rules
445
343
 
446
- | Context | kid format | Example |
447
- | ------------------------- | ----------- | ------------------------------------------------------------ |
448
- | Identity create (genesis) | Bare key ID | `key_r9ev34fvc23z999veaaft8` |
449
- | Identity update/delete | DID URL | `did:dfos:e3vvtck42d4eacdnzvtrn6#key_r9ev34fvc23z999veaaft8` |
450
- | All content ops | DID URL | `did:dfos:e3vvtck42d4eacdnzvtrn6#key_ez9a874tckr3dv933d3ckd` |
344
+ | Context | kid format | Example |
345
+ | ------------------------- | ----------- | ---------------------------- |
346
+ | Identity create (genesis) | Bare key ID | `key_r9ev34fvc23z999veaaft8` |
347
+ | Identity update/delete | DID URL | See below |
348
+ | All content ops | DID URL | See below |
349
+
350
+ DID URL examples:
351
+
352
+ ```
353
+ did:dfos:e3vvtck42d4eacdnzvtrn6#key_r9ev34fvc23z999veaaft8
354
+ did:dfos:e3vvtck42d4eacdnzvtrn6#key_ez9a874tckr3dv933d3ckd
355
+ ```
451
356
 
452
357
  ### `cid` Header
453
358
 
@@ -465,11 +370,7 @@ Every operation JWS (identity-op and content-op) includes a `cid` field in the p
465
370
  - `header.cid` is missing
466
371
  - `header.cid` does not match the derived CID
467
372
 
468
- This provides three benefits:
469
-
470
- - **Pre-verification routing**: The operation CID can be read from the header without parsing the payload or running dag-cbor encoding
471
- - **Cross-implementation consistency**: A CID mismatch between header and derived value immediately surfaces dag-cbor encoding disagreements across implementations
472
- - **Self-documenting tokens**: Each JWS token declares its content-addressed identity
373
+ A CID mismatch between header and derived value immediately surfaces dag-cbor encoding disagreements across implementations.
473
374
 
474
375
  Note: JWT tokens (device auth) do NOT include a `cid` header — this field is specific to operation JWS tokens.
475
376
 
@@ -521,7 +422,7 @@ Where `idEncode` is the 19-char alphabet encoding described above.
521
422
 
522
423
  ## Deterministic Reference Artifacts
523
424
 
524
- All values below are deterministic and reproducible. Private keys are derived from `SHA-256(UTF8("dfos-protocol-reference-key-N"))`.
425
+ 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"))`.
525
426
 
526
427
  ### Key 1 (Genesis Controller)
527
428
 
@@ -599,7 +500,11 @@ JWS Token:
599
500
  eyJhbGciOiJFZERTQSIsInR5cCI6ImRpZDpkZm9zOmlkZW50aXR5LW9wIiwia2lkIjoia2V5X3I5ZXYzNGZ2YzIzejk5OXZlYWFmdDgiLCJjaWQiOiJiYWZ5cmVpYmFuanBnY3FmZmNmaHI0c3B0empmdGhoNXN6b2hoYm81dGpmdWxlbWt3N3VoZGVuNXVxeSJ9.eyJ2ZXJzaW9uIjoxLCJ0eXBlIjoiY3JlYXRlIiwiYXV0aEtleXMiOlt7ImlkIjoia2V5X3I5ZXYzNGZ2YzIzejk5OXZlYWFmdDgiLCJ0eXBlIjoiTXVsdGlrZXkiLCJwdWJsaWNLZXlNdWx0aWJhc2UiOiJ6Nk1rcnpMTU53b0pTVjRQM1ljY1djYnRrOHZkOUx0Z01LbkxlYURMVXFMdUFTamIifV0sImFzc2VydEtleXMiOlt7ImlkIjoia2V5X3I5ZXYzNGZ2YzIzejk5OXZlYWFmdDgiLCJ0eXBlIjoiTXVsdGlrZXkiLCJwdWJsaWNLZXlNdWx0aWJhc2UiOiJ6Nk1rcnpMTU53b0pTVjRQM1ljY1djYnRrOHZkOUx0Z01LbkxlYURMVXFMdUFTamIifV0sImNvbnRyb2xsZXJLZXlzIjpbeyJpZCI6ImtleV9yOWV2MzRmdmMyM3o5OTl2ZWFhZnQ4IiwidHlwZSI6Ik11bHRpa2V5IiwicHVibGljS2V5TXVsdGliYXNlIjoiejZNa3J6TE1Od29KU1Y0UDNZY2NXY2J0azh2ZDlMdGdNS25MZWFETFVxTHVBU2piIn1dLCJjcmVhdGVkQXQiOiIyMDI2LTAzLTA3VDAwOjAwOjAwLjAwMFoifQ.EDryDK1uvtix-17cHun9t6MacFIx2rMmMF1QLzfD5TFlSsOvMcue97pCgGn3CXeLVFtVxgpCoh0kGSXioKKzAw
600
501
  ```
601
502
 
602
- Operation CID: `bafyreibanjpgcqffcfhr4sptzjfthh5szohhbo5tjfulemkw7uhden5uqy`
503
+ Operation CID:
504
+
505
+ ```
506
+ bafyreibanjpgcqffcfhr4sptzjfthh5szohhbo5tjfulemkw7uhden5uqy
507
+ ```
603
508
 
604
509
  **Derived DID: `did:dfos:e3vvtck42d4eacdnzvtrn6`**
605
510
 
@@ -660,7 +565,11 @@ JWS Token:
660
565
  eyJhbGciOiJFZERTQSIsInR5cCI6ImRpZDpkZm9zOmlkZW50aXR5LW9wIiwia2lkIjoiZGlkOmRmb3M6ZTN2dnRjazQyZDRlYWNkbnp2dHJuNiNrZXlfcjlldjM0ZnZjMjN6OTk5dmVhYWZ0OCIsImNpZCI6ImJhZnlyZWljeW00Y3lpZWRubGQ3M3NtYngzMnN6YWVpN3hkdWxxbjRnM3N0ZTVlMncydWxhanIzb3FtIn0.eyJ2ZXJzaW9uIjoxLCJ0eXBlIjoidXBkYXRlIiwicHJldmlvdXNPcGVyYXRpb25DSUQiOiJiYWZ5cmVpYmFuanBnY3FmZmNmaHI0c3B0empmdGhoNXN6b2hoYm81dGpmdWxlbWt3N3VoZGVuNXVxeSIsImF1dGhLZXlzIjpbeyJpZCI6ImtleV9lejlhODc0dGNrcjNkdjkzM2QzY2tkIiwidHlwZSI6Ik11bHRpa2V5IiwicHVibGljS2V5TXVsdGliYXNlIjoiejZNa2ZVZDY1SnJBaGZkZ0Z1TUNjY1U5VGhRdmpCMmZKQU1VSGt1dWFqRjk5MmdLIn1dLCJhc3NlcnRLZXlzIjpbeyJpZCI6ImtleV9lejlhODc0dGNrcjNkdjkzM2QzY2tkIiwidHlwZSI6Ik11bHRpa2V5IiwicHVibGljS2V5TXVsdGliYXNlIjoiejZNa2ZVZDY1SnJBaGZkZ0Z1TUNjY1U5VGhRdmpCMmZKQU1VSGt1dWFqRjk5MmdLIn1dLCJjb250cm9sbGVyS2V5cyI6W3siaWQiOiJrZXlfZXo5YTg3NHRja3IzZHY5MzNkM2NrZCIsInR5cGUiOiJNdWx0aWtleSIsInB1YmxpY0tleU11bHRpYmFzZSI6Ino2TWtmVWQ2NUpyQWhmZGdGdU1DY2NVOVRoUXZqQjJmSkFNVUhrdXVhakY5OTJnSyJ9XSwiY3JlYXRlZEF0IjoiMjAyNi0wMy0wN1QwMDowMTowMC4wMDBaIn0.MScuoBlgOK3j5QX9tFcw1ou0o4LgJziGJEsZ5pvqiBr1SagAyAv5h-wajQhtg8IP7dLlM0U4leW2iRra945cDg
661
566
  ```
662
567
 
663
- Operation CID: `bafyreicym4cyiednld73smbx32szaei7xdulqn4g3ste5e2w2ulajr3oqm`
568
+ Operation CID:
569
+
570
+ ```
571
+ bafyreicym4cyiednld73smbx32szaei7xdulqn4g3ste5e2w2ulajr3oqm
572
+ ```
664
573
 
665
574
  Post-rotation: DID unchanged (`did:dfos:e3vvtck42d4eacdnzvtrn6`), controller rotated to `key_ez9a874tckr3dv933d3ckd`.
666
575
 
@@ -682,7 +591,11 @@ Document (application layer):
682
591
  }
683
592
  ```
684
593
 
685
- Document CID: `bafyreifpvwuarml62sfogdpi2vlltvg2ev6o4xtw74zfud7cpkg7426zne`
594
+ Document CID:
595
+
596
+ ```
597
+ bafyreifpvwuarml62sfogdpi2vlltvg2ev6o4xtw74zfud7cpkg7426zne
598
+ ```
686
599
 
687
600
  Content Create JWS Header:
688
601
 
@@ -719,7 +632,11 @@ Content Create JWS Token:
719
632
  eyJhbGciOiJFZERTQSIsInR5cCI6ImRpZDpkZm9zOmNvbnRlbnQtb3AiLCJraWQiOiJkaWQ6ZGZvczplM3Z2dGNrNDJkNGVhY2RuenZ0cm42I2tleV9lejlhODc0dGNrcjNkdjkzM2QzY2tkIiwiY2lkIjoiYmFmeXJlaWE1ejd6eGtuYWU1ZHM3MmV1aWh1ZjJyZzNpeGw2dDRmYnpqZWZoY29nZzNucXBweW9ncXUifQ.eyJ2ZXJzaW9uIjoxLCJ0eXBlIjoiY3JlYXRlIiwiZG9jdW1lbnRDSUQiOiJiYWZ5cmVpZnB2d3Vhcm1sNjJzZm9nZHBpMnZsbHR2ZzJldjZvNHh0dzc0emZ1ZDdjcGtnNzQyNnpuZSIsImNyZWF0ZWRBdCI6IjIwMjYtMDMtMDdUMDA6MDI6MDAuMDAwWiIsIm5vdGUiOm51bGx9.t_DDkJ_TmNekIGUFO22G-W78QoE4XTg9LKQ4gzAQHaK3B6491Tir9b-wtp-hcwmENu2Hqnieqv5ASiqfFrEbDw
720
633
  ```
721
634
 
722
- Content Operation CID: `bafyreia5z7zxknae5ds72euihuf2rg3ixl6t4fbzjefhcogg3nqppyogqu`
635
+ Content Operation CID:
636
+
637
+ ```
638
+ bafyreia5z7zxknae5ds72euihuf2rg3ixl6t4fbzjefhcogg3nqppyogqu
639
+ ```
723
640
 
724
641
  ### Content Chain: Update
725
642
 
@@ -752,34 +669,16 @@ Updated document:
752
669
  }
753
670
  ```
754
671
 
755
- Document CID (edited): `bafyreieuo26zfmjxwpmw5jk6bqzqhvivxcbckgxtyeuc7ypf3p4sihgq4q`
756
- Content Update CID: `bafyreibb4lsvqmz4j76rsvhkqw3v2b4vp23t7dimm6vl5g5wlninvkemxq`
757
-
758
- ### EdDSA JWT
759
-
760
- Header:
672
+ Document CID (edited):
761
673
 
762
- ```json
763
- { "alg": "EdDSA", "typ": "JWT", "kid": "key_ez9a874tckr3dv933d3ckd" }
764
674
  ```
765
-
766
- Payload:
767
-
768
- ```json
769
- {
770
- "iss": "dfos",
771
- "sub": "did:dfos:e3vvtck42d4eacdnzvtrn6",
772
- "aud": "dfos-api",
773
- "exp": 1772902800,
774
- "iat": 1772899200,
775
- "jti": "session_ref_example_01"
776
- }
675
+ bafyreieuo26zfmjxwpmw5jk6bqzqhvivxcbckgxtyeuc7ypf3p4sihgq4q
777
676
  ```
778
677
 
779
- JWT Token:
678
+ Content Update CID:
780
679
 
781
680
  ```
782
- eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCIsImtpZCI6ImtleV9lejlhODc0dGNrcjNkdjkzM2QzY2tkIn0.eyJpc3MiOiJkZm9zIiwic3ViIjoiZGlkOmRmb3M6ZTN2dnRjazQyZDRlYWNkbnp2dHJuNiIsImF1ZCI6ImRmb3MtYXBpIiwiZXhwIjoxNzcyOTAyODAwLCJpYXQiOjE3NzI4OTkyMDAsImp0aSI6InNlc3Npb25fcmVmX2V4YW1wbGVfMDEifQ.zhKeXJHHF7a1-MwF4QoUTRptCplAwh20-rLnuWGDFT6uJheN4E_SA5NhqvMNflLHxd7h97gdaVnMZGE67SXEBA
681
+ bafyreibb4lsvqmz4j76rsvhkqw3v2b4vp23t7dimm6vl5g5wlninvkemxq
783
682
  ```
784
683
 
785
684
  ---
@@ -788,86 +687,83 @@ eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCIsImtpZCI6ImtleV9lejlhODc0dGNrcjNkdjkzM2QzY2tk
788
687
 
789
688
  Given the artifacts above, verify:
790
689
 
791
- 1. **Multikey decode**: `z6MkrzLMNwoJSV4P3YccWcbtk8vd9LtgMKnLeaDLUqLuASjb` → strip `z`, base58btc decode, strip `[0xed, 0x01]` → public key `ba421e272fad4f941c221e47f87d9253bdc04f7d4ad2625ae667ab9f0688ce32`
690
+ 1. **Multikey decode**: strip `z`, base58btc decode, strip `[0xed, 0x01]` prefix raw public key:
792
691
 
793
- 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. Note the header now contains `cid` alongside `alg`, `typ`, and `kid`.
692
+ ```
693
+ z6MkrzLMNwoJSV4P3YccWcbtk8vd9LtgMKnLeaDLUqLuASjb
694
+ → ba421e272fad4f941c221e47f87d9253bdc04f7d4ad2625ae667ab9f0688ce32
695
+ ```
794
696
 
795
- 3. **Genesis CID**: base64url-decode JWS payload parse JSON dag-cbor canonical encode SHA-256 CIDv1should be `bafyreibanjpgcqffcfhr4sptzjfthh5szohhbo5tjfulemkw7uhden5uqy`
697
+ 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`.
698
+
699
+ 3. **Genesis CID**: base64url-decode JWS payload → parse JSON → dag-cbor canonical encode → SHA-256 → CIDv1 → should be:
700
+
701
+ ```
702
+ bafyreibanjpgcqffcfhr4sptzjfthh5szohhbo5tjfulemkw7uhden5uqy
703
+ ```
796
704
 
797
705
  4. **CID header**: Verify each operation JWS header contains `cid` matching the derived operation CID
798
706
 
799
707
  5. **DID derivation**: take raw CID bytes of genesis CID → SHA-256 → first 22 bytes → `byte % 19` → alphabet lookup → should be `e3vvtck42d4eacdnzvtrn6` → DID = `did:dfos:e3vvtck42d4eacdnzvtrn6`
800
708
 
801
- 6. **Rotation JWS**: kid = `did:dfos:e3vvtck42d4eacdnzvtrn6#key_r9ev34fvc23z999veaaft8` — signed by OLD controller key (key 1). Verify with key 1's public key.
709
+ 6. **Rotation JWS**: signed by OLD controller key (key 1). Verify with key 1's public key. kid:
802
710
 
803
- 7. **Content create JWS**: kid = `did:dfos:e3vvtck42d4eacdnzvtrn6#key_ez9a874tckr3dv933d3ckd` — signed by NEW controller key (key 2, post-rotation). Verify with key 2's public key.
711
+ ```
712
+ did:dfos:e3vvtck42d4eacdnzvtrn6#key_r9ev34fvc23z999veaaft8
713
+ ```
804
714
 
805
- 8. **Document CID**: dag-cbor canonical encode the document JSON SHA-256 CIDv1 should be `bafyreifpvwuarml62sfogdpi2vlltvg2ev6o4xtw74zfud7cpkg7426zne`
715
+ 7. **Content create JWS**: signed by NEW controller key (key 2, post-rotation). Verify with key 2's public key. kid:
806
716
 
807
- 9. **Content chain integrity**: update's `previousOperationCID` matches create's operation CID
717
+ ```
718
+ did:dfos:e3vvtck42d4eacdnzvtrn6#key_ez9a874tckr3dv933d3ckd
719
+ ```
808
720
 
809
- 10. **JWT verify**: same signing mechanics as JWS `ed25519.verify(signature, UTF8(header.payload), key2_publicKey)` true. Check `exp > currentTime`, `iss == "dfos"`, `aud == "dfos-api"`.
721
+ 8. **Document CID**: dag-cbor canonical encode the document JSON SHA-256CIDv1 should be:
810
722
 
811
- ---
723
+ ```
724
+ bafyreifpvwuarml62sfogdpi2vlltvg2ev6o4xtw74zfud7cpkg7426zne
725
+ ```
812
726
 
813
- ## Source Code Reference
814
-
815
- All source lives in `packages/dfos-protocol/` — self-contained, zero monorepo dependencies.
816
-
817
- | File | Contents |
818
- | ----------------------------------- | -------------------------------------------------------------------------------------------------- |
819
- | `src/crypto/ed25519.ts` | `createNewEd25519Keypair`, `importEd25519Keypair`, `signPayloadEd25519`, `isValidEd25519Signature` |
820
- | `src/crypto/jws.ts` | `createJws`, `verifyJws`, `decodeJwsUnsafe`, `JwsVerificationError` |
821
- | `src/crypto/jwt.ts` | `createJwt`, `verifyJwt`, `decodeJwtUnsafe` (EdDSA only) |
822
- | `src/crypto/base64url.ts` | `base64urlEncode`, `base64urlDecode` |
823
- | `src/crypto/multiformats.ts` | `dagCborCanonicalEncode`, `dagCborCanonicalEqual` |
824
- | `src/crypto/id.ts` | `generateId`, `generateIdNoPrefix`, `isValidId` |
825
- | `src/chain/multikey.ts` | `encodeEd25519Multikey`, `decodeMultikey` |
826
- | `src/chain/schemas.ts` | `IdentityOperation`, `ContentOperation`, `MultikeyPublicKey`, `VerifiedIdentity` |
827
- | `src/chain/identity-chain.ts` | `signIdentityOperation`, `verifyIdentityChain` |
828
- | `src/chain/content-chain.ts` | `signContentOperation`, `verifyContentChain` |
829
- | `src/chain/derivation.ts` | `deriveChainIdentifier` |
830
- | `src/registry/schemas.ts` | Registry API Zod types (wire contract) |
831
- | `src/registry/server.ts` | Reference Hono registry server |
832
- | `src/registry/store.ts` | In-memory chain store with linear enforcement |
833
- | `openapi.yaml` | OpenAPI 3.1 spec for registry API |
834
- | `schemas/document-envelope.v1.json` | JSON Schema for the document envelope wrapper |
835
- | `schemas/post.v1.json` | JSON Schema for post documents |
836
- | `schemas/profile.v1.json` | JSON Schema for profile documents |
837
- | `tests/protocol-reference.spec.ts` | Deterministic artifact generator (this doc's source) |
838
- | `verify/go/` | Go cross-language verification (9 tests) |
839
- | `verify/python/` | Python cross-language verification (32 checks) |
840
- | `verify/rust/` | Rust cross-language verification (9 tests) |
841
- | `verify/swift/` | Swift cross-language verification (8 tests) |
727
+ 9. **Content chain integrity**: update's `previousOperationCID` matches create's operation CID
842
728
 
843
- ---
729
+ 10. **Chain completeness**: all operation CIDs, DID derivation, key rotation, and content chain linkage verified end-to-end.
844
730
 
845
- ## Test Coverage (160 checks across 5 languages)
731
+ ---
846
732
 
847
- ### TypeScript dfos-protocol (99 tests)
733
+ ## Source and Verification
848
734
 
849
- - `tests/crypto.spec.ts` (13): ed25519 keypair/sign/verify, JWS round-trip/wrong key/tampered/decode/malformed
850
- - `tests/chain.spec.ts` (39): multikey encoding, identity chain (genesis, DID, rotation, delete, cid header, errors), content chain (lifecycle, clear, delete, cid header, errors)
851
- - `tests/registry.spec.ts` (18): HTTP contract — submission, resubmission, extension, fork rejection, pagination, cross-chain key resolution, 404s
852
- - `tests/schemas.spec.ts` (28): JSON Schema compilation + validation for document envelope, post, profile — conforming documents, missing fields, invalid values, additional properties
853
- - `tests/protocol-reference.spec.ts` (1): deterministic artifact generator
735
+ All source lives in [`packages/dfos-protocol/`](https://github.com/metalabel/dfos/tree/main/packages/dfos-protocol) self-contained, zero monorepo dependencies. 160 checks across 5 languages.
854
736
 
855
- ### Pythonverify/ (35 checks)
737
+ - [`crypto/ed25519`](https://github.com/metalabel/dfos/blob/main/packages/dfos-protocol/src/crypto/ed25519.ts)`createNewEd25519Keypair`, `importEd25519Keypair`, `signPayloadEd25519`, `isValidEd25519Signature`
738
+ - [`crypto/jws`](https://github.com/metalabel/dfos/blob/main/packages/dfos-protocol/src/crypto/jws.ts) — `createJws`, `verifyJws`, `decodeJwsUnsafe`
739
+ - [`crypto/base64url`](https://github.com/metalabel/dfos/blob/main/packages/dfos-protocol/src/crypto/base64url.ts) — `base64urlEncode`, `base64urlDecode`
740
+ - [`crypto/multiformats`](https://github.com/metalabel/dfos/blob/main/packages/dfos-protocol/src/crypto/multiformats.ts) — `dagCborCanonicalEncode`, `dagCborCanonicalEqual`
741
+ - [`crypto/id`](https://github.com/metalabel/dfos/blob/main/packages/dfos-protocol/src/crypto/id.ts) — `generateId`, `generateIdNoPrefix`, `isValidId`
742
+ - [`chain/multikey`](https://github.com/metalabel/dfos/blob/main/packages/dfos-protocol/src/chain/multikey.ts) — `encodeEd25519Multikey`, `decodeMultikey`
743
+ - [`chain/schemas`](https://github.com/metalabel/dfos/blob/main/packages/dfos-protocol/src/chain/schemas.ts) — `IdentityOperation`, `ContentOperation`, `MultikeyPublicKey`, `VerifiedIdentity`
744
+ - [`chain/identity-chain`](https://github.com/metalabel/dfos/blob/main/packages/dfos-protocol/src/chain/identity-chain.ts) — `signIdentityOperation`, `verifyIdentityChain`
745
+ - [`chain/content-chain`](https://github.com/metalabel/dfos/blob/main/packages/dfos-protocol/src/chain/content-chain.ts) — `signContentOperation`, `verifyContentChain`
746
+ - [`chain/derivation`](https://github.com/metalabel/dfos/blob/main/packages/dfos-protocol/src/chain/derivation.ts) — `deriveChainIdentifier`, `deriveContentId`
856
747
 
857
- - Key derivation, multikey, dag-cbor bytes, CID/DID derivation, JWS/JWT signatures, CID header verification, document CID
858
- - Dependencies: `pynacl`, `dag-cbor`, `base58`
748
+ ### Related Specifications
859
749
 
860
- ### Go verify/ (9 tests)
750
+ - [DID Method: `did:dfos`](https://protocol.dfos.com/did-method) W3C DID method specification for identity chains
751
+ - [Content Model](https://protocol.dfos.com/content-model) — Standard content schemas (post, profile) for the document envelope
752
+ - [Registry API](https://protocol.dfos.com/registry-api) — HTTP API for chain storage and resolution
861
753
 
862
- - Key derivation, multikey, dag-cbor, CID/DID derivation, JWS/JWT signatures, CID header verification
863
- - Dependencies: `fxamacker/cbor/v2`, `mr-tron/base58`
754
+ ### Cross-Language Verification
864
755
 
865
- ### Rust verify/ (9 tests)
756
+ | Language | Tests | Source |
757
+ | ---------- | ----- | ---------------------------------------------------------------------------------------------------- |
758
+ | TypeScript | 99 | [`tests/`](https://github.com/metalabel/dfos/tree/main/packages/dfos-protocol/tests) |
759
+ | Python | 35 | [`verify/python/`](https://github.com/metalabel/dfos/tree/main/packages/dfos-protocol/verify/python) |
760
+ | Go | 9 | [`verify/go/`](https://github.com/metalabel/dfos/tree/main/packages/dfos-protocol/verify/go) |
761
+ | Rust | 9 | [`verify/rust/`](https://github.com/metalabel/dfos/tree/main/packages/dfos-protocol/verify/rust) |
762
+ | Swift | 8 | [`verify/swift/`](https://github.com/metalabel/dfos/tree/main/packages/dfos-protocol/verify/swift) |
866
763
 
867
- - Key derivation, multikey, dag-cbor, CID/DID derivation, JWS/JWT signatures, CID header verification
868
- - Dependencies: `ed25519-dalek`, `ciborium`, `sha2`, `bs58`, `base64`, `data-encoding`
764
+ ---
869
765
 
870
- ### Swift — verify/ (8 tests)
766
+ ## Special Thanks
871
767
 
872
- - Key derivation, multikey, CID/DID derivation, JWS/JWT signatures, CID header verification
873
- - Dependencies: Apple `Crypto` (swift-crypto)
768
+ - **Vinny Bellavia** [stcisgood.com](https://stcisgood.com)
769
+ - **Allison Clift-Jennings** [Jura Labs](https://juralabs.com)