@interop/did-cli 0.7.1 → 0.9.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.
Files changed (93) hide show
  1. package/CHANGELOG.md +130 -0
  2. package/README.md +335 -6
  3. package/dist/commands/did.d.ts.map +1 -1
  4. package/dist/commands/did.js +66 -18
  5. package/dist/commands/did.js.map +1 -1
  6. package/dist/commands/edv.d.ts +63 -0
  7. package/dist/commands/edv.d.ts.map +1 -0
  8. package/dist/commands/edv.js +627 -0
  9. package/dist/commands/edv.js.map +1 -0
  10. package/dist/commands/key.d.ts.map +1 -1
  11. package/dist/commands/key.js +87 -13
  12. package/dist/commands/key.js.map +1 -1
  13. package/dist/commands/was/collection.d.ts +121 -0
  14. package/dist/commands/was/collection.d.ts.map +1 -0
  15. package/dist/commands/was/collection.js +316 -0
  16. package/dist/commands/was/collection.js.map +1 -0
  17. package/dist/commands/was/policy.d.ts +50 -0
  18. package/dist/commands/was/policy.d.ts.map +1 -0
  19. package/dist/commands/was/policy.js +133 -0
  20. package/dist/commands/was/policy.js.map +1 -0
  21. package/dist/commands/was/publish.d.ts +67 -0
  22. package/dist/commands/was/publish.d.ts.map +1 -0
  23. package/dist/commands/was/publish.js +151 -0
  24. package/dist/commands/was/publish.js.map +1 -0
  25. package/dist/commands/was/resource.d.ts +148 -0
  26. package/dist/commands/was/resource.d.ts.map +1 -0
  27. package/dist/commands/was/resource.js +402 -0
  28. package/dist/commands/was/resource.js.map +1 -0
  29. package/dist/commands/was/shared.d.ts +156 -0
  30. package/dist/commands/was/shared.d.ts.map +1 -0
  31. package/dist/commands/was/shared.js +237 -0
  32. package/dist/commands/was/shared.js.map +1 -0
  33. package/dist/commands/was/space.d.ts +196 -0
  34. package/dist/commands/was/space.d.ts.map +1 -0
  35. package/dist/commands/was/space.js +516 -0
  36. package/dist/commands/was/space.js.map +1 -0
  37. package/dist/commands/was/tree.d.ts +44 -0
  38. package/dist/commands/was/tree.d.ts.map +1 -0
  39. package/dist/commands/was/tree.js +135 -0
  40. package/dist/commands/was/tree.js.map +1 -0
  41. package/dist/commands/was.d.ts +29 -496
  42. package/dist/commands/was.d.ts.map +1 -1
  43. package/dist/commands/was.js +84 -1473
  44. package/dist/commands/was.js.map +1 -1
  45. package/dist/commands/zcap.d.ts +22 -2
  46. package/dist/commands/zcap.d.ts.map +1 -1
  47. package/dist/commands/zcap.js +6 -20
  48. package/dist/commands/zcap.js.map +1 -1
  49. package/dist/edv/core.d.ts +60 -0
  50. package/dist/edv/core.d.ts.map +1 -0
  51. package/dist/edv/core.js +75 -0
  52. package/dist/edv/core.js.map +1 -0
  53. package/dist/edv/document.d.ts +25 -0
  54. package/dist/edv/document.d.ts.map +1 -0
  55. package/dist/edv/document.js +19 -0
  56. package/dist/edv/document.js.map +1 -0
  57. package/dist/edv/hmac.d.ts +26 -0
  58. package/dist/edv/hmac.d.ts.map +1 -0
  59. package/dist/edv/hmac.js +67 -0
  60. package/dist/edv/hmac.js.map +1 -0
  61. package/dist/edv/recipients.d.ts +65 -0
  62. package/dist/edv/recipients.d.ts.map +1 -0
  63. package/dist/edv/recipients.js +253 -0
  64. package/dist/edv/recipients.js.map +1 -0
  65. package/dist/edv/stream.d.ts +69 -0
  66. package/dist/edv/stream.d.ts.map +1 -0
  67. package/dist/edv/stream.js +169 -0
  68. package/dist/edv/stream.js.map +1 -0
  69. package/dist/index.js +2 -0
  70. package/dist/index.js.map +1 -1
  71. package/dist/meta.d.ts +1 -0
  72. package/dist/meta.d.ts.map +1 -1
  73. package/dist/meta.js +1 -1
  74. package/dist/meta.js.map +1 -1
  75. package/dist/storage.d.ts +6 -4
  76. package/dist/storage.d.ts.map +1 -1
  77. package/dist/storage.js +6 -5
  78. package/dist/storage.js.map +1 -1
  79. package/dist/was/capability.d.ts +10 -13
  80. package/dist/was/capability.d.ts.map +1 -1
  81. package/dist/was/capability.js +1 -59
  82. package/dist/was/capability.js.map +1 -1
  83. package/dist/was/io.d.ts +14 -0
  84. package/dist/was/io.d.ts.map +1 -1
  85. package/dist/was/io.js +20 -7
  86. package/dist/was/io.js.map +1 -1
  87. package/dist/zcap/delegate.d.ts +2 -2
  88. package/dist/zcap/delegate.js +2 -2
  89. package/dist/zcap/resolve.d.ts +15 -0
  90. package/dist/zcap/resolve.d.ts.map +1 -0
  91. package/dist/zcap/resolve.js +55 -0
  92. package/dist/zcap/resolve.js.map +1 -0
  93. package/package.json +17 -14
