@did-btcr2/cli 0.11.0 → 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/cjs/index.js CHANGED
@@ -43,6 +43,7 @@ __export(index_exports, {
43
43
  registerProfileCommand: () => registerProfileCommand,
44
44
  registerResolveCommand: () => registerResolveCommand,
45
45
  registerUpdateCommand: () => registerUpdateCommand,
46
+ resolveDefaultNetwork: () => resolveDefaultNetwork,
46
47
  setConfigPath: () => setConfigPath,
47
48
  unsetConfigPath: () => unsetConfigPath,
48
49
  writeConfigFile: () => writeConfigFile
@@ -57,96 +58,8 @@ var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
57
58
  var import_common3 = require("@did-btcr2/common");
58
59
  var import_commander = require("commander");
59
60
 
60
- // src/error.ts
61
- var import_common = require("@did-btcr2/common");
62
- var CLIError = class extends import_common.DidMethodError {
63
- constructor(message, type = "CLIError", data) {
64
- super(message, { type, name: "CLIError", data });
65
- }
66
- };
67
-
68
- // src/output.ts
69
- function formatResult(result, options) {
70
- if (options.output === "json") {
71
- return JSON.stringify(result, null, 2);
72
- }
73
- const { data } = result;
74
- return typeof data === "string" ? data : JSON.stringify(data, null, 2);
75
- }
76
-
77
- // src/types.ts
78
- var SUPPORTED_NETWORKS = [
79
- "bitcoin",
80
- "testnet3",
81
- "testnet4",
82
- "signet",
83
- "mutinynet",
84
- "regtest"
85
- ];
86
-
87
61
  // src/commands/create.ts
88
- var EXPECTED_BYTES = {
89
- k: { length: 33, label: "secp256k1 compressed public key (33 bytes)" },
90
- x: { length: 32, label: "SHA-256 hash (32 bytes)" }
91
- };
92
- function registerCreateCommand(program, factory, globals) {
93
- program.command("create").description("Create an identifier and initial DID document").requiredOption("-t, --type <type>", "Identifier type <k|x>", "k").requiredOption(
94
- "-n, --network <network>",
95
- "Identifier bitcoin network <bitcoin|testnet3|testnet4|signet|mutinynet|regtest>"
96
- ).requiredOption(
97
- "-b, --bytes <bytes>",
98
- "Genesis bytes as a hex string. If type=k, MUST be secp256k1 public key. If type=x, MUST be SHA-256 hash of a genesis document"
99
- ).action(async (options) => {
100
- const parsed = validateCreateOptions(options);
101
- const api = factory();
102
- const type = parsed.type === "k" ? "deterministic" : "external";
103
- const genesisBytes = Buffer.from(parsed.bytes, "hex");
104
- const data = api.createDid(type, genesisBytes, { network: parsed.network });
105
- const result = { action: "create", data };
106
- console.log(formatResult(result, globals()));
107
- });
108
- }
109
- function validateCreateOptions(options) {
110
- if (!["k", "x"].includes(options.type)) {
111
- throw new CLIError(
112
- 'Invalid type. Must be "k" or "x".',
113
- "INVALID_ARGUMENT_ERROR",
114
- options
115
- );
116
- }
117
- if (!SUPPORTED_NETWORKS.includes(options.network)) {
118
- throw new CLIError(
119
- 'Invalid network. Must be one of "bitcoin", "testnet3", "testnet4", "signet", "mutinynet", or "regtest".',
120
- "INVALID_ARGUMENT_ERROR",
121
- options
122
- );
123
- }
124
- const buf = Buffer.from(options.bytes, "hex");
125
- if (buf.length === 0) {
126
- throw new CLIError(
127
- "Invalid bytes. Must be a non-empty hex string.",
128
- "INVALID_ARGUMENT_ERROR",
129
- options
130
- );
131
- }
132
- const expected = EXPECTED_BYTES[options.type];
133
- if (buf.length !== expected.length) {
134
- throw new CLIError(
135
- `Invalid bytes length for type="${options.type}": expected ${expected.label}, got ${buf.length} bytes.`,
136
- "INVALID_ARGUMENT_ERROR",
137
- options
138
- );
139
- }
140
- return {
141
- type: options.type,
142
- network: options.network,
143
- bytes: options.bytes
144
- };
145
- }
146
-
147
- // src/commands/resolve.ts
148
- var import_api2 = require("@did-btcr2/api");
149
- var import_promises = require("fs/promises");
62
+ var import_utils2 = require("@noble/hashes/utils.js");
150
63
 
151
64
  // src/config.ts
152
65
  var import_api = require("@did-btcr2/api");
@@ -154,6 +67,14 @@ var import_node_fs4 = require("fs");
154
67
  var import_node_os2 = require("os");
155
68
  var import_node_path4 = require("path");
156
69
 
70
+ // src/error.ts
71
+ var import_common = require("@did-btcr2/common");
72
+ var CLIError = class extends import_common.DidMethodError {
73
+ constructor(message, type = "CLIError", data) {
74
+ super(message, { type, name: "CLIError", data });
75
+ }
76
+ };
77
+
157
78
  // src/keystore/atomic.ts
158
79
  var import_node_fs = require("fs");
159
80
  var import_node_path = require("path");
@@ -609,6 +530,16 @@ function promptHidden(label) {
609
530
  return Buffer.from(bytes).toString("utf-8");
610
531
  }
611
532
 
533
+ // src/types.ts
534
+ var SUPPORTED_NETWORKS = [
535
+ "bitcoin",
536
+ "testnet3",
537
+ "testnet4",
538
+ "signet",
539
+ "mutinynet",
540
+ "regtest"
541
+ ];
542
+
612
543
  // src/config.ts
613
544
  var CONFIG_SCHEMA_VERSION = 1;
614
545
  function writeConfigFile(path, mutate) {
@@ -687,6 +618,17 @@ function profileToOverrides(config, profileName) {
687
618
  casGateway: profile.cas?.gateway
688
619
  };
689
620
  }
621
+ function resolveDefaultNetwork(overrides) {
622
+ const configPath = overrides?.config ?? defaultConfigPath();
623
+ const file = readConfigFile(configPath);
624
+ const explicit = file?.defaults?.network;
625
+ if (explicit && SUPPORTED_NETWORKS.includes(explicit)) return explicit;
626
+ const profile = overrides?.profile ?? file?.defaults?.profile;
627
+ if (profile && SUPPORTED_NETWORKS.includes(profile)) {
628
+ return profile;
629
+ }
630
+ return "regtest";
631
+ }
690
632
  function resolveConnectionConfig(network, overrides) {
691
633
  if (!network) return {};
692
634
  const configPath = overrides?.config ?? defaultConfigPath();
@@ -742,7 +684,172 @@ function deriveNetwork(did) {
742
684
  return network;
743
685
  }
744
686
 
687
+ // src/keystore/resolve-key-ref.ts
688
+ function fingerprintOf(id) {
689
+ return /^urn:kms:secp256k1:([0-9a-f]{32})$/.exec(id)?.[1];
690
+ }
691
+ function resolveKeyRef(kms, ref) {
692
+ if (!ref) {
693
+ if (!kms.activeKeyId) {
694
+ throw new CLIError(
695
+ "No key specified and no active key is set. Use --key <ref> or set one with `btcr2 key use <ref>`.",
696
+ "INVALID_ARGUMENT_ERROR"
697
+ );
698
+ }
699
+ return kms.activeKeyId;
700
+ }
701
+ const ids = kms.listKeys();
702
+ if (ids.includes(ref)) return ref;
703
+ const prefix = ref.toLowerCase();
704
+ const byPrefix = ids.filter((id) => fingerprintOf(id)?.startsWith(prefix));
705
+ if (byPrefix.length === 1) return byPrefix[0];
706
+ if (byPrefix.length > 1) {
707
+ throw new CLIError(
708
+ `Ambiguous key reference "${ref}" matches ${byPrefix.length} keys by fingerprint.`,
709
+ "KEY_REF_AMBIGUOUS_ERROR",
710
+ { ref }
711
+ );
712
+ }
713
+ const byName = ids.filter((id) => kms.getEntry(id).tags?.name === ref);
714
+ if (byName.length === 1) return byName[0];
715
+ if (byName.length > 1) {
716
+ throw new CLIError(
717
+ `Ambiguous key name "${ref}" matches ${byName.length} keys.`,
718
+ "KEY_REF_AMBIGUOUS_ERROR",
719
+ { ref }
720
+ );
721
+ }
722
+ throw new CLIError(`No key matches reference "${ref}".`, "KEY_NOT_FOUND_ERROR", { ref });
723
+ }
724
+
725
+ // src/output.ts
726
+ function formatResult(result, options) {
727
+ if (options.output === "json") {
728
+ return JSON.stringify(result, null, 2);
729
+ }
730
+ const { data } = result;
731
+ return typeof data === "string" ? data : JSON.stringify(data, null, 2);
732
+ }
733
+
734
+ // src/commands/create.ts
735
+ var EXPECTED_BYTES = {
736
+ k: { length: 33, label: "secp256k1 compressed public key (33 bytes)" },
737
+ x: { length: 32, label: "SHA-256 hash (32 bytes)" }
738
+ };
739
+ function registerCreateCommand(program, factory, keystoreFactory, globals) {
740
+ program.command("create").description("Create an identifier and initial DID document").option("-t, --type <type>", "Identifier type <k|x>", "k").option(
741
+ "-n, --network <network>",
742
+ "Identifier bitcoin network <bitcoin|testnet3|testnet4|signet|mutinynet|regtest> (default: config defaults.network, else regtest)"
743
+ ).option(
744
+ "-b, --bytes <bytes>",
745
+ "Genesis bytes as a hex string. For type=k, a 33-byte secp256k1 public key (omit to generate a key). For type=x, the 32-byte SHA-256 hash of a genesis document."
746
+ ).action(async (options) => {
747
+ const g = globals();
748
+ if (options.type !== "k" && options.type !== "x") {
749
+ throw new CLIError('Invalid type. Must be "k" or "x".', "INVALID_ARGUMENT_ERROR", options);
750
+ }
751
+ const overrides = overridesFromGlobals(g);
752
+ const network = resolveNetwork(options.network, overrides);
753
+ const signingKey = g.signingKey;
754
+ const print = (result, note) => {
755
+ console.log(formatResult(result, g));
756
+ if (note && g.output !== "json") process.stderr.write(`${note}
757
+ `);
758
+ };
759
+ if (options.type === "x") {
760
+ if (signingKey) {
761
+ throw new CLIError(
762
+ "--signing-key applies only to deterministic identifiers (-t k).",
763
+ "INVALID_ARGUMENT_ERROR"
764
+ );
765
+ }
766
+ if (options.bytes === void 0) {
767
+ throw new CLIError(
768
+ "External identifiers (-t x) require --bytes <hex>, the 32-byte genesis document hash. Key generation is only available for -t k.",
769
+ "INVALID_ARGUMENT_ERROR"
770
+ );
771
+ }
772
+ const genesisBytes = parseGenesisBytes(options.bytes, "x");
773
+ const did2 = factory().createDid("external", genesisBytes, { network });
774
+ print({ action: "create", data: did2 });
775
+ return;
776
+ }
777
+ if (options.bytes !== void 0 && signingKey) {
778
+ throw new CLIError(
779
+ "Provide at most one of --bytes or --signing-key.",
780
+ "INVALID_ARGUMENT_ERROR"
781
+ );
782
+ }
783
+ if (options.bytes !== void 0) {
784
+ const genesisBytes = parseGenesisBytes(options.bytes, "k");
785
+ const did2 = factory().createDid("deterministic", genesisBytes, { network });
786
+ print({ action: "create", data: did2 });
787
+ return;
788
+ }
789
+ if (signingKey) {
790
+ const api2 = keystoreFactory(void 0, overrides);
791
+ const keyId2 = resolveKeyRef(api2.kms.kms, signingKey);
792
+ const publicKey2 = api2.kms.getPublicKey(keyId2);
793
+ const did2 = api2.createDid("deterministic", publicKey2, { network });
794
+ print(
795
+ { action: "create", data: did2, keyId: keyId2, publicKey: (0, import_utils2.bytesToHex)(publicKey2) },
796
+ `Using stored key ${keyId2}.`
797
+ );
798
+ return;
799
+ }
800
+ const api = keystoreFactory(void 0, overrides);
801
+ const { did, keyId } = api.generateDid({ network, setActive: true });
802
+ const publicKey = (0, import_utils2.bytesToHex)(api.kms.getPublicKey(keyId));
803
+ print(
804
+ { action: "create", data: did, keyId, publicKey },
805
+ `Generated and stored key ${keyId} (now the active key).`
806
+ );
807
+ });
808
+ }
809
+ function overridesFromGlobals(g) {
810
+ return {
811
+ config: g.config,
812
+ profile: g.profile,
813
+ keystore: g.keystore,
814
+ passphraseFile: g.passphraseFile
815
+ };
816
+ }
817
+ function resolveNetwork(explicit, overrides) {
818
+ if (!explicit) return resolveDefaultNetwork(overrides);
819
+ if (!SUPPORTED_NETWORKS.includes(explicit)) {
820
+ throw new CLIError(
821
+ 'Invalid network. Must be one of "bitcoin", "testnet3", "testnet4", "signet", "mutinynet", or "regtest".',
822
+ "INVALID_ARGUMENT_ERROR",
823
+ { network: explicit }
824
+ );
825
+ }
826
+ return explicit;
827
+ }
828
+ function parseGenesisBytes(hex, type) {
829
+ const expected = EXPECTED_BYTES[type];
830
+ let bytes;
831
+ try {
832
+ bytes = (0, import_utils2.hexToBytes)(hex.trim());
833
+ } catch {
834
+ throw new CLIError(
835
+ `Invalid bytes: not valid hex. Expected ${expected.label}.`,
836
+ "INVALID_ARGUMENT_ERROR",
837
+ { bytes: hex }
838
+ );
839
+ }
840
+ if (bytes.length !== expected.length) {
841
+ throw new CLIError(
842
+ `Invalid bytes length for type="${type}": expected ${expected.label}, got ${bytes.length} bytes.`,
843
+ "INVALID_ARGUMENT_ERROR",
844
+ { bytes: hex }
845
+ );
846
+ }
847
+ return bytes;
848
+ }
849
+
745
850
  // src/commands/resolve.ts
851
+ var import_api2 = require("@did-btcr2/api");
852
+ var import_promises = require("fs/promises");
746
853
  function registerResolveCommand(program, factory, globals) {
747
854
  program.command("resolve").alias("read").description("Resolve the DID document of the identifier.").requiredOption("-i, --identifier <identifier>", "did:btcr2 identifier").option("-r, --resolution-options <json>", "JSON string containing resolution options").option("-p, --resolution-options-path <path>", "Path to a JSON file containing resolution options").action(async (options) => {
748
855
  const parsed = await validateResolveOptions(options);
@@ -783,46 +890,6 @@ async function validateResolveOptions(options) {
783
890
 
784
891
  // src/commands/update.ts
785
892
  var import_key_manager2 = require("@did-btcr2/key-manager");
786
-
787
- // src/keystore/resolve-key-ref.ts
788
- function fingerprintOf(id) {
789
- return /^urn:kms:secp256k1:([0-9a-f]{32})$/.exec(id)?.[1];
790
- }
791
- function resolveKeyRef(kms, ref) {
792
- if (!ref) {
793
- if (!kms.activeKeyId) {
794
- throw new CLIError(
795
- "No key specified and no active key is set. Use --key <ref> or set one with `btcr2 key use <ref>`.",
796
- "INVALID_ARGUMENT_ERROR"
797
- );
798
- }
799
- return kms.activeKeyId;
800
- }
801
- const ids = kms.listKeys();
802
- if (ids.includes(ref)) return ref;
803
- const prefix = ref.toLowerCase();
804
- const byPrefix = ids.filter((id) => fingerprintOf(id)?.startsWith(prefix));
805
- if (byPrefix.length === 1) return byPrefix[0];
806
- if (byPrefix.length > 1) {
807
- throw new CLIError(
808
- `Ambiguous key reference "${ref}" matches ${byPrefix.length} keys by fingerprint.`,
809
- "KEY_REF_AMBIGUOUS_ERROR",
810
- { ref }
811
- );
812
- }
813
- const byName = ids.filter((id) => kms.getEntry(id).tags?.name === ref);
814
- if (byName.length === 1) return byName[0];
815
- if (byName.length > 1) {
816
- throw new CLIError(
817
- `Ambiguous key name "${ref}" matches ${byName.length} keys.`,
818
- "KEY_REF_AMBIGUOUS_ERROR",
819
- { ref }
820
- );
821
- }
822
- throw new CLIError(`No key matches reference "${ref}".`, "KEY_NOT_FOUND_ERROR", { ref });
823
- }
824
-
825
- // src/commands/update.ts
826
893
  function registerUpdateCommand(program, factory, globals) {
827
894
  program.command("update").description("Update a did:btcr2 document.").requiredOption(
828
895
  "-s, --source-document <json>",
@@ -966,7 +1033,7 @@ function parseJsonArg2(flagName) {
966
1033
 
967
1034
  // src/commands/key.ts
968
1035
  var import_keypair = require("@did-btcr2/keypair");
969
- var import_utils2 = require("@noble/hashes/utils.js");
1036
+ var import_utils3 = require("@noble/hashes/utils.js");
970
1037
  var import_node_fs5 = require("fs");
971
1038
  function registerKeyCommand(program, factory, globals) {
972
1039
  const key = program.command("key").description("Manage keypairs in the encrypted keystore.");
@@ -976,7 +1043,7 @@ function registerKeyCommand(program, factory, globals) {
976
1043
  assertNameAvailable(api.kms.kms, options.name);
977
1044
  const setActive = options.setActive ?? false;
978
1045
  const id = api.kms.generateKey({ ...options.name && { tags: { name: options.name } }, setActive });
979
- print({ action: "key-generate", data: { keyId: id, publicKey: (0, import_utils2.bytesToHex)(api.kms.getPublicKey(id)), active: setActive } });
1046
+ print({ action: "key-generate", data: { keyId: id, publicKey: (0, import_utils3.bytesToHex)(api.kms.getPublicKey(id)), active: setActive } });
980
1047
  });
981
1048
  key.command("list").alias("ls").description("List stored keys.").action(() => {
982
1049
  const kms = factory(void 0, globals()).kms.kms;
@@ -996,7 +1063,7 @@ function registerKeyCommand(program, factory, globals) {
996
1063
  const kms = factory(void 0, globals()).kms.kms;
997
1064
  const id = resolveKeyRef(kms, ref);
998
1065
  const entry = kms.getEntry(id);
999
- print({ action: "key-show", data: { keyId: id, publicKey: (0, import_utils2.bytesToHex)(entry.publicKey), ...entry.tags && { tags: entry.tags } } });
1066
+ print({ action: "key-show", data: { keyId: id, publicKey: (0, import_utils3.bytesToHex)(entry.publicKey), ...entry.tags && { tags: entry.tags } } });
1000
1067
  });
1001
1068
  key.command("import").description("Import a key: a secret from a hex file, or a public key as watch-only.").option("--secret-file <path>", "Path to a file containing a 32-byte secret key as hex.").option("--public <hex>", "A 33-byte compressed public key as hex (imported watch-only).").option("--name <name>", "A human-friendly name, stored as a tag.").option("--set-active", "Make this the active key.", false).action((options) => {
1002
1069
  if (Boolean(options.secretFile) === Boolean(options.public)) {
@@ -1007,13 +1074,13 @@ function registerKeyCommand(program, factory, globals) {
1007
1074
  const keyPair = options.secretFile ? new import_keypair.SchnorrKeyPair({ secretKey: readHexFile(options.secretFile, 32, "--secret-file") }) : new import_keypair.SchnorrKeyPair({ publicKey: parseHex(options.public ?? "", 33, "--public") });
1008
1075
  const setActive = options.setActive ?? false;
1009
1076
  const id = api.kms.import(keyPair, { ...options.name && { tags: { name: options.name } }, setActive });
1010
- print({ action: "key-import", data: { keyId: id, publicKey: (0, import_utils2.bytesToHex)(api.kms.getPublicKey(id)), watchOnly: !options.secretFile, active: setActive } });
1077
+ print({ action: "key-import", data: { keyId: id, publicKey: (0, import_utils3.bytesToHex)(api.kms.getPublicKey(id)), watchOnly: !options.secretFile, active: setActive } });
1011
1078
  });
1012
1079
  key.command("export <ref>").description("Export a key. Public material by default; --secret writes the secret to a file.").option("--secret", "Export the secret key. Requires --out.", false).option("--out <path>", "Write the exported secret to this file (created 0600).").action((ref, options) => {
1013
1080
  const api = factory(void 0, globals());
1014
1081
  const id = resolveKeyRef(api.kms.kms, ref);
1015
1082
  if (!options.secret) {
1016
- print({ action: "key-export", data: { keyId: id, publicKey: (0, import_utils2.bytesToHex)(api.kms.getPublicKey(id)) } });
1083
+ print({ action: "key-export", data: { keyId: id, publicKey: (0, import_utils3.bytesToHex)(api.kms.getPublicKey(id)) } });
1017
1084
  return;
1018
1085
  }
1019
1086
  if (!options.out) {
@@ -1024,7 +1091,7 @@ function registerKeyCommand(program, factory, globals) {
1024
1091
  throw new CLIError(`Key ${id} is watch-only and has no secret to export.`, "INVALID_ARGUMENT_ERROR", { keyId: id });
1025
1092
  }
1026
1093
  process.stderr.write("warning: writing an unencrypted secret key to disk. Protect this file and delete it when done.\n");
1027
- writeSecretFile(options.out, (0, import_utils2.bytesToHex)(keyPair.secretKey.bytes));
1094
+ writeSecretFile(options.out, (0, import_utils3.bytesToHex)(keyPair.secretKey.bytes));
1028
1095
  print({ action: "key-export", data: { keyId: id, secretWrittenTo: options.out } });
1029
1096
  });
1030
1097
  key.command("delete <ref>").alias("rm").description("Delete a key from the keystore.").option("--force", "Delete even if it is the active key.", false).action((ref, options) => {
@@ -1049,7 +1116,7 @@ function assertNameAvailable(kms, name) {
1049
1116
  function parseHex(hex, expectedBytes, label) {
1050
1117
  let bytes;
1051
1118
  try {
1052
- bytes = (0, import_utils2.hexToBytes)(hex.trim());
1119
+ bytes = (0, import_utils3.hexToBytes)(hex.trim());
1053
1120
  } catch {
1054
1121
  throw new CLIError(`Invalid hex for ${label}.`, "INVALID_ARGUMENT_ERROR", { label });
1055
1122
  }
@@ -1246,7 +1313,7 @@ var DidBtcr2Cli = class {
1246
1313
  constructor(factory = defaultApiFactory, keystoreFactory = keystoreApiFactory) {
1247
1314
  this.program = new import_commander.Command("btcr2").version(`btcr2 ${VERSION}`, "-v, --version", "Output the current version").description("CLI tool for the did:btcr2 method").option("-o, --output <format>", "Output format <json|text>", "text").option("--verbose", "Verbose output", false).option("--quiet", "Suppress non-essential output", false).option("-c, --config <path>", "Path to config file (default: $XDG_CONFIG_HOME/btcr2/config.json)").option("--profile <name>", "Config profile name (default: auto-detected from network)").option("--btc-rest <url>", "Override Bitcoin REST endpoint (Esplora API)").option("--btc-rpc-url <url>", "Override Bitcoin Core RPC endpoint").option("--btc-rpc-user <user>", "Bitcoin Core RPC username").option("--btc-rpc-pass <pass>", "Bitcoin Core RPC password").option("--cas-gateway <url>", "IPFS HTTP gateway for CAS reads").option("--keystore <path>", "Path to the keystore file (default: $XDG_DATA_HOME/btcr2/keystore.json)").option("--passphrase-file <path>", "Read the keystore passphrase from a file (unattended use)").option("--signing-key <ref>", "Key for update/deactivate signing: a URN, fingerprint prefix, or name");
1248
1315
  const globals = () => this.program.opts();
1249
- registerCreateCommand(this.program, factory, globals);
1316
+ registerCreateCommand(this.program, factory, keystoreFactory, globals);
1250
1317
  registerResolveCommand(this.program, factory, globals);
1251
1318
  registerUpdateCommand(this.program, keystoreFactory, globals);
1252
1319
  registerDeactivateCommand(this.program, keystoreFactory, globals);
@@ -1312,6 +1379,7 @@ function handleError(error, verbose) {
1312
1379
  registerProfileCommand,
1313
1380
  registerResolveCommand,
1314
1381
  registerUpdateCommand,
1382
+ resolveDefaultNetwork,
1315
1383
  setConfigPath,
1316
1384
  unsetConfigPath,
1317
1385
  writeConfigFile
@@ -40,7 +40,7 @@ export class DidBtcr2Cli {
40
40
  .option('--passphrase-file <path>', 'Read the keystore passphrase from a file (unattended use)')
41
41
  .option('--signing-key <ref>', 'Key for update/deactivate signing: a URN, fingerprint prefix, or name');
42
42
  const globals = () => this.program.opts();
43
- registerCreateCommand(this.program, factory, globals);
43
+ registerCreateCommand(this.program, factory, keystoreFactory, globals);
44
44
  registerResolveCommand(this.program, factory, globals);
45
45
  registerUpdateCommand(this.program, keystoreFactory, globals);
46
46
  registerDeactivateCommand(this.program, keystoreFactory, globals);
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../../src/cli.ts"],"names":[],"mappings":"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"}
@@ -1,49 +1,128 @@
1
+ import { bytesToHex, hexToBytes } from '@noble/hashes/utils.js';
2
+ import { resolveDefaultNetwork } from '../config.js';
1
3
  import { CLIError } from '../error.js';
4
+ import { resolveKeyRef } from '../keystore/resolve-key-ref.js';
2
5
  import { formatResult } from '../output.js';
3
- import { SUPPORTED_NETWORKS, } from '../types.js';
6
+ import { SUPPORTED_NETWORKS } from '../types.js';
4
7
  /** Expected byte length per identifier type: compressed secp256k1 = 33, SHA-256 hash = 32. */
5
8
  const EXPECTED_BYTES = {
6
9
  k: { length: 33, label: 'secp256k1 compressed public key (33 bytes)' },
7
10
  x: { length: 32, label: 'SHA-256 hash (32 bytes)' },
8
11
  };
9
- export function registerCreateCommand(program, factory, globals) {
12
+ /**
13
+ * Registers the `create` command.
14
+ *
15
+ * A deterministic (`-t k`) identifier has three mutually-exclusive input modes,
16
+ * selected by which is present:
17
+ * - generate (neither `--bytes` nor `--signing-key`): mint a fresh key, persist
18
+ * it to the keystore, set it active, and print the identifier. Sealing the
19
+ * secret prompts for the keystore passphrase.
20
+ * - existing (`--signing-key <ref>`): use a stored key's public key as the
21
+ * genesis bytes. Reading a public key never decrypts, so this never prompts.
22
+ * - raw (`--bytes <hex>`): a 33-byte public key as hex. Offline, keystore-free.
23
+ *
24
+ * An external (`-t x`) identifier is raw-bytes-only: a 32-byte genesis-document
25
+ * hash via `--bytes`. Generation and `--signing-key` apply only to `-t k`.
26
+ *
27
+ * The keystore-free `factory` serves the raw-bytes path; the keystore-aware
28
+ * `keystoreFactory` serves the generate and existing-key paths.
29
+ */
30
+ export function registerCreateCommand(program, factory, keystoreFactory, globals) {
10
31
  program
11
32
  .command('create')
12
33
  .description('Create an identifier and initial DID document')
13
- .requiredOption('-t, --type <type>', 'Identifier type <k|x>', 'k')
14
- .requiredOption('-n, --network <network>', 'Identifier bitcoin network <bitcoin|testnet3|testnet4|signet|mutinynet|regtest>')
15
- .requiredOption('-b, --bytes <bytes>', 'Genesis bytes as a hex string. ' +
16
- 'If type=k, MUST be secp256k1 public key. ' +
17
- 'If type=x, MUST be SHA-256 hash of a genesis document')
34
+ .option('-t, --type <type>', 'Identifier type <k|x>', 'k')
35
+ .option('-n, --network <network>', 'Identifier bitcoin network <bitcoin|testnet3|testnet4|signet|mutinynet|regtest> '
36
+ + '(default: config defaults.network, else regtest)')
37
+ .option('-b, --bytes <bytes>', 'Genesis bytes as a hex string. '
38
+ + 'For type=k, a 33-byte secp256k1 public key (omit to generate a key). '
39
+ + 'For type=x, the 32-byte SHA-256 hash of a genesis document.')
18
40
  .action(async (options) => {
19
- const parsed = validateCreateOptions(options);
20
- const api = factory();
21
- const type = parsed.type === 'k' ? 'deterministic' : 'external';
22
- const genesisBytes = Buffer.from(parsed.bytes, 'hex');
23
- const data = api.createDid(type, genesisBytes, { network: parsed.network });
24
- const result = { action: 'create', data };
25
- console.log(formatResult(result, globals()));
41
+ const g = globals();
42
+ if (options.type !== 'k' && options.type !== 'x') {
43
+ throw new CLIError('Invalid type. Must be "k" or "x".', 'INVALID_ARGUMENT_ERROR', options);
44
+ }
45
+ const overrides = overridesFromGlobals(g);
46
+ const network = resolveNetwork(options.network, overrides);
47
+ const signingKey = g.signingKey;
48
+ /** Prints the result, plus a stderr provenance line in text mode. */
49
+ const print = (result, note) => {
50
+ console.log(formatResult(result, g));
51
+ if (note && g.output !== 'json')
52
+ process.stderr.write(`${note}\n`);
53
+ };
54
+ // External: raw-bytes only.
55
+ if (options.type === 'x') {
56
+ if (signingKey) {
57
+ throw new CLIError('--signing-key applies only to deterministic identifiers (-t k).', 'INVALID_ARGUMENT_ERROR');
58
+ }
59
+ if (options.bytes === undefined) {
60
+ throw new CLIError('External identifiers (-t x) require --bytes <hex>, the 32-byte genesis document hash. '
61
+ + 'Key generation is only available for -t k.', 'INVALID_ARGUMENT_ERROR');
62
+ }
63
+ const genesisBytes = parseGenesisBytes(options.bytes, 'x');
64
+ const did = factory().createDid('external', genesisBytes, { network });
65
+ print({ action: 'create', data: did });
66
+ return;
67
+ }
68
+ // Deterministic (KEY): three mutually-exclusive modes.
69
+ if (options.bytes !== undefined && signingKey) {
70
+ throw new CLIError('Provide at most one of --bytes or --signing-key.', 'INVALID_ARGUMENT_ERROR');
71
+ }
72
+ // Raw bytes: keystore-free, offline.
73
+ if (options.bytes !== undefined) {
74
+ const genesisBytes = parseGenesisBytes(options.bytes, 'k');
75
+ const did = factory().createDid('deterministic', genesisBytes, { network });
76
+ print({ action: 'create', data: did });
77
+ return;
78
+ }
79
+ // Existing key: read its public key from the keystore (no passphrase prompt).
80
+ if (signingKey) {
81
+ const api = keystoreFactory(undefined, overrides);
82
+ const keyId = resolveKeyRef(api.kms.kms, signingKey);
83
+ const publicKey = api.kms.getPublicKey(keyId);
84
+ const did = api.createDid('deterministic', publicKey, { network });
85
+ print({ action: 'create', data: did, keyId, publicKey: bytesToHex(publicKey) }, `Using stored key ${keyId}.`);
86
+ return;
87
+ }
88
+ // Generate: mint a fresh key, persist it, and set it active (passphrase prompt).
89
+ const api = keystoreFactory(undefined, overrides);
90
+ const { did, keyId } = api.generateDid({ network, setActive: true });
91
+ const publicKey = bytesToHex(api.kms.getPublicKey(keyId));
92
+ print({ action: 'create', data: did, keyId, publicKey }, `Generated and stored key ${keyId} (now the active key).`);
26
93
  });
27
94
  }
28
- function validateCreateOptions(options) {
29
- if (!['k', 'x'].includes(options.type)) {
30
- throw new CLIError('Invalid type. Must be "k" or "x".', 'INVALID_ARGUMENT_ERROR', options);
95
+ /** Builds the keystore- and config-resolution overrides from the global flags. */
96
+ function overridesFromGlobals(g) {
97
+ return {
98
+ config: g.config,
99
+ profile: g.profile,
100
+ keystore: g.keystore,
101
+ passphraseFile: g.passphraseFile,
102
+ };
103
+ }
104
+ /** Validates an explicit `--network`, or resolves the default from configuration. */
105
+ function resolveNetwork(explicit, overrides) {
106
+ if (!explicit)
107
+ return resolveDefaultNetwork(overrides);
108
+ if (!SUPPORTED_NETWORKS.includes(explicit)) {
109
+ throw new CLIError('Invalid network. Must be one of "bitcoin", "testnet3", "testnet4", "signet", "mutinynet", or "regtest".', 'INVALID_ARGUMENT_ERROR', { network: explicit });
31
110
  }
32
- if (!SUPPORTED_NETWORKS.includes(options.network)) {
33
- throw new CLIError('Invalid network. Must be one of "bitcoin", "testnet3", "testnet4", "signet", "mutinynet", or "regtest".', 'INVALID_ARGUMENT_ERROR', options);
111
+ return explicit;
112
+ }
113
+ /** Parses and length-checks hex genesis bytes for the given identifier type. */
114
+ function parseGenesisBytes(hex, type) {
115
+ const expected = EXPECTED_BYTES[type];
116
+ let bytes;
117
+ try {
118
+ bytes = hexToBytes(hex.trim());
34
119
  }
35
- const buf = Buffer.from(options.bytes, 'hex');
36
- if (buf.length === 0) {
37
- throw new CLIError('Invalid bytes. Must be a non-empty hex string.', 'INVALID_ARGUMENT_ERROR', options);
120
+ catch {
121
+ throw new CLIError(`Invalid bytes: not valid hex. Expected ${expected.label}.`, 'INVALID_ARGUMENT_ERROR', { bytes: hex });
38
122
  }
39
- const expected = EXPECTED_BYTES[options.type];
40
- if (buf.length !== expected.length) {
41
- throw new CLIError(`Invalid bytes length for type="${options.type}": expected ${expected.label}, got ${buf.length} bytes.`, 'INVALID_ARGUMENT_ERROR', options);
123
+ if (bytes.length !== expected.length) {
124
+ throw new CLIError(`Invalid bytes length for type="${type}": expected ${expected.label}, got ${bytes.length} bytes.`, 'INVALID_ARGUMENT_ERROR', { bytes: hex });
42
125
  }
43
- return {
44
- type: options.type,
45
- network: options.network,
46
- bytes: options.bytes,
47
- };
126
+ return bytes;
48
127
  }
49
128
  //# sourceMappingURL=create.js.map