@interop/did-cli 0.8.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +158 -0
- package/README.md +386 -4
- package/dist/commands/did.d.ts +9 -0
- package/dist/commands/did.d.ts.map +1 -1
- package/dist/commands/did.js +1041 -20
- package/dist/commands/did.js.map +1 -1
- package/dist/commands/edv.d.ts +63 -0
- package/dist/commands/edv.d.ts.map +1 -0
- package/dist/commands/edv.js +627 -0
- package/dist/commands/edv.js.map +1 -0
- package/dist/commands/key.d.ts.map +1 -1
- package/dist/commands/key.js +44 -8
- package/dist/commands/key.js.map +1 -1
- package/dist/commands/was/space.d.ts +17 -0
- package/dist/commands/was/space.d.ts.map +1 -1
- package/dist/commands/was/space.js +37 -2
- package/dist/commands/was/space.js.map +1 -1
- package/dist/commands/was.d.ts +1 -1
- package/dist/commands/was.d.ts.map +1 -1
- package/dist/commands/was.js +11 -2
- package/dist/commands/was.js.map +1 -1
- package/dist/edv/core.d.ts +60 -0
- package/dist/edv/core.d.ts.map +1 -0
- package/dist/edv/core.js +75 -0
- package/dist/edv/core.js.map +1 -0
- package/dist/edv/document.d.ts +25 -0
- package/dist/edv/document.d.ts.map +1 -0
- package/dist/edv/document.js +19 -0
- package/dist/edv/document.js.map +1 -0
- package/dist/edv/hmac.d.ts +26 -0
- package/dist/edv/hmac.d.ts.map +1 -0
- package/dist/edv/hmac.js +67 -0
- package/dist/edv/hmac.js.map +1 -0
- package/dist/edv/recipients.d.ts +65 -0
- package/dist/edv/recipients.d.ts.map +1 -0
- package/dist/edv/recipients.js +253 -0
- package/dist/edv/recipients.js.map +1 -0
- package/dist/edv/stream.d.ts +69 -0
- package/dist/edv/stream.d.ts.map +1 -0
- package/dist/edv/stream.js +169 -0
- package/dist/edv/stream.js.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/keys/webvh-driver.d.ts +24 -0
- package/dist/keys/webvh-driver.d.ts.map +1 -0
- package/dist/keys/webvh-driver.js +73 -0
- package/dist/keys/webvh-driver.js.map +1 -0
- package/dist/keys/webvh-signer.d.ts +29 -0
- package/dist/keys/webvh-signer.d.ts.map +1 -0
- package/dist/keys/webvh-signer.js +44 -0
- package/dist/keys/webvh-signer.js.map +1 -0
- package/dist/keys/webvh-update.d.ts +33 -0
- package/dist/keys/webvh-update.d.ts.map +1 -0
- package/dist/keys/webvh-update.js +62 -0
- package/dist/keys/webvh-update.js.map +1 -0
- package/dist/meta.d.ts +1 -0
- package/dist/meta.d.ts.map +1 -1
- package/dist/meta.js +1 -1
- package/dist/meta.js.map +1 -1
- package/dist/storage.d.ts +80 -4
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +75 -6
- package/dist/storage.js.map +1 -1
- package/dist/was/io.d.ts +14 -0
- package/dist/was/io.d.ts.map +1 -1
- package/dist/was/io.js +20 -7
- package/dist/was/io.js.map +1 -1
- package/dist/was/registry.d.ts +2 -1
- package/dist/was/registry.d.ts.map +1 -1
- package/dist/was/registry.js +16 -5
- package/dist/was/registry.js.map +1 -1
- package/package.json +9 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,163 @@
|
|
|
1
1
|
# History
|
|
2
2
|
|
|
3
|
+
## 0.10.0 - 2026-06-25
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- `did show` now resolves a `did:webvh` DID from its stored history log
|
|
8
|
+
(`<did>.jsonl`) -- the source of truth -- rather than printing the saved
|
|
9
|
+
document snapshot, so the output reflects every appended log entry. With
|
|
10
|
+
`--meta` it also reports the parameters resolved from the log: version id,
|
|
11
|
+
last-updated time, portability, pre-rotation, deactivation status, and the
|
|
12
|
+
count of update keys, witnesses, and watchers (also included in
|
|
13
|
+
`--meta --json`). A `did:webvh` DID with no stored log still falls back to the
|
|
14
|
+
saved snapshot.
|
|
15
|
+
- Add `did add-service <did>` and `did remove-service <did>`, which add or
|
|
16
|
+
remove a [service](https://www.w3.org/TR/did-core/#services) entry on a stored
|
|
17
|
+
`did:web` or `did:webvh` DID (the DID may be given as a metadata handle).
|
|
18
|
+
`--id` accepts a bare fragment (expanded to `<did>#fragment`) or a full id;
|
|
19
|
+
the type comes from `--type` (repeatable) and the endpoint from either
|
|
20
|
+
`--endpoint` (repeatable) or `--endpoint-json` (a raw JSON value). For
|
|
21
|
+
`did:web` this edits the stored document in place; for `did:webvh` it appends
|
|
22
|
+
a sparse log entry that carries the update keys and document verification
|
|
23
|
+
methods forward unchanged -- except that, when pre-rotation is armed, the
|
|
24
|
+
update-key ratchet is advanced as part of the change (the staged key signs the
|
|
25
|
+
entry), with `--keep-old-key` and `-y` / `--yes` honored as in
|
|
26
|
+
`webvh rotate-keys`.
|
|
27
|
+
- Add richer create-time options to `did create webvh`, each declared in the
|
|
28
|
+
signed `did.jsonl` log `parameters`: `--portable` / `--no-portable` (portable
|
|
29
|
+
is the default -- a portable DID can later be moved to another domain),
|
|
30
|
+
`--witness <did...>` (repeatable; declare witness `did:key` DIDs authorized to
|
|
31
|
+
co-sign log entries) with `--witness-threshold <n>` (defaults to the number of
|
|
32
|
+
witnesses; requires `--witness`), and `--watcher <url...>` (repeatable;
|
|
33
|
+
`https://` or `http://localhost`). Witnesses are **declaration only** for now
|
|
34
|
+
-- generating witness proofs / a `did-witness.json` sidecar remains out of
|
|
35
|
+
scope.
|
|
36
|
+
- Add key **pre-rotation** to `did:webvh`. `did create webvh` now arms
|
|
37
|
+
pre-rotation by default: the DID commits, in advance, to the hash of the key
|
|
38
|
+
allowed to perform the next update, so a compromise of the active update key
|
|
39
|
+
cannot seize the DID. Update keys are now **decoupled** from the document's
|
|
40
|
+
verification key -- `create` generates an active update key A, a staged next
|
|
41
|
+
update key B (whose hash is committed as `nextKeyHashes`), and a separate
|
|
42
|
+
document key V (wired into the verification relationships as before). On
|
|
43
|
+
`--save`, V is stored in `<did>.keys.json` (keyed by its document
|
|
44
|
+
verification-method id, so it can be selected for signing) and A+B in a new
|
|
45
|
+
`<did>.update-keys.json` sidecar. Pass `--no-prerotation` to create without a
|
|
46
|
+
staged key. Requires `@interop/did-method-webvh` >= 3.2.0 (for the exported
|
|
47
|
+
`deriveNextKeyHash`).
|
|
48
|
+
- Add `did webvh rotate-keys <did>`, which rotates a stored `did:webvh` DID's
|
|
49
|
+
update (authorization) key and never touches the document's verification
|
|
50
|
+
methods. With no flags it advances the pre-rotation ratchet in one step --
|
|
51
|
+
revealing and activating the staged key, then staging a fresh one -- and
|
|
52
|
+
deletes the retired key's secret by default. Flags: `--stop-prerotation` (turn
|
|
53
|
+
pre-rotation off), `--enable-prerotation` (turn it on for a DID without it;
|
|
54
|
+
alone, stage only), `--update-key <multibase...>` (rotate to specific key(s)
|
|
55
|
+
in ordinary mode; rejected while pre-rotation is armed), `--keep-old-key`
|
|
56
|
+
(retain the retired secret), and `-y, --yes` (skip the confirmation prompt).
|
|
57
|
+
- Add `was space meta <space>`, which updates a registered space's local
|
|
58
|
+
registry metadata (`--handle` and/or `--description`) only, leaving the
|
|
59
|
+
server-side space untouched. At least one of the two flags is required;
|
|
60
|
+
passing an empty string (e.g. `--handle ''`) clears that field.
|
|
61
|
+
|
|
62
|
+
- Implement `did create webvh`, which creates a real did:webvh DID via the
|
|
63
|
+
`@interop/did-method-webvh` library (Ed25519 / `eddsa-jcs-2022` only for now;
|
|
64
|
+
`--type ecdsa` is rejected). Requires `--url` (passed as the DID's `address`)
|
|
65
|
+
and supports the same `--with-seed` / `--save` / `--handle` / `--description`
|
|
66
|
+
flags as the other methods. did:webvh is a two-part DID: on `--save`, the
|
|
67
|
+
resolved DID document, keys, and metadata are written as usual under
|
|
68
|
+
`dids/webvh/`, plus the signed `did.jsonl` history log as a raw
|
|
69
|
+
newline-delimited `<did>.jsonl` file alongside them; `did remove` deletes the
|
|
70
|
+
log too. The generated DID is portable, and its single Ed25519 key is wired
|
|
71
|
+
into the same verification relationships as did:web (`authentication`,
|
|
72
|
+
`assertionMethod`, `capabilityDelegation`, `capabilityInvocation`; not
|
|
73
|
+
`keyAgreement`). Requires `@interop/did-method-webvh` >= 3.1.0. Resolving a
|
|
74
|
+
stored webvh DID, witnesses, and the parallel did:web alias are deferred.
|
|
75
|
+
(Update-key rotation and pre-rotation are implemented separately below.)
|
|
76
|
+
- Support `did get`/`did resolve` for `did:webvh` DIDs. A `did:webvh` driver
|
|
77
|
+
(`src/keys/webvh-driver.ts`) wraps `resolveDID` from
|
|
78
|
+
`@interop/did-method-webvh` -- fetching and verifying the DID's history log --
|
|
79
|
+
and is registered onto the security document loader's resolver via the new
|
|
80
|
+
injectable `securityLoader({ didResolver })` seam, so the `did:webvh`
|
|
81
|
+
dependency stays out of the shared loader. A bare DID resolves to its DID
|
|
82
|
+
document; a `did#fragment` URL is dereferenced to its verification method.
|
|
83
|
+
Requires `@interop/security-document-loader` >= 9.4.0.
|
|
84
|
+
|
|
85
|
+
### Changed
|
|
86
|
+
|
|
87
|
+
- Update to `@interop/did-method-webvh` >= 3.3.0. `updateDID` now performs a
|
|
88
|
+
sparse update -- it carries the prior DID document state forward and overlays
|
|
89
|
+
only the fields an update actually supplies -- so `did webvh rotate-keys` no
|
|
90
|
+
longer reconstructs and re-supplies the document's verification methods to
|
|
91
|
+
keep them unchanged; the key-only rotation simply omits all document
|
|
92
|
+
directives.
|
|
93
|
+
|
|
94
|
+
## 0.9.0 - 2026-06-14
|
|
95
|
+
|
|
96
|
+
### Added
|
|
97
|
+
|
|
98
|
+
- Implement `did get <did>` (alias: `resolve`), which resolves a DID to its DID
|
|
99
|
+
document through `@interop/security-document-loader`'s document loader
|
|
100
|
+
(did:key offline, did:web fetched). A DID URL (a `did#fragment` key id)
|
|
101
|
+
dereferences straight to its verification method. Unlike `did show`, which
|
|
102
|
+
reads local storage, `did get` resolves live.
|
|
103
|
+
- Add an `edv` command group with `encrypt` and `decrypt` subcommands
|
|
104
|
+
(Layer 1: raw JWE). `edv encrypt [file]` encrypts stdin or a file to one or
|
|
105
|
+
more X25519 recipients and emits a single flattened JWE (the `jwe` field of
|
|
106
|
+
an EDV Document) to stdout or an `-o` file (convention `*.jwe.json`);
|
|
107
|
+
`edv decrypt [file]` reverses it. Encryption is public-key (key-agreement)
|
|
108
|
+
only, via `@interop/minimal-cipher` with its default algorithm
|
|
109
|
+
(`ECDH-ES+A256KW` key wrap, `XC20P` content encryption). A recipient
|
|
110
|
+
(`-r/--recipient`, repeatable) is a raw X25519 `publicKeyMultibase`, a wallet
|
|
111
|
+
key fingerprint/handle, or a DID / DID URL (its `keyAgreement` key, typed
|
|
112
|
+
either `X25519KeyAgreementKey2020` or `Multikey`); `--recipient-file` reads a
|
|
113
|
+
key-document JSON. `--json` switches both commands
|
|
114
|
+
to object semantics. On decrypt, the secret key is given with `-k/--key` or
|
|
115
|
+
auto-selected from the wallet by matching a recipient `kid`; a non-recipient
|
|
116
|
+
key exits non-zero with a clear error. The full EDV Document envelope,
|
|
117
|
+
chunked streams, and HMAC-blinded indexing are deferred to Layer 2.
|
|
118
|
+
- Add EDV Document support to `edv` (Layer 2, Phase 1). `edv encrypt --document`
|
|
119
|
+
wraps the JWE in a full EDV Document envelope `{ id, sequence, indexed, jwe }`
|
|
120
|
+
(convention `*.edvdoc.json`), encrypting the input as the document `content`
|
|
121
|
+
with an optional `--meta <json>` object alongside it (both encrypted inside the
|
|
122
|
+
`jwe`); `id` is a fresh identity-multihash multibase value and `sequence`
|
|
123
|
+
starts at `0`, byte-faithful to `@interop/edv-client`'s `EdvClientCore` without
|
|
124
|
+
taking that dependency. `--update <file>` versions an existing document
|
|
125
|
+
(reusing its `id`, incrementing `sequence`, and merging its recipients).
|
|
126
|
+
`edv decrypt` detects an envelope automatically and emits its decrypted
|
|
127
|
+
`content` (reporting `meta`/`stream` on stderr); `--document` requires one.
|
|
128
|
+
Chunked streams and HMAC-blinded indexing remain deferred to later phases.
|
|
129
|
+
- Add chunked-stream support to `edv` (Layer 2, Phase 2). `edv encrypt --stream`
|
|
130
|
+
encrypts the input as a sequence of fixed-size chunks (`--chunk-size <bytes>`,
|
|
131
|
+
default 1 MiB) and writes a bundle directory (convention `*.edvdoc/`, so `-o`
|
|
132
|
+
is required): a `document.json` EDV Document carrying a cleartext
|
|
133
|
+
`stream: { sequence, chunks }` descriptor, plus one `chunks/<index>.jwe.json`
|
|
134
|
+
per chunk (`{ sequence, index, offset, jwe }`), mirroring how an EDV / WAS
|
|
135
|
+
server stores stream bytes separately from the document. `--meta` and
|
|
136
|
+
`--update` (a file or bundle) work as for `--document`. `edv decrypt`
|
|
137
|
+
recognizes a bundle directory, reassembles the chunks in order, and writes the
|
|
138
|
+
original bytes to `-o`/stdout (reporting `content`/`meta`/`stream` on stderr).
|
|
139
|
+
HMAC-blinded indexing remains deferred to Layer 2, Phase 3.
|
|
140
|
+
- Add HMAC-blinded indexing to `edv` (Layer 2, Phase 3). In
|
|
141
|
+
`--document`/`--stream` mode, `--index <attribute>` (repeatable; a dotted path
|
|
142
|
+
into `content`/`meta`) populates the envelope's `indexed` array with entries
|
|
143
|
+
whose attribute names and values are HMAC-blinded, so a document is searchable
|
|
144
|
+
the way an EDV / WAS server indexes it without the server learning the
|
|
145
|
+
cleartext. `--unique` marks every `--index` attribute unique; `--hmac <ref>`
|
|
146
|
+
selects the blinding key (auto-selected when the wallet holds exactly one). The
|
|
147
|
+
same key over the same value yields a stable blinded entry, matchable across
|
|
148
|
+
documents. The EDV Document envelope and its blinded `indexed` array are now
|
|
149
|
+
assembled and unwrapped by `@interop/edv-client`'s `EdvClientCore` (a new
|
|
150
|
+
dependency), converging on the reference implementation rather than the
|
|
151
|
+
hand-rolled envelope used in Phases 1-2.
|
|
152
|
+
- Add `key create --type hmac`, generating a `Sha256HmacKey2019` HMAC key (a
|
|
153
|
+
32-byte symmetric secret used to blind EDV index attributes) via
|
|
154
|
+
`@interop/data-integrity-core`'s `SHA256HMACKey`. The key has no public half;
|
|
155
|
+
it is serialized with its secret as an `oct` JWK and identified by a random
|
|
156
|
+
`urn:uuid:` id. HMAC keys list with an `hmac` type label, and `key show`
|
|
157
|
+
prints only their `id`/`type` (never the secret). Like ecdsa/x25519, HMAC
|
|
158
|
+
generation is non-deterministic, so `--with-seed` / `SECRET_KEY_SEED` are not
|
|
159
|
+
supported.
|
|
160
|
+
|
|
3
161
|
## 0.8.0 - 2026-06-13
|
|
4
162
|
|
|
5
163
|
### Added
|
package/README.md
CHANGED
|
@@ -81,7 +81,7 @@ SECRET_KEY_SEED=z1AXVyT6G1Qk3E9cMPkDYY6wVRpZjVGWAZ3TfrAgFZkX6bv ./di key create
|
|
|
81
81
|
```
|
|
82
82
|
|
|
83
83
|
Specify an explicit key type with `--type` (defaults to `ed25519`; supported:
|
|
84
|
-
`ed25519`, `ecdsa`, `x25519`):
|
|
84
|
+
`ed25519`, `ecdsa`, `x25519`, `hmac`):
|
|
85
85
|
|
|
86
86
|
```
|
|
87
87
|
SECRET_KEY_SEED=z1Aaj5A4UCsd... ./di key create --type ed25519
|
|
@@ -132,6 +132,28 @@ private key in multibase encoding:
|
|
|
132
132
|
Like ECDSA, X25519 key generation is non-deterministic, so `--with-seed` and
|
|
133
133
|
`SECRET_KEY_SEED` are not supported with `--type x25519`.
|
|
134
134
|
|
|
135
|
+
Generate a `Sha256HmacKey2019` HMAC key -- a 32-byte symmetric secret used to
|
|
136
|
+
HMAC-blind EDV index attributes (see [Blinded indexing](#blinded-indexing---index))
|
|
137
|
+
-- with `--type hmac`:
|
|
138
|
+
|
|
139
|
+
```
|
|
140
|
+
./di key create --type hmac
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
It is serialized with the secret carried as an `oct` JWK (it has no public
|
|
144
|
+
half), identified by a random `urn:uuid:` id:
|
|
145
|
+
|
|
146
|
+
```json
|
|
147
|
+
{
|
|
148
|
+
"id": "urn:uuid:...",
|
|
149
|
+
"type": "Sha256HmacKey2019",
|
|
150
|
+
"secretKeyJwk": { "kty": "oct", "alg": "HS256", "k": "..." }
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
HMAC key generation is non-deterministic, so `--with-seed` and `SECRET_KEY_SEED`
|
|
155
|
+
are not supported with `--type hmac`.
|
|
156
|
+
|
|
135
157
|
Save the key to local wallet storage (`~/.config/did-cli-wallet/keys/` by default, or
|
|
136
158
|
`$WALLET_DIR/keys/` if set) with `--save`. A `.meta.json` metadata sidecar is
|
|
137
159
|
written next to the key, recording the creation timestamp; `--handle` (a short
|
|
@@ -431,6 +453,164 @@ seed-derivable, so `--with-seed` is not supported with `--type ecdsa` or
|
|
|
431
453
|
./di did add-key did:web:example.com --with-seed
|
|
432
454
|
```
|
|
433
455
|
|
|
456
|
+
#### Create a did:webvh DID
|
|
457
|
+
|
|
458
|
+
Generate a `did:webvh` DID. Like `did:web` it is tied to a domain, so `--url`
|
|
459
|
+
(the HTTPS url that will host the DID's history log) is required:
|
|
460
|
+
|
|
461
|
+
```
|
|
462
|
+
./di did create webvh --url https://example.com
|
|
463
|
+
{
|
|
464
|
+
"id": "did:webvh:Qm...:example.com",
|
|
465
|
+
"didDocument": { ... }
|
|
466
|
+
}
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
`did:webvh` separates two key roles: an **update (authorization) key** that
|
|
470
|
+
signs entries in the DID's append-only history log, and the **document
|
|
471
|
+
verification key** wired into the `authentication`, `assertionMethod`,
|
|
472
|
+
`capabilityDelegation`, and `capabilityInvocation` relationships. They are
|
|
473
|
+
distinct keys, so the update key can be rotated without ever disturbing the
|
|
474
|
+
document.
|
|
475
|
+
|
|
476
|
+
By default `did:webvh` arms **key pre-rotation**: the DID commits, in advance,
|
|
477
|
+
to the *hash* of the key allowed to perform the next update. A compromise of the
|
|
478
|
+
currently active update key cannot be used to seize the DID, because the
|
|
479
|
+
attacker still does not hold the pre-committed next key. So `create` generates
|
|
480
|
+
three keys: the active update key, a staged next update key (whose hash is
|
|
481
|
+
committed as `nextKeyHashes`), and the document key. Pass `--no-prerotation` to
|
|
482
|
+
create the DID without pre-rotation (no next key is staged):
|
|
483
|
+
|
|
484
|
+
```
|
|
485
|
+
./di did create webvh --url https://example.com --no-prerotation
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
Save the DID document, history log, and key material to local storage with
|
|
489
|
+
`--save` (written to `~/.config/did-cli-wallet/dids/webvh/` by default, or
|
|
490
|
+
`$DIDS_DIR` if set). The document key is stored in `<did>.keys.json`; the update
|
|
491
|
+
keys (active, and the staged next key when pre-rotation is on) are stored in a
|
|
492
|
+
separate `<did>.update-keys.json` sidecar, and the signed history log in
|
|
493
|
+
`<did>.jsonl`:
|
|
494
|
+
|
|
495
|
+
```
|
|
496
|
+
./di did create webvh --url https://example.com --save
|
|
497
|
+
DID saved to /home/user/.config/did-cli-wallet/dids/webvh/did:webvh:Qm...:example.com.json
|
|
498
|
+
DID history log saved to /home/user/.config/did-cli-wallet/dids/webvh/did:webvh:Qm...:example.com.jsonl
|
|
499
|
+
Update keys saved to /home/user/.config/did-cli-wallet/dids/webvh/did:webvh:Qm...:example.com.update-keys.json
|
|
500
|
+
{
|
|
501
|
+
"id": "did:webvh:Qm...:example.com",
|
|
502
|
+
"didDocument": { ... }
|
|
503
|
+
}
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
Only Ed25519 update keys are supported (the `eddsa-jcs-2022` cryptosuite the
|
|
507
|
+
method uses requires them), so `--type ecdsa` is rejected.
|
|
508
|
+
|
|
509
|
+
A few more create-time options are recorded in the signed history-log
|
|
510
|
+
`parameters`:
|
|
511
|
+
|
|
512
|
+
- `--portable` / `--no-portable` -- a **portable** DID (the default) can later
|
|
513
|
+
be moved to a different domain; `--no-portable` pins it to its origin.
|
|
514
|
+
- `--witness <did...>` -- declare one or more **witness** `did:key` DIDs
|
|
515
|
+
authorized to co-sign the DID's log entries (repeatable). `--witness-threshold
|
|
516
|
+
<n>` sets how many witness approvals are required (defaults to the number of
|
|
517
|
+
witnesses; it requires `--witness`). This only *declares* the witnesses;
|
|
518
|
+
actually generating witness proofs is not yet supported.
|
|
519
|
+
- `--watcher <url...>` -- declare one or more **watcher** URLs that monitor the
|
|
520
|
+
DID's log (repeatable; `https://`, or `http://localhost` for local testing).
|
|
521
|
+
|
|
522
|
+
```
|
|
523
|
+
./di did create webvh --url https://example.com \
|
|
524
|
+
--witness did:key:z6Mk... --witness did:key:z6Mk... --witness-threshold 1 \
|
|
525
|
+
--watcher https://watcher.example.com --save
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
#### Rotate a did:webvh update key
|
|
529
|
+
|
|
530
|
+
Rotate the update (authorization) key of a locally stored `did:webvh` DID with
|
|
531
|
+
`did webvh rotate-keys`. This appends a new entry to the DID's history log and
|
|
532
|
+
**never touches the document's verification methods** (those are separate keys).
|
|
533
|
+
|
|
534
|
+
With no flags it advances the pre-rotation ratchet in one step: it reveals and
|
|
535
|
+
activates the previously staged next key (signing the new entry with it), and
|
|
536
|
+
stages a fresh next key for the following rotation. The retired update key's
|
|
537
|
+
secret is deleted by default -- a retired key is only ever needed to *verify*
|
|
538
|
+
historic log entries, which uses the public key from the log, not the secret:
|
|
539
|
+
|
|
540
|
+
```
|
|
541
|
+
./di did webvh rotate-keys did:webvh:Qm...:example.com
|
|
542
|
+
DID document saved to /home/user/.config/did-cli-wallet/dids/webvh/did:webvh:Qm...:example.com.json
|
|
543
|
+
DID history log saved to /home/user/.config/did-cli-wallet/dids/webvh/did:webvh:Qm...:example.com.jsonl
|
|
544
|
+
Update keys saved to /home/user/.config/did-cli-wallet/dids/webvh/did:webvh:Qm...:example.com.update-keys.json
|
|
545
|
+
Pre-rotation is armed: a next update key is staged.
|
|
546
|
+
{
|
|
547
|
+
"id": "did:webvh:Qm...:example.com",
|
|
548
|
+
"didDocument": { ... }
|
|
549
|
+
}
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
Pre-rotation requires the staged key to sign its own activation, so the staged
|
|
553
|
+
secret in `<did>.update-keys.json` is what makes the next rotation possible --
|
|
554
|
+
losing it means the DID can never be updated again. Keep that sidecar backed up.
|
|
555
|
+
|
|
556
|
+
Flags:
|
|
557
|
+
|
|
558
|
+
- `--stop-prerotation` -- rotate but commit no next-key hash; pre-rotation turns
|
|
559
|
+
**off** after this entry.
|
|
560
|
+
- `--enable-prerotation` -- for a DID with pre-rotation currently off, turn it
|
|
561
|
+
**on** by staging a next key. Alone it stages only (the active key is
|
|
562
|
+
unchanged, signed by the current key).
|
|
563
|
+
- `--update-key <multibase...>` -- in ordinary (non-pre-rotation) mode, rotate
|
|
564
|
+
to specific update key(s) by public key instead of generating a fresh one.
|
|
565
|
+
Rejected while pre-rotation is armed, where the next keys are fixed by the
|
|
566
|
+
prior commitment.
|
|
567
|
+
- `--keep-old-key` -- retain the retired update key's secret in the sidecar
|
|
568
|
+
instead of dropping it.
|
|
569
|
+
- `-y`, `--yes` -- skip the confirmation prompt (rotation is hard to undo).
|
|
570
|
+
|
|
571
|
+
#### Add or remove a service entry
|
|
572
|
+
|
|
573
|
+
Add or remove a [service](https://www.w3.org/TR/did-core/#services) entry on a
|
|
574
|
+
locally stored `did:web` or `did:webvh` DID. The DID may be given as a full DID
|
|
575
|
+
or a metadata handle. For `did:web` this edits the stored document in place; for
|
|
576
|
+
`did:webvh` it appends a new entry to the history log.
|
|
577
|
+
|
|
578
|
+
```
|
|
579
|
+
./di did add-service did:web:example.com \
|
|
580
|
+
--id files --type LinkedDomains --endpoint https://example.com
|
|
581
|
+
{
|
|
582
|
+
"id": "did:web:example.com",
|
|
583
|
+
"didDocument": { ..., "service": [ ... ] }
|
|
584
|
+
}
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
`--id` accepts a bare fragment (`files`), which is expanded to `<did>#files`, or
|
|
588
|
+
a full service id. The service type comes from `--type` (repeat for multiple
|
|
589
|
+
types). The endpoint comes from either `--endpoint` (repeat for multiple values;
|
|
590
|
+
a single value stays a string, several become an array) or `--endpoint-json` (a
|
|
591
|
+
raw JSON value, e.g. an object); exactly one of the two is required.
|
|
592
|
+
|
|
593
|
+
```
|
|
594
|
+
./di did add-service did:web:example.com \
|
|
595
|
+
--id dwn --type DecentralizedWebNode \
|
|
596
|
+
--endpoint-json '{"nodes":["https://dwn.example"]}'
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
Remove a service by its id (a bare fragment is expanded the same way):
|
|
600
|
+
|
|
601
|
+
```
|
|
602
|
+
./di did remove-service did:web:example.com --id files
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
For `did:webvh`, both commands sign and append a log entry (and so accept `-y` /
|
|
606
|
+
`--yes` to skip the confirmation prompt). The update keys and document
|
|
607
|
+
verification methods are carried forward unchanged -- with one exception: when
|
|
608
|
+
pre-rotation is armed the library requires the staged key to sign the entry, so
|
|
609
|
+
the update-key ratchet is **advanced** as part of the change (the staged key is
|
|
610
|
+
revealed and activated, and a fresh next key is staged), exactly as in
|
|
611
|
+
`webvh rotate-keys`. Pass `--keep-old-key` to retain the retired update key's
|
|
612
|
+
secret in that case.
|
|
613
|
+
|
|
434
614
|
#### List DIDs
|
|
435
615
|
|
|
436
616
|
List the DIDs saved in local storage (via `did create --save`) as a table of
|
|
@@ -466,6 +646,34 @@ did:key:z6Mkr...
|
|
|
466
646
|
did:key:z6Mks...
|
|
467
647
|
```
|
|
468
648
|
|
|
649
|
+
#### Resolve a DID
|
|
650
|
+
|
|
651
|
+
Resolve a DID to its DID document through the security document loader. Unlike
|
|
652
|
+
`did show` (which reads local storage), `did get` resolves live: did:key is
|
|
653
|
+
resolved offline, did:web is fetched over HTTPS, and did:webvh is resolved by
|
|
654
|
+
fetching and verifying its history log over HTTPS. Pass a DID URL (a
|
|
655
|
+
`did#fragment` key id) to dereference straight to its verification method:
|
|
656
|
+
|
|
657
|
+
```
|
|
658
|
+
./di did get did:key:z6Mkr...
|
|
659
|
+
{
|
|
660
|
+
"@context": [ ... ],
|
|
661
|
+
"id": "did:key:z6Mkr...",
|
|
662
|
+
"verificationMethod": [ ... ],
|
|
663
|
+
...
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
./di did get did:key:z6Mkr...#z6Mkr...
|
|
667
|
+
{
|
|
668
|
+
"id": "did:key:z6Mkr...#z6Mkr...",
|
|
669
|
+
"type": "Ed25519VerificationKey2020",
|
|
670
|
+
"controller": "did:key:z6Mkr...",
|
|
671
|
+
"publicKeyMultibase": "z6Mkr..."
|
|
672
|
+
}
|
|
673
|
+
```
|
|
674
|
+
|
|
675
|
+
Alias: `resolve`.
|
|
676
|
+
|
|
469
677
|
#### Show a DID
|
|
470
678
|
|
|
471
679
|
Display the DID document saved in local storage (via `did create --save`),
|
|
@@ -485,6 +693,11 @@ printed as-is:
|
|
|
485
693
|
|
|
486
694
|
Aliases: `view`, `cat`.
|
|
487
695
|
|
|
696
|
+
For a `did:webvh` DID the document is **resolved from its stored history log**
|
|
697
|
+
(`<did>.jsonl`) -- the source of truth -- rather than the saved snapshot, so the
|
|
698
|
+
output reflects every appended log entry. If no log is stored the saved snapshot
|
|
699
|
+
is shown instead.
|
|
700
|
+
|
|
488
701
|
Pass `--meta` to show the DID's metadata instead of the DID document:
|
|
489
702
|
|
|
490
703
|
```
|
|
@@ -499,7 +712,29 @@ Description
|
|
|
499
712
|
Keys 1
|
|
500
713
|
```
|
|
501
714
|
|
|
502
|
-
`--meta
|
|
715
|
+
For a `did:webvh` DID, `--meta` also reports the parameters resolved from the
|
|
716
|
+
log -- the current version id, last-updated time, portability, pre-rotation,
|
|
717
|
+
deactivation status, and the number of update keys, witnesses, and watchers:
|
|
718
|
+
|
|
719
|
+
```
|
|
720
|
+
./di did show my-webvh --meta
|
|
721
|
+
FIELD VALUE
|
|
722
|
+
----------- ----------------------------------------------
|
|
723
|
+
DID did:webvh:Qm...:example.com
|
|
724
|
+
Method webvh
|
|
725
|
+
...
|
|
726
|
+
Version 1-Qm...
|
|
727
|
+
Updated 2026-06-10T17:22:31Z
|
|
728
|
+
Portable yes
|
|
729
|
+
Prerotation yes
|
|
730
|
+
Deactivated no
|
|
731
|
+
Update keys 1
|
|
732
|
+
Witnesses 0
|
|
733
|
+
Watchers 0
|
|
734
|
+
```
|
|
735
|
+
|
|
736
|
+
`--meta --json` prints the same metadata as a JSON object (the `did:webvh`
|
|
737
|
+
fields are included there too).
|
|
503
738
|
|
|
504
739
|
#### Edit DID metadata
|
|
505
740
|
|
|
@@ -1097,8 +1332,10 @@ the server, or the local registry record with `--meta`:
|
|
|
1097
1332
|
`was space update` (alias: `configure`) upserts description fields
|
|
1098
1333
|
(`--name`), also refreshing the registry entry. `was space add` registers an
|
|
1099
1334
|
*existing* remote space (a full space URL, or a bare id plus `--server`) in
|
|
1100
|
-
the local registry, verifying it with a describe first.
|
|
1101
|
-
|
|
1335
|
+
the local registry, verifying it with a describe first. `was space meta
|
|
1336
|
+
<space>` updates only a registered space's local metadata (`--handle` and/or
|
|
1337
|
+
`--description`); the server-side space is untouched, and passing an empty
|
|
1338
|
+
string (`--handle ''`) clears that field. The local/remote delete pair:
|
|
1102
1339
|
|
|
1103
1340
|
- `was space delete <space>` (alias: `rm`) deletes the space **on the
|
|
1104
1341
|
server** (idempotent) and removes the registry entry;
|
|
@@ -1428,6 +1665,151 @@ unless `WAS_TEST_SERVER_URL` points at a running server:
|
|
|
1428
1665
|
WAS_TEST_SERVER_URL=http://localhost:3002 npm run test:node
|
|
1429
1666
|
```
|
|
1430
1667
|
|
|
1668
|
+
### Encrypted Data (EDV)
|
|
1669
|
+
|
|
1670
|
+
The `edv` commands encrypt an object or file to one or more X25519 recipients
|
|
1671
|
+
and decrypt the result, using the EDV / [minimal-cipher](https://www.npmjs.com/package/@interop/minimal-cipher)
|
|
1672
|
+
serialization. The output is a single raw **JWE** (the `jwe` field of an EDV
|
|
1673
|
+
Document), written to stdout or an `-o` file -- by convention `*.jwe.json`.
|
|
1674
|
+
Encryption is public-key (key-agreement) only: there is no password mode. The
|
|
1675
|
+
algorithm is the library default, `ECDH-ES+A256KW` key wrap with `XC20P`
|
|
1676
|
+
(XChaCha20Poly1305) content encryption.
|
|
1677
|
+
|
|
1678
|
+
#### Encrypt
|
|
1679
|
+
|
|
1680
|
+
A recipient (`-r/--recipient`, repeatable, at least one required) is an X25519
|
|
1681
|
+
public key given as a raw `publicKeyMultibase` (starts `z6LS`), a wallet key
|
|
1682
|
+
fingerprint or handle, or a DID / DID URL (the DID's `keyAgreement` key; a DID
|
|
1683
|
+
with several keyAgreement keys needs the `did#fragment` form). A
|
|
1684
|
+
`--recipient-file <path>` is a key-document JSON file holding an X25519 public
|
|
1685
|
+
key.
|
|
1686
|
+
|
|
1687
|
+
Encrypt a JSON object (with `--json`, the input is parsed and encrypted as an
|
|
1688
|
+
object) to a stored x25519 key:
|
|
1689
|
+
|
|
1690
|
+
```
|
|
1691
|
+
./di key create --type x25519 --save --handle alice-kak
|
|
1692
|
+
echo '{"hello": "world"}' | ./di edv encrypt --json -r alice-kak -o secret.jwe.json
|
|
1693
|
+
```
|
|
1694
|
+
|
|
1695
|
+
Without `--json` the raw input bytes are encrypted. Encrypt to several
|
|
1696
|
+
recipients by repeating `-r`:
|
|
1697
|
+
|
|
1698
|
+
```
|
|
1699
|
+
./di edv encrypt photo.png -r alice-kak -r z6LSr... -o photo.jwe.json
|
|
1700
|
+
```
|
|
1701
|
+
|
|
1702
|
+
#### Decrypt
|
|
1703
|
+
|
|
1704
|
+
`-k/--key` is the X25519 secret key (fingerprint or handle) to decrypt with;
|
|
1705
|
+
when omitted, the matching stored key is auto-selected from the wallet. Use
|
|
1706
|
+
`--json` to pretty-print the decrypted object, and `-o` to write to a file.
|
|
1707
|
+
Only plaintext is ever written; secret key material stays in the key store.
|
|
1708
|
+
|
|
1709
|
+
```
|
|
1710
|
+
./di edv decrypt secret.jwe.json --json
|
|
1711
|
+
{
|
|
1712
|
+
"hello": "world"
|
|
1713
|
+
}
|
|
1714
|
+
|
|
1715
|
+
./di edv decrypt photo.jwe.json -k alice-kak -o photo.png
|
|
1716
|
+
```
|
|
1717
|
+
|
|
1718
|
+
Decryption with a key that is not a recipient exits non-zero with a clear
|
|
1719
|
+
error rather than emitting garbage.
|
|
1720
|
+
|
|
1721
|
+
#### EDV Documents (`--document`)
|
|
1722
|
+
|
|
1723
|
+
By default `edv encrypt` emits a bare JWE. With `-d/--document` it wraps that JWE
|
|
1724
|
+
in a full **EDV Document** envelope -- `{ id, sequence, indexed, jwe }`, the
|
|
1725
|
+
shape an EDV / WAS server stores -- written by convention to `*.edvdoc.json`. The
|
|
1726
|
+
input is encrypted as the document's `content`; an optional `--meta <json>`
|
|
1727
|
+
object is encrypted alongside it (both live inside the `jwe`, so only `id`,
|
|
1728
|
+
`sequence`, and `indexed` stay in cleartext). The `id` is a fresh
|
|
1729
|
+
identity-multihash multibase value and `sequence` starts at `0`. Index entries
|
|
1730
|
+
(`indexed`) are always `[]` for now; HMAC-blinded indexing and chunked streams
|
|
1731
|
+
are later phases.
|
|
1732
|
+
|
|
1733
|
+
```
|
|
1734
|
+
echo '{"name": "alice"}' | \
|
|
1735
|
+
./di edv encrypt --document -r alice-kak --meta '{"tag":"demo"}' -o doc.edvdoc.json
|
|
1736
|
+
```
|
|
1737
|
+
|
|
1738
|
+
`--update <file>` versions an existing document: it reuses that document's `id`,
|
|
1739
|
+
increments its `sequence`, and merges its recipients (so you can add a recipient
|
|
1740
|
+
without re-listing the existing ones) before re-encrypting the new content.
|
|
1741
|
+
|
|
1742
|
+
```
|
|
1743
|
+
echo '{"name": "alice", "v": 2}' | \
|
|
1744
|
+
./di edv encrypt --document -r bob-kak --update doc.edvdoc.json -o doc.v2.edvdoc.json
|
|
1745
|
+
```
|
|
1746
|
+
|
|
1747
|
+
`edv decrypt` detects an EDV Document automatically and prints its decrypted
|
|
1748
|
+
`content` (any `meta`/`stream` is reported on stderr); pass `-d/--document` to
|
|
1749
|
+
require an envelope and reject a bare JWE.
|
|
1750
|
+
|
|
1751
|
+
```
|
|
1752
|
+
./di edv decrypt doc.edvdoc.json
|
|
1753
|
+
{
|
|
1754
|
+
"name": "alice"
|
|
1755
|
+
}
|
|
1756
|
+
```
|
|
1757
|
+
|
|
1758
|
+
#### Chunked streams (`--stream`)
|
|
1759
|
+
|
|
1760
|
+
For large inputs, `-s/--stream` encrypts the bytes as a sequence of fixed-size
|
|
1761
|
+
chunks rather than one JWE. The output is a **bundle directory** (convention
|
|
1762
|
+
`*.edvdoc/`, so `-o` is required) holding `document.json` -- an EDV Document whose
|
|
1763
|
+
cleartext `stream: { sequence, chunks }` descriptor records the chunk count -- and
|
|
1764
|
+
one `chunks/<index>.jwe.json` per chunk. This mirrors how an EDV / WAS server
|
|
1765
|
+
stores stream bytes as resources separate from the document. `--chunk-size
|
|
1766
|
+
<bytes>` sets the chunk size (default 1 MiB); `--meta` and `--update` work as for
|
|
1767
|
+
`--document`.
|
|
1768
|
+
|
|
1769
|
+
```
|
|
1770
|
+
./di edv encrypt photo.png --stream -r alice-kak --chunk-size 1048576 -o photo.edvdoc/
|
|
1771
|
+
```
|
|
1772
|
+
|
|
1773
|
+
`edv decrypt` recognizes a bundle directory, reassembles the chunks in order, and
|
|
1774
|
+
writes the original bytes to `-o`/stdout (the document's `content`/`meta`/`stream`
|
|
1775
|
+
are reported on stderr).
|
|
1776
|
+
|
|
1777
|
+
```
|
|
1778
|
+
./di edv decrypt photo.edvdoc/ -o photo.png
|
|
1779
|
+
```
|
|
1780
|
+
|
|
1781
|
+
#### Blinded indexing (`--index`)
|
|
1782
|
+
|
|
1783
|
+
In `--document`/`--stream` mode, `--index <attribute>` populates the envelope's
|
|
1784
|
+
`indexed` array so a document is searchable the way an EDV / WAS server indexes
|
|
1785
|
+
it -- without the server learning the cleartext. The attribute name and value
|
|
1786
|
+
are **HMAC-blinded**: a `Sha256HmacKey2019` key signs them, so the same key over
|
|
1787
|
+
the same value always yields the same opaque entry (matchable across documents),
|
|
1788
|
+
but the cleartext never leaves the client.
|
|
1789
|
+
|
|
1790
|
+
First create an HMAC key in the wallet (a 32-byte secret, no public half):
|
|
1791
|
+
|
|
1792
|
+
```
|
|
1793
|
+
./di key create --type hmac --save --handle vault-index
|
|
1794
|
+
```
|
|
1795
|
+
|
|
1796
|
+
Then declare one or more indexable attribute paths (dotted paths into `content`
|
|
1797
|
+
or `meta`). The HMAC key is auto-selected when the wallet holds exactly one;
|
|
1798
|
+
otherwise pass `--hmac <id|handle>`. `--unique` marks every `--index` attribute
|
|
1799
|
+
as unique.
|
|
1800
|
+
|
|
1801
|
+
```
|
|
1802
|
+
echo '{"type":"Person","name":"alice"}' | \
|
|
1803
|
+
./di edv encrypt --document --json -r alice-kak \
|
|
1804
|
+
--index content.type --index content.name --hmac vault-index -o doc.edvdoc.json
|
|
1805
|
+
```
|
|
1806
|
+
|
|
1807
|
+
The resulting envelope carries `indexed: [{ hmac: { id, type }, sequence,
|
|
1808
|
+
attributes: [{ name, value, unique? }] }]`, where each `name`/`value` is the
|
|
1809
|
+
blinded (opaque) form. The envelope and its blinded entries are assembled by
|
|
1810
|
+
[`@interop/edv-client`](https://www.npmjs.com/package/@interop/edv-client)'s
|
|
1811
|
+
`EdvClientCore`, so they match what an EDV server expects byte-for-byte.
|
|
1812
|
+
|
|
1431
1813
|
## Contribute
|
|
1432
1814
|
|
|
1433
1815
|
PRs accepted. Please follow the code-style and contribution conventions in
|
package/dist/commands/did.d.ts
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
+
import { type DIDLog } from '@interop/did-method-webvh';
|
|
3
|
+
/**
|
|
4
|
+
* Parse a raw did:webvh history log (newline-delimited JSON) into the entry
|
|
5
|
+
* array the library's resolver/updater expect, ignoring blank lines.
|
|
6
|
+
*
|
|
7
|
+
* @param logText {string}
|
|
8
|
+
* @returns {DIDLog}
|
|
9
|
+
*/
|
|
10
|
+
export declare function parseDidLog(logText: string): DIDLog;
|
|
2
11
|
export declare function makeDidCommand(): Command;
|
|
3
12
|
//# sourceMappingURL=did.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"did.d.ts","sourceRoot":"","sources":["../../src/commands/did.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"did.d.ts","sourceRoot":"","sources":["../../src/commands/did.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAOnC,OAAO,EAIL,KAAK,MAAM,EAEZ,MAAM,2BAA2B,CAAA;AA2FlC;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAKnD;AA6qBD,wBAAgB,cAAc,IAAI,OAAO,CA41CxC"}
|