@interop/did-cli 0.10.0 → 0.10.1
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 +58 -0
- package/dist/commands/collection-command.d.ts +169 -0
- package/dist/commands/collection-command.d.ts.map +1 -0
- package/dist/commands/collection-command.js +241 -0
- package/dist/commands/collection-command.js.map +1 -0
- package/dist/commands/did/create.d.ts +63 -0
- package/dist/commands/did/create.d.ts.map +1 -0
- package/dist/commands/did/create.js +625 -0
- package/dist/commands/did/create.js.map +1 -0
- package/dist/commands/did/manage.d.ts +68 -0
- package/dist/commands/did/manage.d.ts.map +1 -0
- package/dist/commands/did/manage.js +287 -0
- package/dist/commands/did/manage.js.map +1 -0
- package/dist/commands/did/service.d.ts +93 -0
- package/dist/commands/did/service.d.ts.map +1 -0
- package/dist/commands/did/service.js +324 -0
- package/dist/commands/did/service.js.map +1 -0
- package/dist/commands/did/webvh-update.d.ts +170 -0
- package/dist/commands/did/webvh-update.d.ts.map +1 -0
- package/dist/commands/did/webvh-update.js +402 -0
- package/dist/commands/did/webvh-update.js.map +1 -0
- package/dist/commands/did.d.ts +8 -8
- package/dist/commands/did.d.ts.map +1 -1
- package/dist/commands/did.js +27 -1519
- package/dist/commands/did.js.map +1 -1
- package/dist/commands/edv.d.ts +15 -6
- package/dist/commands/edv.d.ts.map +1 -1
- package/dist/commands/edv.js +38 -28
- package/dist/commands/edv.js.map +1 -1
- package/dist/commands/key.d.ts +104 -0
- package/dist/commands/key.d.ts.map +1 -1
- package/dist/commands/key.js +432 -402
- package/dist/commands/key.js.map +1 -1
- package/dist/commands/vc.d.ts +2 -1
- package/dist/commands/vc.d.ts.map +1 -1
- package/dist/commands/vc.js +88 -145
- package/dist/commands/vc.js.map +1 -1
- package/dist/commands/was/collection.d.ts.map +1 -1
- package/dist/commands/was/collection.js +31 -85
- package/dist/commands/was/collection.js.map +1 -1
- package/dist/commands/was/publish.d.ts.map +1 -1
- package/dist/commands/was/publish.js +16 -44
- package/dist/commands/was/publish.js.map +1 -1
- package/dist/commands/was/resource.d.ts.map +1 -1
- package/dist/commands/was/resource.js +24 -51
- package/dist/commands/was/resource.js.map +1 -1
- package/dist/commands/was/shared.d.ts +78 -0
- package/dist/commands/was/shared.d.ts.map +1 -1
- package/dist/commands/was/shared.js +84 -0
- package/dist/commands/was/shared.js.map +1 -1
- package/dist/commands/was/space.d.ts.map +1 -1
- package/dist/commands/was/space.js +10 -16
- package/dist/commands/was/space.js.map +1 -1
- package/dist/commands/was/tree.d.ts.map +1 -1
- package/dist/commands/was/tree.js +4 -6
- package/dist/commands/was/tree.js.map +1 -1
- package/dist/commands/was.d.ts.map +1 -1
- package/dist/commands/was.js +60 -74
- package/dist/commands/was.js.map +1 -1
- package/dist/commands/zcap.d.ts +0 -18
- package/dist/commands/zcap.d.ts.map +1 -1
- package/dist/commands/zcap.js +63 -155
- package/dist/commands/zcap.js.map +1 -1
- package/dist/documentLoader.d.ts +5 -0
- package/dist/documentLoader.d.ts.map +1 -0
- package/dist/documentLoader.js +29 -0
- package/dist/documentLoader.js.map +1 -0
- package/dist/edv/hmac.d.ts.map +1 -1
- package/dist/edv/hmac.js +12 -12
- package/dist/edv/hmac.js.map +1 -1
- package/dist/edv/recipients.d.ts +2 -0
- package/dist/edv/recipients.d.ts.map +1 -1
- package/dist/edv/recipients.js +12 -15
- package/dist/edv/recipients.js.map +1 -1
- package/dist/meta.d.ts +36 -11
- package/dist/meta.d.ts.map +1 -1
- package/dist/meta.js +95 -73
- package/dist/meta.js.map +1 -1
- package/dist/storage.d.ts +48 -13
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +40 -16
- package/dist/storage.js.map +1 -1
- package/dist/vc/issue.d.ts.map +1 -1
- package/dist/vc/issue.js +4 -7
- package/dist/vc/issue.js.map +1 -1
- package/dist/vc/verify.d.ts +10 -0
- package/dist/vc/verify.d.ts.map +1 -1
- package/dist/vc/verify.js +38 -1
- package/dist/vc/verify.js.map +1 -1
- package/dist/was/client.d.ts.map +1 -1
- package/dist/was/client.js +8 -2
- package/dist/was/client.js.map +1 -1
- package/dist/was/grant.d.ts +44 -0
- package/dist/was/grant.d.ts.map +1 -0
- package/dist/was/grant.js +78 -0
- package/dist/was/grant.js.map +1 -0
- package/dist/was/registry.d.ts.map +1 -1
- package/dist/was/registry.js +9 -2
- package/dist/was/registry.js.map +1 -1
- package/dist/zcap/delegate.d.ts.map +1 -1
- package/dist/zcap/delegate.js +1 -6
- package/dist/zcap/delegate.js.map +1 -1
- package/dist/zcap/signer.d.ts.map +1 -1
- package/dist/zcap/signer.js.map +1 -1
- package/package.json +5 -4
- package/dist/keys/webvh-driver.d.ts +0 -24
- package/dist/keys/webvh-driver.d.ts.map +0 -1
- package/dist/keys/webvh-driver.js +0 -73
- package/dist/keys/webvh-driver.js.map +0 -1
package/dist/commands/key.js
CHANGED
|
@@ -1,13 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `key` command -- create and manage cryptographic keys in local wallet storage.
|
|
3
|
+
*
|
|
4
|
+
* `key create` generates a key pair of the requested `--type` (ed25519, ecdsa,
|
|
5
|
+
* x25519, or hmac) and prints the exported key object to stdout; `--save` writes
|
|
6
|
+
* it to local wallet storage (`~/.config/did-cli-wallet/keys/`) along with a
|
|
7
|
+
* `.meta.json` metadata sidecar (creation timestamp plus `--handle` /
|
|
8
|
+
* `--description` when given), and `--with-seed` includes the secret key seed in
|
|
9
|
+
* the output. `list` renders a metadata table of the saved keys (with the DIDs
|
|
10
|
+
* each key participates in, re-derived from the stored DID documents), `show`
|
|
11
|
+
* prints one key's public object back (`--meta` for its metadata), `meta` edits
|
|
12
|
+
* the metadata sidecar, and `remove` deletes a stored key; `export` remains a
|
|
13
|
+
* stub. Keys are referenced by their `publicKeyMultibase` fingerprint or by a
|
|
14
|
+
* metadata handle. Data goes to stdout, diagnostics and errors to stderr.
|
|
15
|
+
*/
|
|
1
16
|
import { Command } from 'commander';
|
|
2
17
|
import { decodeSecretKeySeed, generateSecretKeySeed } from '@digitalcredentials/bnid';
|
|
3
18
|
import { Ed25519VerificationKey } from '@interop/ed25519-verification-key';
|
|
4
19
|
import * as EcdsaMultikey from '@interop/ecdsa-multikey';
|
|
5
20
|
import { X25519KeyAgreementKey2020 } from '@interop/x25519-key-agreement-key';
|
|
6
21
|
import { SHA256HMACKey } from '@interop/data-integrity-core';
|
|
7
|
-
import {
|
|
22
|
+
import { saveMetaToCollection, saveToCollection } from '../storage.js';
|
|
8
23
|
import { mapFingerprintsToDids, parseKeyStorageId, resolveKeyRef } from '../meta.js';
|
|
9
24
|
import { renderTable, truncateMiddle } from '../table.js';
|
|
10
25
|
import { isEcdsaPublicKeyMultibase, normalizeEcdsaCurve, SUPPORTED_ECDSA_CURVES, warnIfNotVcIssuanceCapable } from '../keys/ecdsa.js';
|
|
26
|
+
import { runAndExit } from './was/shared.js';
|
|
27
|
+
import { applyMetaEdits, requireSaveForMetaFlags, resolveRefOrReport, runListCollection, runRemoveCollection, writeCreateMeta } from './collection-command.js';
|
|
28
|
+
/** The wallet collection name for stored keys. */
|
|
29
|
+
const COLLECTION = 'keys';
|
|
11
30
|
/**
|
|
12
31
|
* Build the human-readable key type label shown in list/show output, e.g.
|
|
13
32
|
* `ed25519` or `ecdsa-p256`, from a parsed key storage ID.
|
|
@@ -39,219 +58,206 @@ function didsCell({ dids }) {
|
|
|
39
58
|
return dids.length > 1 ? `${first} (+${dids.length - 1})` : first;
|
|
40
59
|
}
|
|
41
60
|
/**
|
|
42
|
-
*
|
|
43
|
-
*
|
|
61
|
+
* Generates a key pair of the requested type, optionally saving it to the
|
|
62
|
+
* wallet, and prints the key (or the seed-wrapped form) to stdout.
|
|
44
63
|
*
|
|
45
64
|
* @param options {object}
|
|
46
|
-
* @param options.
|
|
47
|
-
* @param options.
|
|
48
|
-
* @param [options.
|
|
49
|
-
* @param [options.
|
|
50
|
-
* @
|
|
65
|
+
* @param options.type {string} Key type: ed25519, ecdsa, x25519, or hmac.
|
|
66
|
+
* @param options.curve {string} ECDSA curve, for --type ecdsa.
|
|
67
|
+
* @param [options.save] {boolean} Save the key to wallet storage.
|
|
68
|
+
* @param [options.handle] {string} Short tag stored in the metadata sidecar.
|
|
69
|
+
* @param [options.description] {string} Longer description for the sidecar.
|
|
70
|
+
* @param [options.withSeed] {boolean} Include/derive the secret key seed
|
|
71
|
+
* (ed25519 only).
|
|
72
|
+
* @returns {Promise<number>} The process exit code.
|
|
51
73
|
*/
|
|
52
|
-
async function
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
meta.handle = handle;
|
|
74
|
+
export async function runCreate(options) {
|
|
75
|
+
if (!requireSaveForMetaFlags(options)) {
|
|
76
|
+
return 1;
|
|
56
77
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const envSeed = process.env.SECRET_KEY_SEED;
|
|
83
|
-
const secretKeySeed = options.withSeed
|
|
84
|
-
? (envSeed ?? (await generateSecretKeySeed()))
|
|
85
|
-
: envSeed;
|
|
86
|
-
const seedBytes = secretKeySeed
|
|
87
|
-
? decodeSecretKeySeed({ secretKeySeed })
|
|
88
|
-
: undefined;
|
|
89
|
-
const keyPair = await Ed25519VerificationKey.generate({
|
|
90
|
-
seed: seedBytes
|
|
78
|
+
switch (options.type) {
|
|
79
|
+
case 'ed25519': {
|
|
80
|
+
const envSeed = process.env.SECRET_KEY_SEED;
|
|
81
|
+
const secretKeySeed = options.withSeed
|
|
82
|
+
? (envSeed ?? (await generateSecretKeySeed()))
|
|
83
|
+
: envSeed;
|
|
84
|
+
const seedBytes = secretKeySeed
|
|
85
|
+
? decodeSecretKeySeed({ secretKeySeed })
|
|
86
|
+
: undefined;
|
|
87
|
+
const keyPair = await Ed25519VerificationKey.generate({
|
|
88
|
+
seed: seedBytes
|
|
89
|
+
});
|
|
90
|
+
const exported = await keyPair.export({
|
|
91
|
+
publicKey: true,
|
|
92
|
+
secretKey: true
|
|
93
|
+
});
|
|
94
|
+
if (options.save) {
|
|
95
|
+
const now = new Date();
|
|
96
|
+
const date = now.toISOString().slice(0, 10);
|
|
97
|
+
const rawId = exported.id ?? keyPair.publicKeyMultibase;
|
|
98
|
+
const storageId = `${date}-${options.type}-${rawId}`.replaceAll(':', '_');
|
|
99
|
+
const filePath = await saveToCollection({
|
|
100
|
+
collection: 'keys',
|
|
101
|
+
storageId,
|
|
102
|
+
data: exported
|
|
91
103
|
});
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
104
|
+
await writeCreateMeta({
|
|
105
|
+
collection: COLLECTION,
|
|
106
|
+
storageId,
|
|
107
|
+
created: now.toISOString(),
|
|
108
|
+
handle: options.handle,
|
|
109
|
+
description: options.description
|
|
95
110
|
});
|
|
96
|
-
|
|
97
|
-
const now = new Date();
|
|
98
|
-
const date = now.toISOString().slice(0, 10);
|
|
99
|
-
const rawId = exported.id ?? keyPair.publicKeyMultibase;
|
|
100
|
-
const storageId = `${date}-${options.type}-${rawId}`.replaceAll(':', '_');
|
|
101
|
-
const filePath = await saveToCollection('keys', storageId, exported);
|
|
102
|
-
await writeCreateMeta({
|
|
103
|
-
storageId,
|
|
104
|
-
created: now.toISOString(),
|
|
105
|
-
handle: options.handle,
|
|
106
|
-
description: options.description
|
|
107
|
-
});
|
|
108
|
-
console.error(`Key saved to ${filePath}`);
|
|
109
|
-
}
|
|
110
|
-
const output = options.withSeed
|
|
111
|
-
? { secretKeySeed, keyPair: exported }
|
|
112
|
-
: exported;
|
|
113
|
-
console.log(JSON.stringify(output, null, 2));
|
|
114
|
-
break;
|
|
111
|
+
console.error(`Key saved to ${filePath}`);
|
|
115
112
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
publicKey: true,
|
|
135
|
-
secretKey: true
|
|
136
|
-
});
|
|
137
|
-
if (options.save) {
|
|
138
|
-
const now = new Date();
|
|
139
|
-
const date = now.toISOString().slice(0, 10);
|
|
140
|
-
const rawId = exported.id ?? exported.publicKeyMultibase;
|
|
141
|
-
const curveLabel = curve.replace('-', '').toLowerCase();
|
|
142
|
-
const storageId = `${date}-ecdsa-${curveLabel}-${rawId}`.replaceAll(':', '_');
|
|
143
|
-
const filePath = await saveToCollection('keys', storageId, exported);
|
|
144
|
-
await writeCreateMeta({
|
|
145
|
-
storageId,
|
|
146
|
-
created: now.toISOString(),
|
|
147
|
-
handle: options.handle,
|
|
148
|
-
description: options.description
|
|
149
|
-
});
|
|
150
|
-
console.error(`Key saved to ${filePath}`);
|
|
151
|
-
}
|
|
152
|
-
console.log(JSON.stringify(exported, null, 2));
|
|
153
|
-
break;
|
|
113
|
+
const output = options.withSeed
|
|
114
|
+
? { secretKeySeed, keyPair: exported }
|
|
115
|
+
: exported;
|
|
116
|
+
console.log(JSON.stringify(output, null, 2));
|
|
117
|
+
return 0;
|
|
118
|
+
}
|
|
119
|
+
case 'ecdsa': {
|
|
120
|
+
if (options.withSeed) {
|
|
121
|
+
console.error('--with-seed is not supported for ecdsa keys; ECDSA key ' +
|
|
122
|
+
'generation is non-deterministic and cannot be derived ' +
|
|
123
|
+
'from a seed.');
|
|
124
|
+
return 1;
|
|
125
|
+
}
|
|
126
|
+
const curve = normalizeEcdsaCurve({ curve: options.curve });
|
|
127
|
+
if (!curve) {
|
|
128
|
+
console.error(`Unknown ecdsa curve: ${options.curve}. ` +
|
|
129
|
+
`Supported: ${SUPPORTED_ECDSA_CURVES}`);
|
|
130
|
+
return 1;
|
|
154
131
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
const
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
132
|
+
warnIfNotVcIssuanceCapable({ curve });
|
|
133
|
+
const keyPair = await EcdsaMultikey.generate({ curve });
|
|
134
|
+
const exported = await keyPair.export({
|
|
135
|
+
publicKey: true,
|
|
136
|
+
secretKey: true
|
|
137
|
+
});
|
|
138
|
+
if (options.save) {
|
|
139
|
+
const now = new Date();
|
|
140
|
+
const date = now.toISOString().slice(0, 10);
|
|
141
|
+
const rawId = exported.id ?? exported.publicKeyMultibase;
|
|
142
|
+
const curveLabel = curve.replace('-', '').toLowerCase();
|
|
143
|
+
const storageId = `${date}-ecdsa-${curveLabel}-${rawId}`.replaceAll(':', '_');
|
|
144
|
+
const filePath = await saveToCollection({
|
|
145
|
+
collection: 'keys',
|
|
146
|
+
storageId,
|
|
147
|
+
data: exported
|
|
148
|
+
});
|
|
149
|
+
await writeCreateMeta({
|
|
150
|
+
collection: COLLECTION,
|
|
151
|
+
storageId,
|
|
152
|
+
created: now.toISOString(),
|
|
153
|
+
handle: options.handle,
|
|
154
|
+
description: options.description
|
|
167
155
|
});
|
|
168
|
-
|
|
169
|
-
const now = new Date();
|
|
170
|
-
const date = now.toISOString().slice(0, 10);
|
|
171
|
-
const rawId = exported.id ?? exported.publicKeyMultibase;
|
|
172
|
-
const storageId = `${date}-x25519-${rawId}`.replaceAll(':', '_');
|
|
173
|
-
const filePath = await saveToCollection('keys', storageId, exported);
|
|
174
|
-
await writeCreateMeta({
|
|
175
|
-
storageId,
|
|
176
|
-
created: now.toISOString(),
|
|
177
|
-
handle: options.handle,
|
|
178
|
-
description: options.description
|
|
179
|
-
});
|
|
180
|
-
console.error(`Key saved to ${filePath}`);
|
|
181
|
-
}
|
|
182
|
-
console.log(JSON.stringify(exported, null, 2));
|
|
183
|
-
break;
|
|
156
|
+
console.error(`Key saved to ${filePath}`);
|
|
184
157
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
const exported = await hmac.export({ secretKey: true });
|
|
195
|
-
if (options.save) {
|
|
196
|
-
const now = new Date();
|
|
197
|
-
const date = now.toISOString().slice(0, 10);
|
|
198
|
-
const storageId = `${date}-hmac-${exported.id}`.replaceAll(':', '_');
|
|
199
|
-
const filePath = await saveToCollection('keys', storageId, exported);
|
|
200
|
-
await writeCreateMeta({
|
|
201
|
-
storageId,
|
|
202
|
-
created: now.toISOString(),
|
|
203
|
-
handle: options.handle,
|
|
204
|
-
description: options.description
|
|
205
|
-
});
|
|
206
|
-
console.error(`Key saved to ${filePath}`);
|
|
207
|
-
}
|
|
208
|
-
console.log(JSON.stringify(exported, null, 2));
|
|
209
|
-
break;
|
|
158
|
+
console.log(JSON.stringify(exported, null, 2));
|
|
159
|
+
return 0;
|
|
160
|
+
}
|
|
161
|
+
case 'x25519': {
|
|
162
|
+
if (options.withSeed) {
|
|
163
|
+
console.error('--with-seed is not supported for x25519 keys; X25519 key ' +
|
|
164
|
+
'generation is non-deterministic and cannot be derived ' +
|
|
165
|
+
'from a seed.');
|
|
166
|
+
return 1;
|
|
210
167
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
168
|
+
const keyPair = await X25519KeyAgreementKey2020.generate();
|
|
169
|
+
const exported = await keyPair.export({
|
|
170
|
+
publicKey: true,
|
|
171
|
+
privateKey: true
|
|
172
|
+
});
|
|
173
|
+
if (options.save) {
|
|
174
|
+
const now = new Date();
|
|
175
|
+
const date = now.toISOString().slice(0, 10);
|
|
176
|
+
const rawId = exported.id ?? exported.publicKeyMultibase;
|
|
177
|
+
const storageId = `${date}-x25519-${rawId}`.replaceAll(':', '_');
|
|
178
|
+
const filePath = await saveToCollection({
|
|
179
|
+
collection: 'keys',
|
|
180
|
+
storageId,
|
|
181
|
+
data: exported
|
|
182
|
+
});
|
|
183
|
+
await writeCreateMeta({
|
|
184
|
+
collection: COLLECTION,
|
|
185
|
+
storageId,
|
|
186
|
+
created: now.toISOString(),
|
|
187
|
+
handle: options.handle,
|
|
188
|
+
description: options.description
|
|
189
|
+
});
|
|
190
|
+
console.error(`Key saved to ${filePath}`);
|
|
191
|
+
}
|
|
192
|
+
console.log(JSON.stringify(exported, null, 2));
|
|
193
|
+
return 0;
|
|
215
194
|
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
.action(async (options) => {
|
|
223
|
-
const storageIds = await listCollection('keys');
|
|
224
|
-
if (options.plain) {
|
|
225
|
-
const keyIds = [];
|
|
226
|
-
for (const storageId of storageIds) {
|
|
227
|
-
const key = await loadFromCollection('keys', storageId);
|
|
228
|
-
if (key.publicKeyMultibase) {
|
|
229
|
-
keyIds.push(key.publicKeyMultibase);
|
|
230
|
-
}
|
|
195
|
+
case 'hmac': {
|
|
196
|
+
if (options.withSeed) {
|
|
197
|
+
console.error('--with-seed is not supported for hmac keys; HMAC key ' +
|
|
198
|
+
'generation is non-deterministic and cannot be derived ' +
|
|
199
|
+
'from a seed.');
|
|
200
|
+
return 1;
|
|
231
201
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
202
|
+
const hmac = await SHA256HMACKey.generate();
|
|
203
|
+
const exported = await hmac.export({ secretKey: true });
|
|
204
|
+
if (options.save) {
|
|
205
|
+
const now = new Date();
|
|
206
|
+
const date = now.toISOString().slice(0, 10);
|
|
207
|
+
const storageId = `${date}-hmac-${exported.id}`.replaceAll(':', '_');
|
|
208
|
+
const filePath = await saveToCollection({
|
|
209
|
+
collection: 'keys',
|
|
210
|
+
storageId,
|
|
211
|
+
data: exported
|
|
212
|
+
});
|
|
213
|
+
await writeCreateMeta({
|
|
214
|
+
collection: COLLECTION,
|
|
215
|
+
storageId,
|
|
216
|
+
created: now.toISOString(),
|
|
217
|
+
handle: options.handle,
|
|
218
|
+
description: options.description
|
|
219
|
+
});
|
|
220
|
+
console.error(`Key saved to ${filePath}`);
|
|
235
221
|
}
|
|
236
|
-
|
|
222
|
+
console.log(JSON.stringify(exported, null, 2));
|
|
223
|
+
return 0;
|
|
237
224
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
225
|
+
default:
|
|
226
|
+
console.error(`Unknown key type: ${options.type}. ` +
|
|
227
|
+
'Supported: ed25519, ecdsa, x25519, hmac');
|
|
228
|
+
return 1;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Lists locally stored keys with their metadata: one fingerprint per line with
|
|
233
|
+
* `--plain`, a JSON array with `--json`, otherwise a column-aligned table.
|
|
234
|
+
*
|
|
235
|
+
* @param options {object}
|
|
236
|
+
* @param [options.json] {boolean} Output a JSON array of objects.
|
|
237
|
+
* @param [options.plain] {boolean} Output one fingerprint per line, sorted.
|
|
238
|
+
* @returns {Promise<number>} The process exit code.
|
|
239
|
+
*/
|
|
240
|
+
export async function runList(options) {
|
|
241
|
+
// The key-to-DID associations (one pass over all stored DID documents) are
|
|
242
|
+
// only needed for the table/JSON output, so derive them lazily and once.
|
|
243
|
+
let fingerprintDidsCache;
|
|
244
|
+
const fingerprintDids = async () => (fingerprintDidsCache ??= await mapFingerprintsToDids());
|
|
245
|
+
return runListCollection({
|
|
246
|
+
collection: COLLECTION,
|
|
247
|
+
plain: options.plain,
|
|
248
|
+
json: options.json,
|
|
249
|
+
plainId: key => key.publicKeyMultibase,
|
|
250
|
+
// Storage IDs carry a YYYY-MM-DD prefix, so the listing order is
|
|
251
|
+
// chronological.
|
|
252
|
+
toEntry: async ({ storageId, item, meta }) => {
|
|
243
253
|
// Asymmetric keys are identified by their publicKeyMultibase
|
|
244
254
|
// fingerprint; an HMAC key has no public half, so fall back to its id.
|
|
245
|
-
const fingerprint =
|
|
255
|
+
const fingerprint = item.publicKeyMultibase ?? item.id;
|
|
246
256
|
if (!fingerprint) {
|
|
247
|
-
|
|
257
|
+
return undefined;
|
|
248
258
|
}
|
|
249
|
-
const meta = await loadMetaFromCollection({
|
|
250
|
-
collection: 'keys',
|
|
251
|
-
storageId
|
|
252
|
-
});
|
|
253
259
|
const parsed = parseKeyStorageId({ storageId });
|
|
254
|
-
|
|
260
|
+
return {
|
|
255
261
|
fingerprint,
|
|
256
262
|
storageId,
|
|
257
263
|
type: parsed.type,
|
|
@@ -259,46 +265,244 @@ export function makeKeyCommand() {
|
|
|
259
265
|
created: meta?.created ?? parsed.date,
|
|
260
266
|
handle: meta?.handle,
|
|
261
267
|
description: meta?.description,
|
|
262
|
-
dids: fingerprintDids.get(fingerprint) ?? []
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
268
|
+
dids: (await fingerprintDids()).get(fingerprint) ?? []
|
|
269
|
+
};
|
|
270
|
+
},
|
|
271
|
+
toJson: entry => ({
|
|
272
|
+
fingerprint: entry.fingerprint,
|
|
273
|
+
storageId: entry.storageId,
|
|
274
|
+
...(entry.type && { type: entry.type }),
|
|
275
|
+
...(entry.curve && { curve: entry.curve }),
|
|
276
|
+
...(entry.created && { created: entry.created }),
|
|
277
|
+
...(entry.handle && { handle: entry.handle }),
|
|
278
|
+
...(entry.description && { description: entry.description }),
|
|
279
|
+
dids: entry.dids
|
|
280
|
+
}),
|
|
281
|
+
columns: [
|
|
282
|
+
{ header: 'HANDLE', maxWidth: 16 },
|
|
283
|
+
{ header: 'TYPE' },
|
|
284
|
+
{ header: 'CREATED' },
|
|
285
|
+
{ header: 'FINGERPRINT', maxWidth: 28 },
|
|
286
|
+
{ header: 'DIDS' },
|
|
287
|
+
{ header: 'DESCRIPTION', maxWidth: 40 }
|
|
288
|
+
],
|
|
289
|
+
toRow: entry => [
|
|
283
290
|
entry.handle ?? '',
|
|
284
291
|
keyTypeLabel({ type: entry.type, curve: entry.curve }),
|
|
285
292
|
entry.created?.slice(0, 10) ?? '',
|
|
286
293
|
entry.fingerprint,
|
|
287
294
|
didsCell({ dids: entry.dids }),
|
|
288
295
|
entry.description ?? ''
|
|
289
|
-
]
|
|
296
|
+
]
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Shows a locally stored key (its public key object, or its metadata with
|
|
301
|
+
* `--meta`), resolved by fingerprint or handle.
|
|
302
|
+
*
|
|
303
|
+
* @param options {object}
|
|
304
|
+
* @param options.id {string} The publicKeyMultibase fingerprint or handle.
|
|
305
|
+
* @param [options.meta] {boolean} Show the metadata instead of the key.
|
|
306
|
+
* @param [options.json] {boolean} With --meta, output the metadata as JSON.
|
|
307
|
+
* @returns {Promise<number>} The process exit code.
|
|
308
|
+
*/
|
|
309
|
+
export async function runShow(options) {
|
|
310
|
+
const { id } = options;
|
|
311
|
+
const resolved = await resolveRefOrReport({
|
|
312
|
+
resolve: ref => resolveKeyRef({ ref }),
|
|
313
|
+
ref: id,
|
|
314
|
+
noun: 'key'
|
|
315
|
+
});
|
|
316
|
+
if (!resolved) {
|
|
317
|
+
return 1;
|
|
318
|
+
}
|
|
319
|
+
// An HMAC key has no public fingerprint, so fall back to its id.
|
|
320
|
+
const fingerprint = resolved.key.publicKeyMultibase ?? resolved.key.id;
|
|
321
|
+
if (!fingerprint) {
|
|
322
|
+
console.error(`No locally stored key found for ${id}`);
|
|
323
|
+
return 1;
|
|
324
|
+
}
|
|
325
|
+
const storedKey = resolved.key;
|
|
326
|
+
if (options.meta) {
|
|
327
|
+
const parsed = parseKeyStorageId({ storageId: resolved.storageId });
|
|
328
|
+
const created = resolved.meta?.created ?? parsed.date;
|
|
329
|
+
// The displayed DIDs are always derived from the stored DID documents,
|
|
330
|
+
// never the cached meta.dids, so they cannot be stale.
|
|
331
|
+
const dids = (await mapFingerprintsToDids()).get(fingerprint) ?? [];
|
|
332
|
+
if (options.json) {
|
|
333
|
+
const output = {
|
|
334
|
+
fingerprint,
|
|
335
|
+
storageId: resolved.storageId,
|
|
336
|
+
...(parsed.type && { type: parsed.type }),
|
|
337
|
+
...(parsed.curve && { curve: parsed.curve }),
|
|
338
|
+
...(created && { created }),
|
|
339
|
+
...(resolved.meta?.handle && { handle: resolved.meta.handle }),
|
|
340
|
+
...(resolved.meta?.description && {
|
|
341
|
+
description: resolved.meta.description
|
|
342
|
+
}),
|
|
343
|
+
dids
|
|
344
|
+
};
|
|
345
|
+
console.log(JSON.stringify(output, null, 2));
|
|
346
|
+
return 0;
|
|
347
|
+
}
|
|
348
|
+
const rows = [
|
|
349
|
+
['Fingerprint', fingerprint],
|
|
350
|
+
['Type', parsed.type ?? ''],
|
|
351
|
+
...(parsed.curve ? [['Curve', parsed.curve]] : []),
|
|
352
|
+
['Created', created ?? ''],
|
|
353
|
+
['Handle', resolved.meta?.handle ?? ''],
|
|
354
|
+
['Description', resolved.meta?.description ?? ''],
|
|
355
|
+
['DIDs', dids.join(', ')]
|
|
356
|
+
];
|
|
290
357
|
console.log(renderTable({
|
|
291
|
-
columns: [
|
|
292
|
-
{ header: 'HANDLE', maxWidth: 16 },
|
|
293
|
-
{ header: 'TYPE' },
|
|
294
|
-
{ header: 'CREATED' },
|
|
295
|
-
{ header: 'FINGERPRINT', maxWidth: 28 },
|
|
296
|
-
{ header: 'DIDS' },
|
|
297
|
-
{ header: 'DESCRIPTION', maxWidth: 40 }
|
|
298
|
-
],
|
|
358
|
+
columns: [{ header: 'FIELD' }, { header: 'VALUE' }],
|
|
299
359
|
rows
|
|
300
360
|
}));
|
|
361
|
+
return 0;
|
|
362
|
+
}
|
|
363
|
+
// Re-import the stored key pair and re-export the public half only, so the
|
|
364
|
+
// secret key material never leaves storage in the displayed output.
|
|
365
|
+
let publicKey;
|
|
366
|
+
if (storedKey.type === 'Sha256HmacKey2019') {
|
|
367
|
+
// A symmetric HMAC key has no public half; show only its safe fields,
|
|
368
|
+
// never the secret material.
|
|
369
|
+
publicKey = { id: storedKey.id, type: 'Sha256HmacKey2019' };
|
|
370
|
+
}
|
|
371
|
+
else if (storedKey.type === 'X25519KeyAgreementKey2020') {
|
|
372
|
+
publicKey = await (await X25519KeyAgreementKey2020.from(storedKey)).export({ publicKey: true });
|
|
373
|
+
}
|
|
374
|
+
else if (isEcdsaPublicKeyMultibase({
|
|
375
|
+
publicKeyMultibase: storedKey.publicKeyMultibase
|
|
376
|
+
})) {
|
|
377
|
+
publicKey = await (await EcdsaMultikey.from(storedKey)).export({
|
|
378
|
+
publicKey: true,
|
|
379
|
+
secretKey: false
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
else {
|
|
383
|
+
publicKey = await (await Ed25519VerificationKey.from(storedKey)).export({
|
|
384
|
+
publicKey: true,
|
|
385
|
+
secretKey: false
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
console.log(JSON.stringify(publicKey, null, 2));
|
|
389
|
+
return 0;
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Shows or edits the metadata of a locally stored key (by fingerprint or
|
|
393
|
+
* handle). With no edits, prints the current metadata; otherwise applies the
|
|
394
|
+
* handle/description changes and saves the sidecar.
|
|
395
|
+
*
|
|
396
|
+
* @param options {object}
|
|
397
|
+
* @param options.id {string} The publicKeyMultibase fingerprint or handle.
|
|
398
|
+
* @param [options.handle] {string} Set the handle (empty string clears it).
|
|
399
|
+
* @param [options.description] {string} Set the description (empty clears it).
|
|
400
|
+
* @returns {Promise<number>} The process exit code.
|
|
401
|
+
*/
|
|
402
|
+
export async function runMeta(options) {
|
|
403
|
+
const { id } = options;
|
|
404
|
+
const resolved = await resolveRefOrReport({
|
|
405
|
+
resolve: ref => resolveKeyRef({ ref }),
|
|
406
|
+
ref: id,
|
|
407
|
+
noun: 'key'
|
|
301
408
|
});
|
|
409
|
+
if (!resolved) {
|
|
410
|
+
return 1;
|
|
411
|
+
}
|
|
412
|
+
if (!resolved.key.publicKeyMultibase) {
|
|
413
|
+
console.error(`No locally stored key found for ${id}`);
|
|
414
|
+
return 1;
|
|
415
|
+
}
|
|
416
|
+
const fingerprint = resolved.key.publicKeyMultibase;
|
|
417
|
+
const dids = (await mapFingerprintsToDids()).get(fingerprint) ?? [];
|
|
418
|
+
const hasEdits = options.handle !== undefined || options.description !== undefined;
|
|
419
|
+
if (!hasEdits) {
|
|
420
|
+
const output = { ...(resolved.meta ?? {}) };
|
|
421
|
+
// Show the derived associations, not the cached ones.
|
|
422
|
+
if (dids.length > 0) {
|
|
423
|
+
output.dids = dids;
|
|
424
|
+
}
|
|
425
|
+
else {
|
|
426
|
+
delete output.dids;
|
|
427
|
+
}
|
|
428
|
+
console.log(JSON.stringify(output, null, 2));
|
|
429
|
+
return 0;
|
|
430
|
+
}
|
|
431
|
+
const meta = { ...(resolved.meta ?? {}) };
|
|
432
|
+
if (!meta.created) {
|
|
433
|
+
// Backfill a date-only created from the storage ID's date prefix.
|
|
434
|
+
const { date } = parseKeyStorageId({ storageId: resolved.storageId });
|
|
435
|
+
if (date) {
|
|
436
|
+
meta.created = date;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
applyMetaEdits(meta, {
|
|
440
|
+
handle: options.handle,
|
|
441
|
+
description: options.description
|
|
442
|
+
});
|
|
443
|
+
// Every write also refreshes the cached key-to-DID associations.
|
|
444
|
+
if (dids.length > 0) {
|
|
445
|
+
meta.dids = dids;
|
|
446
|
+
}
|
|
447
|
+
else {
|
|
448
|
+
delete meta.dids;
|
|
449
|
+
}
|
|
450
|
+
const filePath = await saveMetaToCollection({
|
|
451
|
+
collection: COLLECTION,
|
|
452
|
+
storageId: resolved.storageId,
|
|
453
|
+
meta
|
|
454
|
+
});
|
|
455
|
+
console.error(`Metadata saved to ${filePath}`);
|
|
456
|
+
console.log(JSON.stringify(meta, null, 2));
|
|
457
|
+
return 0;
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* Removes a locally stored key and its metadata sidecar, resolved by
|
|
461
|
+
* publicKeyMultibase fingerprint or handle.
|
|
462
|
+
*
|
|
463
|
+
* @param options {object}
|
|
464
|
+
* @param options.id {string} The publicKeyMultibase fingerprint or handle.
|
|
465
|
+
* @returns {Promise<number>} The process exit code.
|
|
466
|
+
*/
|
|
467
|
+
export async function runRemove(options) {
|
|
468
|
+
return runRemoveCollection({
|
|
469
|
+
collection: COLLECTION,
|
|
470
|
+
noun: 'key',
|
|
471
|
+
resolve: ref => resolveKeyRef({ ref }),
|
|
472
|
+
ref: options.id
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* Exports a key by ID in the requested format.
|
|
477
|
+
*
|
|
478
|
+
* @param options {object}
|
|
479
|
+
* @param options.keyId {string} The key to export.
|
|
480
|
+
* @param options.format {string} Export format (jwk|multibase).
|
|
481
|
+
* @returns {Promise<number>} The process exit code.
|
|
482
|
+
*/
|
|
483
|
+
export async function runExport(options) {
|
|
484
|
+
console.error(`Exporting key ${options.keyId} (format: ${options.format})`);
|
|
485
|
+
// TODO: implement
|
|
486
|
+
return 0;
|
|
487
|
+
}
|
|
488
|
+
export function makeKeyCommand() {
|
|
489
|
+
const key = new Command('key').description('Manage cryptographic keys');
|
|
490
|
+
key
|
|
491
|
+
.command('create')
|
|
492
|
+
.description('Create a new key')
|
|
493
|
+
.option('-t, --type <type>', 'key type (supported: ed25519, ecdsa, x25519, hmac)', 'ed25519')
|
|
494
|
+
.option('--curve <curve>', `ECDSA curve for --type ecdsa (supported: ${SUPPORTED_ECDSA_CURVES})`, 'p256')
|
|
495
|
+
.option('--save', 'save the key to local wallet storage (~/.config/did-cli-wallet/keys/)')
|
|
496
|
+
.option('--handle <handle>', 'short tag for the saved key (requires --save)')
|
|
497
|
+
.option('--description <description>', 'longer description of the saved key (requires --save)')
|
|
498
|
+
.option('--with-seed', 'include the secret key seed in output (generated if SECRET_KEY_SEED is not set)')
|
|
499
|
+
.action((options) => runAndExit(runCreate(options)));
|
|
500
|
+
key
|
|
501
|
+
.command('list')
|
|
502
|
+
.description('List locally stored keys with their metadata')
|
|
503
|
+
.option('--json', 'output the list as a JSON array of objects with metadata')
|
|
504
|
+
.option('--plain', 'output one fingerprint per line, sorted (no metadata)')
|
|
505
|
+
.action((options) => runAndExit(runList(options)));
|
|
302
506
|
key
|
|
303
507
|
.command('show <id>')
|
|
304
508
|
.aliases(['view', 'cat'])
|
|
@@ -306,199 +510,25 @@ export function makeKeyCommand() {
|
|
|
306
510
|
'publicKeyMultibase fingerprint or handle')
|
|
307
511
|
.option('--meta', 'show the key metadata instead of the public key object')
|
|
308
512
|
.option('--json', 'with --meta, output the metadata as JSON')
|
|
309
|
-
.action(
|
|
310
|
-
let resolved;
|
|
311
|
-
try {
|
|
312
|
-
resolved = await resolveKeyRef({ ref: id });
|
|
313
|
-
}
|
|
314
|
-
catch (err) {
|
|
315
|
-
console.error(err.message);
|
|
316
|
-
process.exit(1);
|
|
317
|
-
return;
|
|
318
|
-
}
|
|
319
|
-
// An HMAC key has no public fingerprint, so fall back to its id.
|
|
320
|
-
const fingerprint = resolved?.key.publicKeyMultibase ?? resolved?.key.id;
|
|
321
|
-
if (!resolved || !fingerprint) {
|
|
322
|
-
console.error(`No locally stored key found for ${id}`);
|
|
323
|
-
process.exit(1);
|
|
324
|
-
return;
|
|
325
|
-
}
|
|
326
|
-
const storedKey = resolved.key;
|
|
327
|
-
if (options.meta) {
|
|
328
|
-
const parsed = parseKeyStorageId({ storageId: resolved.storageId });
|
|
329
|
-
const created = resolved.meta?.created ?? parsed.date;
|
|
330
|
-
// The displayed DIDs are always derived from the stored DID documents,
|
|
331
|
-
// never the cached meta.dids, so they cannot be stale.
|
|
332
|
-
const dids = (await mapFingerprintsToDids()).get(fingerprint) ?? [];
|
|
333
|
-
if (options.json) {
|
|
334
|
-
const output = {
|
|
335
|
-
fingerprint,
|
|
336
|
-
storageId: resolved.storageId,
|
|
337
|
-
...(parsed.type && { type: parsed.type }),
|
|
338
|
-
...(parsed.curve && { curve: parsed.curve }),
|
|
339
|
-
...(created && { created }),
|
|
340
|
-
...(resolved.meta?.handle && { handle: resolved.meta.handle }),
|
|
341
|
-
...(resolved.meta?.description && {
|
|
342
|
-
description: resolved.meta.description
|
|
343
|
-
}),
|
|
344
|
-
dids
|
|
345
|
-
};
|
|
346
|
-
console.log(JSON.stringify(output, null, 2));
|
|
347
|
-
return;
|
|
348
|
-
}
|
|
349
|
-
const rows = [
|
|
350
|
-
['Fingerprint', fingerprint],
|
|
351
|
-
['Type', parsed.type ?? ''],
|
|
352
|
-
...(parsed.curve ? [['Curve', parsed.curve]] : []),
|
|
353
|
-
['Created', created ?? ''],
|
|
354
|
-
['Handle', resolved.meta?.handle ?? ''],
|
|
355
|
-
['Description', resolved.meta?.description ?? ''],
|
|
356
|
-
['DIDs', dids.join(', ')]
|
|
357
|
-
];
|
|
358
|
-
console.log(renderTable({
|
|
359
|
-
columns: [{ header: 'FIELD' }, { header: 'VALUE' }],
|
|
360
|
-
rows
|
|
361
|
-
}));
|
|
362
|
-
return;
|
|
363
|
-
}
|
|
364
|
-
// Re-import the stored key pair and re-export the public half only, so the
|
|
365
|
-
// secret key material never leaves storage in the displayed output.
|
|
366
|
-
let publicKey;
|
|
367
|
-
if (storedKey.type === 'Sha256HmacKey2019') {
|
|
368
|
-
// A symmetric HMAC key has no public half; show only its safe fields,
|
|
369
|
-
// never the secret material.
|
|
370
|
-
publicKey = { id: storedKey.id, type: 'Sha256HmacKey2019' };
|
|
371
|
-
}
|
|
372
|
-
else if (storedKey.type === 'X25519KeyAgreementKey2020') {
|
|
373
|
-
publicKey = await (await X25519KeyAgreementKey2020.from(storedKey)).export({ publicKey: true });
|
|
374
|
-
}
|
|
375
|
-
else if (isEcdsaPublicKeyMultibase({
|
|
376
|
-
publicKeyMultibase: storedKey.publicKeyMultibase
|
|
377
|
-
})) {
|
|
378
|
-
publicKey = await (await EcdsaMultikey.from(storedKey)).export({
|
|
379
|
-
publicKey: true,
|
|
380
|
-
secretKey: false
|
|
381
|
-
});
|
|
382
|
-
}
|
|
383
|
-
else {
|
|
384
|
-
publicKey = await (await Ed25519VerificationKey.from(storedKey)).export({
|
|
385
|
-
publicKey: true,
|
|
386
|
-
secretKey: false
|
|
387
|
-
});
|
|
388
|
-
}
|
|
389
|
-
console.log(JSON.stringify(publicKey, null, 2));
|
|
390
|
-
});
|
|
513
|
+
.action((id, options) => runAndExit(runShow({ id, ...options })));
|
|
391
514
|
key
|
|
392
515
|
.command('meta <id>')
|
|
393
516
|
.description('Show or edit the metadata of a locally stored key (by fingerprint or ' +
|
|
394
517
|
'handle); with no options, prints the current metadata')
|
|
395
518
|
.option('--handle <handle>', 'set the handle (an empty string clears it)')
|
|
396
519
|
.option('--description <description>', 'set the description (an empty string clears it)')
|
|
397
|
-
.action(
|
|
398
|
-
let resolved;
|
|
399
|
-
try {
|
|
400
|
-
resolved = await resolveKeyRef({ ref: id });
|
|
401
|
-
}
|
|
402
|
-
catch (err) {
|
|
403
|
-
console.error(err.message);
|
|
404
|
-
process.exit(1);
|
|
405
|
-
return;
|
|
406
|
-
}
|
|
407
|
-
if (!resolved?.key.publicKeyMultibase) {
|
|
408
|
-
console.error(`No locally stored key found for ${id}`);
|
|
409
|
-
process.exit(1);
|
|
410
|
-
return;
|
|
411
|
-
}
|
|
412
|
-
const fingerprint = resolved.key.publicKeyMultibase;
|
|
413
|
-
const dids = (await mapFingerprintsToDids()).get(fingerprint) ?? [];
|
|
414
|
-
const hasEdits = options.handle !== undefined || options.description !== undefined;
|
|
415
|
-
if (!hasEdits) {
|
|
416
|
-
const output = { ...(resolved.meta ?? {}) };
|
|
417
|
-
// Show the derived associations, not the cached ones.
|
|
418
|
-
if (dids.length > 0) {
|
|
419
|
-
output.dids = dids;
|
|
420
|
-
}
|
|
421
|
-
else {
|
|
422
|
-
delete output.dids;
|
|
423
|
-
}
|
|
424
|
-
console.log(JSON.stringify(output, null, 2));
|
|
425
|
-
return;
|
|
426
|
-
}
|
|
427
|
-
const meta = { ...(resolved.meta ?? {}) };
|
|
428
|
-
if (!meta.created) {
|
|
429
|
-
// Backfill a date-only created from the storage ID's date prefix.
|
|
430
|
-
const { date } = parseKeyStorageId({ storageId: resolved.storageId });
|
|
431
|
-
if (date) {
|
|
432
|
-
meta.created = date;
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
if (options.handle !== undefined) {
|
|
436
|
-
if (options.handle === '') {
|
|
437
|
-
delete meta.handle;
|
|
438
|
-
}
|
|
439
|
-
else {
|
|
440
|
-
meta.handle = options.handle;
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
if (options.description !== undefined) {
|
|
444
|
-
if (options.description === '') {
|
|
445
|
-
delete meta.description;
|
|
446
|
-
}
|
|
447
|
-
else {
|
|
448
|
-
meta.description = options.description;
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
// Every write also refreshes the cached key-to-DID associations.
|
|
452
|
-
if (dids.length > 0) {
|
|
453
|
-
meta.dids = dids;
|
|
454
|
-
}
|
|
455
|
-
else {
|
|
456
|
-
delete meta.dids;
|
|
457
|
-
}
|
|
458
|
-
const filePath = await saveMetaToCollection({
|
|
459
|
-
collection: 'keys',
|
|
460
|
-
storageId: resolved.storageId,
|
|
461
|
-
meta
|
|
462
|
-
});
|
|
463
|
-
console.error(`Metadata saved to ${filePath}`);
|
|
464
|
-
console.log(JSON.stringify(meta, null, 2));
|
|
465
|
-
});
|
|
520
|
+
.action((id, options) => runAndExit(runMeta({ id, ...options })));
|
|
466
521
|
key
|
|
467
522
|
.command('remove <id>')
|
|
468
523
|
.aliases(['delete', 'rm'])
|
|
469
524
|
.description('Remove a locally stored key and its metadata sidecar (by ' +
|
|
470
525
|
'publicKeyMultibase fingerprint or handle)')
|
|
471
|
-
.action(
|
|
472
|
-
let resolved;
|
|
473
|
-
try {
|
|
474
|
-
resolved = await resolveKeyRef({ ref: id });
|
|
475
|
-
}
|
|
476
|
-
catch (err) {
|
|
477
|
-
console.error(err.message);
|
|
478
|
-
process.exit(1);
|
|
479
|
-
return;
|
|
480
|
-
}
|
|
481
|
-
if (!resolved) {
|
|
482
|
-
console.error(`No locally stored key found for ${id}`);
|
|
483
|
-
process.exit(1);
|
|
484
|
-
return;
|
|
485
|
-
}
|
|
486
|
-
const removed = await removeFromCollection({
|
|
487
|
-
collection: 'keys',
|
|
488
|
-
storageId: resolved.storageId
|
|
489
|
-
});
|
|
490
|
-
for (const filePath of removed) {
|
|
491
|
-
console.error(`Removed ${filePath}`);
|
|
492
|
-
}
|
|
493
|
-
});
|
|
526
|
+
.action((id) => runAndExit(runRemove({ id })));
|
|
494
527
|
key
|
|
495
528
|
.command('export <id>')
|
|
496
529
|
.description('Export a key by ID')
|
|
497
530
|
.option('-f, --format <format>', 'export format (jwk|multibase)', 'jwk')
|
|
498
|
-
.action((keyId, options) => {
|
|
499
|
-
console.error(`Exporting key ${keyId} (format: ${options.format})`);
|
|
500
|
-
// TODO: implement
|
|
501
|
-
});
|
|
531
|
+
.action((keyId, options) => runAndExit(runExport({ keyId, ...options })));
|
|
502
532
|
return key;
|
|
503
533
|
}
|
|
504
534
|
//# sourceMappingURL=key.js.map
|