@did-btcr2/cli 0.15.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/README.md +47 -6
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/cjs/index.js +563 -117
  4. package/dist/esm/src/cli.js +7 -4
  5. package/dist/esm/src/cli.js.map +1 -1
  6. package/dist/esm/src/commands/config.js +11 -15
  7. package/dist/esm/src/commands/config.js.map +1 -1
  8. package/dist/esm/src/commands/create.js +4 -1
  9. package/dist/esm/src/commands/create.js.map +1 -1
  10. package/dist/esm/src/commands/deactivate.js +3 -1
  11. package/dist/esm/src/commands/deactivate.js.map +1 -1
  12. package/dist/esm/src/commands/index.js +2 -0
  13. package/dist/esm/src/commands/index.js.map +1 -1
  14. package/dist/esm/src/commands/init.js +63 -0
  15. package/dist/esm/src/commands/init.js.map +1 -0
  16. package/dist/esm/src/commands/keystore.js +81 -0
  17. package/dist/esm/src/commands/keystore.js.map +1 -0
  18. package/dist/esm/src/commands/profile.js +1 -1
  19. package/dist/esm/src/commands/profile.js.map +1 -1
  20. package/dist/esm/src/commands/update.js +3 -1
  21. package/dist/esm/src/commands/update.js.map +1 -1
  22. package/dist/esm/src/config.js +85 -28
  23. package/dist/esm/src/config.js.map +1 -1
  24. package/dist/esm/src/keystore/file-key-store.js +340 -32
  25. package/dist/esm/src/keystore/file-key-store.js.map +1 -1
  26. package/dist/esm/src/keystore/passphrase.js +40 -10
  27. package/dist/esm/src/keystore/passphrase.js.map +1 -1
  28. package/dist/esm/src/keystore/paths.js +6 -18
  29. package/dist/esm/src/keystore/paths.js.map +1 -1
  30. package/dist/esm/src/paths.js +59 -0
  31. package/dist/esm/src/paths.js.map +1 -0
  32. package/dist/esm/src/types.js.map +1 -1
  33. package/dist/types/src/cli.d.ts.map +1 -1
  34. package/dist/types/src/commands/config.d.ts.map +1 -1
  35. package/dist/types/src/commands/create.d.ts.map +1 -1
  36. package/dist/types/src/commands/deactivate.d.ts.map +1 -1
  37. package/dist/types/src/commands/index.d.ts +2 -0
  38. package/dist/types/src/commands/index.d.ts.map +1 -1
  39. package/dist/types/src/commands/init.d.ts +13 -0
  40. package/dist/types/src/commands/init.d.ts.map +1 -0
  41. package/dist/types/src/commands/keystore.d.ts +10 -0
  42. package/dist/types/src/commands/keystore.d.ts.map +1 -0
  43. package/dist/types/src/commands/update.d.ts.map +1 -1
  44. package/dist/types/src/config.d.ts +38 -14
  45. package/dist/types/src/config.d.ts.map +1 -1
  46. package/dist/types/src/keystore/file-key-store.d.ts +86 -10
  47. package/dist/types/src/keystore/file-key-store.d.ts.map +1 -1
  48. package/dist/types/src/keystore/passphrase.d.ts +16 -1
  49. package/dist/types/src/keystore/passphrase.d.ts.map +1 -1
  50. package/dist/types/src/keystore/paths.d.ts +6 -10
  51. package/dist/types/src/keystore/paths.d.ts.map +1 -1
  52. package/dist/types/src/paths.d.ts +54 -0
  53. package/dist/types/src/paths.d.ts.map +1 -0
  54. package/dist/types/src/types.d.ts +38 -0
  55. package/dist/types/src/types.d.ts.map +1 -1
  56. package/package.json +3 -3
  57. package/src/cli.ts +8 -3
  58. package/src/commands/config.ts +12 -14
  59. package/src/commands/create.ts +4 -1
  60. package/src/commands/deactivate.ts +3 -1
  61. package/src/commands/index.ts +2 -0
  62. package/src/commands/init.ts +74 -0
  63. package/src/commands/keystore.ts +98 -0
  64. package/src/commands/profile.ts +1 -1
  65. package/src/commands/update.ts +3 -1
  66. package/src/config.ts +93 -32
  67. package/src/keystore/file-key-store.ts +455 -43
  68. package/src/keystore/passphrase.ts +48 -8
  69. package/src/keystore/paths.ts +6 -19
  70. package/src/paths.ts +79 -0
  71. package/src/types.ts +13 -1