package/CHANGELOG.md CHANGED
@@ -1,5 +1,135 @@
1
1
  # History
2
2
 
3
+ ## 0.9.0 - 2026-06-14
4
+
5
+ ### Added
6
+
7
+ - Implement `did get <did>` (alias: `resolve`), which resolves a DID to its DID
8
+ document through `@interop/security-document-loader`'s document loader
9
+ (did:key offline, did:web fetched). A DID URL (a `did#fragment` key id)
10
+ dereferences straight to its verification method. Unlike `did show`, which
11
+ reads local storage, `did get` resolves live.
12
+ - Add an `edv` command group with `encrypt` and `decrypt` subcommands
13
+ (Layer 1: raw JWE). `edv encrypt [file]` encrypts stdin or a file to one or
14
+ more X25519 recipients and emits a single flattened JWE (the `jwe` field of
15
+ an EDV Document) to stdout or an `-o` file (convention `*.jwe.json`);
16
+ `edv decrypt [file]` reverses it. Encryption is public-key (key-agreement)
17
+ only, via `@interop/minimal-cipher` with its default algorithm
18
+ (`ECDH-ES+A256KW` key wrap, `XC20P` content encryption). A recipient
19
+ (`-r/--recipient`, repeatable) is a raw X25519 `publicKeyMultibase`, a wallet
20
+ key fingerprint/handle, or a DID / DID URL (its `keyAgreement` key, typed
21
+ either `X25519KeyAgreementKey2020` or `Multikey`); `--recipient-file` reads a
22
+ key-document JSON. `--json` switches both commands
23
+ to object semantics. On decrypt, the secret key is given with `-k/--key` or
24
+ auto-selected from the wallet by matching a recipient `kid`; a non-recipient
25
+ key exits non-zero with a clear error. The full EDV Document envelope,
26
+ chunked streams, and HMAC-blinded indexing are deferred to Layer 2.
27
+ - Add EDV Document support to `edv` (Layer 2, Phase 1). `edv encrypt --document`
28
+ wraps the JWE in a full EDV Document envelope `{ id, sequence, indexed, jwe }`
29
+ (convention `*.edvdoc.json`), encrypting the input as the document `content`
30
+ with an optional `--meta <json>` object alongside it (both encrypted inside the
31
+ `jwe`); `id` is a fresh identity-multihash multibase value and `sequence`
32
+ starts at `0`, byte-faithful to `@interop/edv-client`'s `EdvClientCore` without
33
+ taking that dependency. `--update <file>` versions an existing document
34
+ (reusing its `id`, incrementing `sequence`, and merging its recipients).
35
+ `edv decrypt` detects an envelope automatically and emits its decrypted
36
+ `content` (reporting `meta`/`stream` on stderr); `--document` requires one.
37
+ Chunked streams and HMAC-blinded indexing remain deferred to later phases.
38
+ - Add chunked-stream support to `edv` (Layer 2, Phase 2). `edv encrypt --stream`
39
+ encrypts the input as a sequence of fixed-size chunks (`--chunk-size <bytes>`,
40
+ default 1 MiB) and writes a bundle directory (convention `*.edvdoc/`, so `-o`
41
+ is required): a `document.json` EDV Document carrying a cleartext
42
+ `stream: { sequence, chunks }` descriptor, plus one `chunks/<index>.jwe.json`
43
+ per chunk (`{ sequence, index, offset, jwe }`), mirroring how an EDV / WAS
44
+ server stores stream bytes separately from the document. `--meta` and
45
+ `--update` (a file or bundle) work as for `--document`. `edv decrypt`
46
+ recognizes a bundle directory, reassembles the chunks in order, and writes the
47
+ original bytes to `-o`/stdout (reporting `content`/`meta`/`stream` on stderr).
48
+ HMAC-blinded indexing remains deferred to Layer 2, Phase 3.
49
+ - Add HMAC-blinded indexing to `edv` (Layer 2, Phase 3). In
50
+ `--document`/`--stream` mode, `--index <attribute>` (repeatable; a dotted path
51
+ into `content`/`meta`) populates the envelope's `indexed` array with entries
52
+ whose attribute names and values are HMAC-blinded, so a document is searchable
53
+ the way an EDV / WAS server indexes it without the server learning the
54
+ cleartext. `--unique` marks every `--index` attribute unique; `--hmac <ref>`
55
+ selects the blinding key (auto-selected when the wallet holds exactly one). The
56
+ same key over the same value yields a stable blinded entry, matchable across
57
+ documents. The EDV Document envelope and its blinded `indexed` array are now
58
+ assembled and unwrapped by `@interop/edv-client`'s `EdvClientCore` (a new
59
+ dependency), converging on the reference implementation rather than the
60
+ hand-rolled envelope used in Phases 1-2.
61
+ - Add `key create --type hmac`, generating a `Sha256HmacKey2019` HMAC key (a
62
+ 32-byte symmetric secret used to blind EDV index attributes) via
63
+ `@interop/data-integrity-core`'s `SHA256HMACKey`. The key has no public half;
64
+ it is serialized with its secret as an `oct` JWK and identified by a random
65
+ `urn:uuid:` id. HMAC keys list with an `hmac` type label, and `key show`
66
+ prints only their `id`/`type` (never the secret). Like ecdsa/x25519, HMAC
67
+ generation is non-deterministic, so `--with-seed` / `SECRET_KEY_SEED` are not
68
+ supported.
69
+
70
+ ## 0.8.0 - 2026-06-13
71
+
72
+ ### Added
73
+
74
+ - Add `key create --type x25519` support, generating an
75
+ `X25519KeyAgreementKey2020` (Curve25519 Diffie-Hellman) key pair via
76
+ `@interop/x25519-key-agreement-key`. Saved x25519 keys list with an `x25519`
77
+ type label and `key show` re-exports the public key only. Like ecdsa, x25519
78
+ generation is non-deterministic, so `--with-seed` / `SECRET_KEY_SEED` are not
79
+ supported.
80
+ - Add `did add-key --type x25519` support for `did:web`, adding an
81
+ `X25519KeyAgreementKey2020` to the DID document. Because x25519 keys are key
82
+ agreement (encryption) keys, they are wired into the `keyAgreement`
83
+ verification relationship only; a `--purpose` other than `keyAgreement` is
84
+ rejected, and (as with ecdsa) `--with-seed` is not supported.
85
+ - Add `was resource-meta get` and `was resource-meta put` (group alias
86
+ `meta`), surfacing the Resource Metadata endpoint (`GET`/`PUT .../meta`).
87
+ `get` prints the whole metadata object (server-managed `contentType`,
88
+ `size`, and timestamps plus the user-writable `custom` name/tags); a
89
+ missing/not-visible resource is reported as not-found. `put` updates
90
+ `custom`: `--name` and `--tag key=value` (repeatable) are non-destructive on
91
+ their own (they preserve the other field via the client's `setName` /
92
+ `setTags`), while passing both, or the `--json` escape hatch (inline JSON or
93
+ a file path), is a full `custom` replacement. Both verbs also accept
94
+ `--capability` targeting the resource.
95
+ - Add `was collection backend` and `was collection quota`, surfacing the
96
+ `GET /space/:spaceId/:collectionId/backend` and
97
+ `GET /space/:spaceId/:collectionId/quota` endpoints (the storage backend a
98
+ collection is stored on, and the collection's storage usage scoped to that
99
+ backend). Both render a table by default and take `--json` for the raw
100
+ response; a missing/not-visible collection is reported as not-found, and a
101
+ server (or backend) that does not implement the endpoint (a 501) as an error.
102
+ - Add `was space backends` and `was space quotas`, surfacing the
103
+ `GET /space/:spaceId/backends` and `GET /space/:spaceId/quotas` endpoints
104
+ (the storage backends available within a space, and the space's storage
105
+ report grouped by backend). Both render a table by default and take `--json`
106
+ for the raw response; a server that does not implement the endpoint (a 501)
107
+ is reported as an error.
108
+ - Add `ARCHITECTURE.md`, a codebase map (entry point, command-factory pattern,
109
+ module layout, and the command surface) for contributors, linked from
110
+ `CLAUDE.md` and the README.
111
+ - Add `CONTRIBUTING.md` as the canonical home for code-style and contribution
112
+ conventions (moved out of `CLAUDE.md`, which now imports it).
113
+ - Add `AGENTS.md` (an inline copy of `CONTRIBUTING.md`) so non-Claude coding
114
+ agents pick up the same project guidance.
115
+ - Add an Environment Variables table to the README consolidating `WALLET_DIR`,
116
+ `DIDS_DIR`, `SECRET_KEY_SEED`, `WAS_DID`, `WAS_SERVER_URL`, and
117
+ `ZCAP_CONTROLLER_KEY_SEED`.
118
+
119
+ ### Changed
120
+
121
+ - `zcap delegate --capability` now also accepts the id or metadata handle of
122
+ a zcap saved in local wallet storage, in addition to a multibase string or
123
+ a JSON file path (the same resolution the `was` commands' `--capability`
124
+ flag uses; the shared resolver lives in `src/zcap/resolve.ts`).
125
+ - Split the `was` command module into `src/commands/was/` submodules (by noun:
126
+ `space`, `collection`, `resource`, `tree`, `policy`, `publish`, plus shared
127
+ helpers); `src/commands/was.ts` now holds only `makeWasCommand()`. Internal
128
+ refactor with no behavior change.
129
+ - Rename the `was resource list` positional from `<path>` to `<collection>`,
130
+ matching the sibling `was collection list <space>` and clarifying that the
131
+ argument is the parent collection being enumerated.
132
+
3
133
  ## 0.7.0-0.7.1 - 2026-06-11
