@interop/did-cli 0.12.0 → 0.14.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 +131 -6
- package/README.md +184 -18
- package/dist/commands/did/create.d.ts +22 -0
- package/dist/commands/did/create.d.ts.map +1 -1
- package/dist/commands/did/create.js +6 -27
- package/dist/commands/did/create.js.map +1 -1
- package/dist/commands/did/service.d.ts +19 -4
- package/dist/commands/did/service.d.ts.map +1 -1
- package/dist/commands/did/service.js +46 -45
- package/dist/commands/did/service.js.map +1 -1
- package/dist/commands/did/webvh-update.d.ts +72 -9
- package/dist/commands/did/webvh-update.d.ts.map +1 -1
- package/dist/commands/did/webvh-update.js +177 -61
- package/dist/commands/did/webvh-update.js.map +1 -1
- package/dist/commands/did.d.ts.map +1 -1
- package/dist/commands/did.js +10 -1
- package/dist/commands/did.js.map +1 -1
- package/dist/commands/key.d.ts +2 -16
- package/dist/commands/key.d.ts.map +1 -1
- package/dist/commands/key.js +64 -11
- package/dist/commands/key.js.map +1 -1
- package/dist/commands/was/request-grant.d.ts +37 -0
- package/dist/commands/was/request-grant.d.ts.map +1 -0
- package/dist/commands/was/request-grant.js +248 -0
- package/dist/commands/was/request-grant.js.map +1 -0
- package/dist/commands/was/resource.d.ts +21 -7
- package/dist/commands/was/resource.d.ts.map +1 -1
- package/dist/commands/was/resource.js +40 -15
- package/dist/commands/was/resource.js.map +1 -1
- package/dist/commands/was/shared.d.ts +43 -3
- package/dist/commands/was/shared.d.ts.map +1 -1
- package/dist/commands/was/shared.js +82 -4
- package/dist/commands/was/shared.js.map +1 -1
- package/dist/commands/was/shell/completer.d.ts +24 -0
- package/dist/commands/was/shell/completer.d.ts.map +1 -0
- package/dist/commands/was/shell/completer.js +99 -0
- package/dist/commands/was/shell/completer.js.map +1 -0
- package/dist/commands/was/shell/dispatcher.d.ts +27 -0
- package/dist/commands/was/shell/dispatcher.d.ts.map +1 -0
- package/dist/commands/was/shell/dispatcher.js +227 -0
- package/dist/commands/was/shell/dispatcher.js.map +1 -0
- package/dist/commands/was/shell/session.d.ts +37 -0
- package/dist/commands/was/shell/session.d.ts.map +1 -0
- package/dist/commands/was/shell/session.js +67 -0
- package/dist/commands/was/shell/session.js.map +1 -0
- package/dist/commands/was/shell/tokenize.d.ts +19 -0
- package/dist/commands/was/shell/tokenize.d.ts.map +1 -0
- package/dist/commands/was/shell/tokenize.js +77 -0
- package/dist/commands/was/shell/tokenize.js.map +1 -0
- package/dist/commands/was/shell.d.ts +30 -0
- package/dist/commands/was/shell.d.ts.map +1 -0
- package/dist/commands/was/shell.js +170 -0
- package/dist/commands/was/shell.js.map +1 -0
- package/dist/commands/was/tree.d.ts +6 -1
- package/dist/commands/was/tree.d.ts.map +1 -1
- package/dist/commands/was/tree.js +12 -5
- package/dist/commands/was/tree.js.map +1 -1
- package/dist/commands/was.d.ts.map +1 -1
- package/dist/commands/was.js +31 -1
- package/dist/commands/was.js.map +1 -1
- package/dist/keys/seed.d.ts +21 -0
- package/dist/keys/seed.d.ts.map +1 -0
- package/dist/keys/seed.js +41 -0
- package/dist/keys/seed.js.map +1 -0
- package/dist/keys/webvh-signer.d.ts +18 -13
- package/dist/keys/webvh-signer.d.ts.map +1 -1
- package/dist/keys/webvh-signer.js +5 -20
- package/dist/keys/webvh-signer.js.map +1 -1
- package/dist/meta.js +1 -1
- package/dist/meta.js.map +1 -1
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +57 -10
- package/dist/storage.js.map +1 -1
- package/dist/was/address.d.ts +24 -0
- package/dist/was/address.d.ts.map +1 -1
- package/dist/was/address.js +49 -3
- package/dist/was/address.js.map +1 -1
- package/dist/was/request-grant.d.ts +144 -0
- package/dist/was/request-grant.d.ts.map +1 -0
- package/dist/was/request-grant.js +249 -0
- package/dist/was/request-grant.js.map +1 -0
- package/package.json +22 -20
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,130 @@
|
|
|
1
1
|
# History
|
|
2
2
|
|
|
3
|
+
## 0.14.0 - 2026-09-14
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- `was request-grant` -- the requesting side of capability delegation, the
|
|
8
|
+
inverse of `was grant`. It mints an Ed25519 `did:key` for itself, asks a
|
|
9
|
+
user's wallet for a capability on one of its public collections, prints an
|
|
10
|
+
approval link for the user to open, waits for their answer, and files the
|
|
11
|
+
received capability in the zcap store. The grantee key is minted by the
|
|
12
|
+
command rather than passed in, so a caller driving the CLI never handles key
|
|
13
|
+
material: stdout carries the capability (or, with `--json`, one object holding
|
|
14
|
+
the DID, the handle, the link, and the capability), and the approval link and
|
|
15
|
+
progress notes go to stderr. The key is written only once the grant arrives,
|
|
16
|
+
and a handle already taken by a stored DID or capability is refused up front,
|
|
17
|
+
so handle lookups stay unambiguous across runs. Options are
|
|
18
|
+
`--collection` (default `web`), `--action` (default `GET HEAD PUT POST`),
|
|
19
|
+
`--reason`, `--timeout` (default 600 seconds, the server's exchange
|
|
20
|
+
lifetime), `--handle` (default `agent`), `--description`, `--no-save`,
|
|
21
|
+
`--json`, and `--server`. Persistence is on by default because
|
|
22
|
+
`--capability` resolves its signer out of the local DID store, so a key held
|
|
23
|
+
only for the run cannot sign the write that follows; `--no-save` prints the
|
|
24
|
+
capability without keeping either half. The request carries a single
|
|
25
|
+
`AuthorizationCapabilityQuery` naming a public-collection descriptor, with no
|
|
26
|
+
`DIDAuthentication` query and no `domain`.
|
|
27
|
+
|
|
28
|
+
- `was request-grant --name <name>` sends the agent's self-declared display
|
|
29
|
+
name (the VPR's root `agent: { name }` member, wallet-core 0.53.0), shown on
|
|
30
|
+
the wallet consent screen as what the agent calls itself; at most 64
|
|
31
|
+
characters, no control characters.
|
|
32
|
+
|
|
33
|
+
- `was request-grant --wallet <url>` also prints a link that opens the given
|
|
34
|
+
wallet straight onto its approval page for the exchange
|
|
35
|
+
(`<wallet>/external/request?url=<interaction url>`). The interaction URL is
|
|
36
|
+
still printed, for a wallet on another device. The wallet URL must be
|
|
37
|
+
absolute http(s), and a bad one is refused before any exchange is opened.
|
|
38
|
+
|
|
39
|
+
- `--resource <id>` on `was get`, `was put`, `was meta get`, `was meta put`,
|
|
40
|
+
and `was rm`: names one resource beneath a collection-scoped
|
|
41
|
+
`--capability`, so a grant issued on a whole collection can read, write,
|
|
42
|
+
and delete at a caller-chosen id. `resource add` also takes a collection
|
|
43
|
+
capability, but the server picks the id there. The resource handle inherits
|
|
44
|
+
the collection's bound capability, so the request stays an invocation of
|
|
45
|
+
the received grant. `--resource` without a capability, or alongside a
|
|
46
|
+
capability that already targets a resource, is an input error.
|
|
47
|
+
- A `publish-page` Claude Code skill (`.claude/skills/publish-page/`) and the
|
|
48
|
+
agent storage demo it scripts (`demo/agent-storage/README.md`): an LLM agent
|
|
49
|
+
drafts an `index.html`, asks the user's wallet for a grant on a public
|
|
50
|
+
collection with `was request-grant`, and publishes the page through the
|
|
51
|
+
delegated capability. The skill is model-invoked, states that `--save` is
|
|
52
|
+
required (a key held only for the run cannot sign the write that follows),
|
|
53
|
+
keeps the agent away from key material, and sends a user with no wallet
|
|
54
|
+
account to `<wallet>/signup` before resuming.
|
|
55
|
+
|
|
56
|
+
- `was shell` -- an interactive REPL over the `was` command tree. Start it with
|
|
57
|
+
`di was shell [path]` (`--server`/`--did` seed the session defaults), then run
|
|
58
|
+
any `was` subcommand without re-typing space handles or re-paying the
|
|
59
|
+
cold-start cost. A "current directory" (`cd`/`use`, with `..`, `/`, relative
|
|
60
|
+
paths, and full space URLs) makes every command's path relative to it, and
|
|
61
|
+
`pwd`, `connect <url>`, `did <ref>`, `help`, and `exit`/`quit` round out the
|
|
62
|
+
builtins. Tab completion covers builtins, subcommands, and registered space
|
|
63
|
+
handles. The prompt and diagnostics go to stderr while command data stays on
|
|
64
|
+
stdout, so `di was shell < script` keeps a pipe-clean stdout.
|
|
65
|
+
|
|
66
|
+
### Changed
|
|
67
|
+
|
|
68
|
+
- Update to latest d-i-core, verifier-core, was-client and didwebvh deps.
|
|
69
|
+
- Added `@interop/wallet-request` as a dependency, for the ephemeral-exchange
|
|
70
|
+
create and poll helpers `was request-grant` transports over, and for the
|
|
71
|
+
zcap-only presentation-request composer it sends (moved there from
|
|
72
|
+
wallet-core's `request` subpath).
|
|
73
|
+
- `did webvh rotate-keys` and `did service add|remove` now pass the
|
|
74
|
+
already-resolved log state to `updateDID` as `priorMeta`, so each update
|
|
75
|
+
verifies the history log once instead of twice. The pairing between the log
|
|
76
|
+
and its resolved metadata is now asserted (O(1)) before signing.
|
|
77
|
+
- `did webvh rotate-keys` edits the authorized `updateKeys` set instead of
|
|
78
|
+
rebuilding it from the single local key: a stage-only `--enable-prerotation`
|
|
79
|
+
leaves the set unchanged, and an ordinary rotation replaces only the retired
|
|
80
|
+
key, so co-authorized update keys are no longer silently revoked.
|
|
81
|
+
- `rotate-keys --update-key` now authorizes every supplied key instead of
|
|
82
|
+
silently dropping all but the first.
|
|
83
|
+
- did:webvh updates persist crash-safely: the update-keys sidecar (including
|
|
84
|
+
the superseded secret) is written before the log that authorizes the new
|
|
85
|
+
key, and the signer loaders can recover from a rotation interrupted between
|
|
86
|
+
the two writes.
|
|
87
|
+
- Stored artifacts (DID documents, history logs, sidecars, wallet items) are
|
|
88
|
+
written atomically (temp file + rename), so an interrupted write can no
|
|
89
|
+
longer leave a truncated file.
|
|
90
|
+
- Stored artifacts are now written with mode `0600` (owner read/write only)
|
|
91
|
+
instead of the ambient umask default, since keys are held as plaintext
|
|
92
|
+
Multikey documents. Wallet and DID directories are created with mode `0700`,
|
|
93
|
+
since a DID document is named after its DID. Existing files are not
|
|
94
|
+
migrated.
|
|
95
|
+
- The atomic-write temp file is unlinked and reopened with `wx` rather than
|
|
96
|
+
chmod-ed after the write, so a temp file left behind by an interrupted run
|
|
97
|
+
cannot receive a secret at its own wider mode.
|
|
98
|
+
- Confirmation prompts no longer auto-confirm when stdin is not interactive;
|
|
99
|
+
non-interactive runs must pass `--yes`, and a declined confirmation now
|
|
100
|
+
exits 1.
|
|
101
|
+
- An empty `SECRET_KEY_SEED` env var is treated as unset, and an invalid one
|
|
102
|
+
is reported as a one-line error; seed derivation is shared across
|
|
103
|
+
`key create`, `did create`, and `rotate-keys`.
|
|
104
|
+
- Command errors that escape a runner are reported as one-line messages with
|
|
105
|
+
exit 1 instead of surfacing as unhandled rejections; a corrupt update-keys
|
|
106
|
+
sidecar reports its file path.
|
|
107
|
+
- `did add-service` validates `--endpoint`/`--endpoint-json` arguments before
|
|
108
|
+
resolving (and re-verifying) a did:webvh history log; `rotate-keys` likewise
|
|
109
|
+
checks flag conflicts before resolution.
|
|
110
|
+
- Bumped `@interop/ed25519-verification-key` to `^8.2.0` and switched the
|
|
111
|
+
did:webvh entry signer to its new `didKeySigner()` method, so the caller no
|
|
112
|
+
longer mutates `keyPair.id` before signing.
|
|
113
|
+
|
|
114
|
+
## 0.13.0 - 2026-07-12
|
|
115
|
+
|
|
116
|
+
### Added
|
|
117
|
+
|
|
118
|
+
- `key create --type aes256` generates a symmetric AES-256 key-encryption key
|
|
119
|
+
(KEK): 32 random bytes carried as a `Multikey` `secretKeyMultibase` (the
|
|
120
|
+
AES-256 multicodec header `0xa2 0x01` followed by the raw key bytes),
|
|
121
|
+
identified by a `urn:kek:sha256:` id derived from the SHA-256 digest of the
|
|
122
|
+
raw key bytes. This is the form Wallet Attached Storage servers accept for
|
|
123
|
+
their at-rest key-encryption-key configuration. Generation is always fresh and
|
|
124
|
+
random, so `--with-seed` and `SECRET_KEY_SEED` are not supported for this
|
|
125
|
+
type. `key show` prints only the safe `id` and `type` for a saved KEK, never
|
|
126
|
+
the secret material.
|
|
127
|
+
|
|
3
128
|
## 0.12.0 - 2026-07-09
|
|
4
129
|
|
|
5
130
|
### Changed
|
|
@@ -68,7 +193,7 @@
|
|
|
68
193
|
- `vc issue --did` now accepts a metadata handle (not just a full DID id),
|
|
69
194
|
resolving it via the same lookup used by the other DID-referencing commands.
|
|
70
195
|
Previously passing a handle failed with a confusing `The "path" argument must
|
|
71
|
-
|
|
196
|
+
be of type string. Received undefined` error.
|
|
72
197
|
|
|
73
198
|
### Changed
|
|
74
199
|
|
|
@@ -107,7 +232,7 @@
|
|
|
107
232
|
field); `loadFromCollection` / `saveToCollection` take an options object like
|
|
108
233
|
the rest of the storage helpers; `vc/issue` and `vc/verify` replace their
|
|
109
234
|
`as never` casts with typed ones; `edv/recipients` replaces `Record<string,
|
|
110
|
-
|
|
235
|
+
any>` with a named `VerificationMethodNode` type; `edv`'s encrypt functions
|
|
111
236
|
share an `EnvelopeOptions` type and a `resolveEncryptContextOrReport` wrapper;
|
|
112
237
|
and the `z6Mk` / `z6LS` multibase prefixes are named constants. Added the
|
|
113
238
|
file-level JSDoc header to `key.ts` and moved the `zcap revoke` stub message to
|
|
@@ -443,7 +568,7 @@
|
|
|
443
568
|
`--handle` / `--description` tag it (exit `1` without `--save`).
|
|
444
569
|
- Add `vc list` (`--json` / `--plain`) to render a metadata table of the
|
|
445
570
|
stored credentials (`HANDLE | TYPE | ISSUER | CREATED | ID |
|
|
446
|
-
|
|
571
|
+
DESCRIPTION`), `vc show <id>` (aliases: `view`, `cat`; `--meta` for the
|
|
447
572
|
metadata, `--meta --json` for it as JSON), `vc meta <id>` to edit the
|
|
448
573
|
metadata sidecar, and `vc remove <id>` (aliases: `delete`, `rm`). All
|
|
449
574
|
accept a credential id, a storage id (for id-less credentials), or a
|
|
@@ -480,7 +605,7 @@
|
|
|
480
605
|
`did create --save` and `did add-key`).
|
|
481
606
|
- Add metadata support for locally stored keys and DIDs, persisted as
|
|
482
607
|
`.meta.json` sidecar files next to the stored item (`~/.wallet/keys/
|
|
483
|
-
|
|
608
|
+
<storageId>.meta.json` and `~/.dids/<method>/<did>.meta.json`, following the
|
|
484
609
|
existing `.keys.json` sidecar pattern). Metadata fields: `created` (ISO 8601
|
|
485
610
|
timestamp written at `--save` time), `handle` (a short user-defined tag),
|
|
486
611
|
`description` (longer free text), and -- for keys -- `dids`, a cache of the
|
|
@@ -520,8 +645,8 @@
|
|
|
520
645
|
the new `--plain` flag for the previous one-item-per-line output.
|
|
521
646
|
- **BREAKING**: `key list --json` and `did list --json` now output an array of
|
|
522
647
|
objects with metadata (`{fingerprint, storageId, type, curve?, created?,
|
|
523
|
-
|
|
524
|
-
|
|
648
|
+
handle?, description?, dids}` for keys; `{did, method, created?, handle?,
|
|
649
|
+
description?}` for DIDs) instead of an array of plain strings.
|
|
525
650
|
|
|
526
651
|
## 0.4.0 - 2026-06-09
|
|
527
652
|
|
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@ cryptographic key pairs that underpin them.
|
|
|
25
25
|
It lets you generate and manage these objects locally -- minting DIDs, issuing
|
|
26
26
|
and verifying credentials, delegating capabilities, and encrypting data -- and
|
|
27
27
|
interact with remote
|
|
28
|
-
[Wallet Attached Storage](https://
|
|
28
|
+
[Wallet Attached Storage](https://w3c-ccg.github.io/wallet-attached-storage-spec/)
|
|
29
29
|
servers, all from the terminal and backed by a local file-based wallet.
|
|
30
30
|
|
|
31
31
|
### Features
|
|
@@ -109,6 +109,7 @@ di was ls|get|put|rm [path] depth-dispatching shorthands
|
|
|
109
109
|
di was policy <show|set|clear> manage access-control policies
|
|
110
110
|
di was publish|unpublish <path> toggle world-readable access
|
|
111
111
|
di was grant <path> delegate access via a signed capability
|
|
112
|
+
di was request-grant ask a wallet for a capability on a collection
|
|
112
113
|
```
|
|
113
114
|
|
|
114
115
|
### Environment Variables
|
|
@@ -118,7 +119,7 @@ or secret-key seeds for individual commands. Each is also documented inline in
|
|
|
118
119
|
the relevant command section below.
|
|
119
120
|
|
|
120
121
|
| Variable | Used by | Purpose |
|
|
121
|
-
|
|
122
|
+
| -------------------------- | -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
122
123
|
| `WALLET_DIR` | all | Wallet collections directory (`keys/`, `zcaps/`, `credentials/`, `was-spaces/`). Defaults to `~/.config/did-cli-wallet/` (honors `XDG_CONFIG_HOME`). |
|
|
123
124
|
| `DIDS_DIR` | `did` | DID-documents directory. Defaults to `<WALLET_DIR>/dids/`. |
|
|
124
125
|
| `SECRET_KEY_SEED` | `key create`, `did create` | Multibase-encoded seed for deterministic key/DID generation. Not supported with `--type ecdsa` or `--type x25519`. |
|
|
@@ -126,6 +127,25 @@ the relevant command section below.
|
|
|
126
127
|
| `WAS_SERVER_URL` | `was` | Default WAS server base URL when `--server` is omitted. |
|
|
127
128
|
| `ZCAP_CONTROLLER_KEY_SEED` | `zcap` | Controller signing-key seed for delegating capabilities. |
|
|
128
129
|
|
|
130
|
+
### File Permissions
|
|
131
|
+
|
|
132
|
+
Everything written under `WALLET_DIR` and `DIDS_DIR` -- keys, zcaps,
|
|
133
|
+
credentials, space records, DID documents, sidecars, and history logs -- is
|
|
134
|
+
created with mode `0600`, owner read/write only. Directories are created with
|
|
135
|
+
mode `0700`, since a DID document is named after its DID and the file names
|
|
136
|
+
alone reveal what you hold. Secret key material is stored as plaintext Multikey
|
|
137
|
+
documents, so the file mode is what keeps it off other local accounts.
|
|
138
|
+
|
|
139
|
+
Files written before this became the default are not migrated. To tighten an
|
|
140
|
+
existing wallet directory:
|
|
141
|
+
|
|
142
|
+
```
|
|
143
|
+
chmod -R u=rwX,go= ~/.config/did-cli-wallet
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
The capital `X` matters -- a plain `chmod -R 600` would clear the traverse bit
|
|
147
|
+
on the directories and leave the wallet unreadable to you as well.
|
|
148
|
+
|
|
129
149
|
### Key Management
|
|
130
150
|
|
|
131
151
|
#### Create a key pair
|
|
@@ -166,7 +186,7 @@ SECRET_KEY_SEED=z1AXVyT6G1Qk3E9cMPkDYY6wVRpZjVGWAZ3TfrAgFZkX6bv ./di key create
|
|
|
166
186
|
```
|
|
167
187
|
|
|
168
188
|
Specify an explicit key type with `--type` (defaults to `ed25519`; supported:
|
|
169
|
-
`ed25519`, `ecdsa`, `x25519`, `hmac`):
|
|
189
|
+
`ed25519`, `ecdsa`, `x25519`, `hmac`, `aes256`):
|
|
170
190
|
|
|
171
191
|
```
|
|
172
192
|
SECRET_KEY_SEED=z1Aaj5A4UCsd... ./di key create --type ed25519
|
|
@@ -244,6 +264,32 @@ half), identified by a random `urn:uuid:` id:
|
|
|
244
264
|
HMAC key generation is non-deterministic, so `--with-seed` and `SECRET_KEY_SEED`
|
|
245
265
|
are not supported with `--type hmac`.
|
|
246
266
|
|
|
267
|
+
Generate a symmetric AES-256 key-encryption key (KEK) -- 32 random bytes carried
|
|
268
|
+
as a Multikey `secretKeyMultibase` -- with `--type aes256`:
|
|
269
|
+
|
|
270
|
+
```
|
|
271
|
+
./di key create --type aes256
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
It is serialized as a `Multikey` with the secret carried as a base58btc
|
|
275
|
+
`secretKeyMultibase` (the AES-256 multicodec header `0xa2 0x01` followed by the
|
|
276
|
+
32 raw key bytes, no public half), identified by a `urn:kek:sha256:` id derived
|
|
277
|
+
from the SHA-256 digest of the raw key bytes:
|
|
278
|
+
|
|
279
|
+
```json
|
|
280
|
+
{
|
|
281
|
+
"id": "urn:kek:sha256:...",
|
|
282
|
+
"type": "Multikey",
|
|
283
|
+
"secretKeyMultibase": "z..."
|
|
284
|
+
}
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
This is the form Wallet Attached Storage servers accept for their at-rest
|
|
288
|
+
key-encryption-key configuration (`KMS_RECORD_KEK` / `KMS_RECORD_KEKS`), where
|
|
289
|
+
each KEK is identified by the same derived `urn:kek:sha256:` id. Generation is
|
|
290
|
+
always fresh and random, so `--with-seed` and `SECRET_KEY_SEED` are not
|
|
291
|
+
supported with `--type aes256`.
|
|
292
|
+
|
|
247
293
|
Save the key to local wallet storage (`~/.config/did-cli-wallet/keys/` by
|
|
248
294
|
default, or
|
|
249
295
|
`$WALLET_DIR/keys/` if set) with `--save`. A `.meta.json` metadata sidecar is
|
|
@@ -568,7 +614,7 @@ distinct keys, so the update key can be rotated without ever disturbing the
|
|
|
568
614
|
document.
|
|
569
615
|
|
|
570
616
|
By default `did:webvh` arms **key pre-rotation**: the DID commits, in advance,
|
|
571
|
-
to the
|
|
617
|
+
to the _hash_ of the key allowed to perform the next update. A compromise of the
|
|
572
618
|
currently active update key cannot be used to seize the DID, because the
|
|
573
619
|
attacker still does not hold the pre-committed next key. So `create` generates
|
|
574
620
|
three keys: the active update key, a staged next update key (whose hash is
|
|
@@ -607,8 +653,8 @@ A few more create-time options are recorded in the signed history-log
|
|
|
607
653
|
be moved to a different domain; `--no-portable` pins it to its origin.
|
|
608
654
|
- `--witness <did...>` -- declare one or more **witness** `did:key` DIDs
|
|
609
655
|
authorized to co-sign the DID's log entries (repeatable). `--witness-threshold
|
|
610
|
-
|
|
611
|
-
witnesses; it requires `--witness`). This only
|
|
656
|
+
<n>` sets how many witness approvals are required (defaults to the number of
|
|
657
|
+
witnesses; it requires `--witness`). This only _declares_ the witnesses;
|
|
612
658
|
actually generating witness proofs is not yet supported.
|
|
613
659
|
- `--watcher <url...>` -- declare one or more **watcher** URLs that monitor the
|
|
614
660
|
DID's log (repeatable; `https://`, or `http://localhost` for local testing).
|
|
@@ -628,7 +674,7 @@ Rotate the update (authorization) key of a locally stored `did:webvh` DID with
|
|
|
628
674
|
With no flags it advances the pre-rotation ratchet in one step: it reveals and
|
|
629
675
|
activates the previously staged next key (signing the new entry with it), and
|
|
630
676
|
stages a fresh next key for the following rotation. The retired update key's
|
|
631
|
-
secret is deleted by default -- a retired key is only ever needed to
|
|
677
|
+
secret is deleted by default -- a retired key is only ever needed to _verify_
|
|
632
678
|
historic log entries, which uses the public key from the log, not the secret:
|
|
633
679
|
|
|
634
680
|
```
|
|
@@ -667,6 +713,8 @@ Flags:
|
|
|
667
713
|
new key (a bare `--stop-prerotation` reveal, or rotating to an external
|
|
668
714
|
`--update-key`).
|
|
669
715
|
- `-y`, `--yes` -- skip the confirmation prompt (rotation is hard to undo).
|
|
716
|
+
Required when stdin is not interactive (scripts, cron), where the prompt
|
|
717
|
+
cannot be asked.
|
|
670
718
|
|
|
671
719
|
#### Add or remove a service entry
|
|
672
720
|
|
|
@@ -1020,7 +1068,7 @@ Store an existing Verifiable Credential in local wallet storage with
|
|
|
1020
1068
|
`vc import`. The credential is read from a file argument, an http(s) URL, or,
|
|
1021
1069
|
if neither is given, from stdin. The input must structurally look like a
|
|
1022
1070
|
credential (its `type` must include `VerifiableCredential`); it is stored
|
|
1023
|
-
as-is and is
|
|
1071
|
+
as-is and is _not_ verified on import (run `vc verify` for that). `--handle` /
|
|
1024
1072
|
`--description` tag the saved credential:
|
|
1025
1073
|
|
|
1026
1074
|
```
|
|
@@ -1393,7 +1441,7 @@ valid until it expires (see `--ttl` / `--expires`).
|
|
|
1393
1441
|
### Wallet Attached Storage (WAS)
|
|
1394
1442
|
|
|
1395
1443
|
The `was` command group is a client for
|
|
1396
|
-
[Wallet Attached Storage](https://
|
|
1444
|
+
[Wallet Attached Storage](https://w3c-ccg.github.io/wallet-attached-storage-spec/)
|
|
1397
1445
|
servers, which organize content as `Space > Collection > Resource` behind
|
|
1398
1446
|
zcap-authorized HTTP. Every request is signed with a `did:key` DID stored in
|
|
1399
1447
|
the local wallet (saved with `did create --save`; Ed25519 keys only for now).
|
|
@@ -1472,7 +1520,7 @@ the server, or the local registry record with `--meta`:
|
|
|
1472
1520
|
|
|
1473
1521
|
`was space update` (alias: `configure`) upserts description fields
|
|
1474
1522
|
(`--name`), also refreshing the registry entry. `was space add` registers an
|
|
1475
|
-
|
|
1523
|
+
_existing_ remote space (a full space URL, or a bare id plus `--server`) in
|
|
1476
1524
|
the local registry, verifying it with a describe first. `was space meta
|
|
1477
1525
|
<space>` updates only a registered space's local metadata (`--handle` and/or
|
|
1478
1526
|
`--description`); the server-side space is untouched, and passing an empty
|
|
@@ -1669,6 +1717,103 @@ the zcap store (`~/.config/did-cli-wallet/zcaps/`):
|
|
|
1669
1717
|
|
|
1670
1718
|
Hand the `encoded` string (or the JSON) to the delegatee out-of-band.
|
|
1671
1719
|
|
|
1720
|
+
#### Request a grant
|
|
1721
|
+
|
|
1722
|
+
`was request-grant` is the inverse of `was grant`, for the case where you are
|
|
1723
|
+
the one who needs access. It asks a user's wallet to delegate a capability on
|
|
1724
|
+
one of its public collections, and is what lets a program publish into someone
|
|
1725
|
+
else's storage without ever logging into their wallet.
|
|
1726
|
+
|
|
1727
|
+
The grantee key is minted by the command rather than passed in, so a script (or
|
|
1728
|
+
an LLM agent) driving the CLI never handles key material: the secret goes
|
|
1729
|
+
straight into local DID storage, and stdout carries only the capability. The
|
|
1730
|
+
approval link and the progress notes go to stderr; `--json` puts the DID, the
|
|
1731
|
+
handle, the link, and the capability into one object on stdout instead.
|
|
1732
|
+
|
|
1733
|
+
```
|
|
1734
|
+
./di was request-grant --server https://was.example \
|
|
1735
|
+
--reason "Publish a demo page" --name "demo-publisher"
|
|
1736
|
+
Requesting "web" access for did:key:z6MkAgent...
|
|
1737
|
+
|
|
1738
|
+
Open this in your wallet to approve:
|
|
1739
|
+
|
|
1740
|
+
https://was.example/workflows/ephemeral/exchanges/abc-123/protocols?iuv=1
|
|
1741
|
+
|
|
1742
|
+
Waiting for approval...
|
|
1743
|
+
```
|
|
1744
|
+
|
|
1745
|
+
If you know which wallet the user runs, `--wallet <url>` also prints a link
|
|
1746
|
+
that opens it straight onto the approval page for this request:
|
|
1747
|
+
|
|
1748
|
+
```
|
|
1749
|
+
./di was request-grant --server https://was.example --wallet https://wallet.example
|
|
1750
|
+
...
|
|
1751
|
+
Or open your wallet directly:
|
|
1752
|
+
|
|
1753
|
+
https://wallet.example/external/request?url=https%3A%2F%2Fwas.example%2F...
|
|
1754
|
+
```
|
|
1755
|
+
|
|
1756
|
+
The interaction URL is still printed beside it, since that is what a wallet on
|
|
1757
|
+
another device can be pointed at.
|
|
1758
|
+
|
|
1759
|
+
The user opens that link in their wallet and approves. The command then prints
|
|
1760
|
+
the capability it received (the same `z...` form `--capability` accepts) and
|
|
1761
|
+
files it in the zcap store under `--handle` (default `agent`), together with the
|
|
1762
|
+
minted key:
|
|
1763
|
+
|
|
1764
|
+
```
|
|
1765
|
+
Capability saved to ~/.config/did-cli-wallet/zcaps/urn_uuid_....json
|
|
1766
|
+
Granted. Use it with --capability agent, for example:
|
|
1767
|
+
di was put ./index.html --capability agent --did agent --resource index.html --content-type text/html
|
|
1768
|
+
zkL8vet8M2mn...
|
|
1769
|
+
```
|
|
1770
|
+
|
|
1771
|
+
Saving is what makes the grant usable: `--capability` resolves its signing key
|
|
1772
|
+
out of the local DID store, so a key held only for the run cannot sign the write
|
|
1773
|
+
that follows. `--no-save` prints the capability without keeping either half, for
|
|
1774
|
+
inspecting a grant you do not intend to use.
|
|
1775
|
+
|
|
1776
|
+
The signing DID is taken from `--did` first, then `WAS_DID`, and only then the
|
|
1777
|
+
capability's own `controller`. Naming the minted key explicitly
|
|
1778
|
+
(`--did agent`, matching the handle the grant was filed under) is therefore
|
|
1779
|
+
worth doing whenever `WAS_DID` is set in the environment, or the write is signed
|
|
1780
|
+
by your own DID and the server rejects it.
|
|
1781
|
+
|
|
1782
|
+
A run files the minted key and the received capability under the same handle, so
|
|
1783
|
+
a handle already taken by a stored DID or capability is refused before anything
|
|
1784
|
+
is minted; pass a different `--handle` for a second grant. Nothing is written
|
|
1785
|
+
until the grant arrives -- a request that is declined, expires, or times out
|
|
1786
|
+
leaves no key behind.
|
|
1787
|
+
|
|
1788
|
+
Options are `--collection <name>` (default `web`), `--action <verb...>` (default
|
|
1789
|
+
`GET HEAD PUT POST`), `--reason <text>` (shown to the user at the consent step),
|
|
1790
|
+
`--name <name>` (what the agent calls itself, shown to the user beside its
|
|
1791
|
+
key), `--wallet <url>` (a wallet base URL to print an approval deep link for),
|
|
1792
|
+
`--timeout <seconds>` (default 600, matching the server's exchange
|
|
1793
|
+
lifetime), `--handle` / `--description`, `--no-save`, `--json`, and `--server`
|
|
1794
|
+
(or `WAS_SERVER_URL`).
|
|
1795
|
+
|
|
1796
|
+
The end-to-end flow this command exists for -- an LLM agent drafting a page and
|
|
1797
|
+
publishing it to the user's own storage -- is scripted in
|
|
1798
|
+
[demo/agent-storage](demo/agent-storage/README.md), with the agent-facing
|
|
1799
|
+
version in the `publish-page` skill under `.claude/skills/`.
|
|
1800
|
+
|
|
1801
|
+
`--name` is self-declared, not verified: the wallet shows it as what the agent
|
|
1802
|
+
calls itself, next to the grantee key, so the user should still compare the
|
|
1803
|
+
key rather than trust the name alone. It is at most 64 characters and cannot
|
|
1804
|
+
contain control characters.
|
|
1805
|
+
|
|
1806
|
+
Two things the wallet decides, not this command:
|
|
1807
|
+
|
|
1808
|
+
- A public collection is only ever created public. If the named collection
|
|
1809
|
+
already exists and is not world-readable, the wallet cannot satisfy the
|
|
1810
|
+
request, and the approval comes back granting nothing.
|
|
1811
|
+
- The actions you ask for are a request, not an instruction. The wallet caps
|
|
1812
|
+
them by what the collection allows, and may grant fewer than you asked for.
|
|
1813
|
+
|
|
1814
|
+
The exchange lives on the server for ten minutes. After that the link is dead
|
|
1815
|
+
and the command reports it; run it again for a fresh one.
|
|
1816
|
+
|
|
1672
1817
|
#### Use a received capability
|
|
1673
1818
|
|
|
1674
1819
|
On the receiving side, `ls` / `get` / `put` / `rm` (and `resource
|
|
@@ -1680,21 +1825,32 @@ of:
|
|
|
1680
1825
|
- the capability id or metadata **handle of a zcap** stored in
|
|
1681
1826
|
`~/.config/did-cli-wallet/zcaps/`.
|
|
1682
1827
|
|
|
1683
|
-
Note that a `--capability` reference is
|
|
1828
|
+
Note that a `--capability` reference is _not_ a WAS path -- no space,
|
|
1684
1829
|
collection, or resource address is given (or needed). The capability itself
|
|
1685
1830
|
records what it grants access to in its `invocationTarget`, and that is what
|
|
1686
1831
|
the command operates on:
|
|
1687
1832
|
|
|
1688
|
-
- a capability granted on a **resource** drives `get` / `put` / `rm
|
|
1833
|
+
- a capability granted on a **resource** drives `get` / `put` / `rm` and
|
|
1834
|
+
`meta get` / `meta put`;
|
|
1689
1835
|
- one granted on a **collection** drives `ls`, `resource add`, and `rm`;
|
|
1690
1836
|
- one granted on a whole **space** drives `ls` and `rm`.
|
|
1691
1837
|
|
|
1692
|
-
A
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1838
|
+
A collection-scoped capability also reaches one resource inside it, named
|
|
1839
|
+
with `--resource <id>`: `get`, `put`, `meta get`, `meta put`, and `rm` then
|
|
1840
|
+
act on that id beneath the capability's collection (without the flag, `rm`
|
|
1841
|
+
deletes the collection itself). This is how a grant issued on a whole
|
|
1842
|
+
collection writes at an id you choose (`resource add` also takes a collection
|
|
1843
|
+
capability, but the server picks the id, so it cannot produce `index.html`).
|
|
1844
|
+
The resource handle inherits the collection's capability, so the request is
|
|
1845
|
+
still an invocation of the grant you received.
|
|
1846
|
+
|
|
1847
|
+
Any other depth mismatch (e.g. `get` with a space-scoped capability) is an
|
|
1848
|
+
input error, as is `--resource` without a capability to anchor it. The
|
|
1849
|
+
server URL is taken from the invocation target's origin, and the signing DID
|
|
1850
|
+
defaults to the capability's controller (the delegatee) when that DID is
|
|
1851
|
+
stored locally -- so usually no flags are needed at all.
|
|
1852
|
+
|
|
1853
|
+
In the examples below, `bob-share` is the metadata handle of a _stored zcap_
|
|
1698
1854
|
(not a space or collection handle): say Alice granted Bob `GET`/`PUT` on the
|
|
1699
1855
|
single resource `home/credentials/vc-1`, and the capability was saved with
|
|
1700
1856
|
`--save --handle bob-share` (on Alice's machine via `was grant --save`; on
|
|
@@ -1722,6 +1878,16 @@ Bob's machine he can pass the encoded string or a JSON file directly):
|
|
|
1722
1878
|
# Had the grant been on the whole collection (home/credentials), ls would
|
|
1723
1879
|
# list it and `resource add` could post new resources into it:
|
|
1724
1880
|
./di was ls --capability zkL8vet8M2mn...
|
|
1881
|
+
|
|
1882
|
+
# ...and --resource picks one id inside that collection, so the write lands
|
|
1883
|
+
# at a name you choose rather than a server-generated one:
|
|
1884
|
+
./di was put ./index.html --capability zkL8vet8M2mn... \
|
|
1885
|
+
--resource index.html --content-type text/html
|
|
1886
|
+
./di was get --capability zkL8vet8M2mn... --resource index.html
|
|
1887
|
+
|
|
1888
|
+
# ...and the same flag deletes that one resource, rather than the whole
|
|
1889
|
+
# collection the capability targets:
|
|
1890
|
+
./di was rm --capability zkL8vet8M2mn... --resource index.html
|
|
1725
1891
|
```
|
|
1726
1892
|
|
|
1727
1893
|
#### Policies and public sharing
|
|
@@ -4,6 +4,28 @@
|
|
|
4
4
|
* `add-key` default purpose set (described in its help text).
|
|
5
5
|
*/
|
|
6
6
|
export declare const DEFAULT_VERIFICATION_PURPOSES: readonly ["authentication", "assertionMethod", "capabilityDelegation", "capabilityInvocation"];
|
|
7
|
+
/**
|
|
8
|
+
* Save the artifacts of a newly created DID: the DID document, its keys file,
|
|
9
|
+
* a metadata sidecar (creation timestamp plus the handle and description when
|
|
10
|
+
* given), and the key-to-DID association cache of any matching wallet keys.
|
|
11
|
+
*
|
|
12
|
+
* @param options {object}
|
|
13
|
+
* @param options.method {string}
|
|
14
|
+
* @param options.didDocument {object}
|
|
15
|
+
* @param options.exportedKeys {object}
|
|
16
|
+
* @param options.fingerprints {(string | undefined)[]}
|
|
17
|
+
* @param [options.handle] {string}
|
|
18
|
+
* @param [options.description] {string}
|
|
19
|
+
* @returns {Promise<void>}
|
|
20
|
+
*/
|
|
21
|
+
export declare function saveDidArtifacts({ method, didDocument, exportedKeys, fingerprints, handle, description }: {
|
|
22
|
+
method: string;
|
|
23
|
+
didDocument: object;
|
|
24
|
+
exportedKeys: object;
|
|
25
|
+
fingerprints: (string | undefined)[];
|
|
26
|
+
handle?: string;
|
|
27
|
+
description?: string;
|
|
28
|
+
}): Promise<void>;
|
|
7
29
|
/**
|
|
8
30
|
* Create a new DID of the given method (key, web, or webvh), optionally saving
|
|
9
31
|
* its document, keys, and metadata to local storage, and print it to stdout.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../../src/commands/did/create.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../../src/commands/did/create.ts"],"names":[],"mappings":"AAmCA;;;;GAIG;AACH,eAAO,MAAM,6BAA6B,gGAKhC,CAAA;AAEV;;;;;;;;;;;;;GAaG;AACH,wBAAsB,gBAAgB,CAAC,EACrC,MAAM,EACN,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,MAAM,EACN,WAAW,EACZ,EAAE;IACD,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,EAAE,MAAM,CAAA;IACpB,YAAY,EAAE,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,CAAA;IACpC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,GAAG,OAAO,CAAC,IAAI,CAAC,CAkBhB;AA8ED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,SAAS,CAAC,OAAO,EAAE;IACvC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,GAAG,OAAO,CAAC,MAAM,CAAC,CA2XlB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,SAAS,CAAC,OAAO,EAAE;IACvC,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,GAAG,OAAO,CAAC,MAAM,CAAC,CAsIlB"}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* DID creation and key addition: `runCreate` (key, web, webvh) and `runAddKey`
|
|
3
|
-
* (add a verification key to a stored did:web). Both share the
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* `./webvh-update.js`.
|
|
3
|
+
* (add a verification key to a stored did:web). Both share the ECDSA-curve,
|
|
4
|
+
* non-deterministic-seed-guard, and output helpers at the top of this module;
|
|
5
|
+
* seed derivation comes from `../../keys/seed.js` and the webvh signer/sidecar
|
|
6
|
+
* plumbing is reused from `./webvh-update.js`.
|
|
7
7
|
*/
|
|
8
|
-
import { decodeSecretKeySeed, generateSecretKeySeed } from '@digitalcredentials/bnid';
|
|
9
8
|
import { driver } from '@interop/did-method-key';
|
|
10
9
|
import * as didWeb from '@interop/did-web-resolver';
|
|
11
10
|
import { createDID } from '@interop/did-method-webvh';
|
|
@@ -15,6 +14,7 @@ import { X25519KeyAgreementKey2020 } from '@interop/x25519-key-agreement-key';
|
|
|
15
14
|
import { loadDidDocument, loadDidKeys, saveDidLog, saveDidMeta, saveToDids } from '../../storage.js';
|
|
16
15
|
import { recordKeyDidAssociation } from '../../meta.js';
|
|
17
16
|
import { normalizeEcdsaCurve, SUPPORTED_ECDSA_CURVES, warnIfNotVcIssuanceCapable } from '../../keys/ecdsa.js';
|
|
17
|
+
import { deriveSeed } from '../../keys/seed.js';
|
|
18
18
|
import { exportUpdateKey, generateStagedKey } from '../../keys/webvh-update.js';
|
|
19
19
|
import { requireSaveForMetaFlags } from '../collection-command.js';
|
|
20
20
|
import { makeWebvhEntrySigner, persistUpdateKeysSidecar } from './webvh-update.js';
|
|
@@ -43,7 +43,7 @@ export const DEFAULT_VERIFICATION_PURPOSES = [
|
|
|
43
43
|
* @param [options.description] {string}
|
|
44
44
|
* @returns {Promise<void>}
|
|
45
45
|
*/
|
|
46
|
-
async function saveDidArtifacts({ method, didDocument, exportedKeys, fingerprints, handle, description }) {
|
|
46
|
+
export async function saveDidArtifacts({ method, didDocument, exportedKeys, fingerprints, handle, description }) {
|
|
47
47
|
const did = didDocument.id;
|
|
48
48
|
const docPath = await saveToDids({ method, did, data: didDocument });
|
|
49
49
|
await saveToDids({ method, did, suffix: 'keys', data: exportedKeys });
|
|
@@ -62,27 +62,6 @@ async function saveDidArtifacts({ method, didDocument, exportedKeys, fingerprint
|
|
|
62
62
|
}
|
|
63
63
|
console.error(`DID saved to ${docPath}`);
|
|
64
64
|
}
|
|
65
|
-
/**
|
|
66
|
-
* Resolve the secret key seed for a deterministic (Ed25519) key. With
|
|
67
|
-
* `--with-seed`, an existing `SECRET_KEY_SEED` env var is honored or a fresh
|
|
68
|
-
* seed generated; without it, only an explicitly set env seed is used. Returns
|
|
69
|
-
* the encoded seed (echoed back to the user) and its decoded bytes (for key
|
|
70
|
-
* generation).
|
|
71
|
-
*
|
|
72
|
-
* @param options {object}
|
|
73
|
-
* @param [options.withSeed] {boolean}
|
|
74
|
-
* @returns {Promise<{ secretKeySeed?: string, seedBytes?: Uint8Array }>}
|
|
75
|
-
*/
|
|
76
|
-
async function deriveSeed({ withSeed }) {
|
|
77
|
-
const envSeed = process.env.SECRET_KEY_SEED;
|
|
78
|
-
const secretKeySeed = withSeed
|
|
79
|
-
? (envSeed ?? (await generateSecretKeySeed()))
|
|
80
|
-
: envSeed;
|
|
81
|
-
const seedBytes = secretKeySeed
|
|
82
|
-
? decodeSecretKeySeed({ secretKeySeed })
|
|
83
|
-
: undefined;
|
|
84
|
-
return { secretKeySeed, seedBytes };
|
|
85
|
-
}
|
|
86
65
|
/**
|
|
87
66
|
* Guard a non-deterministic key type against `--with-seed`: ECDSA and X25519
|
|
88
67
|
* keys are generated non-deterministically and cannot be derived from a seed.
|