@@ -6,6 +6,12 @@ import type { DoctorReport, EffectiveConfig } from './config.js';
6
6
  import type { ConfigIssue } from './config-schema.js';
7
7
  export type NetworkOption = 'bitcoin' | 'testnet3' | 'testnet4' | 'signet' | 'mutinynet' | 'regtest';
8
8
  export type OutputFormat = 'json' | 'text';
9
+ /**
10
+ * How a keystore protects its secrets, as reported by `keystore status` and
11
+ * `btcr2 init`: `encrypted` (passphrase-sealed), `dev` (plaintext, testnet-only),
12
+ * or `absent` (no keystore file yet).
13
+ */
14
+ export type KeystoreProtectionLabel = 'encrypted' | 'dev' | 'absent';
9
15
  export declare const SUPPORTED_NETWORKS: NetworkOption[];
10
16
  /**
11
17
  * Normalizes a blank value (empty or whitespace-only) to `undefined`, so a
@@ -88,6 +94,15 @@ export type CommandResult = {
88
94
  keyId: string;
89
95
  active: true;
90
96
  };
97
+ } | {
98
+ action: 'init';
99
+ data: {
100
+ home: string;
101
+ config: string;
102
+ keystore: string;
103
+ created: string[];
104
+ protection: KeystoreProtectionLabel;
105
+ };
91
106
  } | {
92
107
  action: 'config-init';
93
108
  data: {
@@ -121,12 +136,34 @@ export type CommandResult = {
121
136
  } | {
122
137
  action: 'config-path';
123
138
  data: {
139
+ home: string;
124
140
  config: string;
125
141
  keystore: string;
126
142
  };
127
143
  } | {
128
144
  action: 'config-doctor';
129
145
  data: DoctorReport;
146
+ } | {
147
+ action: 'keystore-init';
148
+ data: {
149
+ path: string;
150
+ protection: 'encrypted' | 'dev';
151
+ };
152
+ } | {
153
+ action: 'keystore-status';
154
+ data: {
155
+ path: string;
156
+ protection: KeystoreProtectionLabel;
157
+ established: boolean;
158
+ keyCount: number;
159
+ active: string | undefined;
160
+ };
161
+ } | {
162
+ action: 'keystore-change-passphrase';
163
+ data: {
164
+ path: string;
165
+ rekeyed: number;
166
+ };
130
167
  } | {
131
168
  action: 'profile-add';
132
169
  data: {
@@ -150,6 +187,7 @@ export interface GlobalOptions {
150
187
  output: OutputFormat;
151
188
  verbose: boolean;
152
189
  quiet: boolean;
190
+ home?: string;
153
191
  config?: string;
154
192
  profile?: string;
155
193
  btcRest?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@did-btcr2/cli",
3
- "version": "0.15.0",
3
+ "version": "0.16.0",
4
4
  "type": "module",
5
5
  "description": "CLI for interacting with did-btcr2-js, the JavaScript/TypeScript reference implementation of the did:btcr2 method. Exposes various parts of multiple packages in the did-btcr2-js monorepo.",
6
6
  "main": "./dist/cjs/index.js",
@@ -61,10 +61,10 @@
61
61
  "@scure/base": "^1.2.6",
62
62
  "@web5/dids": "^1.2.0",
63
63
  "commander": "^13.1.0",
64
- "@did-btcr2/keypair": "^0.13.1",
65
64
  "@did-btcr2/api": "^0.16.1",
66
- "@did-btcr2/key-manager": "^0.7.0",
65
+ "@did-btcr2/keypair": "^0.13.1",
67
66
  "@did-btcr2/common": "^9.1.0",
67
+ "@did-btcr2/key-manager": "^0.7.0",
68
68
  "@did-btcr2/method": "^0.54.1"
69
69
  },
70
70
  "devDependencies": {
package/src/cli.ts CHANGED
@@ -5,7 +5,9 @@ import {
5
5
  registerConfigCommand,
6
6
  registerCreateCommand,
7
7
  registerDeactivateCommand,
8
+ registerInitCommand,
8
9
  registerKeyCommand,
10
+ registerKeystoreCommand,
9
11
  registerProfileCommand,
10
12
  registerResolveCommand,
11
13
  registerUpdateCommand,
@@ -44,7 +46,8 @@ export class DidBtcr2Cli {
44
46
  .option('-o, --output <format>', 'Output format <json|text> (default: config defaults.output, else text)')
45
47
  .option('--verbose', 'Verbose output', false)
46
48
  .option('--quiet', 'Suppress non-essential output', false)
47
- .option('-c, --config <path>', 'Path to config file (default: $XDG_CONFIG_HOME/btcr2/config.json)')
49
+ .option('--home <dir>', 'btcr2 home directory holding config.json + keystore.json (default: ~/.btcr2, %LOCALAPPDATA%\\btcr2 on Windows; overrides $BTCR2_HOME)')
50
+ .option('-c, --config <path>', 'Path to config file (default: <home>/config.json)')
48
51
  .option('--profile <name>', 'Config profile name (default: auto-detected from network)')
49
52
  .option('--btc-rest <url>', 'Override Bitcoin REST endpoint (Esplora API)')
50
53
  .option('--btc-rpc-url <url>', 'Override Bitcoin Core RPC endpoint')
@@ -57,7 +60,7 @@ export class DidBtcr2Cli {
57
60
  .option('--btc-rest-header <header>', 'Extra Bitcoin REST header "Key: Value" (repeatable)', collectHeader, [])
58
61
  .option('--btc-rpc-wallet <name>', 'Bitcoin Core wallet name for wallet-scoped RPCs')
59
62
  .option('--btc-rpc-header <header>', 'Extra Bitcoin Core RPC header "Key: Value" (repeatable)', collectHeader, [])
60
- .option('--keystore <path>', 'Path to the keystore file (default: $XDG_DATA_HOME/btcr2/keystore.json)')
63
+ .option('--keystore <path>', 'Path to the keystore file (default: <home>/keystore.json)')
61
64
  .option('--passphrase-file <path>', 'Read the keystore passphrase from a file (unattended use)')
62
65
  .option('--signing-key <ref>', 'Key for create/update/deactivate signing: a URN, fingerprint prefix, or name');
63
66
 
@@ -70,14 +73,16 @@ export class DidBtcr2Cli {
70
73
  // value is written back so every command reads it through globals().output.
71
74
  this.program.hook('preAction', () => {
72
75
  const opts = this.program.opts() as GlobalOptions;
73
- opts.output = resolveOutputFormat({ output: opts.output, config: opts.config });
76
+ opts.output = resolveOutputFormat({ output: opts.output, config: opts.config, home: opts.home });
74
77
  });
75
78
 
79
+ registerInitCommand(this.program, globals);
76
80
  registerCreateCommand(this.program, factory, keystoreFactory, globals);
77
81
  registerResolveCommand(this.program, factory, globals);
78
82
  registerUpdateCommand(this.program, keystoreFactory, globals);
79
83
  registerDeactivateCommand(this.program, keystoreFactory, globals);
80
84
  registerKeyCommand(this.program, keystoreFactory, globals);
85
+ registerKeystoreCommand(this.program, globals);
81
86
  registerConfigCommand(this.program, globals);
82
87
  registerProfileCommand(this.program, globals);
83
88
  registerCompletionCommand(this.program, globals);
@@ -1,6 +1,5 @@
1
1
  import type { Command } from 'commander';
2
2
  import { existsSync } from 'node:fs';
3
- import { dirname } from 'node:path';
4
3
  import {
5
4
  CONFIG_SCHEMA_VERSION,
6
5
  defaultConfigPath,
@@ -9,23 +8,24 @@ import {
9
8
  readConfigFile,
10
9
  resolveDefaultNetwork,
11
10
  resolveEffectiveConfig,
11
+ resolveKeystorePath,
12
12
  runDoctor,
13
13
  setConfigPath,
14
14
  unsetConfigPath,
15
15
  writeConfigFile,
16
+ writeDefaultConfigFile,
16
17
  } from '../config.js';
17
18
  import { findConfigIssues, validateConfigSet } from '../config-schema.js';
18
19
  import { CLIError } from '../error.js';
19
- import { ensureDir, writeFileAtomic } from '../keystore/atomic.js';
20
- import { defaultKeystorePath } from '../keystore/paths.js';
21
20
  import { formatResult, REDACTED, redactSecrets, scrubUrlUserinfo } from '../output.js';
21
+ import { resolveHome } from '../paths.js';
22
22
  import type { CommandResult, GlobalOptions, NetworkOption } from '../types.js';
23
23
  import { SUPPORTED_NETWORKS } from '../types.js';
24
24
 
25
25
  /** Registers the `config` command group for reading and writing CLI configuration. */