4
134
 
5
135
  ### Changed
package/README.md CHANGED
@@ -26,6 +26,21 @@ Help is available with the `--help/-h` command line option:
26
26
  ./di COMMAND -h
27
27
  ```
28
28
 
29
+ ### Environment Variables
30
+
31
+ These environment variables configure storage locations and provide defaults
32
+ or secret-key seeds for individual commands. Each is also documented inline in
33
+ the relevant command section below.
34
+
35
+ | Variable | Used by | Purpose |
36
+ | --- | --- | --- |
37
+ | `WALLET_DIR` | all | Wallet collections directory (`keys/`, `zcaps/`, `credentials/`, `was-spaces/`). Defaults to `~/.config/did-cli-wallet/` (honors `XDG_CONFIG_HOME`). |
38
+ | `DIDS_DIR` | `did` | DID-documents directory. Defaults to `<WALLET_DIR>/dids/`. |
39
+ | `SECRET_KEY_SEED` | `key create`, `did create` | Multibase-encoded seed for deterministic key/DID generation. Not supported with `--type ecdsa` or `--type x25519`. |
40
+ | `WAS_DID` | `was` | Default signing DID (or stored-DID handle) when `--did` is omitted. |
41
+ | `WAS_SERVER_URL` | `was` | Default WAS server base URL when `--server` is omitted. |
42
+ | `ZCAP_CONTROLLER_KEY_SEED` | `zcap` | Controller signing-key seed for delegating capabilities. |
43
+
29
44
  ### Key Management
30
45
 
31
46
  #### Create a key pair
@@ -66,7 +81,7 @@ SECRET_KEY_SEED=z1AXVyT6G1Qk3E9cMPkDYY6wVRpZjVGWAZ3TfrAgFZkX6bv ./di key create
66
81
  ```
