@did-btcr2/cli 0.10.3 → 0.12.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/dist/.tsbuildinfo +1 -1
- package/dist/cjs/index.js +1028 -114
- package/dist/esm/src/cli.js +31 -13
- package/dist/esm/src/cli.js.map +1 -1
- package/dist/esm/src/commands/completion.js +36 -0
- package/dist/esm/src/commands/completion.js.map +1 -0
- package/dist/esm/src/commands/config.js +69 -0
- package/dist/esm/src/commands/config.js.map +1 -0
- package/dist/esm/src/commands/create.js +109 -30
- package/dist/esm/src/commands/create.js.map +1 -1
- package/dist/esm/src/commands/deactivate.js +21 -8
- package/dist/esm/src/commands/deactivate.js.map +1 -1
- package/dist/esm/src/commands/index.js +4 -0
- package/dist/esm/src/commands/index.js.map +1 -1
- package/dist/esm/src/commands/key.js +175 -0
- package/dist/esm/src/commands/key.js.map +1 -0
- package/dist/esm/src/commands/profile.js +63 -0
- package/dist/esm/src/commands/profile.js.map +1 -0
- package/dist/esm/src/commands/update.js +19 -9
- package/dist/esm/src/commands/update.js.map +1 -1
- package/dist/esm/src/config.js +119 -12
- package/dist/esm/src/config.js.map +1 -1
- package/dist/esm/src/keystore/atomic.js +64 -0
- package/dist/esm/src/keystore/atomic.js.map +1 -0
- package/dist/esm/src/keystore/envelope.js +123 -0
- package/dist/esm/src/keystore/envelope.js.map +1 -0
- package/dist/esm/src/keystore/error.js +16 -0
- package/dist/esm/src/keystore/error.js.map +1 -0
- package/dist/esm/src/keystore/file-backed-key-manager.js +78 -0
- package/dist/esm/src/keystore/file-backed-key-manager.js.map +1 -0
- package/dist/esm/src/keystore/file-key-store.js +184 -0
- package/dist/esm/src/keystore/file-key-store.js.map +1 -0
- package/dist/esm/src/keystore/passphrase.js +87 -0
- package/dist/esm/src/keystore/passphrase.js.map +1 -0
- package/dist/esm/src/keystore/paths.js +20 -0
- package/dist/esm/src/keystore/paths.js.map +1 -0
- package/dist/esm/src/keystore/resolve-key-ref.js +47 -0
- package/dist/esm/src/keystore/resolve-key-ref.js.map +1 -0
- package/dist/types/src/cli.d.ts +6 -2
- package/dist/types/src/cli.d.ts.map +1 -1
- package/dist/types/src/commands/completion.d.ts +5 -0
- package/dist/types/src/commands/completion.d.ts.map +1 -0
- package/dist/types/src/commands/config.d.ts +5 -0
- package/dist/types/src/commands/config.d.ts.map +1 -0
- package/dist/types/src/commands/create.d.ts +19 -1
- package/dist/types/src/commands/create.d.ts.map +1 -1
- package/dist/types/src/commands/deactivate.d.ts.map +1 -1
- package/dist/types/src/commands/index.d.ts +4 -0
- package/dist/types/src/commands/index.d.ts.map +1 -1
- package/dist/types/src/commands/key.d.ts +10 -0
- package/dist/types/src/commands/key.d.ts.map +1 -0
- package/dist/types/src/commands/profile.d.ts +5 -0
- package/dist/types/src/commands/profile.d.ts.map +1 -0
- package/dist/types/src/commands/update.d.ts.map +1 -1
- package/dist/types/src/config.d.ts +57 -5
- package/dist/types/src/config.d.ts.map +1 -1
- package/dist/types/src/keystore/atomic.d.ts +19 -0
- package/dist/types/src/keystore/atomic.d.ts.map +1 -0
- package/dist/types/src/keystore/envelope.d.ts +64 -0
- package/dist/types/src/keystore/envelope.d.ts.map +1 -0
- package/dist/types/src/keystore/error.d.ts +14 -0
- package/dist/types/src/keystore/error.d.ts.map +1 -0
- package/dist/types/src/keystore/file-backed-key-manager.d.ts +41 -0
- package/dist/types/src/keystore/file-backed-key-manager.d.ts.map +1 -0
- package/dist/types/src/keystore/file-key-store.d.ts +52 -0
- package/dist/types/src/keystore/file-key-store.d.ts.map +1 -0
- package/dist/types/src/keystore/passphrase.d.ts +20 -0
- package/dist/types/src/keystore/passphrase.d.ts.map +1 -0
- package/dist/types/src/keystore/paths.d.ts +13 -0
- package/dist/types/src/keystore/paths.d.ts.map +1 -0
- package/dist/types/src/keystore/resolve-key-ref.d.ts +19 -0
- package/dist/types/src/keystore/resolve-key-ref.d.ts.map +1 -0
- package/dist/types/src/types.d.ts +93 -5
- package/dist/types/src/types.d.ts.map +1 -1
- package/package.json +9 -4
- package/src/cli.ts +37 -12
- package/src/commands/completion.ts +40 -0
- package/src/commands/config.ts +84 -0
- package/src/commands/create.ts +140 -52
- package/src/commands/deactivate.ts +25 -12
- package/src/commands/index.ts +4 -0
- package/src/commands/key.ts +193 -0
- package/src/commands/profile.ts +65 -0
- package/src/commands/update.ts +23 -13
- package/src/config.ts +165 -20
- package/src/keystore/atomic.ts +73 -0
- package/src/keystore/envelope.ts +172 -0
- package/src/keystore/error.ts +16 -0
- package/src/keystore/file-backed-key-manager.ts +99 -0
- package/src/keystore/file-key-store.ts +242 -0
- package/src/keystore/passphrase.ts +99 -0
- package/src/keystore/paths.ts +20 -0
- package/src/keystore/resolve-key-ref.ts +62 -0
- package/src/types.ts +31 -18
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { SchnorrKeyPair } from '@did-btcr2/keypair';
|
|
2
|
+
import { bytesToHex, hexToBytes } from '@noble/hashes/utils.js';
|
|
3
|
+
import { closeSync, openSync, readFileSync, writeFileSync } from 'node:fs';
|
|
4
|
+
import { CLIError } from '../error.js';
|
|
5
|
+
import { resolveKeyRef } from '../keystore/resolve-key-ref.js';
|
|
6
|
+
import { formatResult } from '../output.js';
|
|
7
|
+
/**
|
|
8
|
+
* Registers the `key` command group for managing keypairs in the encrypted
|
|
9
|
+
* keystore. All subcommands operate offline (no Bitcoin connection) through the
|
|
10
|
+
* keystore-backed KeyManager injected by the factory.
|
|
11
|
+
*/
|
|
12
|
+
export function registerKeyCommand(program, factory, globals) {
|
|
13
|
+
const key = program.command('key').description('Manage keypairs in the encrypted keystore.');
|
|
14
|
+
const print = (result) => console.log(formatResult(result, globals()));
|
|
15
|
+
key
|
|
16
|
+
.command('generate')
|
|
17
|
+
.description('Generate a new keypair and store it.')
|
|
18
|
+
.option('--name <name>', 'A human-friendly name, stored as a tag and usable as a key reference.')
|
|
19
|
+
.option('--set-active', 'Make this the active key.', false)
|
|
20
|
+
.action((options) => {
|
|
21
|
+
const api = factory(undefined, globals());
|
|
22
|
+
assertNameAvailable(api.kms.kms, options.name);
|
|
23
|
+
const setActive = options.setActive ?? false;
|
|
24
|
+
const id = api.kms.generateKey({ ...(options.name && { tags: { name: options.name } }), setActive });
|
|
25
|
+
print({ action: 'key-generate', data: { keyId: id, publicKey: bytesToHex(api.kms.getPublicKey(id)), active: setActive } });
|
|
26
|
+
});
|
|
27
|
+
key
|
|
28
|
+
.command('list')
|
|
29
|
+
.alias('ls')
|
|
30
|
+
.description('List stored keys.')
|
|
31
|
+
.action(() => {
|
|
32
|
+
const kms = factory(undefined, globals()).kms.kms;
|
|
33
|
+
const active = kms.activeKeyId;
|
|
34
|
+
const data = kms.listKeys().map(id => {
|
|
35
|
+
const entry = kms.getEntry(id);
|
|
36
|
+
return {
|
|
37
|
+
keyId: id,
|
|
38
|
+
fingerprint: id.split(':').pop() ?? id,
|
|
39
|
+
...(entry.tags?.name && { name: entry.tags.name }),
|
|
40
|
+
active: id === active,
|
|
41
|
+
};
|
|
42
|
+
});
|
|
43
|
+
print({ action: 'key-list', data });
|
|
44
|
+
});
|
|
45
|
+
key
|
|
46
|
+
.command('show <ref>')
|
|
47
|
+
.description('Show a key\'s public material and tags. Never prints the secret.')
|
|
48
|
+
.action((ref) => {
|
|
49
|
+
const kms = factory(undefined, globals()).kms.kms;
|
|
50
|
+
const id = resolveKeyRef(kms, ref);
|
|
51
|
+
const entry = kms.getEntry(id);
|
|
52
|
+
print({ action: 'key-show', data: { keyId: id, publicKey: bytesToHex(entry.publicKey), ...(entry.tags && { tags: entry.tags }) } });
|
|
53
|
+
});
|
|
54
|
+
key
|
|
55
|
+
.command('import')
|
|
56
|
+
.description('Import a key: a secret from a hex file, or a public key as watch-only.')
|
|
57
|
+
.option('--secret-file <path>', 'Path to a file containing a 32-byte secret key as hex.')
|
|
58
|
+
.option('--public <hex>', 'A 33-byte compressed public key as hex (imported watch-only).')
|
|
59
|
+
.option('--name <name>', 'A human-friendly name, stored as a tag.')
|
|
60
|
+
.option('--set-active', 'Make this the active key.', false)
|
|
61
|
+
.action((options) => {
|
|
62
|
+
if (Boolean(options.secretFile) === Boolean(options.public)) {
|
|
63
|
+
throw new CLIError('Provide exactly one of --secret-file or --public.', 'INVALID_ARGUMENT_ERROR');
|
|
64
|
+
}
|
|
65
|
+
const api = factory(undefined, globals());
|
|
66
|
+
assertNameAvailable(api.kms.kms, options.name);
|
|
67
|
+
const keyPair = options.secretFile
|
|
68
|
+
? new SchnorrKeyPair({ secretKey: readHexFile(options.secretFile, 32, '--secret-file') })
|
|
69
|
+
: new SchnorrKeyPair({ publicKey: parseHex(options.public ?? '', 33, '--public') });
|
|
70
|
+
const setActive = options.setActive ?? false;
|
|
71
|
+
const id = api.kms.import(keyPair, { ...(options.name && { tags: { name: options.name } }), setActive });
|
|
72
|
+
print({ action: 'key-import', data: { keyId: id, publicKey: bytesToHex(api.kms.getPublicKey(id)), watchOnly: !options.secretFile, active: setActive } });
|
|
73
|
+
});
|
|
74
|
+
key
|
|
75
|
+
.command('export <ref>')
|
|
76
|
+
.description('Export a key. Public material by default; --secret writes the secret to a file.')
|
|
77
|
+
.option('--secret', 'Export the secret key. Requires --out.', false)
|
|
78
|
+
.option('--out <path>', 'Write the exported secret to this file (created 0600).')
|
|
79
|
+
.action((ref, options) => {
|
|
80
|
+
const api = factory(undefined, globals());
|
|
81
|
+
const id = resolveKeyRef(api.kms.kms, ref);
|
|
82
|
+
if (!options.secret) {
|
|
83
|
+
print({ action: 'key-export', data: { keyId: id, publicKey: bytesToHex(api.kms.getPublicKey(id)) } });
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (!options.out) {
|
|
87
|
+
throw new CLIError('Exporting a secret requires --out <file> so it is not written to the terminal.', 'INVALID_ARGUMENT_ERROR');
|
|
88
|
+
}
|
|
89
|
+
const keyPair = api.kms.export(id);
|
|
90
|
+
if (!keyPair.hasSecretKey) {
|
|
91
|
+
throw new CLIError(`Key ${id} is watch-only and has no secret to export.`, 'INVALID_ARGUMENT_ERROR', { keyId: id });
|
|
92
|
+
}
|
|
93
|
+
process.stderr.write('warning: writing an unencrypted secret key to disk. Protect this file and delete it when done.\n');
|
|
94
|
+
writeSecretFile(options.out, bytesToHex(keyPair.secretKey.bytes));
|
|
95
|
+
print({ action: 'key-export', data: { keyId: id, secretWrittenTo: options.out } });
|
|
96
|
+
});
|
|
97
|
+
key
|
|
98
|
+
.command('delete <ref>')
|
|
99
|
+
.alias('rm')
|
|
100
|
+
.description('Delete a key from the keystore.')
|
|
101
|
+
.option('--force', 'Delete even if it is the active key.', false)
|
|
102
|
+
.action((ref, options) => {
|
|
103
|
+
const api = factory(undefined, globals());
|
|
104
|
+
const id = resolveKeyRef(api.kms.kms, ref);
|
|
105
|
+
api.kms.removeKey(id, { force: options.force ?? false });
|
|
106
|
+
print({ action: 'key-delete', data: { keyId: id, deleted: true } });
|
|
107
|
+
});
|
|
108
|
+
key
|
|
109
|
+
.command('use <ref>')
|
|
110
|
+
.description('Set the active key, persisted across invocations.')
|
|
111
|
+
.action((ref) => {
|
|
112
|
+
const api = factory(undefined, globals());
|
|
113
|
+
const id = resolveKeyRef(api.kms.kms, ref);
|
|
114
|
+
api.kms.setActive(id);
|
|
115
|
+
print({ action: 'key-use', data: { keyId: id, active: true } });
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
/** Throws if a name tag is already used by another key. */
|
|
119
|
+
function assertNameAvailable(kms, name) {
|
|
120
|
+
if (!name)
|
|
121
|
+
return;
|
|
122
|
+
if (kms.listKeys().some(id => kms.getEntry(id).tags?.name === name)) {
|
|
123
|
+
throw new CLIError(`A key named "${name}" already exists.`, 'INVALID_ARGUMENT_ERROR', { name });
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/** Parses and length-checks a hex string into bytes. */
|
|
127
|
+
function parseHex(hex, expectedBytes, label) {
|
|
128
|
+
let bytes;
|
|
129
|
+
try {
|
|
130
|
+
bytes = hexToBytes(hex.trim());
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
throw new CLIError(`Invalid hex for ${label}.`, 'INVALID_ARGUMENT_ERROR', { label });
|
|
134
|
+
}
|
|
135
|
+
if (bytes.length !== expectedBytes) {
|
|
136
|
+
throw new CLIError(`${label} must be ${expectedBytes} bytes (${expectedBytes * 2} hex chars), got ${bytes.length}.`, 'INVALID_ARGUMENT_ERROR', { label });
|
|
137
|
+
}
|
|
138
|
+
return bytes;
|
|
139
|
+
}
|
|
140
|
+
/** Reads a file and parses its contents as length-checked hex bytes. */
|
|
141
|
+
function readHexFile(path, expectedBytes, label) {
|
|
142
|
+
let content;
|
|
143
|
+
try {
|
|
144
|
+
content = readFileSync(path, 'utf-8');
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
throw new CLIError(`Cannot read ${label} at ${path}.`, 'INVALID_ARGUMENT_ERROR', { label, path });
|
|
148
|
+
}
|
|
149
|
+
return parseHex(content, expectedBytes, label);
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Writes an exported secret to a new file, created exclusively (O_CREAT|O_EXCL,
|
|
153
|
+
* mode 0600). The exclusive flag refuses to clobber an existing file or follow
|
|
154
|
+
* a pre-placed symlink, so the secret never lands in a loose-permissions or
|
|
155
|
+
* redirected target.
|
|
156
|
+
*/
|
|
157
|
+
function writeSecretFile(path, contents) {
|
|
158
|
+
let fd;
|
|
159
|
+
try {
|
|
160
|
+
fd = openSync(path, 'wx', 0o600);
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
if (error.code === 'EEXIST') {
|
|
164
|
+
throw new CLIError(`Refusing to overwrite existing file ${path}. Choose a new --out path.`, 'INVALID_ARGUMENT_ERROR', { path });
|
|
165
|
+
}
|
|
166
|
+
throw error;
|
|
167
|
+
}
|
|
168
|
+
try {
|
|
169
|
+
writeFileSync(fd, contents);
|
|
170
|
+
}
|
|
171
|
+
finally {
|
|
172
|
+
closeSync(fd);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=key.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"key.js","sourceRoot":"","sources":["../../../../src/commands/key.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { defaultConfigPath, readConfigFile, writeConfigFile } from '../config.js';
|
|
2
|
+
import { CLIError } from '../error.js';
|
|
3
|
+
import { formatResult } from '../output.js';
|
|
4
|
+
/** Registers the `profile` command group for managing configuration profiles. */
|
|
5
|
+
export function registerProfileCommand(program, globals) {
|
|
6
|
+
const profile = program.command('profile').description('Manage configuration profiles.');
|
|
7
|
+
const path = () => globals().config ?? defaultConfigPath();
|
|
8
|
+
const print = (result) => console.log(formatResult(result, globals()));
|
|
9
|
+
profile
|
|
10
|
+
.command('add <name>')
|
|
11
|
+
.description('Add an empty profile.')
|
|
12
|
+
.action((name) => {
|
|
13
|
+
writeConfigFile(path(), raw => {
|
|
14
|
+
if (raw.profiles === undefined || raw.profiles === null)
|
|
15
|
+
raw.profiles = {};
|
|
16
|
+
const profiles = raw.profiles;
|
|
17
|
+
if (profiles[name])
|
|
18
|
+
throw new CLIError(`Profile "${name}" already exists.`, 'INVALID_ARGUMENT_ERROR', { name });
|
|
19
|
+
profiles[name] = {};
|
|
20
|
+
});
|
|
21
|
+
print({ action: 'profile-add', data: { profile: name } });
|
|
22
|
+
});
|
|
23
|
+
profile
|
|
24
|
+
.command('use <name>')
|
|
25
|
+
.description('Set the active profile (writes defaults.profile).')
|
|
26
|
+
.action((name) => {
|
|
27
|
+
writeConfigFile(path(), raw => {
|
|
28
|
+
if (raw.defaults === undefined || raw.defaults === null)
|
|
29
|
+
raw.defaults = {};
|
|
30
|
+
raw.defaults.profile = name;
|
|
31
|
+
});
|
|
32
|
+
print({ action: 'profile-use', data: { profile: name } });
|
|
33
|
+
});
|
|
34
|
+
profile
|
|
35
|
+
.command('show [name]')
|
|
36
|
+
.description('Show a profile (defaults to the active profile).')
|
|
37
|
+
.action((name) => {
|
|
38
|
+
const file = readConfigFile(path()) ?? {};
|
|
39
|
+
const target = name ?? file.defaults?.profile;
|
|
40
|
+
if (!target) {
|
|
41
|
+
throw new CLIError('No profile specified and no active profile is set.', 'INVALID_ARGUMENT_ERROR');
|
|
42
|
+
}
|
|
43
|
+
const data = file.profiles?.[target];
|
|
44
|
+
if (!data) {
|
|
45
|
+
throw new CLIError(`Profile "${target}" not found.`, 'INVALID_ARGUMENT_ERROR', { profile: target });
|
|
46
|
+
}
|
|
47
|
+
print({ action: 'profile-show', data: { profile: target, ...data } });
|
|
48
|
+
});
|
|
49
|
+
profile
|
|
50
|
+
.command('remove <name>')
|
|
51
|
+
.alias('rm')
|
|
52
|
+
.description('Remove a profile.')
|
|
53
|
+
.action((name) => {
|
|
54
|
+
writeConfigFile(path(), raw => {
|
|
55
|
+
const profiles = raw.profiles;
|
|
56
|
+
if (!profiles?.[name])
|
|
57
|
+
throw new CLIError(`Profile "${name}" not found.`, 'INVALID_ARGUMENT_ERROR', { name });
|
|
58
|
+
delete profiles[name];
|
|
59
|
+
});
|
|
60
|
+
print({ action: 'profile-remove', data: { profile: name } });
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=profile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profile.js","sourceRoot":"","sources":["../../../../src/commands/profile.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import { KeyManagerSigner } from '@did-btcr2/key-manager';
|
|
1
2
|
import { deriveNetwork } from '../config.js';
|
|
2
3
|
import { CLIError } from '../error.js';
|
|
4
|
+
import { resolveKeyRef } from '../keystore/resolve-key-ref.js';
|
|
5
|
+
import { formatResult } from '../output.js';
|
|
3
6
|
export function registerUpdateCommand(program, factory, globals) {
|
|
4
7
|
program
|
|
5
8
|
.command('update')
|
|
@@ -10,6 +13,9 @@ export function registerUpdateCommand(program, factory, globals) {
|
|
|
10
13
|
.requiredOption('-m, --verification-method-id <id>', 'DID document verification method ID')
|
|
11
14
|
.requiredOption('-b, --beacon-id <json>', 'Beacon ID as a JSON string', parseJsonArg('--beacon-id'))
|
|
12
15
|
.action(async (options) => {
|
|
16
|
+
if (!/^\d+$/.test(options.sourceVersionId)) {
|
|
17
|
+
throw new CLIError('--source-version-id must be a non-negative integer.', 'INVALID_ARGUMENT_ERROR', { value: options.sourceVersionId });
|
|
18
|
+
}
|
|
13
19
|
const parsed = {
|
|
14
20
|
sourceDocument: options.sourceDocument,
|
|
15
21
|
patches: options.patches,
|
|
@@ -21,15 +27,19 @@ export function registerUpdateCommand(program, factory, globals) {
|
|
|
21
27
|
if (!did) {
|
|
22
28
|
throw new CLIError('Source document must contain an "id" field.', 'INVALID_ARGUMENT_ERROR', options);
|
|
23
29
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
const network = deriveNetwork(did);
|
|
31
|
+
const api = factory(network, globals());
|
|
32
|
+
const keyId = resolveKeyRef(api.kms.kms, globals().signingKey);
|
|
33
|
+
const signer = new KeyManagerSigner(api.kms.kms, keyId);
|
|
34
|
+
const data = await api.btcr2.update({
|
|
35
|
+
sourceDocument: parsed.sourceDocument,
|
|
36
|
+
patches: parsed.patches,
|
|
37
|
+
sourceVersionId: parsed.sourceVersionId,
|
|
38
|
+
verificationMethodId: parsed.verificationMethodId,
|
|
39
|
+
beaconId: parsed.beaconId,
|
|
40
|
+
signer,
|
|
41
|
+
});
|
|
42
|
+
console.log(formatResult({ action: 'update', data }, globals()));
|
|
33
43
|
});
|
|
34
44
|
}
|
|
35
45
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update.js","sourceRoot":"","sources":["../../../../src/commands/update.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"update.js","sourceRoot":"","sources":["../../../../src/commands/update.ts"],"names":[],"mappings":"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"}
|
package/dist/esm/src/config.js
CHANGED
|
@@ -1,9 +1,59 @@
|
|
|
1
1
|
import { createApi, Identifier } from '@did-btcr2/api';
|
|
2
2
|
import { readFileSync } from 'node:fs';
|
|
3
3
|
import { homedir } from 'node:os';
|
|
4
|
-
import { join } from 'node:path';
|
|
4
|
+
import { dirname, join } from 'node:path';
|
|
5
5
|
import { CLIError } from './error.js';
|
|
6
|
+
import { ensureDir, writeFileAtomic } from './keystore/atomic.js';
|
|
7
|
+
import { FileBackedKeyManager } from './keystore/file-backed-key-manager.js';
|
|
8
|
+
import { defaultKeystorePath } from './keystore/paths.js';
|
|
9
|
+
import { acquirePassphrase } from './keystore/passphrase.js';
|
|
6
10
|
import { SUPPORTED_NETWORKS } from './types.js';
|
|
11
|
+
/** Current config-file schema version, stamped on every write. */
|
|
12
|
+
export const CONFIG_SCHEMA_VERSION = 1;
|
|
13
|
+
/**
|
|
14
|
+
* Read-modify-write a config file, preserving unknown keys. Reads the raw JSON
|
|
15
|
+
* (so keys outside {@link ConfigFile} survive a rewrite), applies `mutate`,
|
|
16
|
+
* stamps the schema version, and writes atomically (file 0600, dir 0700).
|
|
17
|
+
*/
|
|
18
|
+
export function writeConfigFile(path, mutate) {
|
|
19
|
+
const raw = readConfigFile(path) ?? {};
|
|
20
|
+
mutate(raw);
|
|
21
|
+
raw.schemaVersion = CONFIG_SCHEMA_VERSION;
|
|
22
|
+
ensureDir(dirname(path), 0o700);
|
|
23
|
+
writeFileAtomic(path, `${JSON.stringify(raw, null, 2)}\n`, 0o600);
|
|
24
|
+
}
|
|
25
|
+
/** Reads the value at a dotted path (e.g. `profiles.regtest.btc.rest`). */
|
|
26
|
+
export function getConfigPath(config, path) {
|
|
27
|
+
return path.split('.').reduce((node, key) => node?.[key], config);
|
|
28
|
+
}
|
|
29
|
+
/** Sets the value at a dotted path, creating intermediate objects. */
|
|
30
|
+
export function setConfigPath(config, path, value) {
|
|
31
|
+
const keys = path.split('.');
|
|
32
|
+
const last = keys.pop();
|
|
33
|
+
if (!last)
|
|
34
|
+
throw new CLIError('Config path must be non-empty.', 'INVALID_ARGUMENT_ERROR');
|
|
35
|
+
let node = config;
|
|
36
|
+
for (const key of keys) {
|
|
37
|
+
if (typeof node[key] !== 'object' || node[key] === null)
|
|
38
|
+
node[key] = {};
|
|
39
|
+
node = node[key];
|
|
40
|
+
}
|
|
41
|
+
node[last] = value;
|
|
42
|
+
}
|
|
43
|
+
/** Deletes the value at a dotted path. No-op if the path does not exist. */
|
|
44
|
+
export function unsetConfigPath(config, path) {
|
|
45
|
+
const keys = path.split('.');
|
|
46
|
+
const last = keys.pop();
|
|
47
|
+
if (!last)
|
|
48
|
+
return;
|
|
49
|
+
let node = config;
|
|
50
|
+
for (const key of keys) {
|
|
51
|
+
node = node?.[key];
|
|
52
|
+
if (!node)
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
delete node[last];
|
|
56
|
+
}
|
|
7
57
|
/**
|
|
8
58
|
* Environment variable names consulted by {@link defaultApiFactory}.
|
|
9
59
|
*
|
|
@@ -80,23 +130,43 @@ export function profileToOverrides(config, profileName) {
|
|
|
80
130
|
};
|
|
81
131
|
}
|
|
82
132
|
/**
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
133
|
+
* Resolves the default Bitcoin network for offline identifier creation when no
|
|
134
|
+
* `--network` flag is given. Resolution order: the config file's
|
|
135
|
+
* `defaults.network`, then an active profile named for a network (an explicit
|
|
136
|
+
* `--profile` flag or `defaults.profile`), then `regtest` as the development
|
|
137
|
+
* fallback. Generation itself is offline; this only fixes which network the
|
|
138
|
+
* identifier encodes.
|
|
139
|
+
*/
|
|
140
|
+
export function resolveDefaultNetwork(overrides) {
|
|
141
|
+
const configPath = overrides?.config ?? defaultConfigPath();
|
|
142
|
+
const file = readConfigFile(configPath);
|
|
143
|
+
const explicit = file?.defaults?.network;
|
|
144
|
+
if (explicit && SUPPORTED_NETWORKS.includes(explicit))
|
|
145
|
+
return explicit;
|
|
146
|
+
const profile = overrides?.profile ?? file?.defaults?.profile;
|
|
147
|
+
if (profile && SUPPORTED_NETWORKS.includes(profile)) {
|
|
148
|
+
return profile;
|
|
149
|
+
}
|
|
150
|
+
return 'regtest';
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Resolves the Bitcoin and CAS connection config for a network by merging,
|
|
154
|
+
* in precedence order, CLI flags, environment variables, and the config-file
|
|
155
|
+
* profile on top of the per-network defaults (handled by `BitcoinConnection`).
|
|
86
156
|
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
157
|
+
* Returns an empty config when no network is given, since offline operations
|
|
158
|
+
* (create, key management) need no connection.
|
|
89
159
|
*
|
|
90
|
-
* When no `--profile` is given, the network name is used as the profile
|
|
91
|
-
*
|
|
160
|
+
* When no `--profile` is given, the network name is used as the profile key
|
|
161
|
+
* (e.g. a regtest DID auto-selects the `"regtest"` profile).
|
|
92
162
|
*/
|
|
93
|
-
|
|
163
|
+
function resolveConnectionConfig(network, overrides) {
|
|
94
164
|
if (!network)
|
|
95
|
-
return
|
|
165
|
+
return {};
|
|
96
166
|
// Layer 1: Config file profile (lowest precedence of the three override layers)
|
|
97
167
|
const configPath = overrides?.config ?? defaultConfigPath();
|
|
98
|
-
const profileName = overrides?.profile ?? network;
|
|
99
168
|
const file = readConfigFile(configPath);
|
|
169
|
+
const profileName = overrides?.profile ?? file?.defaults?.profile ?? network;
|
|
100
170
|
const fileOverrides = file ? profileToOverrides(file, profileName) : {};
|
|
101
171
|
// Layer 2: Environment variables
|
|
102
172
|
const env = readEnvOverrides();
|
|
@@ -120,7 +190,44 @@ export function defaultApiFactory(network, overrides) {
|
|
|
120
190
|
};
|
|
121
191
|
}
|
|
122
192
|
const cas = merged.casGateway ? { gateway: merged.casGateway } : undefined;
|
|
123
|
-
return
|
|
193
|
+
return { btc, ...(cas && { cas }) };
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Default {@link ApiFactory} backed by network defaults from
|
|
197
|
+
* `@did-btcr2/bitcoin` (mempool.space for public networks, localhost for
|
|
198
|
+
* regtest). Keystore-free: suitable for offline `create` and read-only
|
|
199
|
+
* `resolve`, which never need a signing identity.
|
|
200
|
+
*
|
|
201
|
+
* Override precedence (highest wins):
|
|
202
|
+
* CLI flags -> env vars -> config file profile -> network defaults.
|
|
203
|
+
*/
|
|
204
|
+
export function defaultApiFactory(network, overrides) {
|
|
205
|
+
return createApi(resolveConnectionConfig(network, overrides));
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Builds a keystore-backed {@link KeyManager} reading secret keys from the
|
|
209
|
+
* encrypted on-disk keystore. The passphrase is acquired lazily, so building
|
|
210
|
+
* this never prompts; a prompt happens only when a secret is actually sealed
|
|
211
|
+
* or opened. The persisted active-key pointer is re-applied (a non-decrypting
|
|
212
|
+
* existence check) so "the active key" survives across invocations.
|
|
213
|
+
*/
|
|
214
|
+
function buildKeystoreKms(overrides) {
|
|
215
|
+
return new FileBackedKeyManager({
|
|
216
|
+
path: overrides?.keystore ?? defaultKeystorePath(),
|
|
217
|
+
getPassphrase: () => acquirePassphrase({ passphraseFile: overrides?.passphraseFile }),
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Keystore-aware {@link ApiFactory} for commands that need a signing identity
|
|
222
|
+
* (key management, update, deactivate). Identical to {@link defaultApiFactory}
|
|
223
|
+
* for Bitcoin and CAS, plus an injected keystore-backed KeyManager. Offline key
|
|
224
|
+
* commands (no network) still get the keystore.
|
|
225
|
+
*/
|
|
226
|
+
export function keystoreApiFactory(network, overrides) {
|
|
227
|
+
return createApi({
|
|
228
|
+
...resolveConnectionConfig(network, overrides),
|
|
229
|
+
kms: buildKeystoreKms(overrides),
|
|
230
|
+
});
|
|
124
231
|
}
|
|
125
232
|
/**
|
|
126
233
|
* Extracts and validates the Bitcoin network from a DID string.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAA2C,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { chmodSync, mkdirSync, renameSync, rmSync, statSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { basename, dirname, join } from 'node:path';
|
|
3
|
+
import { KeyStoreError } from './error.js';
|
|
4
|
+
const isWindows = process.platform === 'win32';
|
|
5
|
+
let permsWarned = false;
|
|
6
|
+
let tmpCounter = 0;
|
|
7
|
+
/**
|
|
8
|
+
* Creates a directory (recursively) and, on POSIX systems, tightens it to the
|
|
9
|
+
* requested mode. `mkdir`'s mode is subject to the umask, so it is reapplied
|
|
10
|
+
* with an explicit `chmod`.
|
|
11
|
+
*/
|
|
12
|
+
export function ensureDir(dir, mode) {
|
|
13
|
+
mkdirSync(dir, { recursive: true, mode });
|
|
14
|
+
if (!isWindows) {
|
|
15
|
+
try {
|
|
16
|
+
chmodSync(dir, mode);
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
// A pre-existing directory we do not own cannot be re-moded; best effort.
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Writes a file atomically: serialize to a sibling temporary file, tighten its
|
|
25
|
+
* permissions, then rename over the target so a crash mid-write cannot leave a
|
|
26
|
+
* truncated or partially-written file. The temporary file is removed on failure.
|
|
27
|
+
*/
|
|
28
|
+
export function writeFileAtomic(path, data, mode) {
|
|
29
|
+
const tmp = join(dirname(path), `.${basename(path)}.${process.pid}.${tmpCounter++}.tmp`);
|
|
30
|
+
try {
|
|
31
|
+
writeFileSync(tmp, data, { mode });
|
|
32
|
+
if (!isWindows)
|
|
33
|
+
chmodSync(tmp, mode);
|
|
34
|
+
renameSync(tmp, path);
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
try {
|
|
38
|
+
rmSync(tmp, { force: true });
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
// Ignore cleanup failure; surface the original write error.
|
|
42
|
+
}
|
|
43
|
+
throw new KeyStoreError(`Failed to write keystore at ${path}.`, 'ATOMIC_WRITE_ERROR', { path, cause: error instanceof Error ? error.message : String(error) });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Fails closed if a keystore file is readable or writable by group or other.
|
|
48
|
+
* On Windows, where POSIX mode bits are not enforced, this is a no-op that
|
|
49
|
+
* warns once on standard error.
|
|
50
|
+
*/
|
|
51
|
+
export function assertSecurePerms(path) {
|
|
52
|
+
if (isWindows) {
|
|
53
|
+
if (!permsWarned) {
|
|
54
|
+
process.stderr.write('warning: file permissions are not enforced on Windows; protect the keystore directory manually.\n');
|
|
55
|
+
permsWarned = true;
|
|
56
|
+
}
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const mode = statSync(path).mode & 0o777;
|
|
60
|
+
if ((mode & 0o077) !== 0) {
|
|
61
|
+
throw new KeyStoreError(`Keystore at ${path} has insecure permissions 0${mode.toString(8)}; expected 0600.`, 'KEYSTORE_PERMISSION_ERROR', { path, mode: `0${mode.toString(8)}` });
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=atomic.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"atomic.js","sourceRoot":"","sources":["../../../../src/keystore/atomic.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC5F,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,MAAM,SAAS,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC;AAC/C,IAAI,WAAW,GAAG,KAAK,CAAC;AACxB,IAAI,UAAU,GAAG,CAAC,CAAC;AAEnB;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAAC,GAAW,EAAE,IAAY;IACjD,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1C,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,IAAI,CAAC;YACH,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACvB,CAAC;QAAC,MAAM,CAAC;YACP,0EAA0E;QAC5E,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,IAAY,EAAE,IAAY;IACtE,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,QAAQ,CAAC,IAAI,CAAC,IAAI,OAAO,CAAC,GAAG,IAAI,UAAU,EAAE,MAAM,CAAC,CAAC;IACzF,IAAI,CAAC;QACH,aAAa,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;QACnC,IAAI,CAAC,SAAS;YAAE,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACrC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACxB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,CAAC;YACH,MAAM,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/B,CAAC;QAAC,MAAM,CAAC;YACP,4DAA4D;QAC9D,CAAC;QACD,MAAM,IAAI,aAAa,CACrB,+BAA+B,IAAI,GAAG,EACtC,oBAAoB,EACpB,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CACxE,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,IAAI,SAAS,EAAE,CAAC;QACd,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,mGAAmG,CACpG,CAAC;YACF,WAAW,GAAG,IAAI,CAAC;QACrB,CAAC;QACD,OAAO;IACT,CAAC;IACD,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,GAAG,KAAK,CAAC;IACzC,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,aAAa,CACrB,eAAe,IAAI,8BAA8B,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,kBAAkB,EACnF,2BAA2B,EAC3B,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,CACvC,CAAC;IACJ,CAAC;AACH,CAAC"}
|