@metalabel/dfos-protocol 0.0.2 → 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 +1 -1
- package/PROTOCOL.md +26 -43
- package/README.md +1 -1
- package/REGISTRY-API.md +13 -13
- package/dist/chain/index.d.ts +5 -5
- package/dist/chain/index.js +3 -3
- package/dist/{chunk-4MMWM2PC.js → chunk-U6DANYPT.js} +32 -32
- package/dist/{chunk-LWC4PWGZ.js → chunk-VEBMLR37.js} +5 -5
- package/dist/index.d.ts +2 -2
- package/dist/index.js +10 -10
- package/dist/registry/index.d.ts +12 -12
- package/dist/registry/index.js +8 -8
- package/examples/content-delete.json +1 -1
- package/examples/content-lifecycle.json +1 -1
- package/openapi.yaml +20 -20
- package/package.json +1 -1
package/CONTENT-MODEL.md
CHANGED
|
@@ -86,7 +86,7 @@ A content chain is a signed append-only log. The protocol enforces ordering, aut
|
|
|
86
86
|
|
|
87
87
|
### Living Document
|
|
88
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
|
|
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
90
|
|
|
91
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
92
|
|
package/PROTOCOL.md
CHANGED
|
@@ -10,21 +10,11 @@ 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
|
|
|
@@ -77,11 +67,11 @@ The `documentCID` in a content chain operation is `CID(dagCborEncode(envelope))`
|
|
|
77
67
|
|
|
78
68
|
Three canonical representations:
|
|
79
69
|
|
|
80
|
-
| Thing
|
|
81
|
-
|
|
|
82
|
-
| Operation or document
|
|
83
|
-
|
|
|
84
|
-
| Identity
|
|
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` |
|
|
85
75
|
|
|
86
76
|
Example CID:
|
|
87
77
|
|
|
@@ -89,7 +79,7 @@ Example CID:
|
|
|
89
79
|
bafyreibanjpgcqffcfhr4sptzjfthh5szohhbo5tjfulemkw7uhden5uqy
|
|
90
80
|
```
|
|
91
81
|
|
|
92
|
-
Operations and documents are CIDs — standard IPLD content addresses.
|
|
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
|
-
|
|
92
|
+
Both operations and documents are content-addressed via **CID** (`dagCborCanonicalEncode(payload)` → SHA-256 → CIDv1). Operations are additionally signed via **JWS**.
|
|
103
93
|
|
|
104
|
-
|
|
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
|
-
|
|
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
|
|
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**
|
|
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.
|
|
125
118
|
|
|
126
|
-
|
|
127
|
-
|
|
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,7 +128,7 @@ 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
|
|
131
|
+
- Ownership or attribution semantics between signers and content chains
|
|
141
132
|
|
|
142
133
|
### Terminal States and Special Operations
|
|
143
134
|
|
|
@@ -145,7 +136,7 @@ This is a deliberate asymmetry with identity chains. Identity chains are self-so
|
|
|
145
136
|
|
|
146
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.
|
|
147
138
|
|
|
148
|
-
**Content-null:** An `update` on a content chain with `documentCID: null` means the
|
|
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.
|
|
149
140
|
|
|
150
141
|
### `typ` Header
|
|
151
142
|
|
|
@@ -153,7 +144,7 @@ The JWS `typ` header (`did:dfos:identity-op`, `did:dfos:content-op`) aids routin
|
|
|
153
144
|
|
|
154
145
|
### Operation Field Limits
|
|
155
146
|
|
|
156
|
-
The protocol defines maximum sizes for all operation fields
|
|
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.
|
|
157
148
|
|
|
158
149
|
| Field | Max | Rationale |
|
|
159
150
|
| -------------------------------------------- | --------- | -------------------------------------- |
|
|
@@ -321,7 +312,7 @@ DID: did:dfos:e3vvtck42d4eacdnzvtrn6
|
|
|
321
312
|
createdAt: string,
|
|
322
313
|
note: string | null }
|
|
323
314
|
|
|
324
|
-
// Permanent
|
|
315
|
+
// Permanent destruction
|
|
325
316
|
{ version: 1, type: "delete",
|
|
326
317
|
previousOperationCID: string,
|
|
327
318
|
createdAt: string,
|
|
@@ -379,11 +370,7 @@ Every operation JWS (identity-op and content-op) includes a `cid` field in the p
|
|
|
379
370
|
- `header.cid` is missing
|
|
380
371
|
- `header.cid` does not match the derived CID
|
|
381
372
|
|
|
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
|
|
373
|
+
A CID mismatch between header and derived value immediately surfaces dag-cbor encoding disagreements across implementations.
|
|
387
374
|
|
|
388
375
|
Note: JWT tokens (device auth) do NOT include a `cid` header — this field is specific to operation JWS tokens.
|
|
389
376
|
|
|
@@ -435,7 +422,7 @@ Where `idEncode` is the 19-char alphabet encoding described above.
|
|
|
435
422
|
|
|
436
423
|
## Deterministic Reference Artifacts
|
|
437
424
|
|
|
438
|
-
All
|
|
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"))`.
|
|
439
426
|
|
|
440
427
|
### Key 1 (Genesis Controller)
|
|
441
428
|
|
|
@@ -707,7 +694,7 @@ Given the artifacts above, verify:
|
|
|
707
694
|
→ ba421e272fad4f941c221e47f87d9253bdc04f7d4ad2625ae667ab9f0688ce32
|
|
708
695
|
```
|
|
709
696
|
|
|
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.
|
|
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`.
|
|
711
698
|
|
|
712
699
|
3. **Genesis CID**: base64url-decode JWS payload → parse JSON → dag-cbor canonical encode → SHA-256 → CIDv1 → should be:
|
|
713
700
|
|
|
@@ -756,7 +743,7 @@ All source lives in [`packages/dfos-protocol/`](https://github.com/metalabel/dfo
|
|
|
756
743
|
- [`chain/schemas`](https://github.com/metalabel/dfos/blob/main/packages/dfos-protocol/src/chain/schemas.ts) — `IdentityOperation`, `ContentOperation`, `MultikeyPublicKey`, `VerifiedIdentity`
|
|
757
744
|
- [`chain/identity-chain`](https://github.com/metalabel/dfos/blob/main/packages/dfos-protocol/src/chain/identity-chain.ts) — `signIdentityOperation`, `verifyIdentityChain`
|
|
758
745
|
- [`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`
|
|
746
|
+
- [`chain/derivation`](https://github.com/metalabel/dfos/blob/main/packages/dfos-protocol/src/chain/derivation.ts) — `deriveChainIdentifier`, `deriveContentId`
|
|
760
747
|
|
|
761
748
|
### Related Specifications
|
|
762
749
|
|
|
@@ -780,7 +767,3 @@ All source lives in [`packages/dfos-protocol/`](https://github.com/metalabel/dfo
|
|
|
780
767
|
|
|
781
768
|
- **Vinny Bellavia** — [stcisgood.com](https://stcisgood.com)
|
|
782
769
|
- **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
|
|
package/REGISTRY-API.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Minimal HTTP API for chain storage, retrieval, and resolution. Any server implementing these endpoints with these semantics is a compatible DFOS registry.
|
|
4
4
|
|
|
5
|
-
The protocol is transport-agnostic — chains can be exchanged through any mechanism. This API defines one standard transport binding: a REST interface for submitting chains, resolving identities and
|
|
5
|
+
The protocol is transport-agnostic — chains can be exchanged through any mechanism. This API defines one standard transport binding: a REST interface for submitting chains, resolving identities and content, and retrieving operations and documents.
|
|
6
6
|
|
|
7
7
|
[Protocol Specification](https://protocol.dfos.com/spec) · [OpenAPI Spec](https://github.com/metalabel/dfos/blob/main/packages/dfos-protocol/openapi.yaml) · [Reference Implementation](https://github.com/metalabel/dfos/blob/main/packages/dfos-protocol/src/registry/server.ts)
|
|
8
8
|
|
|
@@ -17,7 +17,7 @@ Six endpoints, two concerns:
|
|
|
17
17
|
| Concern | Endpoints |
|
|
18
18
|
| ------------------- | ----------------------------------------------- |
|
|
19
19
|
| **Identity chains** | Submit chain, resolve identity, list operations |
|
|
20
|
-
| **Content chains** | Submit chain, resolve
|
|
20
|
+
| **Content chains** | Submit chain, resolve content, list operations |
|
|
21
21
|
| **Lookup** | Resolve operation by CID |
|
|
22
22
|
|
|
23
23
|
All request and response bodies are JSON (`application/json`).
|
|
@@ -105,9 +105,9 @@ Returns operations newest-first, paginated.
|
|
|
105
105
|
|
|
106
106
|
## Content Endpoints
|
|
107
107
|
|
|
108
|
-
### `POST /
|
|
108
|
+
### `POST /content` — Submit or extend a content chain
|
|
109
109
|
|
|
110
|
-
Same mechanics as identity submission. The registry verifies the chain (resolving signing keys from stored identity chains), derives the
|
|
110
|
+
Same mechanics as identity submission. The registry verifies the chain (resolving signing keys from stored identity chains), derives the content ID from the genesis CID, and stores it.
|
|
111
111
|
|
|
112
112
|
**Request:** Same `{ "chain": [...] }` format as identity submission.
|
|
113
113
|
|
|
@@ -115,21 +115,21 @@ Same mechanics as identity submission. The registry verifies the chain (resolvin
|
|
|
115
115
|
|
|
116
116
|
---
|
|
117
117
|
|
|
118
|
-
### `GET /
|
|
118
|
+
### `GET /content/{contentId}` — Resolve current content state
|
|
119
119
|
|
|
120
|
-
Returns the current state for a content chain
|
|
120
|
+
Returns the current state for a content chain.
|
|
121
121
|
|
|
122
122
|
**Parameters:**
|
|
123
123
|
|
|
124
|
-
| Name
|
|
125
|
-
|
|
|
126
|
-
| `
|
|
124
|
+
| Name | In | Pattern | Example |
|
|
125
|
+
| ----------- | ---- | --------------------------- | ------------------------ |
|
|
126
|
+
| `contentId` | path | `[2346789acdefhknrtvz]{22}` | `67t27rzc83v7c22n9t6z7c` |
|
|
127
127
|
|
|
128
|
-
**Response (`
|
|
128
|
+
**Response (`ContentState`):**
|
|
129
129
|
|
|
130
130
|
```json
|
|
131
131
|
{
|
|
132
|
-
"
|
|
132
|
+
"contentId": "67t27rzc83v7c22n9t6z7c",
|
|
133
133
|
"isDeleted": false,
|
|
134
134
|
"currentDocumentCID": "bafyrei...",
|
|
135
135
|
"genesisCID": "bafyrei...",
|
|
@@ -145,7 +145,7 @@ Returns the current state for a content chain entity.
|
|
|
145
145
|
|
|
146
146
|
---
|
|
147
147
|
|
|
148
|
-
### `GET /
|
|
148
|
+
### `GET /content/{contentId}/operations` — List content chain operations
|
|
149
149
|
|
|
150
150
|
Same pagination mechanics as identity operations.
|
|
151
151
|
|
|
@@ -182,7 +182,7 @@ All error responses follow a standard shape:
|
|
|
182
182
|
| Error Code | Used By |
|
|
183
183
|
| ------------- | ---------------------------------------------------------- |
|
|
184
184
|
| `BAD_REQUEST` | Invalid chain, malformed request |
|
|
185
|
-
| `NOT_FOUND` | Identity,
|
|
185
|
+
| `NOT_FOUND` | Identity, content, or operation not found |
|
|
186
186
|
| `CONFLICT` | Fork detected — submitted chain diverges from stored chain |
|
|
187
187
|
|
|
188
188
|
---
|
package/dist/chain/index.d.ts
CHANGED
|
@@ -124,12 +124,12 @@ declare const decodeMultikey: (multibase: string) => {
|
|
|
124
124
|
*/
|
|
125
125
|
declare const deriveChainIdentifier: (cidBytes: Uint8Array, prefix: string) => string;
|
|
126
126
|
/**
|
|
127
|
-
* Derive a bare
|
|
127
|
+
* Derive a bare content identifier from CID bytes
|
|
128
128
|
*
|
|
129
129
|
* Returns the raw 22-char hash with no prefix. Applications may add
|
|
130
130
|
* their own prefix for routing (e.g., post_xxxx) — that's semantic sugar.
|
|
131
131
|
*/
|
|
132
|
-
declare const
|
|
132
|
+
declare const deriveContentId: (cidBytes: Uint8Array) => string;
|
|
133
133
|
|
|
134
134
|
/**
|
|
135
135
|
* Sign an identity operation as a JWS and derive the operation CID
|
|
@@ -156,8 +156,8 @@ declare const verifyIdentityChain: (input: {
|
|
|
156
156
|
}) => Promise<VerifiedIdentity>;
|
|
157
157
|
|
|
158
158
|
interface VerifiedContentChain {
|
|
159
|
-
/**
|
|
160
|
-
|
|
159
|
+
/** Content identifier — bare 22-char hash derived from genesis CID */
|
|
160
|
+
contentId: string;
|
|
161
161
|
/** CID of the genesis operation */
|
|
162
162
|
genesisCID: string;
|
|
163
163
|
/** CID of the most recent operation */
|
|
@@ -193,4 +193,4 @@ declare const verifyContentChain: (input: {
|
|
|
193
193
|
resolveKey: (kid: string) => Promise<Uint8Array>;
|
|
194
194
|
}) => Promise<VerifiedContentChain>;
|
|
195
195
|
|
|
196
|
-
export { ContentOperation, ED25519_PRIV_MULTICODEC, ED25519_PUB_MULTICODEC, IdentityOperation, MultikeyPublicKey, type Signer, type VerifiedContentChain, VerifiedIdentity, decodeMultikey, deriveChainIdentifier,
|
|
196
|
+
export { ContentOperation, ED25519_PRIV_MULTICODEC, ED25519_PUB_MULTICODEC, IdentityOperation, MultikeyPublicKey, type Signer, type VerifiedContentChain, VerifiedIdentity, decodeMultikey, deriveChainIdentifier, deriveContentId, encodeEd25519Multikey, signContentOperation, signIdentityOperation, verifyContentChain, verifyIdentityChain };
|
package/dist/chain/index.js
CHANGED
|
@@ -7,13 +7,13 @@ import {
|
|
|
7
7
|
VerifiedIdentity,
|
|
8
8
|
decodeMultikey,
|
|
9
9
|
deriveChainIdentifier,
|
|
10
|
-
|
|
10
|
+
deriveContentId,
|
|
11
11
|
encodeEd25519Multikey,
|
|
12
12
|
signContentOperation,
|
|
13
13
|
signIdentityOperation,
|
|
14
14
|
verifyContentChain,
|
|
15
15
|
verifyIdentityChain
|
|
16
|
-
} from "../chunk-
|
|
16
|
+
} from "../chunk-VEBMLR37.js";
|
|
17
17
|
import "../chunk-ZXXP5W5N.js";
|
|
18
18
|
export {
|
|
19
19
|
ContentOperation,
|
|
@@ -24,7 +24,7 @@ export {
|
|
|
24
24
|
VerifiedIdentity,
|
|
25
25
|
decodeMultikey,
|
|
26
26
|
deriveChainIdentifier,
|
|
27
|
-
|
|
27
|
+
deriveContentId,
|
|
28
28
|
encodeEd25519Multikey,
|
|
29
29
|
signContentOperation,
|
|
30
30
|
signIdentityOperation,
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
decodeMultikey,
|
|
4
4
|
verifyContentChain,
|
|
5
5
|
verifyIdentityChain
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-VEBMLR37.js";
|
|
7
7
|
import {
|
|
8
8
|
dagCborCanonicalEncode,
|
|
9
9
|
decodeJwsUnsafe
|
|
@@ -41,15 +41,15 @@ var SubmitContentChainRequest = z.strictObject({
|
|
|
41
41
|
chain: z.array(z.string()).min(1)
|
|
42
42
|
});
|
|
43
43
|
var SubmitContentChainResponse = z.strictObject({
|
|
44
|
-
|
|
44
|
+
contentId: z.string(),
|
|
45
45
|
isDeleted: z.boolean(),
|
|
46
46
|
currentDocumentCID: z.string().nullable(),
|
|
47
47
|
genesisCID: z.string(),
|
|
48
48
|
headCID: z.string()
|
|
49
49
|
});
|
|
50
|
-
var
|
|
51
|
-
var
|
|
52
|
-
var
|
|
50
|
+
var ResolveContentResponse = SubmitContentChainResponse;
|
|
51
|
+
var ContentOperationsParams = PaginationParams;
|
|
52
|
+
var ContentOperationsResponse = PaginatedOperations;
|
|
53
53
|
var ResolveOperationResponse = z.strictObject({
|
|
54
54
|
cid: z.string(),
|
|
55
55
|
jwsToken: z.string()
|
|
@@ -64,11 +64,11 @@ import { Hono } from "hono";
|
|
|
64
64
|
|
|
65
65
|
// src/registry/store.ts
|
|
66
66
|
var ChainStore = class {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
// ---
|
|
67
|
+
identityChains = /* @__PURE__ */ new Map();
|
|
68
|
+
contentChains = /* @__PURE__ */ new Map();
|
|
69
|
+
// --- identityChains ---
|
|
70
70
|
getIdentityChain(did) {
|
|
71
|
-
return this.
|
|
71
|
+
return this.identityChains.get(did);
|
|
72
72
|
}
|
|
73
73
|
/**
|
|
74
74
|
* Submit an identity chain. Returns 'accepted' | 'noop' | 'conflict'.
|
|
@@ -77,22 +77,22 @@ var ChainStore = class {
|
|
|
77
77
|
* - conflict: submitted chain diverges from stored chain (fork)
|
|
78
78
|
*/
|
|
79
79
|
submitIdentityChain(did, operations) {
|
|
80
|
-
return this.submitChain(this.
|
|
80
|
+
return this.submitChain(this.identityChains, did, operations);
|
|
81
81
|
}
|
|
82
|
-
// ---
|
|
83
|
-
|
|
84
|
-
return this.
|
|
82
|
+
// --- contentChains ---
|
|
83
|
+
getContentChain(contentId) {
|
|
84
|
+
return this.contentChains.get(contentId);
|
|
85
85
|
}
|
|
86
|
-
|
|
87
|
-
return this.submitChain(this.
|
|
86
|
+
submitContentChain(contentId, operations) {
|
|
87
|
+
return this.submitChain(this.contentChains, contentId, operations);
|
|
88
88
|
}
|
|
89
89
|
// --- operations (lookup across all chains) ---
|
|
90
90
|
getOperation(cid) {
|
|
91
|
-
for (const chain of this.
|
|
91
|
+
for (const chain of this.identityChains.values()) {
|
|
92
92
|
const op = chain.operations.find((o) => o.cid === cid);
|
|
93
93
|
if (op) return op;
|
|
94
94
|
}
|
|
95
|
-
for (const chain of this.
|
|
95
|
+
for (const chain of this.contentChains.values()) {
|
|
96
96
|
const op = chain.operations.find((o) => o.cid === cid);
|
|
97
97
|
if (op) return op;
|
|
98
98
|
}
|
|
@@ -233,7 +233,7 @@ var createRegistryServer = (store = new ChainStore()) => {
|
|
|
233
233
|
const { cursor, limit } = paginationParams(c.req);
|
|
234
234
|
return c.json(paginate(chain.operations, cursor, limit));
|
|
235
235
|
});
|
|
236
|
-
app.post("/
|
|
236
|
+
app.post("/content", async (c) => {
|
|
237
237
|
const body = await c.req.json();
|
|
238
238
|
if (!body.chain || !Array.isArray(body.chain) || body.chain.length === 0) {
|
|
239
239
|
return c.json(err("BAD_REQUEST", "chain must be a non-empty array of JWS tokens"), 400);
|
|
@@ -245,13 +245,13 @@ var createRegistryServer = (store = new ChainStore()) => {
|
|
|
245
245
|
} catch (e) {
|
|
246
246
|
return c.json(err("BAD_REQUEST", `chain verification failed: ${e.message}`), 400);
|
|
247
247
|
}
|
|
248
|
-
const result = store.
|
|
248
|
+
const result = store.submitContentChain(verified.contentId, operations);
|
|
249
249
|
if (result === "conflict") {
|
|
250
250
|
return c.json(err("CONFLICT", "submitted chain conflicts with stored chain"), 409);
|
|
251
251
|
}
|
|
252
252
|
return c.json(
|
|
253
253
|
{
|
|
254
|
-
|
|
254
|
+
contentId: verified.contentId,
|
|
255
255
|
isDeleted: verified.isDeleted,
|
|
256
256
|
currentDocumentCID: verified.currentDocumentCID,
|
|
257
257
|
genesisCID: verified.genesisCID,
|
|
@@ -260,27 +260,27 @@ var createRegistryServer = (store = new ChainStore()) => {
|
|
|
260
260
|
result === "accepted" ? 201 : 200
|
|
261
261
|
);
|
|
262
262
|
});
|
|
263
|
-
app.get("/
|
|
264
|
-
const
|
|
265
|
-
const chain = store.
|
|
266
|
-
if (!chain) return c.json(err("NOT_FOUND", "
|
|
263
|
+
app.get("/content/:contentId", (c) => {
|
|
264
|
+
const contentId = c.req.param("contentId");
|
|
265
|
+
const chain = store.getContentChain(contentId);
|
|
266
|
+
if (!chain) return c.json(err("NOT_FOUND", "content not found"), 404);
|
|
267
267
|
const genesis = chain.operations[0];
|
|
268
268
|
const head = chain.operations[chain.operations.length - 1];
|
|
269
269
|
const headDecoded = decodeJwsUnsafe(head.jwsToken);
|
|
270
270
|
const headPayload = headDecoded?.payload;
|
|
271
271
|
const headType = headPayload?.type;
|
|
272
272
|
return c.json({
|
|
273
|
-
|
|
273
|
+
contentId,
|
|
274
274
|
isDeleted: headType === "delete",
|
|
275
275
|
currentDocumentCID: headType === "delete" ? null : headPayload?.documentCID ?? null,
|
|
276
276
|
genesisCID: genesis.cid,
|
|
277
277
|
headCID: head.cid
|
|
278
278
|
});
|
|
279
279
|
});
|
|
280
|
-
app.get("/
|
|
281
|
-
const
|
|
282
|
-
const chain = store.
|
|
283
|
-
if (!chain) return c.json(err("NOT_FOUND", "
|
|
280
|
+
app.get("/content/:contentId/operations", (c) => {
|
|
281
|
+
const contentId = c.req.param("contentId");
|
|
282
|
+
const chain = store.getContentChain(contentId);
|
|
283
|
+
if (!chain) return c.json(err("NOT_FOUND", "content not found"), 404);
|
|
284
284
|
const { cursor, limit } = paginationParams(c.req);
|
|
285
285
|
return c.json(paginate(chain.operations, cursor, limit));
|
|
286
286
|
});
|
|
@@ -301,9 +301,9 @@ export {
|
|
|
301
301
|
IdentityOperationsResponse,
|
|
302
302
|
SubmitContentChainRequest,
|
|
303
303
|
SubmitContentChainResponse,
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
304
|
+
ResolveContentResponse,
|
|
305
|
+
ContentOperationsParams,
|
|
306
|
+
ContentOperationsResponse,
|
|
307
307
|
ResolveOperationResponse,
|
|
308
308
|
RegistryError,
|
|
309
309
|
ChainStore,
|
|
@@ -127,7 +127,7 @@ var deriveChainIdentifier = (cidBytes, prefix) => {
|
|
|
127
127
|
const id = generateIdNoPrefix({ seed: cidBytes });
|
|
128
128
|
return `${prefix}:${id}`;
|
|
129
129
|
};
|
|
130
|
-
var
|
|
130
|
+
var deriveContentId = (cidBytes) => {
|
|
131
131
|
return generateIdNoPrefix({ seed: cidBytes });
|
|
132
132
|
};
|
|
133
133
|
|
|
@@ -289,7 +289,7 @@ var signContentOperation = async (input) => {
|
|
|
289
289
|
var verifyContentChain = async (input) => {
|
|
290
290
|
if (input.log.length === 0) throw new Error("log must have at least one operation");
|
|
291
291
|
const state = {
|
|
292
|
-
|
|
292
|
+
contentId: null,
|
|
293
293
|
genesisCID: null,
|
|
294
294
|
headCID: null,
|
|
295
295
|
isDeleted: false,
|
|
@@ -339,7 +339,7 @@ var verifyContentChain = async (input) => {
|
|
|
339
339
|
}
|
|
340
340
|
if (idx === 0) {
|
|
341
341
|
state.genesisCID = operationCID;
|
|
342
|
-
state.
|
|
342
|
+
state.contentId = deriveContentId(encoded.cid.bytes);
|
|
343
343
|
}
|
|
344
344
|
state.headCID = operationCID;
|
|
345
345
|
state.previousCID = operationCID;
|
|
@@ -358,7 +358,7 @@ var verifyContentChain = async (input) => {
|
|
|
358
358
|
}
|
|
359
359
|
}
|
|
360
360
|
return {
|
|
361
|
-
|
|
361
|
+
contentId: state.contentId,
|
|
362
362
|
genesisCID: state.genesisCID,
|
|
363
363
|
headCID: state.headCID,
|
|
364
364
|
isDeleted: state.isDeleted,
|
|
@@ -377,7 +377,7 @@ export {
|
|
|
377
377
|
encodeEd25519Multikey,
|
|
378
378
|
decodeMultikey,
|
|
379
379
|
deriveChainIdentifier,
|
|
380
|
-
|
|
380
|
+
deriveContentId,
|
|
381
381
|
signIdentityOperation,
|
|
382
382
|
verifyIdentityChain,
|
|
383
383
|
signContentOperation,
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { JwsHeader, JwsVerificationError, JwtClaims, JwtCreateOptions, JwtHeader, JwtVerificationError, JwtVerifyOptions, PrefixedID, base64urlDecode, base64urlEncode, createJws, createJwt, createNewEd25519Keypair, dagCborCanonicalEncode, decodeJwsUnsafe, decodeJwtUnsafe, generateId, generateIdNoPrefix, importEd25519Keypair, isCanonicallyEqual, isValidEd25519Signature, isValidId, normalizedId, parseDagCborCID, signPayloadEd25519, verifyJws, verifyJwt } from './crypto/index.js';
|
|
2
|
-
export { ContentOperation, ED25519_PRIV_MULTICODEC, ED25519_PUB_MULTICODEC, IdentityOperation, MultikeyPublicKey, Signer, VerifiedContentChain, VerifiedIdentity, decodeMultikey, deriveChainIdentifier,
|
|
3
|
-
export { ChainStore,
|
|
2
|
+
export { ContentOperation, ED25519_PRIV_MULTICODEC, ED25519_PUB_MULTICODEC, IdentityOperation, MultikeyPublicKey, Signer, VerifiedContentChain, VerifiedIdentity, decodeMultikey, deriveChainIdentifier, deriveContentId, encodeEd25519Multikey, signContentOperation, signIdentityOperation, verifyContentChain, verifyIdentityChain } from './chain/index.js';
|
|
3
|
+
export { ChainStore, ContentOperationsParams, ContentOperationsResponse, IdentityOperationsParams, IdentityOperationsResponse, OperationEntry, PaginationParams, RegistryError, ResolveContentResponse, ResolveIdentityResponse, ResolveOperationResponse, StoredChain, SubmitContentChainRequest, SubmitContentChainResponse, SubmitIdentityChainRequest, SubmitIdentityChainResponse, createRegistryServer } from './registry/index.js';
|
|
4
4
|
import 'multiformats';
|
|
5
5
|
import 'multiformats/cid';
|
|
6
6
|
import 'zod';
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ChainStore,
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
ContentOperationsParams,
|
|
4
|
+
ContentOperationsResponse,
|
|
5
5
|
IdentityOperationsParams,
|
|
6
6
|
IdentityOperationsResponse,
|
|
7
7
|
RegistryError,
|
|
8
|
-
|
|
8
|
+
ResolveContentResponse,
|
|
9
9
|
ResolveIdentityResponse,
|
|
10
10
|
ResolveOperationResponse,
|
|
11
11
|
SubmitContentChainRequest,
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
SubmitIdentityChainRequest,
|
|
14
14
|
SubmitIdentityChainResponse,
|
|
15
15
|
createRegistryServer
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-U6DANYPT.js";
|
|
17
17
|
import {
|
|
18
18
|
ContentOperation,
|
|
19
19
|
ED25519_PRIV_MULTICODEC,
|
|
@@ -23,13 +23,13 @@ import {
|
|
|
23
23
|
VerifiedIdentity,
|
|
24
24
|
decodeMultikey,
|
|
25
25
|
deriveChainIdentifier,
|
|
26
|
-
|
|
26
|
+
deriveContentId,
|
|
27
27
|
encodeEd25519Multikey,
|
|
28
28
|
signContentOperation,
|
|
29
29
|
signIdentityOperation,
|
|
30
30
|
verifyContentChain,
|
|
31
31
|
verifyIdentityChain
|
|
32
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-VEBMLR37.js";
|
|
33
33
|
import {
|
|
34
34
|
JwsVerificationError,
|
|
35
35
|
JwtVerificationError,
|
|
@@ -56,10 +56,10 @@ import {
|
|
|
56
56
|
export {
|
|
57
57
|
ChainStore,
|
|
58
58
|
ContentOperation,
|
|
59
|
+
ContentOperationsParams,
|
|
60
|
+
ContentOperationsResponse,
|
|
59
61
|
ED25519_PRIV_MULTICODEC,
|
|
60
62
|
ED25519_PUB_MULTICODEC,
|
|
61
|
-
EntityOperationsParams,
|
|
62
|
-
EntityOperationsResponse,
|
|
63
63
|
IdentityOperation,
|
|
64
64
|
IdentityOperationsParams,
|
|
65
65
|
IdentityOperationsResponse,
|
|
@@ -67,7 +67,7 @@ export {
|
|
|
67
67
|
JwtVerificationError,
|
|
68
68
|
MultikeyPublicKey,
|
|
69
69
|
RegistryError,
|
|
70
|
-
|
|
70
|
+
ResolveContentResponse,
|
|
71
71
|
ResolveIdentityResponse,
|
|
72
72
|
ResolveOperationResponse,
|
|
73
73
|
SubmitContentChainRequest,
|
|
@@ -86,7 +86,7 @@ export {
|
|
|
86
86
|
decodeJwtUnsafe,
|
|
87
87
|
decodeMultikey,
|
|
88
88
|
deriveChainIdentifier,
|
|
89
|
-
|
|
89
|
+
deriveContentId,
|
|
90
90
|
encodeEd25519Multikey,
|
|
91
91
|
generateId,
|
|
92
92
|
generateIdNoPrefix,
|
package/dist/registry/index.d.ts
CHANGED
|
@@ -75,26 +75,26 @@ declare const SubmitContentChainRequest: z.ZodObject<{
|
|
|
75
75
|
}, z.core.$strict>;
|
|
76
76
|
type SubmitContentChainRequest = z.infer<typeof SubmitContentChainRequest>;
|
|
77
77
|
declare const SubmitContentChainResponse: z.ZodObject<{
|
|
78
|
-
|
|
78
|
+
contentId: z.ZodString;
|
|
79
79
|
isDeleted: z.ZodBoolean;
|
|
80
80
|
currentDocumentCID: z.ZodNullable<z.ZodString>;
|
|
81
81
|
genesisCID: z.ZodString;
|
|
82
82
|
headCID: z.ZodString;
|
|
83
83
|
}, z.core.$strict>;
|
|
84
84
|
type SubmitContentChainResponse = z.infer<typeof SubmitContentChainResponse>;
|
|
85
|
-
declare const
|
|
86
|
-
|
|
85
|
+
declare const ResolveContentResponse: z.ZodObject<{
|
|
86
|
+
contentId: z.ZodString;
|
|
87
87
|
isDeleted: z.ZodBoolean;
|
|
88
88
|
currentDocumentCID: z.ZodNullable<z.ZodString>;
|
|
89
89
|
genesisCID: z.ZodString;
|
|
90
90
|
headCID: z.ZodString;
|
|
91
91
|
}, z.core.$strict>;
|
|
92
|
-
type
|
|
93
|
-
declare const
|
|
92
|
+
type ResolveContentResponse = SubmitContentChainResponse;
|
|
93
|
+
declare const ContentOperationsParams: z.ZodObject<{
|
|
94
94
|
cursor: z.ZodOptional<z.ZodString>;
|
|
95
95
|
limit: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
96
96
|
}, z.core.$strip>;
|
|
97
|
-
declare const
|
|
97
|
+
declare const ContentOperationsResponse: z.ZodObject<{
|
|
98
98
|
operations: z.ZodArray<z.ZodObject<{
|
|
99
99
|
cid: z.ZodString;
|
|
100
100
|
jwsToken: z.ZodString;
|
|
@@ -102,7 +102,7 @@ declare const EntityOperationsResponse: z.ZodObject<{
|
|
|
102
102
|
}, z.core.$strict>>;
|
|
103
103
|
nextCursor: z.ZodNullable<z.ZodString>;
|
|
104
104
|
}, z.core.$strict>;
|
|
105
|
-
type
|
|
105
|
+
type ContentOperationsResponse = z.infer<typeof ContentOperationsResponse>;
|
|
106
106
|
declare const ResolveOperationResponse: z.ZodObject<{
|
|
107
107
|
cid: z.ZodString;
|
|
108
108
|
jwsToken: z.ZodString;
|
|
@@ -119,8 +119,8 @@ interface StoredChain {
|
|
|
119
119
|
operations: OperationEntry[];
|
|
120
120
|
}
|
|
121
121
|
declare class ChainStore {
|
|
122
|
-
private
|
|
123
|
-
private
|
|
122
|
+
private identityChains;
|
|
123
|
+
private contentChains;
|
|
124
124
|
getIdentityChain(did: string): StoredChain | undefined;
|
|
125
125
|
/**
|
|
126
126
|
* Submit an identity chain. Returns 'accepted' | 'noop' | 'conflict'.
|
|
@@ -129,8 +129,8 @@ declare class ChainStore {
|
|
|
129
129
|
* - conflict: submitted chain diverges from stored chain (fork)
|
|
130
130
|
*/
|
|
131
131
|
submitIdentityChain(did: string, operations: OperationEntry[]): 'accepted' | 'noop' | 'conflict';
|
|
132
|
-
|
|
133
|
-
|
|
132
|
+
getContentChain(contentId: string): StoredChain | undefined;
|
|
133
|
+
submitContentChain(contentId: string, operations: OperationEntry[]): 'accepted' | 'noop' | 'conflict';
|
|
134
134
|
getOperation(cid: string): OperationEntry | undefined;
|
|
135
135
|
private submitChain;
|
|
136
136
|
}
|
|
@@ -140,4 +140,4 @@ declare const createRegistryServer: (store?: ChainStore) => {
|
|
|
140
140
|
store: ChainStore;
|
|
141
141
|
};
|
|
142
142
|
|
|
143
|
-
export { ChainStore,
|
|
143
|
+
export { ChainStore, ContentOperationsParams, ContentOperationsResponse, IdentityOperationsParams, IdentityOperationsResponse, OperationEntry, PaginationParams, RegistryError, ResolveContentResponse, ResolveIdentityResponse, ResolveOperationResponse, type StoredChain, SubmitContentChainRequest, SubmitContentChainResponse, SubmitIdentityChainRequest, SubmitIdentityChainResponse, createRegistryServer };
|
package/dist/registry/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ChainStore,
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
ContentOperationsParams,
|
|
4
|
+
ContentOperationsResponse,
|
|
5
5
|
IdentityOperationsParams,
|
|
6
6
|
IdentityOperationsResponse,
|
|
7
7
|
RegistryError,
|
|
8
|
-
|
|
8
|
+
ResolveContentResponse,
|
|
9
9
|
ResolveIdentityResponse,
|
|
10
10
|
ResolveOperationResponse,
|
|
11
11
|
SubmitContentChainRequest,
|
|
@@ -13,17 +13,17 @@ import {
|
|
|
13
13
|
SubmitIdentityChainRequest,
|
|
14
14
|
SubmitIdentityChainResponse,
|
|
15
15
|
createRegistryServer
|
|
16
|
-
} from "../chunk-
|
|
17
|
-
import "../chunk-
|
|
16
|
+
} from "../chunk-U6DANYPT.js";
|
|
17
|
+
import "../chunk-VEBMLR37.js";
|
|
18
18
|
import "../chunk-ZXXP5W5N.js";
|
|
19
19
|
export {
|
|
20
20
|
ChainStore,
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
ContentOperationsParams,
|
|
22
|
+
ContentOperationsResponse,
|
|
23
23
|
IdentityOperationsParams,
|
|
24
24
|
IdentityOperationsResponse,
|
|
25
25
|
RegistryError,
|
|
26
|
-
|
|
26
|
+
ResolveContentResponse,
|
|
27
27
|
ResolveIdentityResponse,
|
|
28
28
|
ResolveOperationResponse,
|
|
29
29
|
SubmitContentChainRequest,
|
package/openapi.yaml
CHANGED
|
@@ -101,13 +101,13 @@ paths:
|
|
|
101
101
|
schema:
|
|
102
102
|
$ref: '#/components/schemas/Error'
|
|
103
103
|
|
|
104
|
-
/
|
|
104
|
+
/content:
|
|
105
105
|
post:
|
|
106
106
|
operationId: submitContentChain
|
|
107
107
|
summary: Submit or extend a content chain
|
|
108
108
|
description: |
|
|
109
109
|
Verifies the chain (resolving signing keys from stored identity chains),
|
|
110
|
-
derives the
|
|
110
|
+
derives the content ID from the genesis CID, and stores it.
|
|
111
111
|
Same status code semantics as identity submission.
|
|
112
112
|
requestBody:
|
|
113
113
|
required: true
|
|
@@ -121,13 +121,13 @@ paths:
|
|
|
121
121
|
content:
|
|
122
122
|
application/json:
|
|
123
123
|
schema:
|
|
124
|
-
$ref: '#/components/schemas/
|
|
124
|
+
$ref: '#/components/schemas/ContentState'
|
|
125
125
|
'200':
|
|
126
126
|
description: Chain already stored (noop)
|
|
127
127
|
content:
|
|
128
128
|
application/json:
|
|
129
129
|
schema:
|
|
130
|
-
$ref: '#/components/schemas/
|
|
130
|
+
$ref: '#/components/schemas/ContentState'
|
|
131
131
|
'400':
|
|
132
132
|
description: Invalid chain
|
|
133
133
|
content:
|
|
@@ -141,32 +141,32 @@ paths:
|
|
|
141
141
|
schema:
|
|
142
142
|
$ref: '#/components/schemas/Error'
|
|
143
143
|
|
|
144
|
-
/
|
|
144
|
+
/content/{contentId}:
|
|
145
145
|
get:
|
|
146
|
-
operationId:
|
|
147
|
-
summary: Resolve current
|
|
146
|
+
operationId: resolveContent
|
|
147
|
+
summary: Resolve current content state
|
|
148
148
|
parameters:
|
|
149
|
-
- $ref: '#/components/parameters/
|
|
149
|
+
- $ref: '#/components/parameters/contentId'
|
|
150
150
|
responses:
|
|
151
151
|
'200':
|
|
152
|
-
description:
|
|
152
|
+
description: Content state
|
|
153
153
|
content:
|
|
154
154
|
application/json:
|
|
155
155
|
schema:
|
|
156
|
-
$ref: '#/components/schemas/
|
|
156
|
+
$ref: '#/components/schemas/ContentState'
|
|
157
157
|
'404':
|
|
158
|
-
description:
|
|
158
|
+
description: Content not found
|
|
159
159
|
content:
|
|
160
160
|
application/json:
|
|
161
161
|
schema:
|
|
162
162
|
$ref: '#/components/schemas/Error'
|
|
163
163
|
|
|
164
|
-
/
|
|
164
|
+
/content/{contentId}/operations:
|
|
165
165
|
get:
|
|
166
|
-
operationId:
|
|
166
|
+
operationId: listContentOperations
|
|
167
167
|
summary: List content chain operations (newest-first, paginated)
|
|
168
168
|
parameters:
|
|
169
|
-
- $ref: '#/components/parameters/
|
|
169
|
+
- $ref: '#/components/parameters/contentId'
|
|
170
170
|
- $ref: '#/components/parameters/cursor'
|
|
171
171
|
- $ref: '#/components/parameters/limit'
|
|
172
172
|
responses:
|
|
@@ -177,7 +177,7 @@ paths:
|
|
|
177
177
|
schema:
|
|
178
178
|
$ref: '#/components/schemas/PaginatedOperations'
|
|
179
179
|
'404':
|
|
180
|
-
description:
|
|
180
|
+
description: Content not found
|
|
181
181
|
content:
|
|
182
182
|
application/json:
|
|
183
183
|
schema:
|
|
@@ -216,8 +216,8 @@ components:
|
|
|
216
216
|
pattern: '^did:dfos:[2346789acdefhknrtvz]{22}$'
|
|
217
217
|
example: 'did:dfos:e3vvtck42d4eacdnzvtrn6'
|
|
218
218
|
|
|
219
|
-
|
|
220
|
-
name:
|
|
219
|
+
contentId:
|
|
220
|
+
name: contentId
|
|
221
221
|
in: path
|
|
222
222
|
required: true
|
|
223
223
|
schema:
|
|
@@ -305,11 +305,11 @@ components:
|
|
|
305
305
|
items:
|
|
306
306
|
$ref: '#/components/schemas/MultikeyPublicKey'
|
|
307
307
|
|
|
308
|
-
|
|
308
|
+
ContentState:
|
|
309
309
|
type: object
|
|
310
|
-
required: [
|
|
310
|
+
required: [contentId, isDeleted, currentDocumentCID, genesisCID, headCID]
|
|
311
311
|
properties:
|
|
312
|
-
|
|
312
|
+
contentId:
|
|
313
313
|
type: string
|
|
314
314
|
example: '67t27rzc83v7c22n9t6z7c'
|
|
315
315
|
isDeleted:
|