67
82
 
68
83
  Specify an explicit key type with `--type` (defaults to `ed25519`; supported:
69
- `ed25519`, `ecdsa`):
84
+ `ed25519`, `ecdsa`, `x25519`, `hmac`):
70
85
 
71
86
  ```
72
87
  SECRET_KEY_SEED=z1Aaj5A4UCsd... ./di key create --type ed25519
@@ -96,6 +111,49 @@ ECDSA keys are serialized as Multikey, the same as Ed25519. Note that ECDSA key
96
111
  generation is non-deterministic (it cannot be derived from a seed), so
97
112
  `--with-seed` and `SECRET_KEY_SEED` are not supported with `--type ecdsa`.
98
113
 
114
+ Generate an X25519 (Curve25519) key agreement key -- for Diffie-Hellman key
115
+ exchange / encryption, not signing -- with `--type x25519`:
116
+
117
+ ```
118
+ ./di key create --type x25519
119
+ ```
120
+
121
+ It is serialized as an `X25519KeyAgreementKey2020` document with the public and
122
+ private key in multibase encoding:
123
+
124
+ ```json
125
+ {
126
+ "type": "X25519KeyAgreementKey2020",
127
+ "publicKeyMultibase": "z6LS...",
128
+ "privateKeyMultibase": "z3we..."
129
+ }
130
+ ```
131
+
132
+ Like ECDSA, X25519 key generation is non-deterministic, so `--with-seed` and
133
+ `SECRET_KEY_SEED` are not supported with `--type x25519`.
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
+
99
157
  Save the key to local wallet storage (`~/.config/did-cli-wallet/keys/` by default, or
100
158
  `$WALLET_DIR/keys/` if set) with `--save`. A `.meta.json` metadata sidecar is
101
159
  written next to the key, recording the creation timestamp; `--handle` (a short
@@ -376,10 +434,20 @@ By default the new key is Ed25519; pass `--type ecdsa` (with an optional
376
434
  ./di did add-key did:web:example.com --type ecdsa --curve p384
377
435
  ```
