@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/CONTENT-MODEL.md +107 -0
- package/DID-METHOD.md +326 -0
- package/PROTOCOL.md +137 -241
- package/README.md +9 -7
- package/REGISTRY-API.md +242 -0
- package/dist/chain/index.d.ts +5 -5
- package/dist/chain/index.js +3 -3
- package/dist/{chunk-3PB644X2.js → chunk-U6DANYPT.js} +32 -51
- package/dist/{chunk-LWC4PWGZ.js → chunk-VEBMLR37.js} +5 -5
- package/dist/index.d.ts +2 -2
- package/dist/index.js +10 -12
- package/dist/registry/index.d.ts +12 -20
- package/dist/registry/index.js +8 -10
- package/examples/content-delete.json +1 -1
- package/examples/content-lifecycle.json +1 -1
- package/openapi.yaml +20 -52
- package/package.json +11 -6
package/CONTENT-MODEL.md
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# DFOS Content Model
|
|
2
|
+
|
|
3
|
+
Standard content schemas for documents committed to DFOS content chains. JSON Schema (draft 2020-12) definitions for what goes inside the document envelope's `content` field.
|
|
4
|
+
|
|
5
|
+
These schemas are conventions, not protocol requirements. The DFOS Protocol commits to documents by CID without inspecting their contents — any valid JSON object with a `$schema` field can be committed. The content model defines the vocabulary that DFOS uses internally, provided as a starting point for applications built on the protocol.
|
|
6
|
+
|
|
7
|
+
[Protocol Specification](https://protocol.dfos.com/spec) · [schemas.dfos.com](https://schemas.dfos.com) · [Source](https://github.com/metalabel/dfos/tree/main/packages/dfos-protocol/schemas)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Schema Convention
|
|
12
|
+
|
|
13
|
+
Every document committed to a content chain is wrapped in a [document envelope](https://protocol.dfos.com/spec#document-envelope). The envelope's `content` field holds the application-defined payload. The protocol requires one thing of this payload: it must include a `$schema` property identifying its content type.
|
|
14
|
+
|
|
15
|
+
```json
|
|
16
|
+
{
|
|
17
|
+
"$schema": "https://schemas.dfos.com/post/v1",
|
|
18
|
+
"format": "short-post",
|
|
19
|
+
"body": "Hello world."
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Because `$schema` 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. Documents are self-describing.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Schema Evolution
|
|
28
|
+
|
|
29
|
+
Schemas are versioned via the URI path (`/post/v1`, `/post/v2`). Evolution rules:
|
|
30
|
+
|
|
31
|
+
- **Strictly additive within a version** — new optional fields can be added to an existing version at any time without breaking existing documents
|
|
32
|
+
- **Breaking changes require a new version** — removing fields, changing types, or adding new required fields means a new version URI
|
|
33
|
+
- **Implementations declare which versions they understand** — a registry or application can accept `post/v1` and `post/v2` simultaneously, or only `post/v1`
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Standard Schemas
|
|
38
|
+
|
|
39
|
+
Schema files live in [`schemas/`](https://github.com/metalabel/dfos/tree/main/packages/dfos-protocol/schemas) in the protocol package. Each is a standalone JSON Schema (draft 2020-12) definition, served at `https://schemas.dfos.com`.
|
|
40
|
+
|
|
41
|
+
### Post (`https://schemas.dfos.com/post/v1`)
|
|
42
|
+
|
|
43
|
+
The primary content type. Covers short posts, long-form posts, comments, and replies via the `format` discriminator.
|
|
44
|
+
|
|
45
|
+
| Field | Type | Required | Description |
|
|
46
|
+
| ------------- | -------- | -------- | ---------------------------------------------------------------------------------- |
|
|
47
|
+
| `$schema` | string | yes | `"https://schemas.dfos.com/post/v1"` |
|
|
48
|
+
| `format` | enum | yes | `"short-post"`, `"long-post"`, `"comment"`, `"reply"` — immutable, set at creation |
|
|
49
|
+
| `title` | string | no | Post title (typically for long-post format) |
|
|
50
|
+
| `body` | string | no | Post body content |
|
|
51
|
+
| `cover` | media | no | Cover image |
|
|
52
|
+
| `attachments` | media[] | no | Attached media objects |
|
|
53
|
+
| `topics` | string[] | no | Topic names (stored as names for portability) |
|
|
54
|
+
|
|
55
|
+
### Profile (`https://schemas.dfos.com/profile/v1`)
|
|
56
|
+
|
|
57
|
+
The displayable identity for any agent, person, group, or space.
|
|
58
|
+
|
|
59
|
+
| Field | Type | Required | Description |
|
|
60
|
+
| ------------- | ------ | -------- | --------------------------------------- |
|
|
61
|
+
| `$schema` | string | yes | `"https://schemas.dfos.com/profile/v1"` |
|
|
62
|
+
| `name` | string | no | Display name |
|
|
63
|
+
| `description` | string | no | Short bio or description |
|
|
64
|
+
| `avatar` | media | no | Avatar image |
|
|
65
|
+
| `banner` | media | no | Banner image |
|
|
66
|
+
| `background` | media | no | Background image |
|
|
67
|
+
|
|
68
|
+
### Media Object
|
|
69
|
+
|
|
70
|
+
Several schemas reference media objects. The standard representation:
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
{
|
|
74
|
+
"id": "media_abc123",
|
|
75
|
+
"uri": "https://cdn.example.com/media/abc123.jpg"
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
`id` is required (opaque identifier). `uri` is optional.
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Chain Interpretation
|
|
84
|
+
|
|
85
|
+
A content chain is a signed append-only log. The protocol enforces ordering, authorship, and integrity. It does not prescribe what the chain _means_. How an application interprets a content chain depends on the content types committed to it.
|
|
86
|
+
|
|
87
|
+
### Living Document
|
|
88
|
+
|
|
89
|
+
The chain represents a single evolving thing — a profile, a post, a policy document. Each operation is a **revision**. The resolved state is the latest `documentCID`. History is audit trail. The content _is_ the current version.
|
|
90
|
+
|
|
91
|
+
This is the default interpretation for the standard schemas. The document envelope's `baseDocumentCID` field supports edit lineage — each new document version can point back to the one it replaced.
|
|
92
|
+
|
|
93
|
+
### Stream
|
|
94
|
+
|
|
95
|
+
The chain represents a sequence — a feed, a journal, a log. Each operation is a discrete emission, not a revision. There is no single "current state" — the chain _is_ the content. Previous documents aren't superseded, they're siblings in a series.
|
|
96
|
+
|
|
97
|
+
### Other Patterns
|
|
98
|
+
|
|
99
|
+
The protocol cannot distinguish these patterns — the operation schema is identical in both cases. The difference is a reading convention, signaled by the `$schema` of the documents. Future content types could define event-sourcing patterns, append-only collections, or interpretations not yet imagined.
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## Custom Schemas
|
|
104
|
+
|
|
105
|
+
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.
|
|
106
|
+
|
|
107
|
+
Custom schema URIs should use a namespace you control (e.g., `https://schemas.example.com/my-type/v1`) to avoid collisions with the standard library.
|
package/DID-METHOD.md
ADDED
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
# DID Method: `did:dfos`
|
|
2
|
+
|
|
3
|
+
W3C DID Method specification for DFOS identity chains. Self-certifying, transport-agnostic, Ed25519-based decentralized identifiers.
|
|
4
|
+
|
|
5
|
+
This spec is under active review. Discuss it in the [clear.txt](https://clear.dfos.com) space on DFOS.
|
|
6
|
+
|
|
7
|
+
[Source](https://github.com/metalabel/dfos/tree/main/packages/dfos-protocol) · [Protocol Specification](https://protocol.dfos.com/spec) · [npm](https://www.npmjs.com/package/@metalabel/dfos-protocol)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Abstract
|
|
12
|
+
|
|
13
|
+
This document defines the `did:dfos` DID method in conformance with the [W3C Decentralized Identifiers (DIDs) v1.0](https://www.w3.org/TR/did-core/) specification. A `did:dfos` identifier is derived deterministically from the genesis operation of a cryptographically signed identity chain. Resolution is verification-first and transport-agnostic — the identifier itself is the trust anchor, not any particular registry or consensus layer.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Status
|
|
18
|
+
|
|
19
|
+
This specification is under active development. It has not been submitted to any formal standards body.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Conformance
|
|
24
|
+
|
|
25
|
+
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.rfc-editor.org/rfc/rfc2119).
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## 1. Introduction
|
|
30
|
+
|
|
31
|
+
DFOS is a protocol for verifiable identity and content chains using Ed25519 signatures and content-addressed CIDs. Every identity in DFOS is an append-only chain of signed operations — a self-sovereign log of key management events. The DID for an identity is derived deterministically from the hash of the chain's genesis operation, making `did:dfos` identifiers **self-certifying**: given the chain, anyone can independently verify the DID without trusting the source.
|
|
32
|
+
|
|
33
|
+
This property makes `did:dfos` fundamentally transport-agnostic. There is no privileged registry, blockchain, or consensus layer. The chain can be obtained from any source — an HTTP API, a peer-to-peer exchange, a local file, a USB drive — and the verifier can independently confirm the chain belongs to the claimed DID.
|
|
34
|
+
|
|
35
|
+
For full protocol details including cryptographic primitives, chain mechanics, and test vectors, see the [DFOS Protocol Specification](https://protocol.dfos.com/spec).
|
|
36
|
+
|
|
37
|
+
### 1.1 Design Goals
|
|
38
|
+
|
|
39
|
+
- **Self-certifying** — The DID is a deterministic derivation of the genesis content. No external authority is needed to verify the binding between identifier and chain.
|
|
40
|
+
- **Transport-agnostic** — Resolution requires obtaining and verifying a chain, not querying a specific endpoint. Any system that stores and serves identity chains is a valid source.
|
|
41
|
+
- **Key rotation** — Identity chains support full key rotation via signed update operations. Keys can be added, removed, and replaced without changing the DID.
|
|
42
|
+
- **Deactivation** — Identities can be permanently deactivated via a signed delete operation.
|
|
43
|
+
- **Minimal** — The method defines identifiers and verification. It deliberately does not define discovery, gossip, or consensus mechanisms.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 2. DID Method Name
|
|
48
|
+
|
|
49
|
+
The method name is `dfos`. A DID using this method MUST begin with the prefix `did:dfos:`.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## 3. Method-Specific Identifier
|
|
54
|
+
|
|
55
|
+
The method-specific identifier is a 22-character string derived from the genesis operation CID of an identity chain.
|
|
56
|
+
|
|
57
|
+
### 3.1 ABNF
|
|
58
|
+
|
|
59
|
+
```abnf
|
|
60
|
+
dfos-did = "did:dfos:" dfos-id
|
|
61
|
+
dfos-id = 22dfos-char
|
|
62
|
+
dfos-char = "2" / "3" / "4" / "6" / "7" / "8" / "9" /
|
|
63
|
+
"a" / "c" / "d" / "e" / "f" / "h" / "k" /
|
|
64
|
+
"n" / "r" / "t" / "v" / "z"
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
The alphabet is 19 characters: `2346789acdefhknrtvz`. The identifier is exactly 22 characters, providing ~93.4 bits of entropy.
|
|
68
|
+
|
|
69
|
+
### 3.2 Derivation
|
|
70
|
+
|
|
71
|
+
The method-specific identifier is derived deterministically from the genesis identity operation:
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
1. Construct the genesis identity operation payload (type: "create")
|
|
75
|
+
2. Canonical-encode the payload as dag-cbor → CBOR bytes
|
|
76
|
+
3. Hash: SHA-256(CBOR bytes) → 32-byte digest
|
|
77
|
+
4. Construct CIDv1: [0x01, 0x71, 0x12, 0x20, ...32 digest bytes] → CID bytes
|
|
78
|
+
5. Hash the CID: SHA-256(CID bytes) → 32-byte digest
|
|
79
|
+
6. Encode: for each of the first 22 bytes → alphabet[byte % 19]
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
The resulting 22-character string is the method-specific identifier. The full DID is `did:dfos:` prepended to this string.
|
|
83
|
+
|
|
84
|
+
### 3.3 Example
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
Genesis CID bytes (hex): 01711220206a5e6140a5114f1e49f3ca4b339fb2cb8e70bbb34968b23156fd0e3237b486
|
|
88
|
+
SHA-256 of CID bytes: 4360cfbcbbb3f1614c8e02dbfe8d55935e1195cd2129820ab8aef94bde12ea8a
|
|
89
|
+
First 22 bytes encoded: e3vvtck42d4eacdnzvtrn6
|
|
90
|
+
DID: did:dfos:e3vvtck42d4eacdnzvtrn6
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
See the [DFOS Protocol Specification](https://protocol.dfos.com/spec) for the complete worked example with key material, CBOR encoding, and CID construction.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## 4. DID Document
|
|
98
|
+
|
|
99
|
+
A resolved `did:dfos` DID Document is constructed from the current state of the identity chain — specifically, the key sets declared in the most recent non-terminal operation.
|
|
100
|
+
|
|
101
|
+
### 4.1 DID Document Structure
|
|
102
|
+
|
|
103
|
+
```json
|
|
104
|
+
{
|
|
105
|
+
"@context": ["https://www.w3.org/ns/did/v1", "https://w3id.org/security/multikey/v1"],
|
|
106
|
+
"id": "did:dfos:e3vvtck42d4eacdnzvtrn6",
|
|
107
|
+
"controller": "did:dfos:e3vvtck42d4eacdnzvtrn6",
|
|
108
|
+
"verificationMethod": [
|
|
109
|
+
{
|
|
110
|
+
"id": "did:dfos:e3vvtck42d4eacdnzvtrn6#key_r9ev34fvc23z999veaaft8",
|
|
111
|
+
"type": "Multikey",
|
|
112
|
+
"controller": "did:dfos:e3vvtck42d4eacdnzvtrn6",
|
|
113
|
+
"publicKeyMultibase": "z6MkrzLMNwoJSV4P3YccWcbtk8vd9LtgMKnLeaDLUqLuASjb"
|
|
114
|
+
}
|
|
115
|
+
],
|
|
116
|
+
"authentication": ["did:dfos:e3vvtck42d4eacdnzvtrn6#key_r9ev34fvc23z999veaaft8"],
|
|
117
|
+
"assertionMethod": ["did:dfos:e3vvtck42d4eacdnzvtrn6#key_r9ev34fvc23z999veaaft8"],
|
|
118
|
+
"capabilityInvocation": ["did:dfos:e3vvtck42d4eacdnzvtrn6#key_r9ev34fvc23z999veaaft8"]
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### 4.2 Verification Method Mapping
|
|
123
|
+
|
|
124
|
+
Identity chain operations declare three key sets. These map to W3C verification relationships as follows:
|
|
125
|
+
|
|
126
|
+
| Identity Chain Key Set | W3C Verification Relationship | Purpose |
|
|
127
|
+
| ---------------------- | ----------------------------- | -------------------------------------------------------------------- |
|
|
128
|
+
| `authKeys` | `authentication` | Prove control of the DID (e.g., login, session establishment) |
|
|
129
|
+
| `assertKeys` | `assertionMethod` | Issue verifiable assertions (e.g., sign content chain operations) |
|
|
130
|
+
| `controllerKeys` | `capabilityInvocation` | Manage the DID itself (sign identity chain update/delete operations) |
|
|
131
|
+
|
|
132
|
+
Each key in the identity chain state becomes a `verificationMethod` entry. The `id` is constructed as a DID URL: `did:dfos:<id>#<keyId>`. The `type` is `Multikey`. The `publicKeyMultibase` is the W3C Multikey encoding (multicodec `0xed01` prefix + base58btc + `z` multibase prefix).
|
|
133
|
+
|
|
134
|
+
### 4.3 Controller
|
|
135
|
+
|
|
136
|
+
`did:dfos` identities are self-sovereign. The `controller` property of the DID Document is always the DID itself. Only keys within the identity chain's `controllerKeys` set can sign operations that modify the chain.
|
|
137
|
+
|
|
138
|
+
### 4.4 Key Rotation
|
|
139
|
+
|
|
140
|
+
When an identity chain includes `update` operations that change the key sets, the DID Document reflects the **current state** — the key sets from the most recent operation. Previous keys are not included in the resolved DID Document. Historical key states can be recovered by walking the chain.
|
|
141
|
+
|
|
142
|
+
### 4.5 Services
|
|
143
|
+
|
|
144
|
+
The core `did:dfos` method does not define any `service` entries in the DID Document. Applications MAY extend the DID Document with service endpoints through application-layer conventions.
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## 5. Operations
|
|
149
|
+
|
|
150
|
+
### 5.1 Create
|
|
151
|
+
|
|
152
|
+
Creating a `did:dfos` identifier means constructing and signing a genesis identity chain operation.
|
|
153
|
+
|
|
154
|
+
1. Generate one or more Ed25519 key pairs.
|
|
155
|
+
2. Construct the genesis operation payload with `type: "create"`, populating `authKeys`, `assertKeys`, and `controllerKeys`. At least one `controllerKeys` entry is REQUIRED.
|
|
156
|
+
3. Canonical-encode the payload as dag-cbor, derive the CID, and include it in the JWS protected header as `cid`.
|
|
157
|
+
4. Sign the operation as a JWS Compact Serialization token using one of the controller keys. The `kid` in the protected header is the bare key ID (not a DID URL, since the DID does not yet exist).
|
|
158
|
+
5. The DID is derived from the genesis CID as described in [Section 3.2](#32-derivation).
|
|
159
|
+
|
|
160
|
+
The identity chain now exists as a single-operation chain. It can be stored in any system that serves identity chains.
|
|
161
|
+
|
|
162
|
+
### 5.2 Read (Resolve)
|
|
163
|
+
|
|
164
|
+
Resolving a `did:dfos` DID means obtaining the identity chain and constructing a DID Document from its current state.
|
|
165
|
+
|
|
166
|
+
#### 5.2.1 Resolution Algorithm
|
|
167
|
+
|
|
168
|
+
Given a DID `did:dfos:<id>`:
|
|
169
|
+
|
|
170
|
+
1. **Obtain** the identity chain from any available source. The method does not prescribe how chains are discovered or transported.
|
|
171
|
+
2. **Verify** the chain:
|
|
172
|
+
a. Decode each JWS token and parse the operation payload.
|
|
173
|
+
b. The first operation MUST be `type: "create"`.
|
|
174
|
+
c. Derive the genesis operation CID via dag-cbor canonical encoding.
|
|
175
|
+
d. Verify that `SHA-256(genesis CID bytes)` encoded with the ID alphabet produces `<id>`. If it does not match, the chain does not belong to this DID — reject it.
|
|
176
|
+
e. For each operation, verify the JWS EdDSA signature against the appropriate key (controller key from current chain state).
|
|
177
|
+
f. Verify `previousOperationCID` linkage, `createdAt` ordering, and `header.cid` consistency.
|
|
178
|
+
g. See the [DFOS Protocol Specification](https://protocol.dfos.com/spec) for complete verification rules.
|
|
179
|
+
3. **Construct** the DID Document from the terminal chain state using the mapping in [Section 4.2](#42-verification-method-mapping).
|
|
180
|
+
|
|
181
|
+
#### 5.2.2 Resolution Metadata
|
|
182
|
+
|
|
183
|
+
| Property | Value |
|
|
184
|
+
| ---------------- | ------------------------------------------------------------ |
|
|
185
|
+
| `contentType` | `application/did+ld+json` |
|
|
186
|
+
| `created` | `createdAt` from the genesis operation |
|
|
187
|
+
| `updated` | `createdAt` from the most recent operation |
|
|
188
|
+
| `deactivated` | `true` if the chain's terminal operation is `type: "delete"` |
|
|
189
|
+
| `operationCount` | Number of operations in the chain |
|
|
190
|
+
|
|
191
|
+
#### 5.2.3 Self-Certification
|
|
192
|
+
|
|
193
|
+
The critical property of `did:dfos` resolution: **the DID is verified against the chain, not the source.** Step 2d above is the self-certification check — it proves the chain belongs to the claimed DID using only the chain content and a hash function. This means:
|
|
194
|
+
|
|
195
|
+
- A resolver does not need to trust the registry, server, or peer that provided the chain.
|
|
196
|
+
- The same chain can be served by multiple independent sources with identical results.
|
|
197
|
+
- Chains can be cached, replicated, and redistributed without loss of verifiability.
|
|
198
|
+
- Offline resolution is possible if the chain is available locally.
|
|
199
|
+
|
|
200
|
+
#### 5.2.4 Transport Bindings (Non-Normative)
|
|
201
|
+
|
|
202
|
+
The `did:dfos` method is transport-agnostic. Any system that can deliver an ordered sequence of JWS tokens (the identity chain) is a valid transport. Examples include:
|
|
203
|
+
|
|
204
|
+
- **HTTP API** — The DFOS Registry API ([OpenAPI spec](https://github.com/metalabel/dfos/blob/main/packages/dfos-protocol/openapi.yaml)) provides a REST interface for chain storage and retrieval.
|
|
205
|
+
- **Peer-to-peer exchange** — Chains can be exchanged directly between parties.
|
|
206
|
+
- **Local storage** — Chains can be stored in local files, databases, or key-value stores.
|
|
207
|
+
- **Bundle export** — Applications can export chains as portable bundles (e.g., JSON arrays of JWS tokens).
|
|
208
|
+
|
|
209
|
+
### 5.3 Update
|
|
210
|
+
|
|
211
|
+
Updating a `did:dfos` DID means appending a signed `update` operation to the identity chain.
|
|
212
|
+
|
|
213
|
+
1. Construct an update operation payload with `type: "update"`, the new key sets, and `previousOperationCID` set to the CID of the current chain tip.
|
|
214
|
+
2. Sign the operation using a key from the **current** `controllerKeys` set. The `kid` is a DID URL: `did:dfos:<id>#<keyId>`.
|
|
215
|
+
3. Append the signed JWS token to the chain.
|
|
216
|
+
|
|
217
|
+
The DID does not change. The resolved DID Document now reflects the new key sets.
|
|
218
|
+
|
|
219
|
+
### 5.4 Deactivate (Delete)
|
|
220
|
+
|
|
221
|
+
Deactivating a `did:dfos` DID means appending a signed `delete` operation to the identity chain.
|
|
222
|
+
|
|
223
|
+
1. Construct a delete operation payload with `type: "delete"` and `previousOperationCID` set to the CID of the current chain tip.
|
|
224
|
+
2. Sign the operation using a key from the current `controllerKeys` set. The `kid` is a DID URL.
|
|
225
|
+
3. Append the signed JWS token to the chain.
|
|
226
|
+
|
|
227
|
+
After deactivation:
|
|
228
|
+
|
|
229
|
+
- The chain is in a **terminal state**. No further operations can be appended.
|
|
230
|
+
- Resolution MUST return a DID Document with `deactivated: true` in the resolution metadata.
|
|
231
|
+
- The DID Document SHOULD contain an empty set of verification methods, as the identity no longer has active keys.
|
|
232
|
+
|
|
233
|
+
Deactivation is **permanent and irreversible**. The DID cannot be reactivated.
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## 6. Security Considerations
|
|
238
|
+
|
|
239
|
+
### 6.1 Self-Certifying Identifiers
|
|
240
|
+
|
|
241
|
+
`did:dfos` identifiers are derived from a cryptographic hash of the genesis operation content. This binding is verified during resolution (Section 5.2.1, step 2d). An attacker cannot present a forged chain for a given DID — the genesis content would hash to a different identifier.
|
|
242
|
+
|
|
243
|
+
### 6.2 Key Compromise
|
|
244
|
+
|
|
245
|
+
If a controller key is compromised, the legitimate holder should immediately sign a key rotation (`update`) operation removing the compromised key. The protocol does not support key pre-rotation — there is no mechanism to pre-commit to a future key. The window of vulnerability exists between compromise and rotation.
|
|
246
|
+
|
|
247
|
+
### 6.3 Equivocation
|
|
248
|
+
|
|
249
|
+
Because `did:dfos` has no global consensus layer, an identity holder could theoretically sign two different operations at the same chain position (same `previousOperationCID`, different payloads). This creates a **fork** — two valid chain branches.
|
|
250
|
+
|
|
251
|
+
Equivocation is **detectable**: a verifier who encounters two valid operations sharing a `previousOperationCID` can identify the conflict. Resolution policy for equivocation (reject both branches, prefer one, flag for human review) is an application-level concern and is deliberately outside the scope of this method specification.
|
|
252
|
+
|
|
253
|
+
In practice, equivocation requires the identity holder to act against themselves — no external party can extend an identity chain, since all operations must be signed by a current controller key.
|
|
254
|
+
|
|
255
|
+
### 6.4 Transport Security
|
|
256
|
+
|
|
257
|
+
The `did:dfos` method does not mandate any specific transport security. Because resolution is verification-first (the chain is validated against the DID, not the source), transport-layer attacks (MITM, DNS hijacking) cannot produce a valid chain for a targeted DID. An attacker who intercepts a chain request can:
|
|
258
|
+
|
|
259
|
+
- **Withhold** the chain (denial of service) — the resolver gets no result
|
|
260
|
+
- **Serve a stale chain** — the resolver gets a valid but outdated DID Document
|
|
261
|
+
- **Serve a completely different chain** — the self-certification check fails, the resolver rejects it
|
|
262
|
+
|
|
263
|
+
An attacker **cannot** serve a modified or forged chain that passes the self-certification check.
|
|
264
|
+
|
|
265
|
+
### 6.5 Denial of Service
|
|
266
|
+
|
|
267
|
+
A resolver that depends on a single source for chain retrieval is vulnerable to denial of service. Applications SHOULD support multiple chain sources and MAY cache verified chains locally to mitigate this.
|
|
268
|
+
|
|
269
|
+
### 6.6 Cryptographic Agility
|
|
270
|
+
|
|
271
|
+
The current specification uses Ed25519 exclusively. The protocol does not currently support multiple signature algorithms. Future versions MAY introduce additional algorithms via new multicodec identifiers and verification method types. Implementations MUST reject operations signed with unrecognized algorithms.
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
## 7. Privacy Considerations
|
|
276
|
+
|
|
277
|
+
### 7.1 Correlation
|
|
278
|
+
|
|
279
|
+
`did:dfos` identifiers are persistent and globally unique. Any content chain signed by a DID can be correlated to the same identity. Users who require unlinkability across contexts should use distinct identities (distinct identity chains and DIDs) for each context.
|
|
280
|
+
|
|
281
|
+
### 7.2 Key Material
|
|
282
|
+
|
|
283
|
+
Identity chains contain only public keys. Private key material is never included in the chain and MUST NOT be transmitted during resolution.
|
|
284
|
+
|
|
285
|
+
### 7.3 Chain History
|
|
286
|
+
|
|
287
|
+
The full identity chain is available to any resolver. This reveals the history of key rotations, including timestamps (`createdAt`). Applications that consider key rotation history sensitive should be aware that this metadata is inherently public as part of the chain.
|
|
288
|
+
|
|
289
|
+
### 7.4 Herd Privacy
|
|
290
|
+
|
|
291
|
+
Because `did:dfos` resolution can happen through any transport (including local storage), a resolver does not necessarily reveal which DIDs it is interested in. However, when using a shared registry API, the registry operator can observe resolution patterns. Applications with strong privacy requirements SHOULD resolve chains through privacy-preserving transports or maintain local chain caches.
|
|
292
|
+
|
|
293
|
+
---
|
|
294
|
+
|
|
295
|
+
## 8. Reference Implementation
|
|
296
|
+
|
|
297
|
+
A complete reference implementation is available as the `@metalabel/dfos-protocol` npm package:
|
|
298
|
+
|
|
299
|
+
- **npm**: [@metalabel/dfos-protocol](https://www.npmjs.com/package/@metalabel/dfos-protocol)
|
|
300
|
+
- **Source**: [github.com/metalabel/dfos](https://github.com/metalabel/dfos)
|
|
301
|
+
- **Cross-language verification**: Go, Python, Rust, and Swift implementations verify the same deterministic test vectors
|
|
302
|
+
|
|
303
|
+
---
|
|
304
|
+
|
|
305
|
+
## 9. References
|
|
306
|
+
|
|
307
|
+
### 9.1 Normative References
|
|
308
|
+
|
|
309
|
+
| Reference | URI |
|
|
310
|
+
| --------------------------- | --------------------------------------------------- |
|
|
311
|
+
| W3C DID Core 1.0 | https://www.w3.org/TR/did-core/ |
|
|
312
|
+
| W3C Multikey | https://www.w3.org/TR/controller-document/#multikey |
|
|
313
|
+
| RFC 2119 (Key Words) | https://www.rfc-editor.org/rfc/rfc2119 |
|
|
314
|
+
| RFC 7515 (JWS) | https://www.rfc-editor.org/rfc/rfc7515 |
|
|
315
|
+
| RFC 8032 (Ed25519) | https://www.rfc-editor.org/rfc/rfc8032 |
|
|
316
|
+
| DFOS Protocol Specification | https://protocol.dfos.com/spec |
|
|
317
|
+
|
|
318
|
+
### 9.2 Informative References
|
|
319
|
+
|
|
320
|
+
| Reference | URI |
|
|
321
|
+
| ----------------------- | ------------------------------------------------------------------------------- |
|
|
322
|
+
| W3C DID Spec Registries | https://w3c.github.io/did-spec-registries/ |
|
|
323
|
+
| Multicodec Table | https://github.com/multiformats/multicodec |
|
|
324
|
+
| CIDv1 Specification | https://github.com/multiformats/cid |
|
|
325
|
+
| dag-cbor Codec | https://ipld.io/specs/codecs/dag-cbor/spec/ |
|
|
326
|
+
| DFOS Registry OpenAPI | https://github.com/metalabel/dfos/blob/main/packages/dfos-protocol/openapi.yaml |
|