buckyos 0.7.119 → 0.7.121

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 (44) hide show
  1. package/cli/README.md +24 -3
  2. package/cli/core/app.ts +28 -0
  3. package/cli/deno.json +2 -1
  4. package/cli/dist/cli.mjs +777 -169
  5. package/cli/dist/cli.mjs.map +1 -1
  6. package/cli/modules/app.ts +213 -94
  7. package/cli/modules/provision.ts +690 -0
  8. package/cli/runtime/host.ts +25 -2
  9. package/cli/runtime/host_deno.ts +4 -0
  10. package/cli/runtime/host_node.ts +4 -0
  11. package/cli/version.ts +1 -1
  12. package/dist/browser.cjs +1 -1
  13. package/dist/browser.mjs +124 -124
  14. package/dist/index.cjs +1 -1
  15. package/dist/index.mjs +124 -124
  16. package/dist/{ndm_proxy-c00bc561.mjs → ndm_proxy-8f547002.mjs} +113 -216
  17. package/dist/ndm_proxy-8f547002.mjs.map +1 -0
  18. package/dist/ndm_proxy-93a8869b.js +13 -0
  19. package/dist/ndm_proxy-93a8869b.js.map +1 -0
  20. package/dist/ndn_types-44c69036.js +2 -0
  21. package/dist/ndn_types-44c69036.js.map +1 -0
  22. package/dist/{ndn_types-f6c08d20.mjs → ndn_types-d768245f.mjs} +149 -39
  23. package/dist/ndn_types-d768245f.mjs.map +1 -0
  24. package/dist/node.cjs +1 -1
  25. package/dist/node.mjs +124 -124
  26. package/dist/provision.cjs +6 -6
  27. package/dist/provision.cjs.map +1 -1
  28. package/dist/provision.d.ts +2 -0
  29. package/dist/provision.d.ts.map +1 -1
  30. package/dist/provision.mjs +1084 -35
  31. package/dist/provision.mjs.map +1 -1
  32. package/dist/provision_activate.d.ts +146 -0
  33. package/dist/provision_activate.d.ts.map +1 -0
  34. package/dist/{tus_client-573066b3.mjs → tus_client-b1c35e8c.mjs} +2 -2
  35. package/dist/{tus_client-573066b3.mjs.map → tus_client-b1c35e8c.mjs.map} +1 -1
  36. package/dist/{tus_client-83ad0f4f.js → tus_client-d0798623.js} +2 -2
  37. package/dist/{tus_client-83ad0f4f.js.map → tus_client-d0798623.js.map} +1 -1
  38. package/package.json +1 -1
  39. package/dist/ndm_proxy-68354a89.js +0 -13
  40. package/dist/ndm_proxy-68354a89.js.map +0 -1
  41. package/dist/ndm_proxy-c00bc561.mjs.map +0 -1
  42. package/dist/ndn_types-859bbd48.js +0 -2
  43. package/dist/ndn_types-859bbd48.js.map +0 -1
  44. package/dist/ndn_types-f6c08d20.mjs.map +0 -1
@@ -5,13 +5,13 @@ var __publicField = (obj, key, value) => {
5
5
  return value;
6
6
  };
7
7
  var _a;
8
- import { N as NODE_IDENTITY_SCHEMA_V2, v as DID, A as newDeviceDocumentByJwk, B as verifyJwtEdDSA, E as deviceDocumentToOrderedJson, G as decodeJwtClaimWithoutVerify, H as commonjsGlobal, I as createJwkByX, J as newOwnerDocument, K as ownerDocumentToOrderedJson, L as parseOODDescription, M as oodDescriptionToString, P as newZoneBootDocument, Q as newZoneDocument, R as encodeZoneBootDocument, T as DEFAULT_EXPIRE_TIME, U as zoneDocumentToOrderedJson, V as newDeviceMiniDocument, W as deviceMiniDocumentToJwt, X as encodeDeviceDocument, Y as newDeviceMiniDocumentByDeviceDocument, Z as newDeviceDocumentByMiniDocument, _ as buckyosGetUnixTimestamp, $ as buildNamedObjectByJson } from "./ndn_types-f6c08d20.mjs";
8
+ import { N as NODE_IDENTITY_SCHEMA_V2, v as DID, H as newDeviceDocumentByJwk, I as verifyJwtEdDSA, J as deviceDocumentToOrderedJson, K as decodeJwtClaimWithoutVerify, L as commonjsGlobal, M as DEFAULT_EXPIRE_TIME, h as hashPassword, P as buckyosGetUnixTimestamp, Q as getPublicKeyXFromPrivatePem, R as getXFromJwk, T as createJwkByX, U as newOwnerDocument, V as ownerDocumentSetDefaultZoneDid, W as ownerDocumentToOrderedJson, X as oodDescriptionToString, Y as parseOODDescription, Z as encodeZoneBootDocument, _ as newZoneBootDocument, $ as encodeDeviceDocument, a0 as newDeviceMiniDocumentByDeviceDocument, a1 as deviceMiniDocumentToJwt, a2 as newZoneDocument, a3 as encodeZoneDocument, a4 as zoneDocumentToOrderedJson, a5 as newDeviceMiniDocument, a6 as newDeviceDocumentByMiniDocument, a7 as buildNamedObjectByJson } from "./ndn_types-d768245f.mjs";
9
9
  import { Buffer as Buffer$1 } from "node:buffer";
10
10
  import { pbkdf2Sync, createPublicKey, createPrivateKey, createECDH, createHmac } from "node:crypto";
11
11
  const DEVICE_DOC_JWT_FILE_NAME = "device_doc.jwt";
12
12
  const DEVICE_MINI_DOC_JWT_FILE_NAME = "device_mini_doc.jwt";
13
13
  const NODE_GATEWAY_PARAMS_FILE_NAME = "node_gateway_params.json";