378
436
 
437
+ Pass `--type x25519` to add an X25519 (Curve25519) key agreement key. X25519
438
+ keys are encryption/key-exchange keys, not signing keys, so they are wired into
439
+ the `keyAgreement` relationship only -- a `--purpose` other than `keyAgreement`
440
+ is rejected:
441
+
442
+ ```
443
+ ./di did add-key did:web:example.com --type x25519
444
+ ```
445
+
379
446
  For Ed25519 keys, the new key is derived from a seed (as with `did create`):
380
447
  pass `--with-seed` to generate (and print) a fresh seed, or set `SECRET_KEY_SEED`
381
- to derive the key deterministically. ECDSA keys are not seed-derivable, so
382
- `--with-seed` is not supported with `--type ecdsa`:
448
+ to derive the key deterministically. ECDSA and X25519 keys are not
449
+ seed-derivable, so `--with-seed` is not supported with `--type ecdsa` or
450
+ `--type x25519`:
383
451
 
384
452
  ```
385
453
  ./di did add-key did:web:example.com --with-seed
@@ -420,6 +488,33 @@ did:key:z6Mkr...
420
488
  did:key:z6Mks...
421
489
  ```
422
490
 
491
+ #### Resolve a DID
492
+
493
+ Resolve a DID to its DID document through the security document loader. Unlike
494
+ `did show` (which reads local storage), `did get` resolves live: did:key is
495
+ resolved offline, did:web is fetched over HTTPS. Pass a DID URL (a
496
+ `did#fragment` key id) to dereference straight to its verification method:
497
+
498
+ ```
499
+ ./di did get did:key:z6Mkr...
500
+ {
501
+ "@context": [ ... ],
502
+ "id": "did:key:z6Mkr...",
503
+ "verificationMethod": [ ... ],
504
+ ...
505
+ }
506
+
507
+ ./di did get did:key:z6Mkr...#z6Mkr...
508
+ {
509
+ "id": "did:key:z6Mkr...#z6Mkr...",
510
+ "type": "Ed25519VerificationKey2020",
511
+ "controller": "did:key:z6Mkr...",
512
+ "publicKeyMultibase": "z6Mkr..."
513
+ }
514
+ ```
515
+
516
+ Alias: `resolve`.
517
+
423
518
  #### Show a DID
424
519
 
425
520
  Display the DID document saved in local storage (via `did create --save`),
@@ -834,8 +929,9 @@ ZCAP_CONTROLLER_KEY_SEED=z1AZK4h5w5YZkKYEgqtcFfvSbWQ3tZ3ZFgmLsXMZsTVoeK7 \
834
929
  ```
835
930
 
836
931
  To delegate an _existing_ capability further down the chain, pass it as
837
- `--capability` instead of `--url` -- either the `encoded` string from a previous
838
- delegation or a path to a JSON file containing the capability. Use
932
+ `--capability` instead of `--url` -- the `encoded` string from a previous
933
+ delegation, a path to a JSON file containing the capability, or the id or
934
+ metadata handle of a zcap saved in local wallet storage. Use
839
935
  `--invocation-target` to attenuate (narrow) the parent's target to a sub-path:
840
936
 
841
937
  ```
@@ -1057,6 +1153,24 @@ delete pair:
1057
1153
  server** (idempotent) and removes the registry entry;
1058
1154
  - `was space forget <space>` removes only the local registry entry.
1059
1155
 