26
26
  export function registerConfigCommand(program: Command, globals: () => GlobalOptions): void {
27
27
  const config = program.command('config').description('Read and write CLI configuration.');
28
- const path = (): string => globals().config ?? defaultConfigPath();
28
+ const path = (): string => globals().config ?? defaultConfigPath(globals());
29
29
  const print = (result: CommandResult): void => console.log(formatResult(result, globals()));
30
30
 
31
31
  config
@@ -37,13 +37,7 @@ export function registerConfigCommand(program: Command, globals: () => GlobalOpt
37
37
  if (existsSync(p) && !options.force) {
38
38
  throw new CLIError(`Config already exists at ${p}. Use --force to overwrite.`, 'INVALID_ARGUMENT_ERROR', { path: p });
39
39
  }
40
- const scaffold = {
41
- schemaVersion : CONFIG_SCHEMA_VERSION,
42
- defaults : { output: 'text' },
43
- profiles : Object.fromEntries(SUPPORTED_NETWORKS.map(n => [ n, {} ])),
44
- };
45
- ensureDir(dirname(p), 0o700);
46
- writeFileAtomic(p, `${JSON.stringify(scaffold, null, 2)}\n`, 0o600);
40
+ writeDefaultConfigFile(p);
47
41
  print({ action: 'config-init', data: { path: p } });
48
42
  });