14
- function requireNode$2(moduleName) {
14
+ function requireNode$3(moduleName) {
15
15
  const proc = globalThis.process;
16
16
  if (typeof (proc == null ? void 0 : proc.getBuiltinModule) === "function") {
17
17
  const builtin = proc.getBuiltinModule(moduleName);
@@ -28,8 +28,8 @@ function asDid(value) {
28
28
  return value instanceof DID ? value : DID.fromStr(value);
29
29
  }
30
30
  function writeJsonPretty(filePath, value) {
31
- const fs = requireNode$2("node:fs");
32
- const path = requireNode$2("node:path");
31
+ const fs = requireNode$3("node:fs");
32
+ const path = requireNode$3("node:path");
33
33
  fs.mkdirSync(path.dirname(filePath), { recursive: true });
34
34
  fs.writeFileSync(filePath, JSON.stringify(value, null, 2));
35
35
  }
@@ -45,7 +45,7 @@ function newLocalNodeIdentityConfig(params) {
45
45
  };
46
46
  }
47
47
  function loadLocalNodeIdentityConfig(filePath) {
48
- const fs = requireNode$2("node:fs");
48
+ const fs = requireNode$3("node:fs");
49
49
  const config = JSON.parse(fs.readFileSync(filePath, "utf8"));
50
50
  if (config.schema !== NODE_IDENTITY_SCHEMA_V2) {
51
51
  throw new Error(
@@ -55,7 +55,7 @@ function loadLocalNodeIdentityConfig(filePath) {
55
55
  return config;
56
56
  }
57
57
  function deviceIdentityPathsForRoots(roots, deviceDid) {
58
- const path = requireNode$2("node:path");
58
+ const path = requireNode$3("node:path");
59
59
  const deviceDidStr = asDid(deviceDid).toString();
60
60
  const publicDir = roots.publicDir(deviceDidStr);
61
61
  return {
@@ -94,11 +94,11 @@ function newDeviceDocumentByJwkWithDid(name, publicKeyJwk, deviceDid, now) {
94
94
  return bindDeviceDocumentDid(newDeviceDocumentByJwk(name, publicKeyJwk, now), deviceDid);
95
95
  }
96
96
  function loadDeviceDocJwtForRoots(roots, deviceDid) {
97
- const fs = requireNode$2("node:fs");
97
+ const fs = requireNode$3("node:fs");
98
98
  return fs.readFileSync(deviceIdentityPathsForRoots(roots, deviceDid).deviceDocJwt, "utf8");
99
99
  }
100
100
  function loadDeviceMiniDocJwtForRoots(roots, deviceDid) {
101
- const fs = requireNode$2("node:fs");
101
+ const fs = requireNode$3("node:fs");
102
102
  return fs.readFileSync(deviceIdentityPathsForRoots(roots, deviceDid).deviceMiniDocJwt, "utf8");
103
103
  }
104
104
  async function loadLocalDeviceDocumentForRoots(roots, nodeIdentity, verify) {
@@ -112,7 +112,7 @@ async function loadLocalDeviceDocumentForRoots(roots, nodeIdentity, verify) {
112
112
  return [deviceDocJwt, deviceDoc];
113
113
  }
114
114
  function saveNodeGatewayParams(etcDir, deviceDid) {
115
- const path = requireNode$2("node:path");
115
+ const path = requireNode$3("node:path");
116
116
  writeJsonPretty(path.join(etcDir, NODE_GATEWAY_PARAMS_FILE_NAME), {
117
117
  params: {
118
118
  device_did: asDid(deviceDid).toString()
@@ -120,8 +120,8 @@ function saveNodeGatewayParams(etcDir, deviceDid) {
120
120
  });
121
121
  }
122
122
  function saveLocalDeviceIdentityForRoots(etcDir, roots, nodeIdentity, deviceDoc, deviceDocJwt, deviceMiniDocJwt, devicePrivateKeyPem) {
123
- const fs = requireNode$2("node:fs");
124
- const path = requireNode$2("node:path");
123
+ const fs = requireNode$3("node:fs");
124
+ const path = requireNode$3("node:path");
125
125
  const paths = deviceIdentityPathsForRoots(roots, nodeIdentity.device_did);
126
126
  fs.mkdirSync(paths.publicDir, { recursive: true });
127
127
  fs.mkdirSync(paths.securityDir, { recursive: true });
@@ -13157,7 +13157,7 @@ AsnEcSignatureFormatter.namedCurveSize.set("P-256", 32);
13157
13157
  AsnEcSignatureFormatter.namedCurveSize.set("K-256", 32);
13158
13158
  AsnEcSignatureFormatter.namedCurveSize.set("P-384", 48);
13159
13159
  AsnEcSignatureFormatter.namedCurveSize.set("P-521", 66);
13160
- function requireNode$1(moduleName) {
13160
+ function requireNode$2(moduleName) {
13161
13161
  const proc = globalThis.process;
13162
13162
  if (typeof (proc == null ? void 0 : proc.getBuiltinModule) === "function") {
13163
13163
  const builtin = proc.getBuiltinModule(moduleName);
@@ -13175,7 +13175,7 @@ function getCrypto() {
13175
13175
  if (webcrypto == null ? void 0 : webcrypto.subtle) {
13176
13176
  return webcrypto;
13177
13177
  }
13178
- const nodeCrypto = requireNode$1("node:crypto");
13178
+ const nodeCrypto = requireNode$2("node:crypto");
13179
13179
  return nodeCrypto.webcrypto;
13180
13180
  }
13181
13181
  const CA_SIGNING_ALG = {
@@ -13239,7 +13239,7 @@ function encodeUtf8(value) {
13239
13239
  if (typeof TextEncoder !== "undefined") {
13240
13240
  return new TextEncoder().encode(value);
13241
13241
  }
13242
- const buffer = requireNode$1("node:buffer").Buffer;
13242
+ const buffer = requireNode$2("node:buffer").Buffer;
13243
13243
  return new Uint8Array(buffer.from(value, "utf8"));
13244
13244
  }
13245
13245
  function encodeIdentityDirName(rawHostUri) {
@@ -13343,7 +13343,7 @@ function trimToNull(value) {
13343
13343
  return trimmed.length > 0 ? trimmed : null;
13344
13344
  }
13345
13345
  function x509PathsForDirs(publicDir, securityDir, usage) {
13346
- const path = requireNode$1("node:path");
13346
+ const path = requireNode$2("node:path");
13347
13347
  return {
13348
13348
  cert: path.join(publicDir, identityFileName(usage, "cert")),
13349
13349
  chain: path.join(publicDir, identityFileName(usage, "chain")),
@@ -13356,12 +13356,12 @@ function x509PathsForDirs(publicDir, securityDir, usage) {
13356
13356
  }
13357
13357
  class IdentityRoots {
13358
13358
  constructor(publicRoot, securityRoot) {
13359
- const path = requireNode$1("node:path");
13359
+ const path = requireNode$2("node:path");
13360
13360
  this.publicRoot = path.resolve(publicRoot);
13361
13361
  this.securityRoot = path.resolve(securityRoot);
13362
13362
  }
13363
13363
  static fromEnvOrBuckyosRoot(options = {}) {
13364
- const path = requireNode$1("node:path");
13364
+ const path = requireNode$2("node:path");
13365
13365
  const env = getProcessEnv();
13366
13366
  const buckyosRoot = trimToNull(options.buckyosRoot) ?? trimToNull(env.BUCKYOS_ROOT) ?? DEFAULT_BUCKYOS_ROOT;
13367
13367
  const publicRoot = trimToNull(options.publicRoot) ?? trimToNull(env.BUCKYOS_IDENTITY_ROOT) ?? path.join(buckyosRoot, "local", "identity");
@@ -13375,19 +13375,19 @@ class IdentityRoots {
13375
13375
  return identityDirName(didOrHostname);
13376
13376
  }
13377
13377
  publicDir(didOrHostname) {
13378
- const path = requireNode$1("node:path");
13378
+ const path = requireNode$2("node:path");
13379
13379
  return path.join(this.publicRoot, this.dirName(didOrHostname));
13380
13380
  }
13381
13381
  securityDir(didOrHostname) {
13382
- const path = requireNode$1("node:path");
13382
+ const path = requireNode$2("node:path");
13383
13383
  return path.join(this.securityRoot, this.dirName(didOrHostname));
13384
13384
  }
13385
13385
  publicFile(didOrHostname, usage, material) {
13386
- const path = requireNode$1("node:path");
13386
+ const path = requireNode$2("node:path");
13387
13387
  return path.join(this.publicDir(didOrHostname), identityFileName(usage, material));
13388
13388
  }
13389
13389
  securityFile(didOrHostname, usage, material) {
13390
- const path = requireNode$1("node:path");
13390
+ const path = requireNode$2("node:path");
13391
13391
  return path.join(this.securityDir(didOrHostname), identityFileName(usage, material));
13392
13392
  }
13393
13393
  x509Paths(didOrHostname, usage = "server") {
@@ -13406,7 +13406,7 @@ class IdentityRoots {
13406
13406
  };
13407
13407
  }
13408
13408
  findX509Paths(didOrHostname, usage = "server") {
13409
- const fs = requireNode$1("node:fs");
13409
+ const fs = requireNode$2("node:fs");
13410
13410
  const exact = this.identityDirMatch(didOrHostname);
13411
13411
  const exactPaths = x509PathsForDirs(exact.publicDir, exact.securityDir, usage);
13412
13412
  if (fs.existsSync(exactPaths.fullchain) || fs.existsSync(exactPaths.cert)) {
@@ -13423,7 +13423,7 @@ class IdentityRoots {
13423
13423
  }
13424
13424
  const wildcardHost = `_.${labels.slice(1).join(".")}`;
13425
13425
  const wildcardDirName = encodeIdentityDirName(wildcardHost);
13426
- const path = requireNode$1("node:path");
13426
+ const path = requireNode$2("node:path");
13427
13427
  const publicDir = path.join(this.publicRoot, wildcardDirName);
13428
13428
  const securityDir = path.join(this.securityRoot, wildcardDirName);
13429
13429
  const wildcardPaths = x509PathsForDirs(publicDir, securityDir, usage);
@@ -13516,8 +13516,8 @@ function getCaPemValidationFailure(pem) {
13516
13516
  }
13517
13517
  }
13518
13518
  async function createCa(outputDir, name = "devtest") {
13519
- const fs = requireNode$1("node:fs");
13520
- const path = requireNode$1("node:path");
13519
+ const fs = requireNode$2("node:fs");
13520
+ const path = requireNode$2("node:path");
13521
13521
  const crypto2 = getCrypto();
13522
13522
  cryptoProvider.set(crypto2);
13523
13523
  fs.mkdirSync(outputDir, { recursive: true });
@@ -13548,8 +13548,8 @@ async function createCa(outputDir, name = "devtest") {
13548
13548
  return { caCertPath, caKeyPath };
13549
13549
  }
13550
13550
  async function ensureCa(caDir, name = "devtest") {
13551
- const fs = requireNode$1("node:fs");
13552
- const path = requireNode$1("node:path");
13551
+ const fs = requireNode$2("node:fs");
13552
+ const path = requireNode$2("node:path");
13553
13553
  const caCertPath = path.join(caDir, `${name}_ca_cert.pem`);
13554
13554
  const caKeyPath = path.join(caDir, `${name}_ca_key.pem`);
13555
13555
  if (fs.existsSync(caCertPath) && fs.existsSync(caKeyPath)) {
@@ -13564,8 +13564,8 @@ async function ensureCa(caDir, name = "devtest") {
13564
13564
  return createCa(caDir, name);
13565
13565
  }
13566
13566
  function findCaFiles(caDir) {
13567
- const fs = requireNode$1("node:fs");
13568
- const path = requireNode$1("node:path");
13567
+ const fs = requireNode$2("node:fs");
13568
+ const path = requireNode$2("node:path");
13569
13569
  const certFiles = fs.readdirSync(caDir).filter((f) => f.endsWith("_ca_cert.pem"));
13570
13570
  if (certFiles.length === 0) {
13571
13571
  throw new Error(`No CA certificate found matching *_ca_cert.pem pattern in ${caDir}`);
@@ -13581,7 +13581,7 @@ function findCaFiles(caDir) {
13581
13581
  return { caCertPath, caKeyPath };
13582
13582
  }
13583
13583
  async function issueX509CertFromCa(caDir, dnsNames, uriSans = [], usage = "server") {
13584
- const fs = requireNode$1("node:fs");
13584
+ const fs = requireNode$2("node:fs");
13585
13585
  const crypto2 = getCrypto();
13586
13586
  cryptoProvider.set(crypto2);
13587
13587
  const { caCertPath, caKeyPath } = findCaFiles(caDir);
@@ -13634,8 +13634,8 @@ async function issueX509CertFromCa(caDir, dnsNames, uriSans = [], usage = "serve
13634
13634
  };
13635
13635
  }
13636
13636
  async function createCertFromCa(caDir, hostname, targetDir, hostnames) {
13637
- const fs = requireNode$1("node:fs");
13638
- const path = requireNode$1("node:path");
13637
+ const fs = requireNode$2("node:fs");
13638
+ const path = requireNode$2("node:path");
13639
13639
  const dnsNames = hostnames && hostnames.length > 0 ? hostnames : [hostname];
13640
13640
  const commonName = dnsNames[0];
13641
13641
  const safeHostname = commonName.replace(/\*/g, "wildcard").replace(/\./g, "_");
@@ -13653,8 +13653,8 @@ function normalizePem(pem) {
13653
13653
  `;
13654
13654
  }
13655
13655
  function writeFileAtomicSync(filePath, content, mode) {
13656
- const fs = requireNode$1("node:fs");
13657
- const path = requireNode$1("node:path");
13656
+ const fs = requireNode$2("node:fs");
13657
+ const path = requireNode$2("node:path");
13658
13658
  const dir = path.dirname(filePath);
13659
13659
  const tempPath = path.join(dir, `.${path.basename(filePath)}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`);
13660
13660
  try {
@@ -13753,7 +13753,7 @@ async function buildX509Metadata(did, usage, rawHostUri, dirName, issued) {
13753
13753
  };
13754
13754
  }
13755
13755
  async function createIdentityCertFromCa(caDir, didOrHostname, rootsInput, options = {}) {
13756
- const fs = requireNode$1("node:fs");
13756
+ const fs = requireNode$2("node:fs");
13757
13757
  const roots = resolveIdentityRoots(rootsInput);
13758
13758
  const usage = options.usage ?? "server";
13759
13759
  const did = canonicalIdentityDid(didOrHostname);
@@ -14111,6 +14111,1035 @@ function getDevTestEvmAccountByUsername(username) {
14111
14111
  }
14112
14112
  return account;
14113
14113
  }
14114
+ const ACTIVATION_DEVICE_NAME = "ood1";
14115
+ const ACTIVATION_NET_ID = "wan";
14116
+ const ACTIVATION_DEFAULT_RTCP_PORT = 2980;
14117
+ const ACTIVATION_DOCUMENT_VALIDITY_SECONDS = DEFAULT_EXPIRE_TIME;
14118
+ const ACTIVATION_MIN_PASSWORD_LENGTH = 8;
14119
+ const ACTIVATION_LOCK_FILE_NAME = "provision_activation.lock";
14120
+ const ZONE_DNS_RECORDS_FILE_NAME = "zone_dns_records.json";
14121
+ const ZONE_DOCUMENT_JWT_FILE_NAME = "zone_document.jwt";
14122
+ const UNACTIVATED_GATEWAY_DEVICE_DID = "did:bns:unactivated.local";
14123
+ const MAX_INLINE_DOCUMENT_BYTES = 4096;
14124
+ const ACTIVATION_LOCK_SCHEMA_VERSION = 1;
14125
+ class ActivationError extends Error {
14126
+ constructor(code, message, details = {}) {
14127
+ super(message);
14128
+ this.name = "ActivationError";
14129
+ this.code = code;
14130
+ this.details = details;
14131
+ }
14132
+ }
14133
+ function requireNode$1(moduleName) {
14134
+ const proc = globalThis.process;
14135
+ if (typeof (proc == null ? void 0 : proc.getBuiltinModule) === "function") {
14136
+ const builtin = proc.getBuiltinModule(moduleName);
14137
+ if (builtin) {
14138
+ return builtin;
14139
+ }
14140
+ }
14141
+ if (typeof require === "function") {
14142
+ return require(moduleName);
14143
+ }
14144
+ throw new ActivationError(
14145
+ "RUNTIME_UNSUPPORTED",
14146
+ `buckyos provision cannot load builtin module ${moduleName} in this runtime (Node >= 22.13 or Deno >= 2.2 is required)`
14147
+ );
14148
+ }
14149
+ function isWindows() {
14150
+ var _a3;
14151
+ return ((_a3 = globalThis.process) == null ? void 0 : _a3.platform) === "win32";
14152
+ }
14153
+ function toPosixRelative(rootDir, filePath) {
14154
+ const path = requireNode$1("node:path");
14155
+ return path.relative(rootDir, filePath).split(path.sep).join("/");
14156
+ }
14157
+ function sha256Hex(content) {
14158
+ const crypto2 = requireNode$1("node:crypto");
14159
+ return crypto2.createHash("sha256").update(content, "utf8").digest("hex");
14160
+ }
14161
+ function randomSuffix() {
14162
+ const crypto2 = requireNode$1("node:crypto");
14163
+ return crypto2.randomBytes(6).toString("hex");
14164
+ }
14165
+ function lstatOrNull(filePath) {
14166
+ const fs = requireNode$1("node:fs");
14167
+ try {
14168
+ return fs.lstatSync(filePath);
14169
+ } catch (error) {
14170
+ const code = error.code;
14171
+ if (code === "ENOENT" || code === "ENOTDIR")
14172
+ return null;
14173
+ throw error;
14174
+ }
14175
+ }
14176
+ function statOrNull(filePath) {
14177
+ const fs = requireNode$1("node:fs");
14178
+ try {
14179
+ return fs.statSync(filePath);
14180
+ } catch (error) {
14181
+ if (error.code === "ENOENT" || error.code === "ENOTDIR") {
14182
+ return null;
14183
+ }
14184
+ throw error;
14185
+ }
14186
+ }
14187
+ function isDirectory(filePath) {
14188
+ var _a3;
14189
+ return ((_a3 = statOrNull(filePath)) == null ? void 0 : _a3.isDirectory()) === true;
14190
+ }
14191
+ function isFile(filePath) {
14192
+ var _a3;
14193
+ return ((_a3 = statOrNull(filePath)) == null ? void 0 : _a3.isFile()) === true;
14194
+ }
14195
+ function readJsonObjectOrThrow(filePath) {
14196
+ const fs = requireNode$1("node:fs");
14197
+ const value = JSON.parse(fs.readFileSync(filePath, "utf8"));
14198
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
14199
+ throw new Error(`${filePath} is not a JSON object`);
14200
+ }
14201
+ return value;
14202
+ }
14203
+ function jsonPretty(value) {
14204
+ return JSON.stringify(value, null, 2);
14205
+ }
14206
+ function commitFile(filePath, content, mode, dirMode) {
14207
+ const fs = requireNode$1("node:fs");
14208
+ const path = requireNode$1("node:path");
14209
+ const directory = path.dirname(filePath);
14210
+ fs.mkdirSync(directory, { recursive: true, mode: dirMode });
14211
+ if (!isWindows()) {
14212
+ fs.chmodSync(directory, dirMode);
14213
+ }
14214
+ if (lstatOrNull(filePath)) {
14215
+ throw new Error(`refusing to overwrite existing file: ${filePath}`);
14216
+ }
14217
+ const temporary = path.join(directory, `.${path.basename(filePath)}.provision-${randomSuffix()}`);
14218
+ try {
14219
+ fs.writeFileSync(temporary, content, { mode, flag: "wx" });
14220
+ if (!isWindows()) {
14221
+ fs.chmodSync(temporary, mode);
14222
+ }
14223
+ fs.renameSync(temporary, filePath);
14224
+ } finally {
14225
+ if (lstatOrNull(temporary)) {
14226
+ fs.rmSync(temporary, { force: true });
14227
+ }
14228
+ }
14229
+ }
14230
+ function normalizeDomain(value) {
14231
+ return value.trim().toLowerCase().replace(/\.$/, "");
14232
+ }
14233
+ function validateZoneDomain(value) {
14234
+ const domain = normalizeDomain(String(value ?? ""));
14235
+ if (domain.length === 0 || domain.length > 253 || !domain.includes(".") || domain.split(".").some(
14236
+ (label) => label.length === 0 || label.length > 63 || label.startsWith("-") || label.endsWith("-") || !/^[a-z0-9-]+$/.test(label)
14237
+ )) {
14238
+ throw new ActivationError("INVALID_ARGUMENT", `invalid did:web domain: ${value}`, { field: "domain" });
14239
+ }
14240
+ return domain;
14241
+ }
14242
+ function validateOwnerName(value) {
14243
+ const name = String(value ?? "").trim().toLowerCase();
14244
+ if (name.length === 0 || name.length > 63 || !/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/.test(name)) {
14245
+ throw new ActivationError(
14246
+ "INVALID_ARGUMENT",
14247
+ "owner name must be a lowercase DNS label (letters, digits, and hyphens)",
14248
+ { field: "owner_name" }
14249
+ );
14250
+ }
14251
+ return name;
14252
+ }
14253
+ function validateRtcpPort(value) {
14254
+ const port = value ?? ACTIVATION_DEFAULT_RTCP_PORT;
14255
+ if (!Number.isInteger(port) || port < 1 || port > 65535) {
14256
+ throw new ActivationError("INVALID_ARGUMENT", `invalid RTCP port: ${String(value)}`, { field: "rtcp_port" });
14257
+ }
14258
+ return port;
14259
+ }
14260
+ function validatePublicIp(value) {
14261
+ const publicIp = (value == null ? void 0 : value.trim()) || void 0;
14262
+ if (publicIp === void 0)
14263
+ return void 0;
14264
+ const net = requireNode$1("node:net");
14265
+ if (net.isIP(publicIp) === 0) {
14266
+ throw new ActivationError("INVALID_ARGUMENT", `invalid public IP address: ${publicIp}`, { field: "public_ip" });
14267
+ }
14268
+ return publicIp;
14269
+ }
14270
+ function validateAdminPassword(value) {
14271
+ if (typeof value !== "string" || value.length < ACTIVATION_MIN_PASSWORD_LENGTH) {
14272
+ throw new ActivationError(
14273
+ "INVALID_ARGUMENT",
14274
+ `administrator password must contain at least ${ACTIVATION_MIN_PASSWORD_LENGTH} characters`,
14275
+ { field: "admin_password" }
14276
+ );
14277
+ }
14278
+ return value;
14279
+ }
14280
+ function hashAdminPassword(ownerName, password) {
14281
+ return hashPassword(ownerName, password, null);
14282
+ }
14283
+ function buildActivationDnsRecords(domain, publicIp) {
14284
+ const records = [];
14285
+ if (publicIp) {
14286
+ const net = requireNode$1("node:net");
14287
+ records.push({ type: net.isIP(publicIp) === 6 ? "AAAA" : "A", name: domain, value: publicIp });
14288
+ }
14289
+ return records;
14290
+ }
14291
+ function generateProvisionKeyPair() {
14292
+ const crypto2 = requireNode$1("node:crypto");
14293
+ const { privateKey, publicKey } = crypto2.generateKeyPairSync("ed25519");
14294
+ const jwk = publicKey.export({ format: "jwk" });
14295
+ if (typeof jwk.x !== "string" || jwk.x.length === 0) {
14296
+ throw new ActivationError("KEY_GENERATION_FAILED", "generated Ed25519 public key has no x coordinate");
14297
+ }
14298
+ return {
14299
+ privateKeyPem: privateKey.export({ format: "pem", type: "pkcs8" }).toString(),
14300
+ publicKeyX: jwk.x
14301
+ };
14302
+ }
14303
+ function normalizeActivationParams(options) {
14304
+ const path = requireNode$1("node:path");
14305
+ if (typeof options.rootDir !== "string" || options.rootDir.trim().length === 0) {
14306
+ throw new ActivationError("INVALID_ARGUMENT", "target root is required", { field: "root" });
14307
+ }
14308
+ if (typeof options.ownerKeyBackupPath !== "string" || options.ownerKeyBackupPath.trim().length === 0) {
14309
+ throw new ActivationError("INVALID_ARGUMENT", "owner key backup path is required", { field: "owner_key_backup" });
14310
+ }
14311
+ const domain = validateZoneDomain(options.domain);
14312
+ const zoneDid = new DID("web", domain);
14313
+ return {
14314
+ rootDir: path.resolve(options.rootDir),
14315
+ domain,
14316
+ ownerName: validateOwnerName(options.ownerName),
14317
+ ownerKeyBackupPath: path.resolve(options.ownerKeyBackupPath),
14318
+ rtcpPort: validateRtcpPort(options.rtcpPort),
14319
+ guestAccess: options.guestAccess ?? false,
14320
+ publicIp: validatePublicIp(options.publicIp),
14321
+ ownerDid: zoneDid,
14322
+ zoneDid,
14323
+ deviceDid: buildDeviceDid(ACTIVATION_DEVICE_NAME, zoneDid)
14324
+ };
14325
+ }
14326
+ function identityRootsFor(rootDir) {
14327
+ const path = requireNode$1("node:path");
14328
+ return new IdentityRoots(path.join(rootDir, "local", "identity"), path.join(rootDir, "security"));
14329
+ }
14330
+ function layoutFor(rootDir, zoneDid, deviceDid) {
14331
+ const path = requireNode$1("node:path");
14332
+ const etcDir = path.join(rootDir, "etc");
14333
+ const paths = deviceIdentityPathsForRoots(identityRootsFor(rootDir), deviceDid);
14334
+ return {
14335
+ etcDir,
14336
+ lockPath: path.join(etcDir, ACTIVATION_LOCK_FILE_NAME),
14337
+ nodeIdentityPath: path.join(etcDir, "node_identity.json"),
14338
+ startConfigPath: path.join(etcDir, "start_config.json"),
14339
+ zoneDocumentJwtPath: path.join(etcDir, ZONE_DOCUMENT_JWT_FILE_NAME),
14340
+ gatewayParamsPath: path.join(etcDir, "node_gateway_params.json"),
14341
+ dnsRecordsPath: path.join(etcDir, ZONE_DNS_RECORDS_FILE_NAME),
14342
+ zoneBootOverridePath: path.join(etcDir, `${zoneDid.toRawHostName()}.zone.json`),
14343
+ didJsonPath: paths.didJson,
14344
+ deviceDocJwtPath: paths.deviceDocJwt,
14345
+ deviceMiniDocJwtPath: paths.deviceMiniDocJwt,
14346
+ devicePrivateKeyPath: paths.authenticationPrivateKey,
14347
+ publicDir: paths.publicDir,
14348
+ securityDir: paths.securityDir
14349
+ };
14350
+ }
14351
+ function plannedFiles(rootDir, layout) {
14352
+ const entry = (absolutePath, role, required, secret) => ({
14353
+ path: toPosixRelative(rootDir, absolutePath),
14354
+ role,
14355
+ required,
14356
+ secret,
14357
+ absolutePath,
14358
+ mode: secret ? 384 : 420,
14359
+ dirMode: secret ? 448 : 493
14360
+ });
14361
+ return [
14362
+ entry(layout.devicePrivateKeyPath, "device_private_key", true, true),
14363
+ entry(layout.didJsonPath, "device_document", true, false),
14364
+ entry(layout.deviceDocJwtPath, "device_document_jwt", true, false),
14365
+ entry(layout.deviceMiniDocJwtPath, "device_mini_document_jwt", true, false),
14366
+ entry(layout.zoneBootOverridePath, "zone_boot_override", true, false),
14367
+ entry(layout.zoneDocumentJwtPath, "zone_document_jwt", true, false),
14368
+ entry(layout.dnsRecordsPath, "dns_records", false, false),
14369
+ entry(layout.startConfigPath, "start_config", true, false),
14370
+ entry(layout.gatewayParamsPath, "gateway_params", true, false),
14371
+ entry(layout.nodeIdentityPath, "node_identity", true, false)
14372
+ ];
14373
+ }
14374
+ function publicEntry(file) {
14375
+ return { path: file.path, role: file.role, required: file.required, secret: file.secret };
14376
+ }
14377
+ function readLock(lockPath) {
14378
+ if (!lstatOrNull(lockPath))
14379
+ return null;
14380
+ const base = {
14381
+ path: lockPath,
14382
+ schemaVersion: null,
14383
+ traceId: null,
14384
+ startedAt: null,
14385
+ domain: null,
14386
+ stage: null,
14387
+ committedFiles: [],
14388
+ ownerKeyBackupPath: null,
14389
+ corrupt: false
14390
+ };
14391
+ try {
14392
+ const value = readJsonObjectOrThrow(lockPath);
14393
+ const stage = value.stage;
14394
+ return {
14395
+ ...base,
14396
+ schemaVersion: typeof value.schema_version === "number" ? value.schema_version : null,
14397
+ traceId: typeof value.trace_id === "string" ? value.trace_id : null,
14398
+ startedAt: typeof value.started_at === "number" ? value.started_at : null,
14399
+ domain: typeof value.domain === "string" ? value.domain : null,
14400
+ stage: stage === "locked" || stage === "backup_saved" || stage === "committing" || stage === "committed" ? stage : null,
14401
+ committedFiles: Array.isArray(value.committed) ? value.committed.filter((item) => typeof item === "string") : [],
14402
+ ownerKeyBackupPath: typeof value.owner_key_backup === "string" ? value.owner_key_backup : null
14403
+ };
14404
+ } catch {
14405
+ return { ...base, corrupt: true };
14406
+ }
14407
+ }
14408
+ function acquireLock(lockPath, record) {
14409
+ const fs = requireNode$1("node:fs");
14410
+ try {
14411
+ fs.writeFileSync(lockPath, jsonPretty(record), { mode: 384, flag: "wx" });
14412
+ } catch (error) {
14413
+ if (error.code === "EEXIST") {
14414
+ throw new ActivationError(
14415
+ "ACTIVATION_IN_PROGRESS",
14416
+ `another activation holds the lock ${lockPath}; inspect it with provision status`,
14417
+ { lock_file: lockPath, lock: readLock(lockPath) }
14418
+ );
14419
+ }
14420
+ throw error;
14421
+ }
14422
+ }
14423
+ function updateLock(lockPath, record) {
14424
+ const fs = requireNode$1("node:fs");
14425
+ const path = requireNode$1("node:path");
14426
+ const temporary = path.join(path.dirname(lockPath), `.${ACTIVATION_LOCK_FILE_NAME}.${randomSuffix()}`);
14427
+ try {
14428
+ fs.writeFileSync(temporary, jsonPretty(record), { mode: 384, flag: "wx" });
14429
+ fs.renameSync(temporary, lockPath);
14430
+ } finally {
14431
+ if (lstatOrNull(temporary)) {
14432
+ fs.rmSync(temporary, { force: true });
14433
+ }
14434
+ }
14435
+ }
14436
+ function listSubdirectories(dirPath) {
14437
+ const fs = requireNode$1("node:fs");
14438
+ if (!isDirectory(dirPath))
14439
+ return [];
14440
+ return fs.readdirSync(dirPath, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort();
14441
+ }
14442
+ function listFilesMatching(dirPath, predicate) {
14443
+ const fs = requireNode$1("node:fs");
14444
+ if (!isDirectory(dirPath))
14445
+ return [];
14446
+ return fs.readdirSync(dirPath, { withFileTypes: true }).filter((entry) => entry.isFile() && predicate(entry.name)).map((entry) => entry.name).sort();
14447
+ }
14448
+ function observeIdentity(nodeIdentityPath, problems) {
14449
+ if (!isFile(nodeIdentityPath))
14450
+ return { nodeIdentity: null, zoneDid: null, deviceDid: null };
14451
+ try {
14452
+ const value = readJsonObjectOrThrow(nodeIdentityPath);
14453
+ if (value.schema !== NODE_IDENTITY_SCHEMA_V2) {
14454
+ problems.push({
14455
+ code: "NODE_IDENTITY_SCHEMA",
14456
+ message: `etc/node_identity.json schema '${String(value.schema)}' is not ${NODE_IDENTITY_SCHEMA_V2}`
14457
+ });
14458
+ return { nodeIdentity: value, zoneDid: null, deviceDid: null };
14459
+ }
14460
+ const zoneDid = DID.fromStr(String(value.zone_did));
14461
+ const deviceDid = DID.fromStr(String(value.device_did));
14462
+ return { nodeIdentity: value, zoneDid, deviceDid };
14463
+ } catch (error) {
14464
+ problems.push({
14465
+ code: "NODE_IDENTITY_UNREADABLE",
14466
+ message: `etc/node_identity.json cannot be parsed: ${error instanceof Error ? error.message : String(error)}`
14467
+ });
14468
+ return { nodeIdentity: null, zoneDid: null, deviceDid: null };
14469
+ }
14470
+ }
14471
+ function gatewayParamsIsPlaceholder(gatewayParamsPath) {
14472
+ try {
14473
+ const value = readJsonObjectOrThrow(gatewayParamsPath);
14474
+ const params = value.params;
14475
+ return !!params && typeof params === "object" && params.device_did === UNACTIVATED_GATEWAY_DEVICE_DID;
14476
+ } catch {
14477
+ return false;
14478
+ }
14479
+ }
14480
+ async function validateActivationMaterials(rootDir, layout, identity, problems) {
14481
+ var _a3, _b, _c;
14482
+ const fs = requireNode$1("node:fs");
14483
+ const nodeIdentity = identity.nodeIdentity;
14484
+ if (!nodeIdentity || !identity.zoneDid || !identity.deviceDid) {
14485
+ return { ownerDid: null, accessHostname: null };
14486
+ }
14487
+ const problem = (code, message) => problems.push({ code, message });
14488
+ const zoneDid = identity.zoneDid;
14489
+ const deviceDid = identity.deviceDid;
14490
+ const ownerDid = String(nodeIdentity.owner_did);
14491
+ const ownerKey = nodeIdentity.owner_public_key;
14492
+ if (zoneDid.method !== "web") {
14493
+ problem("IDENTITY_NOT_DID_WEB", `zone DID ${zoneDid.toString()} is not a did:web identity`);
14494
+ }
14495
+ if (ownerDid !== zoneDid.toString()) {
14496
+ problem("OWNER_ZONE_MISMATCH", `owner DID ${ownerDid} differs from zone DID ${zoneDid.toString()}`);
14497
+ }
14498
+ const expectedDeviceDid = buildDeviceDid(String(nodeIdentity.device_name), zoneDid).toString();
14499
+ if (deviceDid.toString() !== expectedDeviceDid) {
14500
+ problem("DEVICE_DID_MISMATCH", `device DID ${deviceDid.toString()} is not ${expectedDeviceDid}`);
14501
+ }
14502
+ if (!ownerKey || typeof ownerKey !== "object" || typeof ownerKey.x !== "string") {
14503
+ problem("OWNER_KEY_MISSING", "etc/node_identity.json owner_public_key is not an Ed25519 JWK");
14504
+ return { ownerDid, accessHostname: null };
14505
+ }
14506
+ let startConfig = null;
14507
+ try {
14508
+ startConfig = readJsonObjectOrThrow(layout.startConfigPath);
14509
+ } catch (error) {
14510
+ problem("START_CONFIG_UNREADABLE", `etc/start_config.json cannot be parsed: ${error instanceof Error ? error.message : String(error)}`);
14511
+ }
14512
+ let accessHostname = null;
14513
+ const jwts = {};
14514
+ if (startConfig) {
14515
+ for (const key of ["user_name", "zone_name", "access_hostname", "admin_password_hash"]) {
14516
+ if (typeof startConfig[key] !== "string" || startConfig[key].length === 0) {
14517
+ problem("START_CONFIG_FIELD", `etc/start_config.json is missing ${key}`);
14518
+ }
14519
+ }
14520
+ for (const key of ["boot_config_jwt", "device_doc_jwt", "device_mini_doc_jwt", "zone_document_jwt", "ood_jwt"]) {
14521
+ if (typeof startConfig[key] === "string" && startConfig[key].length > 0) {
14522
+ jwts[key] = startConfig[key];
14523
+ } else {
14524
+ problem("START_CONFIG_FIELD", `etc/start_config.json is missing ${key}`);
14525
+ }
14526
+ }
14527
+ if (startConfig.zone_name !== zoneDid.toString()) {
14528
+ problem("START_CONFIG_ZONE", `etc/start_config.json zone_name is not ${zoneDid.toString()}`);
14529
+ }
14530
+ if (typeof startConfig.access_hostname === "string") {
14531
+ accessHostname = startConfig.access_hostname;
14532
+ if (accessHostname !== zoneDid.toRawHostName()) {
14533
+ problem("START_CONFIG_HOSTNAME", `etc/start_config.json access_hostname is not ${zoneDid.toRawHostName()}`);
14534
+ }
14535
+ }
14536
+ const ownerDocument = startConfig.owner_document;
14537
+ if (!ownerDocument || typeof ownerDocument !== "object") {
14538
+ problem("START_CONFIG_OWNER", "etc/start_config.json is missing owner_document");
14539
+ } else {
14540
+ if (ownerDocument.id !== ownerDid) {
14541
+ problem("START_CONFIG_OWNER", `owner_document id ${String(ownerDocument.id)} is not ${ownerDid}`);
14542
+ }
14543
+ const method = Array.isArray(ownerDocument.verificationMethod) ? ownerDocument.verificationMethod.find((item) => item && item.id === "#main_key") : void 0;
14544
+ if (!method || getXFromJwkSafe(method.publicKeyJwk) !== ownerKey.x) {
14545
+ problem("START_CONFIG_OWNER_KEY", "owner_document #main_key differs from node_identity owner_public_key");
14546
+ }
14547
+ }
14548
+ if (jwts.ood_jwt && jwts.device_doc_jwt && jwts.ood_jwt !== jwts.device_doc_jwt) {
14549
+ problem("START_CONFIG_OOD_JWT", "etc/start_config.json ood_jwt differs from device_doc_jwt");
14550
+ }
14551
+ for (const [key, secret] of [["private_key", true], ["device_private_key", true], ["sn_access_token", true], ["bns_evm_private_key", true]]) {
14552
+ if (secret && key in startConfig)
14553
+ problem("START_CONFIG_SECRET", `etc/start_config.json must not contain ${key}`);
14554
+ }
14555
+ }
14556
+ const readText = (filePath, label) => {
14557
+ try {
14558
+ return String(fs.readFileSync(filePath, "utf8"));
14559
+ } catch (error) {
14560
+ problem("FILE_UNREADABLE", `${label} cannot be read: ${error instanceof Error ? error.message : String(error)}`);
14561
+ return null;
14562
+ }
14563
+ };
14564
+ const zoneDocumentJwt = ((_a3 = readText(layout.zoneDocumentJwtPath, "etc/zone_document.jwt")) == null ? void 0 : _a3.trim()) ?? null;
14565
+ const deviceDocJwt = ((_b = readText(layout.deviceDocJwtPath, toPosixRelative(rootDir, layout.deviceDocJwtPath))) == null ? void 0 : _b.trim()) ?? null;
14566
+ const deviceMiniDocJwt = ((_c = readText(layout.deviceMiniDocJwtPath, toPosixRelative(rootDir, layout.deviceMiniDocJwtPath))) == null ? void 0 : _c.trim()) ?? null;
14567
+ if (zoneDocumentJwt && jwts.zone_document_jwt && zoneDocumentJwt !== jwts.zone_document_jwt) {
14568
+ problem("ZONE_DOCUMENT_MISMATCH", "etc/zone_document.jwt differs from start_config zone_document_jwt");
14569
+ }
14570
+ if (deviceDocJwt && jwts.device_doc_jwt && deviceDocJwt !== jwts.device_doc_jwt) {
14571
+ problem("DEVICE_DOCUMENT_MISMATCH", "device_doc.jwt differs from start_config device_doc_jwt");
14572
+ }
14573
+ if (deviceMiniDocJwt && jwts.device_mini_doc_jwt && deviceMiniDocJwt !== jwts.device_mini_doc_jwt) {
14574
+ problem("DEVICE_MINI_DOCUMENT_MISMATCH", "device_mini_doc.jwt differs from start_config device_mini_doc_jwt");
14575
+ }
14576
+ const verify = async (jwt, label) => {
14577
+ if (!jwt)
14578
+ return null;
14579
+ try {
14580
+ return await verifyJwtEdDSA(jwt, ownerKey);
14581
+ } catch (error) {
14582
+ problem("JWT_SIGNATURE", `${label} is not signed by the owner key: ${error instanceof Error ? error.message : String(error)}`);
14583
+ return null;
14584
+ }
14585
+ };
14586
+ const boot = await verify(jwts.boot_config_jwt ?? null, "boot_config_jwt");
14587
+ const device = await verify(deviceDocJwt ?? jwts.device_doc_jwt ?? null, "device_doc.jwt");
14588
+ const mini = await verify(deviceMiniDocJwt ?? jwts.device_mini_doc_jwt ?? null, "device_mini_doc.jwt");
14589
+ const zone = await verify(zoneDocumentJwt ?? jwts.zone_document_jwt ?? null, "zone_document.jwt");
14590
+ const now = buckyosGetUnixTimestamp();
14591
+ if (boot) {
14592
+ if (boot.id !== zoneDid.toString())
14593
+ problem("BOOT_DOCUMENT", "boot document id differs from zone DID");
14594
+ if (!Array.isArray(boot.oods) || boot.oods.length === 0)
14595
+ problem("BOOT_DOCUMENT", "boot document has no oods");
14596
+ if ("sn" in boot)
14597
+ problem("BOOT_DOCUMENT", "boot document must not reference an SN in offline mode");
14598
+ if (typeof boot.exp !== "number" || boot.exp <= now)
14599
+ problem("BOOT_DOCUMENT", "boot document has expired");
14600
+ try {
14601
+ const override = readJsonObjectOrThrow(layout.zoneBootOverridePath);
14602
+ if (JSON.stringify(override) !== JSON.stringify(boot)) {
14603
+ problem("BOOT_OVERRIDE_MISMATCH", `${toPosixRelative(rootDir, layout.zoneBootOverridePath)} differs from boot_config_jwt`);
14604
+ }
14605
+ } catch (error) {
14606
+ problem("BOOT_OVERRIDE_UNREADABLE", `local boot override cannot be parsed: ${error instanceof Error ? error.message : String(error)}`);
14607
+ }
14608
+ }
14609
+ let deviceKeyX = null;
14610
+ if (device) {
14611
+ if (device.id !== deviceDid.toString())
14612
+ problem("DEVICE_DOCUMENT", "device document id differs from node_identity device_did");
14613
+ if (device.owner !== ownerDid)
14614
+ problem("DEVICE_DOCUMENT", "device document owner differs from owner DID");
14615
+ if (device.zone_did !== zoneDid.toString())
14616
+ problem("DEVICE_DOCUMENT", "device document zone_did differs from zone DID");
14617
+ if (typeof device.exp !== "number" || device.exp <= now)
14618
+ problem("DEVICE_DOCUMENT", "device document has expired");
14619
+ const method = Array.isArray(device.verificationMethod) ? device.verificationMethod.find((item) => item && item.id === "#main_key") : void 0;
14620
+ deviceKeyX = method ? getXFromJwkSafe(method.publicKeyJwk) : null;
14621
+ if (!deviceKeyX)
14622
+ problem("DEVICE_DOCUMENT", "device document has no #main_key");
14623
+ try {
14624
+ const didJson = readJsonObjectOrThrow(layout.didJsonPath);
14625
+ if (didJson.id !== deviceDid.toString()) {
14626
+ problem("DEVICE_DID_JSON", `${toPosixRelative(rootDir, layout.didJsonPath)} id differs from node_identity device_did`);
14627
+ }
14628
+ } catch (error) {
14629
+ problem("DEVICE_DID_JSON", `device did.json cannot be parsed: ${error instanceof Error ? error.message : String(error)}`);
14630
+ }
14631
+ }
14632
+ if (mini && deviceKeyX && mini.x !== deviceKeyX) {
14633
+ problem("DEVICE_MINI_DOCUMENT", "device mini document key differs from device document key");
14634
+ }
14635
+ if (zone) {
14636
+ if (zone.id !== zoneDid.toString())
14637
+ problem("ZONE_DOCUMENT", "zone document id differs from zone DID");
14638
+ if (zone.owner !== ownerDid)
14639
+ problem("ZONE_DOCUMENT", "zone document owner differs from owner DID");
14640
+ if (jwts.boot_config_jwt && zone.boot_jwt !== jwts.boot_config_jwt) {
14641
+ problem("ZONE_DOCUMENT", "zone document boot_jwt differs from start_config boot_config_jwt");
14642
+ }
14643
+ if ("sn" in zone)
14644
+ problem("ZONE_DOCUMENT", "zone document must not reference an SN in offline mode");
14645
+ }
14646
+ if (deviceKeyX) {
14647
+ try {
14648
+ const pem = String(fs.readFileSync(layout.devicePrivateKeyPath, "utf8"));
14649
+ const x = await getPublicKeyXFromPrivatePem(pem);
14650
+ if (x !== deviceKeyX)
14651
+ problem("DEVICE_PRIVATE_KEY", "device private key does not match the device document key");
14652
+ } catch (error) {
14653
+ problem("DEVICE_PRIVATE_KEY", `device private key cannot be loaded: ${error instanceof Error ? error.message : String(error)}`);
14654
+ }
14655
+ if (!isWindows()) {
14656
+ const stats = lstatOrNull(layout.devicePrivateKeyPath);
14657
+ if (stats && (stats.mode & 63) !== 0) {
14658
+ problem("DEVICE_PRIVATE_KEY_MODE", "device private key is readable by other users");
14659
+ }
14660
+ }
14661
+ }
14662
+ return { ownerDid, accessHostname };
14663
+ }
14664
+ function getXFromJwkSafe(jwk) {
14665
+ try {
14666
+ return getXFromJwk(jwk);
14667
+ } catch {
14668
+ return null;
14669
+ }
14670
+ }
14671
+ async function inspectActivationRoot(rootDir) {
14672
+ const path = requireNode$1("node:path");
14673
+ const root = path.resolve(String(rootDir ?? ""));
14674
+ const observedAt = buckyosGetUnixTimestamp();
14675
+ const problems = [];
14676
+ const warnings = [];
14677
+ const base = (state) => ({
14678
+ rootDir: root,
14679
+ state,
14680
+ startupRequired: state === "configured",
14681
+ observedAt,
14682
+ zoneDid: null,
14683
+ ownerDid: null,
14684
+ deviceDid: null,
14685
+ deviceName: null,
14686
+ accessHostname: null,
14687
+ files: [],
14688
+ problems,
14689
+ warnings,
14690
+ lock: null
14691
+ });
14692
+ if (!isDirectory(root)) {
14693
+ problems.push({ code: "ROOT_MISSING", message: `BUCKYOS_ROOT does not exist: ${root}` });
14694
+ return base("not_installed");
14695
+ }
14696
+ const missingDirs = ["bin", "etc"].filter((name) => !isDirectory(path.join(root, name)));
14697
+ if (missingDirs.length > 0) {
14698
+ for (const name of missingDirs) {
14699
+ problems.push({ code: "INSTALL_INCOMPLETE", message: `installed BuckyOS directory is missing: ${path.join(root, name)}` });
14700
+ }
14701
+ return base("not_installed");
14702
+ }
14703
+ const etcDir = path.join(root, "etc");
14704
+ const lock = readLock(path.join(etcDir, ACTIVATION_LOCK_FILE_NAME));
14705
+ const identity = observeIdentity(path.join(etcDir, "node_identity.json"), problems);
14706
+ let zoneDid = identity.zoneDid;
14707
+ let deviceDid = identity.deviceDid;
14708
+ const publicDirs = listSubdirectories(path.join(root, "local", "identity"));
14709
+ const securityDirs = listSubdirectories(path.join(root, "security"));
14710
+ const zoneJsonFiles = listFilesMatching(etcDir, (name) => name.endsWith(".zone.json"));
14711
+ if (!zoneDid && zoneJsonFiles.length > 0) {
14712
+ const host = zoneJsonFiles[0].slice(0, -".zone.json".length);
14713
+ if (!host.endsWith(".did"))
14714
+ zoneDid = new DID("web", host);
14715
+ }
14716
+ if (!deviceDid && zoneDid)
14717
+ deviceDid = buildDeviceDid(ACTIVATION_DEVICE_NAME, zoneDid);
14718
+ const strayIdentityDirs = [...publicDirs, ...securityDirs].filter(
14719
+ (name) => !deviceDid || name !== path.basename(deviceIdentityPathsForRoots(identityRootsFor(root), deviceDid).publicDir)
14720
+ );
14721
+ const files = [];
14722
+ let layout = null;
14723
+ if (zoneDid && deviceDid) {
14724
+ layout = layoutFor(root, zoneDid, deviceDid);
14725
+ for (const planned of plannedFiles(root, layout)) {
14726
+ files.push({ ...publicEntry(planned), present: isFile(planned.absolutePath) });
14727
+ }
14728
+ } else {
14729
+ for (const [name, role, required] of [
14730
+ ["node_identity.json", "node_identity", true],
14731
+ ["start_config.json", "start_config", true],
14732
+ [ZONE_DOCUMENT_JWT_FILE_NAME, "zone_document_jwt", true],
14733
+ ["node_gateway_params.json", "gateway_params", true],
14734
+ [ZONE_DNS_RECORDS_FILE_NAME, "dns_records", false]
14735
+ ]) {
14736
+ files.push({ path: `etc/${name}`, role, required, secret: false, present: isFile(path.join(etcDir, name)) });
14737
+ }
14738
+ for (const name of zoneJsonFiles) {
14739
+ files.push({ path: `etc/${name}`, role: "zone_boot_override", required: true, secret: false, present: true });
14740
+ }
14741
+ }
14742
+ const gatewayEntry = files.find((file) => file.role === "gateway_params");
14743
+ const gatewayPlaceholder = (gatewayEntry == null ? void 0 : gatewayEntry.present) === true && gatewayParamsIsPlaceholder(path.join(etcDir, "node_gateway_params.json"));
14744
+ if (gatewayEntry && gatewayPlaceholder)
14745
+ gatewayEntry.present = false;
14746
+ const markers = files.filter((file) => file.present && file.role !== "dns_records");
14747
+ const requiredMissing = files.filter((file) => file.required && !file.present);
14748
+ const status = base("unactivated");
14749
+ status.files = files;
14750
+ status.lock = lock;
14751
+ status.zoneDid = (zoneDid == null ? void 0 : zoneDid.toString()) ?? null;
14752
+ status.deviceDid = (deviceDid == null ? void 0 : deviceDid.toString()) ?? null;
14753
+ status.deviceName = identity.nodeIdentity ? String(identity.nodeIdentity.device_name) : deviceDid ? ACTIVATION_DEVICE_NAME : null;
14754
+ status.ownerDid = identity.nodeIdentity ? String(identity.nodeIdentity.owner_did) : null;
14755
+ if (lock) {
14756
+ warnings.push(
14757
+ lock.corrupt ? `activation lock ${lock.path} exists but cannot be parsed` : `activation lock ${lock.path} exists (stage=${lock.stage ?? "unknown"}, committed=${lock.committedFiles.length})`
14758
+ );
14759
+ }
14760
+ for (const name of strayIdentityDirs) {
14761
+ warnings.push(`unexpected identity directory: ${name}`);
14762
+ }
14763
+ if (markers.length === 0 && strayIdentityDirs.length === 0 && !lock && problems.length === 0) {
14764
+ status.state = "unactivated";
14765
+ return status;
14766
+ }
14767
+ if (requiredMissing.length > 0 || !layout || strayIdentityDirs.length > 0 && markers.length === 0) {
14768
+ for (const file of requiredMissing) {
14769
+ problems.push({ code: "FILE_MISSING", message: `${file.path} is missing` });
14770
+ }
14771
+ status.state = "partial";
14772
+ return status;
14773
+ }
14774
+ const validated = await validateActivationMaterials(root, layout, identity, problems);
14775
+ status.ownerDid = validated.ownerDid ?? status.ownerDid;
14776
+ status.accessHostname = validated.accessHostname ?? (zoneDid ? zoneDid.toRawHostName() : null);
14777
+ if (lock && lock.stage !== "committed") {
14778
+ problems.push({
14779
+ code: "ACTIVATION_INTERRUPTED",
14780
+ message: `activation lock is still at stage ${lock.stage ?? "unknown"}; the commit did not finish cleanly`
14781
+ });
14782
+ }
14783
+ status.state = problems.length === 0 ? "configured" : "invalid";
14784
+ status.startupRequired = status.state === "configured";
14785
+ return status;
14786
+ }
14787
+ function inspectOwnerKeyBackupTarget(backupPath, problems) {
14788
+ const path = requireNode$1("node:path");
14789
+ const directory = path.dirname(backupPath);
14790
+ const directoryExists = isDirectory(directory);
14791
+ const exists = lstatOrNull(backupPath) !== null;
14792
+ if (!directoryExists) {
14793
+ problems.push({
14794
+ code: "OWNER_KEY_BACKUP_DIRECTORY_MISSING",
14795
+ message: `owner key backup directory does not exist: ${directory}`
14796
+ });
14797
+ }
14798
+ if (exists) {
14799
+ problems.push({ code: "OWNER_KEY_BACKUP_EXISTS", message: `owner key backup already exists: ${backupPath}` });
14800
+ }
14801
+ return { path: backupPath, directoryExists, exists };
14802
+ }
14803
+ function targetStateProblem(status) {
14804
+ if (status.lock) {
14805
+ return {
14806
+ code: "ACTIVATION_IN_PROGRESS",
14807
+ message: `activation lock exists: ${status.lock.path} (stage=${status.lock.stage ?? "unknown"})`
14808
+ };
14809
+ }
14810
+ switch (status.state) {
14811
+ case "unactivated":
14812
+ return null;
14813
+ case "not_installed":
14814
+ return { code: "TARGET_NOT_INSTALLED", message: `BuckyOS is not installed at ${status.rootDir}` };
14815
+ case "configured":
14816
+ return { code: "TARGET_ALREADY_ACTIVATED", message: `BuckyOS is already activated at ${status.rootDir}` };
14817
+ case "invalid":
14818
+ return {
14819
+ code: "TARGET_ALREADY_ACTIVATED",
14820
+ message: `BuckyOS activation material exists but is invalid at ${status.rootDir}; it is not overwritten`
14821
+ };
14822
+ case "partial":
14823
+ return {
14824
+ code: "TARGET_PARTIALLY_ACTIVATED",
14825
+ message: `BuckyOS is partially activated at ${status.rootDir}; remove the partial material or reinstall`
14826
+ };
14827
+ }
14828
+ }
14829
+ async function checkOfflineActivation(options) {
14830
+ const params = normalizeActivationParams(options);
14831
+ const status = await inspectActivationRoot(params.rootDir);
14832
+ const problems = [];
14833
+ const stateProblem = targetStateProblem(status);
14834
+ if (stateProblem)
14835
+ problems.push(stateProblem);
14836
+ const ownerKeyBackup = inspectOwnerKeyBackupTarget(params.ownerKeyBackupPath, problems);
14837
+ const layout = layoutFor(params.rootDir, params.zoneDid, params.deviceDid);
14838
+ return {
14839
+ ready: problems.length === 0,
14840
+ rootDir: params.rootDir,
14841
+ state: status.state,
14842
+ domain: params.domain,
14843
+ ownerName: params.ownerName,
14844
+ ownerDid: params.ownerDid.toString(),
14845
+ zoneDid: params.zoneDid.toString(),
14846
+ deviceDid: params.deviceDid.toString(),
14847
+ deviceName: ACTIVATION_DEVICE_NAME,
14848
+ accessHostname: params.domain,
14849
+ rtcpPort: params.rtcpPort,
14850
+ guestAccess: params.guestAccess,
14851
+ publicIp: params.publicIp ?? null,
14852
+ dnsRecords: buildActivationDnsRecords(params.domain, params.publicIp),
14853
+ ownerKeyBackup,
14854
+ plannedFiles: plannedFiles(params.rootDir, layout).map(publicEntry),
14855
+ problems,
14856
+ status
14857
+ };
14858
+ }
14859
+ function precheckError(precheck) {
14860
+ const first = precheck.problems[0];
14861
+ return new ActivationError(
14862
+ precheck.problems.length === 1 ? first.code : "ACTIVATION_PRECHECK_FAILED",
14863
+ precheck.problems.length === 1 ? first.message : `activation precheck failed: ${precheck.problems.map((item) => item.message).join("; ")}`,
14864
+ { state: precheck.state, problems: precheck.problems }
14865
+ );
14866
+ }
14867
+ async function generateMaterial(params, adminPassword, ownerKeyPair, deviceKeyPair) {
14868
+ const now = buckyosGetUnixTimestamp();
14869
+ const exp = now + ACTIVATION_DOCUMENT_VALIDITY_SECONDS;
14870
+ const ownerJwk = createJwkByX(ownerKeyPair.publicKeyX);
14871
+ const deviceJwk = createJwkByX(deviceKeyPair.publicKeyX);
14872
+ const ownerDidStr = params.ownerDid.toString();
14873
+ const zoneDidStr = params.zoneDid.toString();
14874
+ const deviceDidStr = params.deviceDid.toString();
14875
+ const ownerDoc = newOwnerDocument({
14876
+ did: params.ownerDid,
14877
+ name: params.ownerName,
14878
+ displayName: params.ownerName,
14879
+ publicKeyJwk: ownerJwk,
14880
+ now
14881
+ });
14882
+ ownerDoc.exp = exp;
14883
+ ownerDocumentSetDefaultZoneDid(ownerDoc, params.zoneDid);
14884
+ const ownerDocument = ownerDocumentToOrderedJson(ownerDoc);
14885
+ const oodString = oodDescriptionToString(parseOODDescription(`${ACTIVATION_DEVICE_NAME}@${ACTIVATION_NET_ID}`));
14886
+ const bootJwt = await encodeZoneBootDocument(
14887
+ newZoneBootDocument({ id: params.zoneDid, oods: [oodString], exp }),
14888
+ ownerKeyPair.privateKeyPem
14889
+ );
14890
+ const bootDocument = decodeJwtClaimWithoutVerify(bootJwt);
14891
+ const deviceDoc = newDeviceDocumentByJwkWithDid(
14892
+ ACTIVATION_DEVICE_NAME,
14893
+ deviceJwk,
14894
+ params.deviceDid,
14895
+ now
14896
+ );
14897
+ delete deviceDoc.support_container;
14898
+ deviceDoc.owner = ownerDidStr;
14899
+ deviceDoc.zone_did = zoneDidStr;
14900
+ deviceDoc.net_id = ACTIVATION_NET_ID;
14901
+ deviceDoc.rtcp_port = params.rtcpPort;
14902
+ deviceDoc.iat = now;
14903
+ deviceDoc.exp = exp;
14904
+ deviceDoc.version_seq = 0;
14905
+ const deviceDocument = deviceDocumentToOrderedJson(deviceDoc);
14906
+ const deviceDocJwt = await encodeDeviceDocument(deviceDoc, ownerKeyPair.privateKeyPem);
14907
+ const miniDoc = newDeviceMiniDocumentByDeviceDocument(deviceDoc);
14908
+ miniDoc.iat = now;
14909
+ const deviceMiniDocJwt = await deviceMiniDocumentToJwt(miniDoc, ownerKeyPair.privateKeyPem);
14910
+ const zoneDoc = newZoneDocument({
14911
+ id: params.zoneDid,
14912
+ ownerDid: params.ownerDid,
14913
+ publicKeyJwk: ownerJwk,
14914
+ now
14915
+ });
14916
+ zoneDoc.hostname = params.domain;
14917
+ zoneDoc.owner = ownerDidStr;
14918
+ zoneDoc.oods = [oodString];
14919
+ zoneDoc.boot_jwt = bootJwt;
14920
+ zoneDoc.devices = { [ACTIVATION_DEVICE_NAME]: deviceDocument };
14921
+ zoneDoc.mini_device_jwts = { [ACTIVATION_DEVICE_NAME]: deviceMiniDocJwt };
14922
+ zoneDoc.iat = now;
14923
+ zoneDoc.exp = exp;
14924
+ zoneDoc.version_seq = 0;
14925
+ delete zoneDoc.sn;
14926
+ const zoneDocumentJwt = await encodeZoneDocument(zoneDoc, ownerKeyPair.privateKeyPem);
14927
+ if (new TextEncoder().encode(zoneDocumentJwt).length >= MAX_INLINE_DOCUMENT_BYTES) {
14928
+ throw new ActivationError(
14929
+ "ZONE_DOCUMENT_TOO_LARGE",
14930
+ `zone document JWT exceeds the ${MAX_INLINE_DOCUMENT_BYTES} byte inline limit`
14931
+ );
14932
+ }
14933
+ const nodeIdentity = newLocalNodeIdentityConfig({
14934
+ zoneDid: params.zoneDid,
14935
+ ownerDid: params.ownerDid,
14936
+ ownerPublicKey: ownerJwk,
14937
+ deviceName: ACTIVATION_DEVICE_NAME,
14938
+ deviceDid: params.deviceDid,
14939
+ zoneIat: now
14940
+ });
14941
+ const startConfig = {
14942
+ user_name: params.ownerName,
14943
+ owner_document: ownerDocument,
14944
+ zone_name: zoneDidStr,
14945
+ access_hostname: params.domain,
14946
+ zone_document_jwt: zoneDocumentJwt,
14947
+ boot_config_jwt: bootJwt,
14948
+ device_doc_jwt: deviceDocJwt,
14949
+ device_mini_doc_jwt: deviceMiniDocJwt,
14950
+ ood_jwt: deviceDocJwt,
14951
+ admin_password_hash: hashAdminPassword(params.ownerName, adminPassword),
14952
+ guest_access: params.guestAccess,
14953
+ friend_passcode: "",
14954
+ enabled_features: {},
14955
+ ai_provider_config: {},
14956
+ jarvis_msg_tunnel_config: {}
14957
+ };
14958
+ return {
14959
+ documentIat: now,
14960
+ documentExp: exp,
14961
+ ownerDocument,
14962
+ bootDocument,
14963
+ bootJwt,
14964
+ deviceDocument,
14965
+ deviceDocJwt,
14966
+ deviceMiniDocJwt,
14967
+ zoneDocumentJwt,
14968
+ nodeIdentity,
14969
+ startConfig,
14970
+ gatewayParams: { params: { device_did: deviceDidStr } },
14971
+ dnsRecords: buildActivationDnsRecords(params.domain, params.publicIp)
14972
+ };
14973
+ }
14974
+ function fileContent(file, material, deviceKeyPair, domain) {
14975
+ switch (file.role) {
14976
+ case "device_private_key":
14977
+ return deviceKeyPair.privateKeyPem;
14978
+ case "device_document":
14979
+ return jsonPretty(material.deviceDocument);
14980
+ case "device_document_jwt":
14981
+ return material.deviceDocJwt;
14982
+ case "device_mini_document_jwt":
14983
+ return material.deviceMiniDocJwt;
14984
+ case "zone_boot_override":
14985
+ return jsonPretty(material.bootDocument);
14986
+ case "zone_document_jwt":
14987
+ return material.zoneDocumentJwt;
14988
+ case "dns_records":
14989
+ return jsonPretty({ hostname: domain, records: material.dnsRecords });
14990
+ case "start_config":
14991
+ return jsonPretty(material.startConfig);
14992
+ case "gateway_params":
14993
+ return jsonPretty(material.gatewayParams);
14994
+ case "node_identity":
14995
+ return jsonPretty(material.nodeIdentity);
14996
+ }
14997
+ }
14998
+ function saveOwnerKeyBackup(backupPath, privateKeyPem) {
14999
+ const fs = requireNode$1("node:fs");
15000
+ fs.writeFileSync(backupPath, privateKeyPem, { mode: 384, flag: "wx" });
15001
+ if (!isWindows()) {
15002
+ fs.chmodSync(backupPath, 384);
15003
+ }
15004
+ }
15005
+ function recoverySteps(lockPath, backupPath, committed) {
15006
+ return [
15007
+ `inspect the target with: buckyos provision status --root <root>`,
15008
+ committed.length > 0 ? `the following files were committed and are left in place: ${committed.join(", ")}` : "no activation file was committed to the target root",
15009
+ `the owner private key backup was saved at ${backupPath}; keep it or delete it before retrying (the backup path must not exist)`,
15010
+ `remove the committed files and the lock file ${lockPath} (or reinstall BuckyOS), then run provision check again`,
15011
+ "the generated identity is not deleted or regenerated automatically"
15012
+ ];
15013
+ }
15014
+ async function activateOfflineZone(options) {
15015
+ const fs = requireNode$1("node:fs");
15016
+ const params = normalizeActivationParams(options);
15017
+ const adminPassword = validateAdminPassword(options.adminPassword);
15018
+ const precheck = await checkOfflineActivation(options);
15019
+ if (!precheck.ready)
15020
+ throw precheckError(precheck);
15021
+ const ownerKeyPair = options.ownerKeyPair ?? generateProvisionKeyPair();
15022
+ const deviceKeyPair = options.deviceKeyPair ?? generateProvisionKeyPair();
15023
+ const material = await generateMaterial(params, adminPassword, ownerKeyPair, deviceKeyPair);
15024
+ const layout = layoutFor(params.rootDir, params.zoneDid, params.deviceDid);
15025
+ const files = plannedFiles(params.rootDir, layout);
15026
+ const warnings = [];
15027
+ const lockRecord = {
15028
+ schema_version: ACTIVATION_LOCK_SCHEMA_VERSION,
15029
+ trace_id: options.traceId ?? null,
15030
+ started_at: buckyosGetUnixTimestamp(),
15031
+ domain: params.domain,
15032
+ owner_key_backup: params.ownerKeyBackupPath,
15033
+ stage: "locked",
15034
+ committed: []
15035
+ };
15036
+ acquireLock(layout.lockPath, lockRecord);
15037
+ const releaseLock = () => {
15038
+ try {
15039
+ fs.rmSync(layout.lockPath, { force: true });
15040
+ } catch {
15041
+ }
15042
+ };
15043
+ let recheck;
15044
+ try {
15045
+ recheck = await checkOfflineActivation(options);
15046
+ } catch (error) {
15047
+ releaseLock();
15048
+ throw error;
15049
+ }
15050
+ const recheckProblems = recheck.problems.filter((problem) => problem.code !== "ACTIVATION_IN_PROGRESS");
15051
+ if (recheckProblems.length > 0) {
15052
+ releaseLock();
15053
+ throw precheckError({ ...recheck, problems: recheckProblems });
15054
+ }
15055
+ for (const file of files) {
15056
+ if (lstatOrNull(file.absolutePath)) {
15057
+ releaseLock();
15058
+ throw new ActivationError(
15059
+ "TARGET_PARTIALLY_ACTIVATED",
15060
+ `activation file already exists: ${file.path}`,
15061
+ { state: "partial", problems: [{ code: "FILE_EXISTS", message: `${file.path} already exists` }] }
15062
+ );
15063
+ }
15064
+ }
15065
+ try {
15066
+ saveOwnerKeyBackup(params.ownerKeyBackupPath, ownerKeyPair.privateKeyPem);
15067
+ } catch (error) {
15068
+ releaseLock();
15069
+ const code = error.code;
15070
+ throw new ActivationError(
15071
+ code === "EEXIST" ? "OWNER_KEY_BACKUP_EXISTS" : "OWNER_KEY_BACKUP_FAILED",
15072
+ `owner key backup could not be saved: ${error instanceof Error ? error.message : String(error)}`,
15073
+ { owner_key_backup: { path: params.ownerKeyBackupPath, saved: false }, committed_files: [] }
15074
+ );
15075
+ }
15076
+ lockRecord.stage = "backup_saved";
15077
+ const committed = [];
15078
+ const failCommit = (error, stage) => {
15079
+ throw new ActivationError(
15080
+ "ACTIVATION_COMMIT_FAILED",
15081
+ `activation commit failed at ${stage}: ${error instanceof Error ? error.message : String(error)}`,
15082
+ {
15083
+ stage,
15084
+ owner_key_backup: { path: params.ownerKeyBackupPath, saved: true },
15085
+ committed_files: committed,
15086
+ lock_file: layout.lockPath,
15087
+ recovery: recoverySteps(layout.lockPath, params.ownerKeyBackupPath, committed.map((file) => file.path))
15088
+ }
15089
+ );
15090
+ };
15091
+ try {
15092
+ updateLock(layout.lockPath, lockRecord);
15093
+ } catch (error) {
15094
+ failCommit(error, "lock:backup_saved");
15095
+ }
15096
+ lockRecord.stage = "committing";
15097
+ for (const file of files) {
15098
+ const content = fileContent(file, material, deviceKeyPair, params.domain);
15099
+ try {
15100
+ commitFile(file.absolutePath, content, file.mode, file.dirMode);
15101
+ } catch (error) {
15102
+ failCommit(error, file.path);
15103
+ }
15104
+ committed.push({ ...publicEntry(file), sha256: file.secret ? null : sha256Hex(content) });
15105
+ lockRecord.committed = committed.map((item) => item.path);
15106
+ try {
15107
+ updateLock(layout.lockPath, lockRecord);
15108
+ } catch (error) {
15109
+ failCommit(error, `lock:${file.path}`);
15110
+ }
15111
+ }
15112
+ lockRecord.stage = "committed";
15113
+ try {
15114
+ updateLock(layout.lockPath, lockRecord);
15115
+ fs.rmSync(layout.lockPath, { force: true });
15116
+ } catch (error) {
15117
+ warnings.push(
15118
+ `activation completed but the lock ${layout.lockPath} could not be removed: ${error instanceof Error ? error.message : String(error)}`
15119
+ );
15120
+ }
15121
+ return {
15122
+ rootDir: params.rootDir,
15123
+ state: "configured",
15124
+ startupRequired: true,
15125
+ domain: params.domain,
15126
+ ownerName: params.ownerName,
15127
+ ownerDid: params.ownerDid.toString(),
15128
+ zoneDid: params.zoneDid.toString(),
15129
+ deviceDid: params.deviceDid.toString(),
15130
+ deviceName: ACTIVATION_DEVICE_NAME,
15131
+ accessHostname: params.domain,
15132
+ rtcpPort: params.rtcpPort,
15133
+ guestAccess: params.guestAccess,
15134
+ publicIp: params.publicIp ?? null,
15135
+ documentIat: material.documentIat,
15136
+ documentExp: material.documentExp,
15137
+ ownerKeyBackup: { path: params.ownerKeyBackupPath, saved: true },
15138
+ dnsRecords: material.dnsRecords,
15139
+ committedFiles: committed,
15140
+ warnings
15141
+ };
15142
+ }
14114
15143
  const PROVISION_BASE_TIME = 1743478939;
14115
15144
  const DEFAULT_EXP_YEARS = 10;
14116
15145
  const PROVISION_DEFAULT_EXP = PROVISION_BASE_TIME + 3600 * 24 * 365 * DEFAULT_EXP_YEARS;
@@ -14831,6 +15860,13 @@ function buildDidDocs(outputDir, options) {
14831
15860
  return written;
14832
15861
  }
14833
15862
  export {
15863
+ ACTIVATION_DEFAULT_RTCP_PORT,
15864
+ ACTIVATION_DEVICE_NAME,
15865
+ ACTIVATION_DOCUMENT_VALIDITY_SECONDS,
15866
+ ACTIVATION_LOCK_FILE_NAME,
15867
+ ACTIVATION_MIN_PASSWORD_LENGTH,
15868
+ ACTIVATION_NET_ID,
15869
+ ActivationError,
14834
15870
  DEVICE_DOC_JWT_FILE_NAME,
14835
15871
  DEVICE_MINI_DOC_JWT_FILE_NAME,
14836
15872
  DEV_TEST_EVM_ACCOUNTS,
@@ -14850,11 +15886,16 @@ export {
14850
15886
  NODE_IDENTITY_SCHEMA_V2,
14851
15887
  PROVISION_BASE_TIME,
14852
15888
  PROVISION_DEFAULT_EXP,
15889
+ ZONE_DNS_RECORDS_FILE_NAME,
15890
+ ZONE_DOCUMENT_JWT_FILE_NAME,
15891
+ activateOfflineZone,
14853
15892
  assertProvisionRuntime,
14854
15893
  bindDeviceDocumentDid,
15894
+ buildActivationDnsRecords,
14855
15895
  buildDeviceDid,
14856
15896
  buildDidDocs,
14857
15897
  calcPkgMetaObjId,
15898
+ checkOfflineActivation,
14858
15899
  createCa,
14859
15900
  createCertFromCa,
14860
15901
  createIdentityCertFromCa,
@@ -14871,13 +15912,16 @@ export {
14871
15912
  didWebDocumentUrl,
14872
15913
  encodeIdentityDirName,
14873
15914
  ensureCa,
15915
+ generateProvisionKeyPair,
14874
15916
  getDevTestEvmAccountByIndex,
14875
15917
  getDevTestEvmAccountByUsername,
14876
15918
  getDevTestKeyPairById,
14877
15919
  getDevTestKeyPairByIndex,
15920
+ hashAdminPassword,
14878
15921
  identityDirName,
14879
15922
  identityFileName,
14880
15923
  identityRawHostUri,
15924
+ inspectActivationRoot,
14881
15925
  loadDeviceDocJwtForRoots,
14882
15926
  loadDeviceMiniDocJwtForRoots,
14883
15927
  loadLocalDeviceDocumentForRoots,
@@ -14892,6 +15936,11 @@ export {
14892
15936
  saveNodeGatewayParams,
14893
15937
  setPkgMeta,
14894
15938
  uniqueNameToDid,
15939
+ validateAdminPassword,
15940
+ validateOwnerName,
15941
+ validatePublicIp,
15942
+ validateRtcpPort,
15943
+ validateZoneDomain,
14895
15944
  versionToInt
14896
15945
  };
14897
15946
  //# sourceMappingURL=provision.mjs.map