1156
+ `was space backends` lists the storage backends available within a space, and
1157
+ `was space quotas` shows the space's storage report grouped by backend (usage,
1158
+ limit, and any restricted actions). Both render a table by default and take
1159
+ `--json` for the raw response:
1160
+
1161
+ ```
1162
+ ./di was space backends home
1163
+ ID NAME MANAGED BY STORAGE MODE PERSISTENCE
1164
+ default Filesystem server document, blob durable
1165
+
1166
+ ./di was space quotas home
1167
+ BACKEND STATE USAGE (B) LIMIT (B) RESTRICTED
1168
+ default (Filesystem) ok 2048 1048576
1169
+ ```
1170
+
1171
+ A server that does not implement these endpoints (a 501) is reported as an
1172
+ error.
1173
+
1060
1174
  #### Manage collections
1061
1175
 
1062
1176
  The `collection` group (alias: `coll`) manages collections within a space.
@@ -1081,6 +1195,37 @@ credentials Credentials http://localhost:3002/space/8124...cf2e/credentials
1081
1195
  Deleted http://localhost:3002/space/8124...cf2e/credentials on the server.
1082
1196
  ```
1083
1197
 
1198
+ `was collection backend` shows the storage backend a collection is stored on,
1199
+ and `was collection quota` shows the collection's storage usage scoped to that
1200
+ backend (state, usage, limit, and any restricted actions). Both render a table
1201
+ by default and take `--json` for the raw response:
1202
+
1203
+ ```
1204
+ ./di was collection backend home/credentials
1205
+ FIELD VALUE
1206
+ ------------ --------------
1207
+ ID default
1208
+ Name Filesystem
1209
+ Managed By server
1210
+ Storage Mode document, blob
1211
+ Persistence durable
1212
+
1213
+ ./di was collection quota home/credentials
1214
+ FIELD VALUE
1215
+ ----------- --------------------
1216
+ Backend default (Filesystem)
1217
+ Managed By server
1218
+ State ok
1219
+ Usage (B) 2048
1220
+ Limit (B) 1048576
1221
+ Restricted
1222
+ Measured At 2026-06-13T00:00:00Z
1223
+ ```
1224
+
1225
+ A missing or not-visible collection is reported as not-found; a server (or
1226
+ backend) that does not implement these endpoints (a 501) is reported as an
1227
+ error.
1228
+
1084
1229
  #### Add and read resources
1085
1230
 
1086
1231
  The `resource` group (alias: `res`) manages the content itself. Payloads come
@@ -1121,6 +1266,40 @@ files); a missing or not-visible resource prints
1121
1266
  `list` renders the resources of a collection (`ID | CONTENT TYPE | URL`), and
1122
1267
  `delete` (alias: `rm`) removes one (idempotent).
1123
1268
 
1269
+ #### Resource metadata (name and tags)
1270
+
1271
+ The `resource-meta` group (alias: `meta`) reads and updates a resource's
1272
+ metadata. `get` prints the whole metadata object -- the server-managed
1273
+ `contentType`, `size`, and timestamps plus the user-writable `custom` (its
1274
+ `name` and `tags`):
1275
+
1276
+ ```
1277
+ ./di was resource-meta get home/credentials/vc-1
1278
+ ```
1279
+
1280
+ `put` updates the user-writable `custom`. `--name` sets the display name shown
1281
+ in collection listings and `--tag key=value` (repeatable) sets annotations;
1282
+ used on their own each is non-destructive -- `--name` preserves existing tags
1283
+ and `--tag` preserves the existing name:
1284
+
1285
+ ```
1286
+ ./di was resource-meta put home/credentials/vc-1 --name 'Diploma'
1287
+ ./di was resource-meta put home/credentials/vc-1 --tag year=2026 --tag status=verified
1288
+ ```
1289
+
1290
+ Giving both `--name` and `--tag` together replaces `custom` wholesale (any
1291
+ field you do not pass is cleared). The `--json` escape hatch takes the full
1292
+ `custom` object as inline JSON or a JSON file path, for the same full
1293
+ replacement (pass `--json '{}'` to clear everything):
1294
+
1295
+ ```
1296
+ ./di was resource-meta put home/credentials/vc-1 \
1297
+ --json '{"name":"Diploma","tags":{"year":"2026"}}'
1298
+ ./di was resource-meta put home/credentials/vc-1 --json custom.json
1299
+ ```
1300
+
1301
+ After a successful update the command prints the resulting metadata.
1302
+
1124
1303
  #### Shorthand verbs
1125
1304
 
1126
1305
  For day-to-day use, the top-level verbs dispatch on the path depth:
@@ -1298,9 +1477,159 @@ unless `WAS_TEST_SERVER_URL` points at a running server:
1298
1477
  WAS_TEST_SERVER_URL=http://localhost:3002 npm run test:node
1299
1478
  ```