49
43
 
@@ -128,11 +122,15 @@ export function registerConfigCommand(program: Command, globals: () => GlobalOpt
128
122
 
129
123
  config
130
124
  .command('path')
131
- .description('Print the resolved config-file and keystore paths.')
125
+ .description('Print the resolved home directory, config-file, and keystore paths.')
132
126
  .action(() => {
127
+ const g = globals();
133
128
  const data = {
134
- config : globals().config ?? defaultConfigPath(),
135
- keystore : globals().keystore ?? defaultKeystorePath(),
129
+ home : resolveHome(g),
130
+ config : g.config ?? defaultConfigPath(g),
131
+ // Diagnostic command: report the path even when the config is malformed
132
+ // (this is a command you run to find and fix a broken config).
133
+ keystore : resolveKeystorePath(g, { lenient: true }),
136
134
  };
137
135
  print({ action: 'config-path', data });
138
136
  });
@@ -1,7 +1,7 @@
1
1
  import { bytesToHex, hexToBytes } from '@noble/hashes/utils.js';
2
2
  import type { Command } from 'commander';
3
3
  import type { ApiFactory, ConnectionOverrides } from '../config.js';
4
- import { profileNetworkMismatch, resolveDefaultNetwork } from '../config.js';
4
+ import { assertKeystoreAllowedForNetwork, profileNetworkMismatch, resolveDefaultNetwork } from '../config.js';
5
5
  import { CLIError } from '../error.js';
6
6
  import { resolveKeyRef } from '../keystore/resolve-key-ref.js';
7
7
  import { formatResult } from '../output.js';
@@ -132,6 +132,8 @@ export function registerCreateCommand(
132
132
  }
133
133
 
134
134
  // Generate: mint a fresh key, persist it, and set it active (passphrase prompt).
135
+ // Refuse to seal a fresh mainnet key into an unencrypted dev keystore (ADR 080).
136
+ assertKeystoreAllowedForNetwork(network, overrides);
135
137
  const api = keystoreFactory(undefined, overrides);
136
138
  const { did, keyId } = api.generateDid({ network, setActive: true });
137
139
  const publicKey = bytesToHex(api.kms.getPublicKey(keyId));
@@ -145,6 +147,7 @@ export function registerCreateCommand(
145
147
  /** Builds the keystore- and config-resolution overrides from the global flags. */
146
148
  function overridesFromGlobals(g: GlobalOptions): ConnectionOverrides {
147
149
  return {
150
+ home : g.home,
148
151
  config : g.config,
149
152
  profile : g.profile,
150
153
  keystore : g.keystore,
@@ -1,7 +1,7 @@
1
1
  import type { PublishToCasMode } from '@did-btcr2/api';
2
2
  import { KeyManagerSigner } from '@did-btcr2/key-manager';
3
3
  import type { Command } from 'commander';
4
- import { deriveNetwork, resolveBroadcastOptions, resolveSigningKeyRef, type ApiFactory } from '../config.js';
4
+ import { assertKeystoreAllowedForNetwork, deriveNetwork, resolveBroadcastOptions, resolveSigningKeyRef, type ApiFactory } from '../config.js';
5
5
  import { CLIError } from '../error.js';
6
6
  import { resolveKeyRef } from '../keystore/resolve-key-ref.js';
7
7
  import { formatResult } from '../output.js';
@@ -88,6 +88,8 @@ export function registerDeactivateCommand(
88
88
  // Deactivation is an update that applies the deactivation patch. The core
89
89
  // method has no separate deactivate path, so this routes through update.
90
90
  const network = deriveNetwork(did);
91
+ // Refuse to sign a mainnet deactivation with an unencrypted dev keystore (ADR 080).
92
+ assertKeystoreAllowedForNetwork(network, globals());
91
93
  const api = factory(network, globals());
92
94
  const keyId = resolveKeyRef(api.kms.kms, resolveSigningKeyRef(globals()));
93
95
  const signer = new KeyManagerSigner(api.kms.kms, keyId);
@@ -1,8 +1,10 @@
1
+ export { registerInitCommand } from './init.js';
1
2
  export { registerCreateCommand } from './create.js';
2
3
  export { registerResolveCommand } from './resolve.js';
3
4
  export { registerUpdateCommand } from './update.js';
4
5
  export { registerDeactivateCommand } from './deactivate.js';
5
6
  export { registerKeyCommand } from './key.js';
7
+ export { registerKeystoreCommand } from './keystore.js';
6
8
  export { registerConfigCommand } from './config.js';
7
9
  export { registerProfileCommand } from './profile.js';
8
10
  export { registerCompletionCommand } from './completion.js';
@@ -0,0 +1,74 @@
1
+ import type { Command } from 'commander';
2
+ import { existsSync } from 'node:fs';
3
+ import { defaultConfigPath, resolveKeystorePath, writeDefaultConfigFile } from '../config.js';
4
+ import { ensureDir } from '../keystore/atomic.js';
5
+ import { initKeystore, keystoreSummary } from '../keystore/file-key-store.js';
6
+ import { acquirePassphrase } from '../keystore/passphrase.js';
7
+ import { formatResult } from '../output.js';
8
+ import { resolveHome } from '../paths.js';
9
+ import type { CommandResult, GlobalOptions } from '../types.js';
10
+
11
+ /**
12
+ * Registers the top-level `btcr2 init`: the one-command entry point that creates
13
+ * the btcr2 home (ADR 079), writes a default config if none exists, and
14
+ * establishes the keystore if none exists (encrypted with a confirmed passphrase
15
+ * by default, or `--dev` for an unencrypted testnet keystore). Idempotent:
16
+ * existing files are left untouched unless `--force` is given. Establishing the
17
+ * passphrase here, up front and confirmed, is what keeps the first `key generate`
18
+ * off the accidental-first-seal path (ADR 080).
19
+ */
20
+ export function registerInitCommand(program: Command, globals: () => GlobalOptions): void {
21
+ const print = (result: CommandResult): void => console.log(formatResult(result, globals()));
22
+
23
+ program
24
+ .command('init')
25
+ .description('Set up the btcr2 home: create the directory, a default config, and establish the keystore.')
26
+ .option('--dev', 'Establish an UNENCRYPTED dev keystore: plaintext keys, no passphrase. Testnet only.', false)
27
+ .option('--force', 'Re-create the config and keystore even if they already exist.', false)
28
+ .action((options: { dev?: boolean; force?: boolean }) => {
29
+ const g = globals();
30
+ const home = resolveHome(g);
31
+ const configPath = g.config ?? defaultConfigPath(g);
32
+ const keystorePath = resolveKeystorePath(g);
33
+ ensureDir(home, 0o700);
34
+
35
+ const created: string[] = [];
36
+
37
+ // The config is regenerable, so --force may re-scaffold it.
38
+ if (!existsSync(configPath) || options.force) {
39
+ writeDefaultConfigFile(configPath);
40
+ created.push('config');
41
+ }
42
+
43
+ // The keystore holds unrecoverable secret keys, so `init` never overwrites
44
+ // an existing one, even with --force: re-establishing a keystore is the
45
+ // explicit, deliberate `keystore init --force`. `init` only establishes a
46
+ // keystore when none exists.
47
+ const keystoreExists = existsSync(keystorePath);
48
+ if (keystoreExists && options.force && !g.quiet) {
49
+ process.stderr.write(
50
+ `note: a keystore already exists at ${keystorePath} and was left intact. `
51
+ + 'To re-establish it (discarding its keys), run "btcr2 keystore init --force".\n',
52
+ );
53
+ }
54
+ if (!keystoreExists) {
55
+ if (options.dev && !g.quiet) {
56
+ process.stderr.write(
57
+ 'warning: establishing an UNENCRYPTED dev keystore. Keys are stored in plaintext. '
58
+ + 'Use it only for disposable testnet material; mainnet operations will be refused.\n',
59
+ );
60
+ }
61
+ initKeystore(keystorePath, {
62
+ protection : options.dev ? 'none' : 'passphrase',
63
+ getPassphrase : (opts) => acquirePassphrase({ passphraseFile: g.passphraseFile, confirm: opts?.confirm, prompt: 'New keystore passphrase: ' }),
64
+ });
65
+ created.push('keystore');
66
+ }
67
+
68
+ const protection = keystoreSummary(keystorePath).protection;
69
+ print({ action: 'init', data: { home, config: configPath, keystore: keystorePath, created, protection } });
70
+ if (!g.quiet && g.output !== 'json') {
71
+ process.stderr.write(`btcr2 home ready at ${home}. Next: btcr2 key generate --set-active\n`);
72
+ }
73
+ });
74
+ }
@@ -0,0 +1,98 @@
1
+ import type { Command } from 'commander';
2
+ import { existsSync } from 'node:fs';
3
+ import { resolveKeystorePath } from '../config.js';
4
+ import { CLIError } from '../error.js';
5
+ import { changeKeystorePassphrase, initKeystore, keystoreSummary } from '../keystore/file-key-store.js';
6
+ import { acquirePassphrase } from '../keystore/passphrase.js';
7
+ import { formatResult } from '../output.js';
8
+ import type { CommandResult, GlobalOptions } from '../types.js';
9
+
10
+ /**
11
+ * Registers the `keystore` command group: establish, inspect, and re-key the
12
+ * encrypted keystore (ADR 080). These operate on the keystore file directly (no
13
+ * Bitcoin connection or KeyManager) and never decrypt a key except when
14
+ * re-sealing during `change-passphrase`.
15
+ */
16
+ export function registerKeystoreCommand(program: Command, globals: () => GlobalOptions): void {
17
+ const keystore = program.command('keystore').description('Establish, inspect, and re-key the keystore.');
18
+ const print = (result: CommandResult): void => console.log(formatResult(result, globals()));
19
+
20
+ keystore
21
+ .command('init')
22
+ .description('Establish the keystore (encrypted by default). Prompts for a passphrase and confirms it.')
23
+ .option('--dev', 'Create an UNENCRYPTED dev keystore: plaintext keys, no passphrase. Testnet only; mainnet operations are refused.', false)
24
+ .option('--force', 'Re-establish even if a keystore already exists (discards its keys).', false)
25
+ .action((options: { dev?: boolean; force?: boolean }) => {
26
+ const g = globals();
27
+ const path = resolveKeystorePath(g);
28
+ if (existsSync(path) && !options.force) {
29
+ throw new CLIError(
30
+ `A keystore already exists at ${path}. Use --force to re-establish it (this discards its keys).`,
31
+ 'INVALID_ARGUMENT_ERROR',
32
+ { path },
33
+ );
34
+ }
35
+ // --force re-establishes over an existing keystore; make the key loss loud.
36
+ if (existsSync(path) && options.force && !g.quiet) {
37
+ const { keyCount } = keystoreSummary(path);
38
+ if (keyCount > 0) {
39
+ process.stderr.write(
40
+ `warning: re-establishing the keystore at ${path} permanently discards its ${keyCount} existing key(s).\n`,
41
+ );
42
+ }
43
+ }
44
+ if (options.dev && !g.quiet) {
45
+ process.stderr.write(
46
+ 'warning: creating an UNENCRYPTED dev keystore. Keys are stored in plaintext. '
47
+ + 'Use it only for disposable testnet material; mainnet operations will be refused.\n',
48
+ );
49
+ }
50
+ initKeystore(path, {
51
+ protection : options.dev ? 'none' : 'passphrase',
52
+ getPassphrase : (opts) => acquirePassphrase({ passphraseFile: g.passphraseFile, confirm: opts?.confirm, prompt: 'New keystore passphrase: ' }),
53
+ });
54
+ print({ action: 'keystore-init', data: { path, protection: options.dev ? 'dev' : 'encrypted' } });
55
+ });
56
+
57
+ keystore
58
+ .command('status')
59
+ .description('Show the keystore path, protection mode, and key count. Never decrypts or prompts.')
60
+ .action(() => {
61
+ const g = globals();
62
+ // Diagnostic command: report status even when the config is malformed,
63
+ // rather than crashing on the config you ran this to inspect.
64
+ const path = resolveKeystorePath(g, { lenient: true });
65
+ const summary = keystoreSummary(path);
66
+ if (summary.protection === 'dev' && !g.quiet && g.output !== 'json') {
67
+ process.stderr.write('warning: this is an UNENCRYPTED dev keystore; keys are stored in plaintext.\n');
68
+ }
69
+ print({ action: 'keystore-status', data: { path, ...summary } });
70
+ });
71
+
72
+ keystore
73
+ .command('change-passphrase')
74
+ .alias('passwd')
75
+ .description('Change the keystore passphrase, re-sealing every key under the new one. Encrypted keystores only.')
76
+ .action(() => {
77
+ const g = globals();
78
+ const path = resolveKeystorePath(g);
79
+ const summary = keystoreSummary(path);
80
+ if (summary.protection === 'absent') {
81
+ throw new CLIError(`No keystore at ${path}. Run "btcr2 keystore init" first.`, 'INVALID_ARGUMENT_ERROR', { path });
82
+ }
83
+ if (summary.protection === 'dev') {
84
+ throw new CLIError(
85
+ `The keystore at ${path} is an unencrypted dev keystore; there is no passphrase to change.`,
86
+ 'INVALID_ARGUMENT_ERROR',
87
+ { path },
88
+ );
89
+ }
90
+ // Current passphrase may come from the env var / file (unattended); the new
91
+ // one must be entered fresh (forcePrompt) so it cannot be silently satisfied
92
+ // by the same source and make the change a no-op.
93
+ const oldPassphrase = acquirePassphrase({ passphraseFile: g.passphraseFile, prompt: 'Current keystore passphrase: ' });
94
+ const newPassphrase = acquirePassphrase({ forcePrompt: true, confirm: true, prompt: 'New keystore passphrase: ' });
95
+ const rekeyed = changeKeystorePassphrase(path, oldPassphrase, newPassphrase);
96
+ print({ action: 'keystore-change-passphrase', data: { path, rekeyed } });
97
+ });
98
+ }
@@ -7,7 +7,7 @@ import type { CommandResult, GlobalOptions } from '../types.js';
7
7
  /** Registers the `profile` command group for managing configuration profiles. */
8
8
  export function registerProfileCommand(program: Command, globals: () => GlobalOptions): void {
9
9
  const profile = program.command('profile').description('Manage configuration profiles.');
10
- const path = (): string => globals().config ?? defaultConfigPath();
10
+ const path = (): string => globals().config ?? defaultConfigPath(globals());
11
11
  const print = (result: CommandResult): void => console.log(formatResult(result, globals()));
12
12
 
13
13
  profile
@@ -1,7 +1,7 @@
1
1
  import type { PublishToCasMode } from '@did-btcr2/api';
2
2
  import { KeyManagerSigner } from '@did-btcr2/key-manager';
3
3
  import type { Command } from 'commander';
4
- import { deriveNetwork, resolveBroadcastOptions, resolveSigningKeyRef, type ApiFactory } from '../config.js';
4
+ import { assertKeystoreAllowedForNetwork, deriveNetwork, resolveBroadcastOptions, resolveSigningKeyRef, type ApiFactory } from '../config.js';
5
5
  import { CLIError } from '../error.js';
6
6
  import { resolveKeyRef } from '../keystore/resolve-key-ref.js';
7
7
  import { formatResult } from '../output.js';
@@ -88,6 +88,8 @@ export function registerUpdateCommand(
88
88
  );
89
89
  }
90
90
  const network = deriveNetwork(did);
91
+ // Refuse to sign a mainnet update with an unencrypted dev keystore (ADR 080).
92
+ assertKeystoreAllowedForNetwork(network, globals());
91
93
  const api = factory(network, globals());
92
94
  const keyId = resolveKeyRef(api.kms.kms, resolveSigningKeyRef(globals()));
93
95
  const signer = new KeyManagerSigner(api.kms.kms, keyId);