1300
1479
 
1480
+ ### Encrypted Data (EDV)
1481
+
1482
+ The `edv` commands encrypt an object or file to one or more X25519 recipients
1483
+ and decrypt the result, using the EDV / [minimal-cipher](https://www.npmjs.com/package/@interop/minimal-cipher)
1484
+ serialization. The output is a single raw **JWE** (the `jwe` field of an EDV
1485
+ Document), written to stdout or an `-o` file -- by convention `*.jwe.json`.
1486
+ Encryption is public-key (key-agreement) only: there is no password mode. The
1487
+ algorithm is the library default, `ECDH-ES+A256KW` key wrap with `XC20P`
1488
+ (XChaCha20Poly1305) content encryption.
1489
+
1490
+ #### Encrypt
1491
+
1492
+ A recipient (`-r/--recipient`, repeatable, at least one required) is an X25519
1493
+ public key given as a raw `publicKeyMultibase` (starts `z6LS`), a wallet key
1494
+ fingerprint or handle, or a DID / DID URL (the DID's `keyAgreement` key; a DID
1495
+ with several keyAgreement keys needs the `did#fragment` form). A
1496
+ `--recipient-file <path>` is a key-document JSON file holding an X25519 public
1497
+ key.
1498
+
1499
+ Encrypt a JSON object (with `--json`, the input is parsed and encrypted as an
1500
+ object) to a stored x25519 key:
1501
+
1502
+ ```
1503
+ ./di key create --type x25519 --save --handle alice-kak
1504
+ echo '{"hello": "world"}' | ./di edv encrypt --json -r alice-kak -o secret.jwe.json
1505
+ ```
1506
+
1507
+ Without `--json` the raw input bytes are encrypted. Encrypt to several
1508
+ recipients by repeating `-r`:
1509
+
1510
+ ```
1511
+ ./di edv encrypt photo.png -r alice-kak -r z6LSr... -o photo.jwe.json
1512
+ ```
1513
+
1514
+ #### Decrypt
1515
+
1516
+ `-k/--key` is the X25519 secret key (fingerprint or handle) to decrypt with;
1517
+ when omitted, the matching stored key is auto-selected from the wallet. Use
1518
+ `--json` to pretty-print the decrypted object, and `-o` to write to a file.
1519
+ Only plaintext is ever written; secret key material stays in the key store.
1520
+
1521
+ ```
1522
+ ./di edv decrypt secret.jwe.json --json
1523
+ {
1524
+ "hello": "world"
1525
+ }
1526
+
1527
+ ./di edv decrypt photo.jwe.json -k alice-kak -o photo.png
1528
+ ```
1529
+
1530
+ Decryption with a key that is not a recipient exits non-zero with a clear
1531
+ error rather than emitting garbage.
1532
+
1533
+ #### EDV Documents (`--document`)
1534
+
1535
+ By default `edv encrypt` emits a bare JWE. With `-d/--document` it wraps that JWE
1536
+ in a full **EDV Document** envelope -- `{ id, sequence, indexed, jwe }`, the
1537
+ shape an EDV / WAS server stores -- written by convention to `*.edvdoc.json`. The
1538
+ input is encrypted as the document's `content`; an optional `--meta <json>`
1539
+ object is encrypted alongside it (both live inside the `jwe`, so only `id`,
1540
+ `sequence`, and `indexed` stay in cleartext). The `id` is a fresh
1541
+ identity-multihash multibase value and `sequence` starts at `0`. Index entries
1542
+ (`indexed`) are always `[]` for now; HMAC-blinded indexing and chunked streams
1543
+ are later phases.
1544
+
1545
+ ```
1546
+ echo '{"name": "alice"}' | \
1547
+ ./di edv encrypt --document -r alice-kak --meta '{"tag":"demo"}' -o doc.edvdoc.json
1548
+ ```
1549
+
1550
+ `--update <file>` versions an existing document: it reuses that document's `id`,
1551
+ increments its `sequence`, and merges its recipients (so you can add a recipient
1552
+ without re-listing the existing ones) before re-encrypting the new content.
1553
+
1554
+ ```
1555
+ echo '{"name": "alice", "v": 2}' | \
1556
+ ./di edv encrypt --document -r bob-kak --update doc.edvdoc.json -o doc.v2.edvdoc.json
1557
+ ```
1558
+
1559
+ `edv decrypt` detects an EDV Document automatically and prints its decrypted
1560
+ `content` (any `meta`/`stream` is reported on stderr); pass `-d/--document` to
1561
+ require an envelope and reject a bare JWE.
1562
+
1563
+ ```
1564
+ ./di edv decrypt doc.edvdoc.json
1565
+ {
1566
+ "name": "alice"
1567
+ }
1568
+ ```
1569
+
1570
+ #### Chunked streams (`--stream`)
1571
+
1572
+ For large inputs, `-s/--stream` encrypts the bytes as a sequence of fixed-size
1573
+ chunks rather than one JWE. The output is a **bundle directory** (convention
1574
+ `*.edvdoc/`, so `-o` is required) holding `document.json` -- an EDV Document whose
1575
+ cleartext `stream: { sequence, chunks }` descriptor records the chunk count -- and
1576
+ one `chunks/<index>.jwe.json` per chunk. This mirrors how an EDV / WAS server
1577
+ stores stream bytes as resources separate from the document. `--chunk-size
1578
+ <bytes>` sets the chunk size (default 1 MiB); `--meta` and `--update` work as for
1579
+ `--document`.
1580
+
1581
+ ```
1582
+ ./di edv encrypt photo.png --stream -r alice-kak --chunk-size 1048576 -o photo.edvdoc/
1583
+ ```
1584
+
1585
+ `edv decrypt` recognizes a bundle directory, reassembles the chunks in order, and
1586
+ writes the original bytes to `-o`/stdout (the document's `content`/`meta`/`stream`
1587
+ are reported on stderr).
1588
+
1589
+ ```
1590
+ ./di edv decrypt photo.edvdoc/ -o photo.png
1591
+ ```
1592
+
1593
+ #### Blinded indexing (`--index`)
1594
+
1595
+ In `--document`/`--stream` mode, `--index <attribute>` populates the envelope's
1596
+ `indexed` array so a document is searchable the way an EDV / WAS server indexes
1597
+ it -- without the server learning the cleartext. The attribute name and value
1598
+ are **HMAC-blinded**: a `Sha256HmacKey2019` key signs them, so the same key over
1599
+ the same value always yields the same opaque entry (matchable across documents),
1600
+ but the cleartext never leaves the client.
1601
+
1602
+ First create an HMAC key in the wallet (a 32-byte secret, no public half):
1603
+
1604
+ ```
1605
+ ./di key create --type hmac --save --handle vault-index
1606
+ ```
1607
+
1608
+ Then declare one or more indexable attribute paths (dotted paths into `content`
1609
+ or `meta`). The HMAC key is auto-selected when the wallet holds exactly one;
1610
+ otherwise pass `--hmac <id|handle>`. `--unique` marks every `--index` attribute
1611
+ as unique.
1612
+
1613
+ ```
1614
+ echo '{"type":"Person","name":"alice"}' | \
1615
+ ./di edv encrypt --document --json -r alice-kak \
1616
+ --index content.type --index content.name --hmac vault-index -o doc.edvdoc.json
1617
+ ```
1618
+
1619
+ The resulting envelope carries `indexed: [{ hmac: { id, type }, sequence,
1620
+ attributes: [{ name, value, unique? }] }]`, where each `name`/`value` is the
1621
+ blinded (opaque) form. The envelope and its blinded entries are assembled by
1622
+ [`@interop/edv-client`](https://www.npmjs.com/package/@interop/edv-client)'s
1623
+ `EdvClientCore`, so they match what an EDV server expects byte-for-byte.
1624
+
1301
1625
  ## Contribute
1302
1626
 
1303
- PRs accepted.
1627
+ PRs accepted. Please follow the code-style and contribution conventions in
1628
+ [CONTRIBUTING.md](CONTRIBUTING.md).
1629
+
1630
+ For a map of the codebase -- the module layout, the command-factory pattern,
1631
+ and the build/test commands -- see [ARCHITECTURE.md](ARCHITECTURE.md). Storage
1632
+ layout is documented in [STORAGE.md](STORAGE.md).
1304
1633
 
1305
1634
  If editing the Readme, please conform to the
1306
1635
  [standard-readme](https://github.com/RichardLitt/standard-readme) specification.
@@ -1 +1 @@
1
- {"version":3,"file":"did.d.ts","sourceRoot":"","sources":["../../src/commands/did.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AA+EnC,wBAAgB,cAAc,IAAI,OAAO,CAurBxC"}
1
+ {"version":3,"file":"did.d.ts","sourceRoot":"","sources":["../../src/commands/did.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AA0FnC,wBAAgB,cAAc,IAAI,OAAO,CA0uBxC"}