@toon-protocol/townhouse 0.1.0 → 0.1.2

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.
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/constants.js","../../../node_modules/.pnpm/node-gyp-build@4.8.4/node_modules/node-gyp-build/node-gyp-build.js","../../../node_modules/.pnpm/node-gyp-build@4.8.4/node_modules/node-gyp-build/index.js","../../../node_modules/.pnpm/bufferutil@4.1.0/node_modules/bufferutil/fallback.js","../../../node_modules/.pnpm/bufferutil@4.1.0/node_modules/bufferutil/index.js","../../../node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/buffer-util.js","../../../node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/limiter.js","../../../node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/permessage-deflate.js","../../../node_modules/.pnpm/utf-8-validate@5.0.10/node_modules/utf-8-validate/fallback.js","../../../node_modules/.pnpm/utf-8-validate@5.0.10/node_modules/utf-8-validate/index.js","../../../node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/validation.js","../../../node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/receiver.js","../../../node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/sender.js","../../../node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/event-target.js","../../../node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/extension.js","../../../node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/websocket.js","../../../node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/stream.js","../../../node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/subprotocol.js","../../../node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/websocket-server.js","../src/config/defaults.ts","../src/config/validator.ts","../src/config/loader.ts","../src/connector/config-generator.ts","../src/connector/admin-client.ts","../src/docker/orchestrator.ts","../src/connector/transport-probe.ts","../src/connector/hs-config-writer.ts","../src/compose-loader.ts","../src/state/nodes-yaml.ts","../src/reconciler.ts","../src/earnings/snapshot-writer.ts","../src/wallet/storage.ts","../src/wallet/crypto.ts","../src/state/image-manifest.ts","../src/wallet/manager.ts","../src/wallet/ar-cache.ts","../src/earnings/aggregator.ts","../src/earnings/snapshot-reader.ts","../src/registry/peer-type-resolver.ts","../src/api/server.ts","../../../node_modules/.pnpm/ws@8.19.0/node_modules/ws/wrapper.mjs","../src/api/build-app.ts","../src/api/cors.ts","../src/api/routes/nodes.ts","../src/api/routes/wallet.ts","../src/chain/evm-rpc.ts","../src/chain/solana-rpc.ts","../src/chain/mina-graphql.ts","../src/api/routes/wallet-balances.ts","../src/api/routes/wallet-reveal.ts","../src/api/routes/wallet-withdraw.ts","../src/chain/evm-tx.ts","../src/api/config-mutex.ts","../src/api/routes/nodes-patch.ts","../src/api/routes/nodes-lifecycle.ts","../src/api/routes/metrics-ws.ts","../src/api/routes/wizard.ts","../src/api/routes/transport.ts","../src/api/routes/earnings.ts","../src/api/schemas/earnings.ts","../src/api/routes/logs.ts","../src/docker/log-tail.ts","../src/api/wizard-server.ts"],"sourcesContent":["'use strict';\n\nconst BINARY_TYPES = ['nodebuffer', 'arraybuffer', 'fragments'];\nconst hasBlob = typeof Blob !== 'undefined';\n\nif (hasBlob) BINARY_TYPES.push('blob');\n\nmodule.exports = {\n BINARY_TYPES,\n CLOSE_TIMEOUT: 30000,\n EMPTY_BUFFER: Buffer.alloc(0),\n GUID: '258EAFA5-E914-47DA-95CA-C5AB0DC85B11',\n hasBlob,\n kForOnEventAttribute: Symbol('kIsForOnEventAttribute'),\n kListener: Symbol('kListener'),\n kStatusCode: Symbol('status-code'),\n kWebSocket: Symbol('websocket'),\n NOOP: () => {}\n};\n","var fs = require('fs')\nvar path = require('path')\nvar os = require('os')\n\n// Workaround to fix webpack's build warnings: 'the request of a dependency is an expression'\nvar runtimeRequire = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require // eslint-disable-line\n\nvar vars = (process.config && process.config.variables) || {}\nvar prebuildsOnly = !!process.env.PREBUILDS_ONLY\nvar abi = process.versions.modules // TODO: support old node where this is undef\nvar runtime = isElectron() ? 'electron' : (isNwjs() ? 'node-webkit' : 'node')\n\nvar arch = process.env.npm_config_arch || os.arch()\nvar platform = process.env.npm_config_platform || os.platform()\nvar libc = process.env.LIBC || (isAlpine(platform) ? 'musl' : 'glibc')\nvar armv = process.env.ARM_VERSION || (arch === 'arm64' ? '8' : vars.arm_version) || ''\nvar uv = (process.versions.uv || '').split('.')[0]\n\nmodule.exports = load\n\nfunction load (dir) {\n return runtimeRequire(load.resolve(dir))\n}\n\nload.resolve = load.path = function (dir) {\n dir = path.resolve(dir || '.')\n\n try {\n var name = runtimeRequire(path.join(dir, 'package.json')).name.toUpperCase().replace(/-/g, '_')\n if (process.env[name + '_PREBUILD']) dir = process.env[name + '_PREBUILD']\n } catch (err) {}\n\n if (!prebuildsOnly) {\n var release = getFirst(path.join(dir, 'build/Release'), matchBuild)\n if (release) return release\n\n var debug = getFirst(path.join(dir, 'build/Debug'), matchBuild)\n if (debug) return debug\n }\n\n var prebuild = resolve(dir)\n if (prebuild) return prebuild\n\n var nearby = resolve(path.dirname(process.execPath))\n if (nearby) return nearby\n\n var target = [\n 'platform=' + platform,\n 'arch=' + arch,\n 'runtime=' + runtime,\n 'abi=' + abi,\n 'uv=' + uv,\n armv ? 'armv=' + armv : '',\n 'libc=' + libc,\n 'node=' + process.versions.node,\n process.versions.electron ? 'electron=' + process.versions.electron : '',\n typeof __webpack_require__ === 'function' ? 'webpack=true' : '' // eslint-disable-line\n ].filter(Boolean).join(' ')\n\n throw new Error('No native build was found for ' + target + '\\n loaded from: ' + dir + '\\n')\n\n function resolve (dir) {\n // Find matching \"prebuilds/<platform>-<arch>\" directory\n var tuples = readdirSync(path.join(dir, 'prebuilds')).map(parseTuple)\n var tuple = tuples.filter(matchTuple(platform, arch)).sort(compareTuples)[0]\n if (!tuple) return\n\n // Find most specific flavor first\n var prebuilds = path.join(dir, 'prebuilds', tuple.name)\n var parsed = readdirSync(prebuilds).map(parseTags)\n var candidates = parsed.filter(matchTags(runtime, abi))\n var winner = candidates.sort(compareTags(runtime))[0]\n if (winner) return path.join(prebuilds, winner.file)\n }\n}\n\nfunction readdirSync (dir) {\n try {\n return fs.readdirSync(dir)\n } catch (err) {\n return []\n }\n}\n\nfunction getFirst (dir, filter) {\n var files = readdirSync(dir).filter(filter)\n return files[0] && path.join(dir, files[0])\n}\n\nfunction matchBuild (name) {\n return /\\.node$/.test(name)\n}\n\nfunction parseTuple (name) {\n // Example: darwin-x64+arm64\n var arr = name.split('-')\n if (arr.length !== 2) return\n\n var platform = arr[0]\n var architectures = arr[1].split('+')\n\n if (!platform) return\n if (!architectures.length) return\n if (!architectures.every(Boolean)) return\n\n return { name, platform, architectures }\n}\n\nfunction matchTuple (platform, arch) {\n return function (tuple) {\n if (tuple == null) return false\n if (tuple.platform !== platform) return false\n return tuple.architectures.includes(arch)\n }\n}\n\nfunction compareTuples (a, b) {\n // Prefer single-arch prebuilds over multi-arch\n return a.architectures.length - b.architectures.length\n}\n\nfunction parseTags (file) {\n var arr = file.split('.')\n var extension = arr.pop()\n var tags = { file: file, specificity: 0 }\n\n if (extension !== 'node') return\n\n for (var i = 0; i < arr.length; i++) {\n var tag = arr[i]\n\n if (tag === 'node' || tag === 'electron' || tag === 'node-webkit') {\n tags.runtime = tag\n } else if (tag === 'napi') {\n tags.napi = true\n } else if (tag.slice(0, 3) === 'abi') {\n tags.abi = tag.slice(3)\n } else if (tag.slice(0, 2) === 'uv') {\n tags.uv = tag.slice(2)\n } else if (tag.slice(0, 4) === 'armv') {\n tags.armv = tag.slice(4)\n } else if (tag === 'glibc' || tag === 'musl') {\n tags.libc = tag\n } else {\n continue\n }\n\n tags.specificity++\n }\n\n return tags\n}\n\nfunction matchTags (runtime, abi) {\n return function (tags) {\n if (tags == null) return false\n if (tags.runtime && tags.runtime !== runtime && !runtimeAgnostic(tags)) return false\n if (tags.abi && tags.abi !== abi && !tags.napi) return false\n if (tags.uv && tags.uv !== uv) return false\n if (tags.armv && tags.armv !== armv) return false\n if (tags.libc && tags.libc !== libc) return false\n\n return true\n }\n}\n\nfunction runtimeAgnostic (tags) {\n return tags.runtime === 'node' && tags.napi\n}\n\nfunction compareTags (runtime) {\n // Precedence: non-agnostic runtime, abi over napi, then by specificity.\n return function (a, b) {\n if (a.runtime !== b.runtime) {\n return a.runtime === runtime ? -1 : 1\n } else if (a.abi !== b.abi) {\n return a.abi ? -1 : 1\n } else if (a.specificity !== b.specificity) {\n return a.specificity > b.specificity ? -1 : 1\n } else {\n return 0\n }\n }\n}\n\nfunction isNwjs () {\n return !!(process.versions && process.versions.nw)\n}\n\nfunction isElectron () {\n if (process.versions && process.versions.electron) return true\n if (process.env.ELECTRON_RUN_AS_NODE) return true\n return typeof window !== 'undefined' && window.process && window.process.type === 'renderer'\n}\n\nfunction isAlpine (platform) {\n return platform === 'linux' && fs.existsSync('/etc/alpine-release')\n}\n\n// Exposed for unit tests\n// TODO: move to lib\nload.parseTags = parseTags\nload.matchTags = matchTags\nload.compareTags = compareTags\nload.parseTuple = parseTuple\nload.matchTuple = matchTuple\nload.compareTuples = compareTuples\n","const runtimeRequire = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require // eslint-disable-line\nif (typeof runtimeRequire.addon === 'function') { // if the platform supports native resolving prefer that\n module.exports = runtimeRequire.addon.bind(runtimeRequire)\n} else { // else use the runtime version here\n module.exports = require('./node-gyp-build.js')\n}\n","'use strict';\n\n/**\n * Masks a buffer using the given mask.\n *\n * @param {Buffer} source The buffer to mask\n * @param {Buffer} mask The mask to use\n * @param {Buffer} output The buffer where to store the result\n * @param {Number} offset The offset at which to start writing\n * @param {Number} length The number of bytes to mask.\n * @public\n */\nconst mask = (source, mask, output, offset, length) => {\n for (var i = 0; i < length; i++) {\n output[offset + i] = source[i] ^ mask[i & 3];\n }\n};\n\n/**\n * Unmasks a buffer using the given mask.\n *\n * @param {Buffer} buffer The buffer to unmask\n * @param {Buffer} mask The mask to use\n * @public\n */\nconst unmask = (buffer, mask) => {\n // Required until https://github.com/nodejs/node/issues/9006 is resolved.\n const length = buffer.length;\n for (var i = 0; i < length; i++) {\n buffer[i] ^= mask[i & 3];\n }\n};\n\nmodule.exports = { mask, unmask };\n","'use strict';\n\ntry {\n module.exports = require('node-gyp-build')(__dirname);\n} catch (e) {\n module.exports = require('./fallback');\n}\n","'use strict';\n\nconst { EMPTY_BUFFER } = require('./constants');\n\nconst FastBuffer = Buffer[Symbol.species];\n\n/**\n * Merges an array of buffers into a new buffer.\n *\n * @param {Buffer[]} list The array of buffers to concat\n * @param {Number} totalLength The total length of buffers in the list\n * @return {Buffer} The resulting buffer\n * @public\n */\nfunction concat(list, totalLength) {\n if (list.length === 0) return EMPTY_BUFFER;\n if (list.length === 1) return list[0];\n\n const target = Buffer.allocUnsafe(totalLength);\n let offset = 0;\n\n for (let i = 0; i < list.length; i++) {\n const buf = list[i];\n target.set(buf, offset);\n offset += buf.length;\n }\n\n if (offset < totalLength) {\n return new FastBuffer(target.buffer, target.byteOffset, offset);\n }\n\n return target;\n}\n\n/**\n * Masks a buffer using the given mask.\n *\n * @param {Buffer} source The buffer to mask\n * @param {Buffer} mask The mask to use\n * @param {Buffer} output The buffer where to store the result\n * @param {Number} offset The offset at which to start writing\n * @param {Number} length The number of bytes to mask.\n * @public\n */\nfunction _mask(source, mask, output, offset, length) {\n for (let i = 0; i < length; i++) {\n output[offset + i] = source[i] ^ mask[i & 3];\n }\n}\n\n/**\n * Unmasks a buffer using the given mask.\n *\n * @param {Buffer} buffer The buffer to unmask\n * @param {Buffer} mask The mask to use\n * @public\n */\nfunction _unmask(buffer, mask) {\n for (let i = 0; i < buffer.length; i++) {\n buffer[i] ^= mask[i & 3];\n }\n}\n\n/**\n * Converts a buffer to an `ArrayBuffer`.\n *\n * @param {Buffer} buf The buffer to convert\n * @return {ArrayBuffer} Converted buffer\n * @public\n */\nfunction toArrayBuffer(buf) {\n if (buf.length === buf.buffer.byteLength) {\n return buf.buffer;\n }\n\n return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.length);\n}\n\n/**\n * Converts `data` to a `Buffer`.\n *\n * @param {*} data The data to convert\n * @return {Buffer} The buffer\n * @throws {TypeError}\n * @public\n */\nfunction toBuffer(data) {\n toBuffer.readOnly = true;\n\n if (Buffer.isBuffer(data)) return data;\n\n let buf;\n\n if (data instanceof ArrayBuffer) {\n buf = new FastBuffer(data);\n } else if (ArrayBuffer.isView(data)) {\n buf = new FastBuffer(data.buffer, data.byteOffset, data.byteLength);\n } else {\n buf = Buffer.from(data);\n toBuffer.readOnly = false;\n }\n\n return buf;\n}\n\nmodule.exports = {\n concat,\n mask: _mask,\n toArrayBuffer,\n toBuffer,\n unmask: _unmask\n};\n\n/* istanbul ignore else */\nif (!process.env.WS_NO_BUFFER_UTIL) {\n try {\n const bufferUtil = require('bufferutil');\n\n module.exports.mask = function (source, mask, output, offset, length) {\n if (length < 48) _mask(source, mask, output, offset, length);\n else bufferUtil.mask(source, mask, output, offset, length);\n };\n\n module.exports.unmask = function (buffer, mask) {\n if (buffer.length < 32) _unmask(buffer, mask);\n else bufferUtil.unmask(buffer, mask);\n };\n } catch (e) {\n // Continue regardless of the error.\n }\n}\n","'use strict';\n\nconst kDone = Symbol('kDone');\nconst kRun = Symbol('kRun');\n\n/**\n * A very simple job queue with adjustable concurrency. Adapted from\n * https://github.com/STRML/async-limiter\n */\nclass Limiter {\n /**\n * Creates a new `Limiter`.\n *\n * @param {Number} [concurrency=Infinity] The maximum number of jobs allowed\n * to run concurrently\n */\n constructor(concurrency) {\n this[kDone] = () => {\n this.pending--;\n this[kRun]();\n };\n this.concurrency = concurrency || Infinity;\n this.jobs = [];\n this.pending = 0;\n }\n\n /**\n * Adds a job to the queue.\n *\n * @param {Function} job The job to run\n * @public\n */\n add(job) {\n this.jobs.push(job);\n this[kRun]();\n }\n\n /**\n * Removes a job from the queue and runs it if possible.\n *\n * @private\n */\n [kRun]() {\n if (this.pending === this.concurrency) return;\n\n if (this.jobs.length) {\n const job = this.jobs.shift();\n\n this.pending++;\n job(this[kDone]);\n }\n }\n}\n\nmodule.exports = Limiter;\n","'use strict';\n\nconst zlib = require('zlib');\n\nconst bufferUtil = require('./buffer-util');\nconst Limiter = require('./limiter');\nconst { kStatusCode } = require('./constants');\n\nconst FastBuffer = Buffer[Symbol.species];\nconst TRAILER = Buffer.from([0x00, 0x00, 0xff, 0xff]);\nconst kPerMessageDeflate = Symbol('permessage-deflate');\nconst kTotalLength = Symbol('total-length');\nconst kCallback = Symbol('callback');\nconst kBuffers = Symbol('buffers');\nconst kError = Symbol('error');\n\n//\n// We limit zlib concurrency, which prevents severe memory fragmentation\n// as documented in https://github.com/nodejs/node/issues/8871#issuecomment-250915913\n// and https://github.com/websockets/ws/issues/1202\n//\n// Intentionally global; it's the global thread pool that's an issue.\n//\nlet zlibLimiter;\n\n/**\n * permessage-deflate implementation.\n */\nclass PerMessageDeflate {\n /**\n * Creates a PerMessageDeflate instance.\n *\n * @param {Object} [options] Configuration options\n * @param {(Boolean|Number)} [options.clientMaxWindowBits] Advertise support\n * for, or request, a custom client window size\n * @param {Boolean} [options.clientNoContextTakeover=false] Advertise/\n * acknowledge disabling of client context takeover\n * @param {Number} [options.concurrencyLimit=10] The number of concurrent\n * calls to zlib\n * @param {(Boolean|Number)} [options.serverMaxWindowBits] Request/confirm the\n * use of a custom server window size\n * @param {Boolean} [options.serverNoContextTakeover=false] Request/accept\n * disabling of server context takeover\n * @param {Number} [options.threshold=1024] Size (in bytes) below which\n * messages should not be compressed if context takeover is disabled\n * @param {Object} [options.zlibDeflateOptions] Options to pass to zlib on\n * deflate\n * @param {Object} [options.zlibInflateOptions] Options to pass to zlib on\n * inflate\n * @param {Boolean} [isServer=false] Create the instance in either server or\n * client mode\n * @param {Number} [maxPayload=0] The maximum allowed message length\n */\n constructor(options, isServer, maxPayload) {\n this._maxPayload = maxPayload | 0;\n this._options = options || {};\n this._threshold =\n this._options.threshold !== undefined ? this._options.threshold : 1024;\n this._isServer = !!isServer;\n this._deflate = null;\n this._inflate = null;\n\n this.params = null;\n\n if (!zlibLimiter) {\n const concurrency =\n this._options.concurrencyLimit !== undefined\n ? this._options.concurrencyLimit\n : 10;\n zlibLimiter = new Limiter(concurrency);\n }\n }\n\n /**\n * @type {String}\n */\n static get extensionName() {\n return 'permessage-deflate';\n }\n\n /**\n * Create an extension negotiation offer.\n *\n * @return {Object} Extension parameters\n * @public\n */\n offer() {\n const params = {};\n\n if (this._options.serverNoContextTakeover) {\n params.server_no_context_takeover = true;\n }\n if (this._options.clientNoContextTakeover) {\n params.client_no_context_takeover = true;\n }\n if (this._options.serverMaxWindowBits) {\n params.server_max_window_bits = this._options.serverMaxWindowBits;\n }\n if (this._options.clientMaxWindowBits) {\n params.client_max_window_bits = this._options.clientMaxWindowBits;\n } else if (this._options.clientMaxWindowBits == null) {\n params.client_max_window_bits = true;\n }\n\n return params;\n }\n\n /**\n * Accept an extension negotiation offer/response.\n *\n * @param {Array} configurations The extension negotiation offers/reponse\n * @return {Object} Accepted configuration\n * @public\n */\n accept(configurations) {\n configurations = this.normalizeParams(configurations);\n\n this.params = this._isServer\n ? this.acceptAsServer(configurations)\n : this.acceptAsClient(configurations);\n\n return this.params;\n }\n\n /**\n * Releases all resources used by the extension.\n *\n * @public\n */\n cleanup() {\n if (this._inflate) {\n this._inflate.close();\n this._inflate = null;\n }\n\n if (this._deflate) {\n const callback = this._deflate[kCallback];\n\n this._deflate.close();\n this._deflate = null;\n\n if (callback) {\n callback(\n new Error(\n 'The deflate stream was closed while data was being processed'\n )\n );\n }\n }\n }\n\n /**\n * Accept an extension negotiation offer.\n *\n * @param {Array} offers The extension negotiation offers\n * @return {Object} Accepted configuration\n * @private\n */\n acceptAsServer(offers) {\n const opts = this._options;\n const accepted = offers.find((params) => {\n if (\n (opts.serverNoContextTakeover === false &&\n params.server_no_context_takeover) ||\n (params.server_max_window_bits &&\n (opts.serverMaxWindowBits === false ||\n (typeof opts.serverMaxWindowBits === 'number' &&\n opts.serverMaxWindowBits > params.server_max_window_bits))) ||\n (typeof opts.clientMaxWindowBits === 'number' &&\n !params.client_max_window_bits)\n ) {\n return false;\n }\n\n return true;\n });\n\n if (!accepted) {\n throw new Error('None of the extension offers can be accepted');\n }\n\n if (opts.serverNoContextTakeover) {\n accepted.server_no_context_takeover = true;\n }\n if (opts.clientNoContextTakeover) {\n accepted.client_no_context_takeover = true;\n }\n if (typeof opts.serverMaxWindowBits === 'number') {\n accepted.server_max_window_bits = opts.serverMaxWindowBits;\n }\n if (typeof opts.clientMaxWindowBits === 'number') {\n accepted.client_max_window_bits = opts.clientMaxWindowBits;\n } else if (\n accepted.client_max_window_bits === true ||\n opts.clientMaxWindowBits === false\n ) {\n delete accepted.client_max_window_bits;\n }\n\n return accepted;\n }\n\n /**\n * Accept the extension negotiation response.\n *\n * @param {Array} response The extension negotiation response\n * @return {Object} Accepted configuration\n * @private\n */\n acceptAsClient(response) {\n const params = response[0];\n\n if (\n this._options.clientNoContextTakeover === false &&\n params.client_no_context_takeover\n ) {\n throw new Error('Unexpected parameter \"client_no_context_takeover\"');\n }\n\n if (!params.client_max_window_bits) {\n if (typeof this._options.clientMaxWindowBits === 'number') {\n params.client_max_window_bits = this._options.clientMaxWindowBits;\n }\n } else if (\n this._options.clientMaxWindowBits === false ||\n (typeof this._options.clientMaxWindowBits === 'number' &&\n params.client_max_window_bits > this._options.clientMaxWindowBits)\n ) {\n throw new Error(\n 'Unexpected or invalid parameter \"client_max_window_bits\"'\n );\n }\n\n return params;\n }\n\n /**\n * Normalize parameters.\n *\n * @param {Array} configurations The extension negotiation offers/reponse\n * @return {Array} The offers/response with normalized parameters\n * @private\n */\n normalizeParams(configurations) {\n configurations.forEach((params) => {\n Object.keys(params).forEach((key) => {\n let value = params[key];\n\n if (value.length > 1) {\n throw new Error(`Parameter \"${key}\" must have only a single value`);\n }\n\n value = value[0];\n\n if (key === 'client_max_window_bits') {\n if (value !== true) {\n const num = +value;\n if (!Number.isInteger(num) || num < 8 || num > 15) {\n throw new TypeError(\n `Invalid value for parameter \"${key}\": ${value}`\n );\n }\n value = num;\n } else if (!this._isServer) {\n throw new TypeError(\n `Invalid value for parameter \"${key}\": ${value}`\n );\n }\n } else if (key === 'server_max_window_bits') {\n const num = +value;\n if (!Number.isInteger(num) || num < 8 || num > 15) {\n throw new TypeError(\n `Invalid value for parameter \"${key}\": ${value}`\n );\n }\n value = num;\n } else if (\n key === 'client_no_context_takeover' ||\n key === 'server_no_context_takeover'\n ) {\n if (value !== true) {\n throw new TypeError(\n `Invalid value for parameter \"${key}\": ${value}`\n );\n }\n } else {\n throw new Error(`Unknown parameter \"${key}\"`);\n }\n\n params[key] = value;\n });\n });\n\n return configurations;\n }\n\n /**\n * Decompress data. Concurrency limited.\n *\n * @param {Buffer} data Compressed data\n * @param {Boolean} fin Specifies whether or not this is the last fragment\n * @param {Function} callback Callback\n * @public\n */\n decompress(data, fin, callback) {\n zlibLimiter.add((done) => {\n this._decompress(data, fin, (err, result) => {\n done();\n callback(err, result);\n });\n });\n }\n\n /**\n * Compress data. Concurrency limited.\n *\n * @param {(Buffer|String)} data Data to compress\n * @param {Boolean} fin Specifies whether or not this is the last fragment\n * @param {Function} callback Callback\n * @public\n */\n compress(data, fin, callback) {\n zlibLimiter.add((done) => {\n this._compress(data, fin, (err, result) => {\n done();\n callback(err, result);\n });\n });\n }\n\n /**\n * Decompress data.\n *\n * @param {Buffer} data Compressed data\n * @param {Boolean} fin Specifies whether or not this is the last fragment\n * @param {Function} callback Callback\n * @private\n */\n _decompress(data, fin, callback) {\n const endpoint = this._isServer ? 'client' : 'server';\n\n if (!this._inflate) {\n const key = `${endpoint}_max_window_bits`;\n const windowBits =\n typeof this.params[key] !== 'number'\n ? zlib.Z_DEFAULT_WINDOWBITS\n : this.params[key];\n\n this._inflate = zlib.createInflateRaw({\n ...this._options.zlibInflateOptions,\n windowBits\n });\n this._inflate[kPerMessageDeflate] = this;\n this._inflate[kTotalLength] = 0;\n this._inflate[kBuffers] = [];\n this._inflate.on('error', inflateOnError);\n this._inflate.on('data', inflateOnData);\n }\n\n this._inflate[kCallback] = callback;\n\n this._inflate.write(data);\n if (fin) this._inflate.write(TRAILER);\n\n this._inflate.flush(() => {\n const err = this._inflate[kError];\n\n if (err) {\n this._inflate.close();\n this._inflate = null;\n callback(err);\n return;\n }\n\n const data = bufferUtil.concat(\n this._inflate[kBuffers],\n this._inflate[kTotalLength]\n );\n\n if (this._inflate._readableState.endEmitted) {\n this._inflate.close();\n this._inflate = null;\n } else {\n this._inflate[kTotalLength] = 0;\n this._inflate[kBuffers] = [];\n\n if (fin && this.params[`${endpoint}_no_context_takeover`]) {\n this._inflate.reset();\n }\n }\n\n callback(null, data);\n });\n }\n\n /**\n * Compress data.\n *\n * @param {(Buffer|String)} data Data to compress\n * @param {Boolean} fin Specifies whether or not this is the last fragment\n * @param {Function} callback Callback\n * @private\n */\n _compress(data, fin, callback) {\n const endpoint = this._isServer ? 'server' : 'client';\n\n if (!this._deflate) {\n const key = `${endpoint}_max_window_bits`;\n const windowBits =\n typeof this.params[key] !== 'number'\n ? zlib.Z_DEFAULT_WINDOWBITS\n : this.params[key];\n\n this._deflate = zlib.createDeflateRaw({\n ...this._options.zlibDeflateOptions,\n windowBits\n });\n\n this._deflate[kTotalLength] = 0;\n this._deflate[kBuffers] = [];\n\n this._deflate.on('data', deflateOnData);\n }\n\n this._deflate[kCallback] = callback;\n\n this._deflate.write(data);\n this._deflate.flush(zlib.Z_SYNC_FLUSH, () => {\n if (!this._deflate) {\n //\n // The deflate stream was closed while data was being processed.\n //\n return;\n }\n\n let data = bufferUtil.concat(\n this._deflate[kBuffers],\n this._deflate[kTotalLength]\n );\n\n if (fin) {\n data = new FastBuffer(data.buffer, data.byteOffset, data.length - 4);\n }\n\n //\n // Ensure that the callback will not be called again in\n // `PerMessageDeflate#cleanup()`.\n //\n this._deflate[kCallback] = null;\n\n this._deflate[kTotalLength] = 0;\n this._deflate[kBuffers] = [];\n\n if (fin && this.params[`${endpoint}_no_context_takeover`]) {\n this._deflate.reset();\n }\n\n callback(null, data);\n });\n }\n}\n\nmodule.exports = PerMessageDeflate;\n\n/**\n * The listener of the `zlib.DeflateRaw` stream `'data'` event.\n *\n * @param {Buffer} chunk A chunk of data\n * @private\n */\nfunction deflateOnData(chunk) {\n this[kBuffers].push(chunk);\n this[kTotalLength] += chunk.length;\n}\n\n/**\n * The listener of the `zlib.InflateRaw` stream `'data'` event.\n *\n * @param {Buffer} chunk A chunk of data\n * @private\n */\nfunction inflateOnData(chunk) {\n this[kTotalLength] += chunk.length;\n\n if (\n this[kPerMessageDeflate]._maxPayload < 1 ||\n this[kTotalLength] <= this[kPerMessageDeflate]._maxPayload\n ) {\n this[kBuffers].push(chunk);\n return;\n }\n\n this[kError] = new RangeError('Max payload size exceeded');\n this[kError].code = 'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH';\n this[kError][kStatusCode] = 1009;\n this.removeListener('data', inflateOnData);\n\n //\n // The choice to employ `zlib.reset()` over `zlib.close()` is dictated by the\n // fact that in Node.js versions prior to 13.10.0, the callback for\n // `zlib.flush()` is not called if `zlib.close()` is used. Utilizing\n // `zlib.reset()` ensures that either the callback is invoked or an error is\n // emitted.\n //\n this.reset();\n}\n\n/**\n * The listener of the `zlib.InflateRaw` stream `'error'` event.\n *\n * @param {Error} err The emitted error\n * @private\n */\nfunction inflateOnError(err) {\n //\n // There is no need to call `Zlib#close()` as the handle is automatically\n // closed when an error is emitted.\n //\n this[kPerMessageDeflate]._inflate = null;\n\n if (this[kError]) {\n this[kCallback](this[kError]);\n return;\n }\n\n err[kStatusCode] = 1007;\n this[kCallback](err);\n}\n","'use strict';\n\n/**\n * Checks if a given buffer contains only correct UTF-8.\n * Ported from https://www.cl.cam.ac.uk/%7Emgk25/ucs/utf8_check.c by\n * Markus Kuhn.\n *\n * @param {Buffer} buf The buffer to check\n * @return {Boolean} `true` if `buf` contains only correct UTF-8, else `false`\n * @public\n */\nfunction isValidUTF8(buf) {\n const len = buf.length;\n let i = 0;\n\n while (i < len) {\n if ((buf[i] & 0x80) === 0x00) { // 0xxxxxxx\n i++;\n } else if ((buf[i] & 0xe0) === 0xc0) { // 110xxxxx 10xxxxxx\n if (\n i + 1 === len ||\n (buf[i + 1] & 0xc0) !== 0x80 ||\n (buf[i] & 0xfe) === 0xc0 // overlong\n ) {\n return false;\n }\n\n i += 2;\n } else if ((buf[i] & 0xf0) === 0xe0) { // 1110xxxx 10xxxxxx 10xxxxxx\n if (\n i + 2 >= len ||\n (buf[i + 1] & 0xc0) !== 0x80 ||\n (buf[i + 2] & 0xc0) !== 0x80 ||\n buf[i] === 0xe0 && (buf[i + 1] & 0xe0) === 0x80 || // overlong\n buf[i] === 0xed && (buf[i + 1] & 0xe0) === 0xa0 // surrogate (U+D800 - U+DFFF)\n ) {\n return false;\n }\n\n i += 3;\n } else if ((buf[i] & 0xf8) === 0xf0) { // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx\n if (\n i + 3 >= len ||\n (buf[i + 1] & 0xc0) !== 0x80 ||\n (buf[i + 2] & 0xc0) !== 0x80 ||\n (buf[i + 3] & 0xc0) !== 0x80 ||\n buf[i] === 0xf0 && (buf[i + 1] & 0xf0) === 0x80 || // overlong\n buf[i] === 0xf4 && buf[i + 1] > 0x8f || buf[i] > 0xf4 // > U+10FFFF\n ) {\n return false;\n }\n\n i += 4;\n } else {\n return false;\n }\n }\n\n return true;\n}\n\nmodule.exports = isValidUTF8;\n","'use strict';\n\ntry {\n module.exports = require('node-gyp-build')(__dirname);\n} catch (e) {\n module.exports = require('./fallback');\n}\n","'use strict';\n\nconst { isUtf8 } = require('buffer');\n\nconst { hasBlob } = require('./constants');\n\n//\n// Allowed token characters:\n//\n// '!', '#', '$', '%', '&', ''', '*', '+', '-',\n// '.', 0-9, A-Z, '^', '_', '`', a-z, '|', '~'\n//\n// tokenChars[32] === 0 // ' '\n// tokenChars[33] === 1 // '!'\n// tokenChars[34] === 0 // '\"'\n// ...\n//\n// prettier-ignore\nconst tokenChars = [\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 15\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 31\n 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, // 32 - 47\n 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48 - 63\n 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64 - 79\n 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, // 80 - 95\n 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96 - 111\n 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0 // 112 - 127\n];\n\n/**\n * Checks if a status code is allowed in a close frame.\n *\n * @param {Number} code The status code\n * @return {Boolean} `true` if the status code is valid, else `false`\n * @public\n */\nfunction isValidStatusCode(code) {\n return (\n (code >= 1000 &&\n code <= 1014 &&\n code !== 1004 &&\n code !== 1005 &&\n code !== 1006) ||\n (code >= 3000 && code <= 4999)\n );\n}\n\n/**\n * Checks if a given buffer contains only correct UTF-8.\n * Ported from https://www.cl.cam.ac.uk/%7Emgk25/ucs/utf8_check.c by\n * Markus Kuhn.\n *\n * @param {Buffer} buf The buffer to check\n * @return {Boolean} `true` if `buf` contains only correct UTF-8, else `false`\n * @public\n */\nfunction _isValidUTF8(buf) {\n const len = buf.length;\n let i = 0;\n\n while (i < len) {\n if ((buf[i] & 0x80) === 0) {\n // 0xxxxxxx\n i++;\n } else if ((buf[i] & 0xe0) === 0xc0) {\n // 110xxxxx 10xxxxxx\n if (\n i + 1 === len ||\n (buf[i + 1] & 0xc0) !== 0x80 ||\n (buf[i] & 0xfe) === 0xc0 // Overlong\n ) {\n return false;\n }\n\n i += 2;\n } else if ((buf[i] & 0xf0) === 0xe0) {\n // 1110xxxx 10xxxxxx 10xxxxxx\n if (\n i + 2 >= len ||\n (buf[i + 1] & 0xc0) !== 0x80 ||\n (buf[i + 2] & 0xc0) !== 0x80 ||\n (buf[i] === 0xe0 && (buf[i + 1] & 0xe0) === 0x80) || // Overlong\n (buf[i] === 0xed && (buf[i + 1] & 0xe0) === 0xa0) // Surrogate (U+D800 - U+DFFF)\n ) {\n return false;\n }\n\n i += 3;\n } else if ((buf[i] & 0xf8) === 0xf0) {\n // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx\n if (\n i + 3 >= len ||\n (buf[i + 1] & 0xc0) !== 0x80 ||\n (buf[i + 2] & 0xc0) !== 0x80 ||\n (buf[i + 3] & 0xc0) !== 0x80 ||\n (buf[i] === 0xf0 && (buf[i + 1] & 0xf0) === 0x80) || // Overlong\n (buf[i] === 0xf4 && buf[i + 1] > 0x8f) ||\n buf[i] > 0xf4 // > U+10FFFF\n ) {\n return false;\n }\n\n i += 4;\n } else {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Determines whether a value is a `Blob`.\n *\n * @param {*} value The value to be tested\n * @return {Boolean} `true` if `value` is a `Blob`, else `false`\n * @private\n */\nfunction isBlob(value) {\n return (\n hasBlob &&\n typeof value === 'object' &&\n typeof value.arrayBuffer === 'function' &&\n typeof value.type === 'string' &&\n typeof value.stream === 'function' &&\n (value[Symbol.toStringTag] === 'Blob' ||\n value[Symbol.toStringTag] === 'File')\n );\n}\n\nmodule.exports = {\n isBlob,\n isValidStatusCode,\n isValidUTF8: _isValidUTF8,\n tokenChars\n};\n\nif (isUtf8) {\n module.exports.isValidUTF8 = function (buf) {\n return buf.length < 24 ? _isValidUTF8(buf) : isUtf8(buf);\n };\n} /* istanbul ignore else */ else if (!process.env.WS_NO_UTF_8_VALIDATE) {\n try {\n const isValidUTF8 = require('utf-8-validate');\n\n module.exports.isValidUTF8 = function (buf) {\n return buf.length < 32 ? _isValidUTF8(buf) : isValidUTF8(buf);\n };\n } catch (e) {\n // Continue regardless of the error.\n }\n}\n","'use strict';\n\nconst { Writable } = require('stream');\n\nconst PerMessageDeflate = require('./permessage-deflate');\nconst {\n BINARY_TYPES,\n EMPTY_BUFFER,\n kStatusCode,\n kWebSocket\n} = require('./constants');\nconst { concat, toArrayBuffer, unmask } = require('./buffer-util');\nconst { isValidStatusCode, isValidUTF8 } = require('./validation');\n\nconst FastBuffer = Buffer[Symbol.species];\n\nconst GET_INFO = 0;\nconst GET_PAYLOAD_LENGTH_16 = 1;\nconst GET_PAYLOAD_LENGTH_64 = 2;\nconst GET_MASK = 3;\nconst GET_DATA = 4;\nconst INFLATING = 5;\nconst DEFER_EVENT = 6;\n\n/**\n * HyBi Receiver implementation.\n *\n * @extends Writable\n */\nclass Receiver extends Writable {\n /**\n * Creates a Receiver instance.\n *\n * @param {Object} [options] Options object\n * @param {Boolean} [options.allowSynchronousEvents=true] Specifies whether\n * any of the `'message'`, `'ping'`, and `'pong'` events can be emitted\n * multiple times in the same tick\n * @param {String} [options.binaryType=nodebuffer] The type for binary data\n * @param {Object} [options.extensions] An object containing the negotiated\n * extensions\n * @param {Boolean} [options.isServer=false] Specifies whether to operate in\n * client or server mode\n * @param {Number} [options.maxPayload=0] The maximum allowed message length\n * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or\n * not to skip UTF-8 validation for text and close messages\n */\n constructor(options = {}) {\n super();\n\n this._allowSynchronousEvents =\n options.allowSynchronousEvents !== undefined\n ? options.allowSynchronousEvents\n : true;\n this._binaryType = options.binaryType || BINARY_TYPES[0];\n this._extensions = options.extensions || {};\n this._isServer = !!options.isServer;\n this._maxPayload = options.maxPayload | 0;\n this._skipUTF8Validation = !!options.skipUTF8Validation;\n this[kWebSocket] = undefined;\n\n this._bufferedBytes = 0;\n this._buffers = [];\n\n this._compressed = false;\n this._payloadLength = 0;\n this._mask = undefined;\n this._fragmented = 0;\n this._masked = false;\n this._fin = false;\n this._opcode = 0;\n\n this._totalPayloadLength = 0;\n this._messageLength = 0;\n this._fragments = [];\n\n this._errored = false;\n this._loop = false;\n this._state = GET_INFO;\n }\n\n /**\n * Implements `Writable.prototype._write()`.\n *\n * @param {Buffer} chunk The chunk of data to write\n * @param {String} encoding The character encoding of `chunk`\n * @param {Function} cb Callback\n * @private\n */\n _write(chunk, encoding, cb) {\n if (this._opcode === 0x08 && this._state == GET_INFO) return cb();\n\n this._bufferedBytes += chunk.length;\n this._buffers.push(chunk);\n this.startLoop(cb);\n }\n\n /**\n * Consumes `n` bytes from the buffered data.\n *\n * @param {Number} n The number of bytes to consume\n * @return {Buffer} The consumed bytes\n * @private\n */\n consume(n) {\n this._bufferedBytes -= n;\n\n if (n === this._buffers[0].length) return this._buffers.shift();\n\n if (n < this._buffers[0].length) {\n const buf = this._buffers[0];\n this._buffers[0] = new FastBuffer(\n buf.buffer,\n buf.byteOffset + n,\n buf.length - n\n );\n\n return new FastBuffer(buf.buffer, buf.byteOffset, n);\n }\n\n const dst = Buffer.allocUnsafe(n);\n\n do {\n const buf = this._buffers[0];\n const offset = dst.length - n;\n\n if (n >= buf.length) {\n dst.set(this._buffers.shift(), offset);\n } else {\n dst.set(new Uint8Array(buf.buffer, buf.byteOffset, n), offset);\n this._buffers[0] = new FastBuffer(\n buf.buffer,\n buf.byteOffset + n,\n buf.length - n\n );\n }\n\n n -= buf.length;\n } while (n > 0);\n\n return dst;\n }\n\n /**\n * Starts the parsing loop.\n *\n * @param {Function} cb Callback\n * @private\n */\n startLoop(cb) {\n this._loop = true;\n\n do {\n switch (this._state) {\n case GET_INFO:\n this.getInfo(cb);\n break;\n case GET_PAYLOAD_LENGTH_16:\n this.getPayloadLength16(cb);\n break;\n case GET_PAYLOAD_LENGTH_64:\n this.getPayloadLength64(cb);\n break;\n case GET_MASK:\n this.getMask();\n break;\n case GET_DATA:\n this.getData(cb);\n break;\n case INFLATING:\n case DEFER_EVENT:\n this._loop = false;\n return;\n }\n } while (this._loop);\n\n if (!this._errored) cb();\n }\n\n /**\n * Reads the first two bytes of a frame.\n *\n * @param {Function} cb Callback\n * @private\n */\n getInfo(cb) {\n if (this._bufferedBytes < 2) {\n this._loop = false;\n return;\n }\n\n const buf = this.consume(2);\n\n if ((buf[0] & 0x30) !== 0x00) {\n const error = this.createError(\n RangeError,\n 'RSV2 and RSV3 must be clear',\n true,\n 1002,\n 'WS_ERR_UNEXPECTED_RSV_2_3'\n );\n\n cb(error);\n return;\n }\n\n const compressed = (buf[0] & 0x40) === 0x40;\n\n if (compressed && !this._extensions[PerMessageDeflate.extensionName]) {\n const error = this.createError(\n RangeError,\n 'RSV1 must be clear',\n true,\n 1002,\n 'WS_ERR_UNEXPECTED_RSV_1'\n );\n\n cb(error);\n return;\n }\n\n this._fin = (buf[0] & 0x80) === 0x80;\n this._opcode = buf[0] & 0x0f;\n this._payloadLength = buf[1] & 0x7f;\n\n if (this._opcode === 0x00) {\n if (compressed) {\n const error = this.createError(\n RangeError,\n 'RSV1 must be clear',\n true,\n 1002,\n 'WS_ERR_UNEXPECTED_RSV_1'\n );\n\n cb(error);\n return;\n }\n\n if (!this._fragmented) {\n const error = this.createError(\n RangeError,\n 'invalid opcode 0',\n true,\n 1002,\n 'WS_ERR_INVALID_OPCODE'\n );\n\n cb(error);\n return;\n }\n\n this._opcode = this._fragmented;\n } else if (this._opcode === 0x01 || this._opcode === 0x02) {\n if (this._fragmented) {\n const error = this.createError(\n RangeError,\n `invalid opcode ${this._opcode}`,\n true,\n 1002,\n 'WS_ERR_INVALID_OPCODE'\n );\n\n cb(error);\n return;\n }\n\n this._compressed = compressed;\n } else if (this._opcode > 0x07 && this._opcode < 0x0b) {\n if (!this._fin) {\n const error = this.createError(\n RangeError,\n 'FIN must be set',\n true,\n 1002,\n 'WS_ERR_EXPECTED_FIN'\n );\n\n cb(error);\n return;\n }\n\n if (compressed) {\n const error = this.createError(\n RangeError,\n 'RSV1 must be clear',\n true,\n 1002,\n 'WS_ERR_UNEXPECTED_RSV_1'\n );\n\n cb(error);\n return;\n }\n\n if (\n this._payloadLength > 0x7d ||\n (this._opcode === 0x08 && this._payloadLength === 1)\n ) {\n const error = this.createError(\n RangeError,\n `invalid payload length ${this._payloadLength}`,\n true,\n 1002,\n 'WS_ERR_INVALID_CONTROL_PAYLOAD_LENGTH'\n );\n\n cb(error);\n return;\n }\n } else {\n const error = this.createError(\n RangeError,\n `invalid opcode ${this._opcode}`,\n true,\n 1002,\n 'WS_ERR_INVALID_OPCODE'\n );\n\n cb(error);\n return;\n }\n\n if (!this._fin && !this._fragmented) this._fragmented = this._opcode;\n this._masked = (buf[1] & 0x80) === 0x80;\n\n if (this._isServer) {\n if (!this._masked) {\n const error = this.createError(\n RangeError,\n 'MASK must be set',\n true,\n 1002,\n 'WS_ERR_EXPECTED_MASK'\n );\n\n cb(error);\n return;\n }\n } else if (this._masked) {\n const error = this.createError(\n RangeError,\n 'MASK must be clear',\n true,\n 1002,\n 'WS_ERR_UNEXPECTED_MASK'\n );\n\n cb(error);\n return;\n }\n\n if (this._payloadLength === 126) this._state = GET_PAYLOAD_LENGTH_16;\n else if (this._payloadLength === 127) this._state = GET_PAYLOAD_LENGTH_64;\n else this.haveLength(cb);\n }\n\n /**\n * Gets extended payload length (7+16).\n *\n * @param {Function} cb Callback\n * @private\n */\n getPayloadLength16(cb) {\n if (this._bufferedBytes < 2) {\n this._loop = false;\n return;\n }\n\n this._payloadLength = this.consume(2).readUInt16BE(0);\n this.haveLength(cb);\n }\n\n /**\n * Gets extended payload length (7+64).\n *\n * @param {Function} cb Callback\n * @private\n */\n getPayloadLength64(cb) {\n if (this._bufferedBytes < 8) {\n this._loop = false;\n return;\n }\n\n const buf = this.consume(8);\n const num = buf.readUInt32BE(0);\n\n //\n // The maximum safe integer in JavaScript is 2^53 - 1. An error is returned\n // if payload length is greater than this number.\n //\n if (num > Math.pow(2, 53 - 32) - 1) {\n const error = this.createError(\n RangeError,\n 'Unsupported WebSocket frame: payload length > 2^53 - 1',\n false,\n 1009,\n 'WS_ERR_UNSUPPORTED_DATA_PAYLOAD_LENGTH'\n );\n\n cb(error);\n return;\n }\n\n this._payloadLength = num * Math.pow(2, 32) + buf.readUInt32BE(4);\n this.haveLength(cb);\n }\n\n /**\n * Payload length has been read.\n *\n * @param {Function} cb Callback\n * @private\n */\n haveLength(cb) {\n if (this._payloadLength && this._opcode < 0x08) {\n this._totalPayloadLength += this._payloadLength;\n if (this._totalPayloadLength > this._maxPayload && this._maxPayload > 0) {\n const error = this.createError(\n RangeError,\n 'Max payload size exceeded',\n false,\n 1009,\n 'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH'\n );\n\n cb(error);\n return;\n }\n }\n\n if (this._masked) this._state = GET_MASK;\n else this._state = GET_DATA;\n }\n\n /**\n * Reads mask bytes.\n *\n * @private\n */\n getMask() {\n if (this._bufferedBytes < 4) {\n this._loop = false;\n return;\n }\n\n this._mask = this.consume(4);\n this._state = GET_DATA;\n }\n\n /**\n * Reads data bytes.\n *\n * @param {Function} cb Callback\n * @private\n */\n getData(cb) {\n let data = EMPTY_BUFFER;\n\n if (this._payloadLength) {\n if (this._bufferedBytes < this._payloadLength) {\n this._loop = false;\n return;\n }\n\n data = this.consume(this._payloadLength);\n\n if (\n this._masked &&\n (this._mask[0] | this._mask[1] | this._mask[2] | this._mask[3]) !== 0\n ) {\n unmask(data, this._mask);\n }\n }\n\n if (this._opcode > 0x07) {\n this.controlMessage(data, cb);\n return;\n }\n\n if (this._compressed) {\n this._state = INFLATING;\n this.decompress(data, cb);\n return;\n }\n\n if (data.length) {\n //\n // This message is not compressed so its length is the sum of the payload\n // length of all fragments.\n //\n this._messageLength = this._totalPayloadLength;\n this._fragments.push(data);\n }\n\n this.dataMessage(cb);\n }\n\n /**\n * Decompresses data.\n *\n * @param {Buffer} data Compressed data\n * @param {Function} cb Callback\n * @private\n */\n decompress(data, cb) {\n const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];\n\n perMessageDeflate.decompress(data, this._fin, (err, buf) => {\n if (err) return cb(err);\n\n if (buf.length) {\n this._messageLength += buf.length;\n if (this._messageLength > this._maxPayload && this._maxPayload > 0) {\n const error = this.createError(\n RangeError,\n 'Max payload size exceeded',\n false,\n 1009,\n 'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH'\n );\n\n cb(error);\n return;\n }\n\n this._fragments.push(buf);\n }\n\n this.dataMessage(cb);\n if (this._state === GET_INFO) this.startLoop(cb);\n });\n }\n\n /**\n * Handles a data message.\n *\n * @param {Function} cb Callback\n * @private\n */\n dataMessage(cb) {\n if (!this._fin) {\n this._state = GET_INFO;\n return;\n }\n\n const messageLength = this._messageLength;\n const fragments = this._fragments;\n\n this._totalPayloadLength = 0;\n this._messageLength = 0;\n this._fragmented = 0;\n this._fragments = [];\n\n if (this._opcode === 2) {\n let data;\n\n if (this._binaryType === 'nodebuffer') {\n data = concat(fragments, messageLength);\n } else if (this._binaryType === 'arraybuffer') {\n data = toArrayBuffer(concat(fragments, messageLength));\n } else if (this._binaryType === 'blob') {\n data = new Blob(fragments);\n } else {\n data = fragments;\n }\n\n if (this._allowSynchronousEvents) {\n this.emit('message', data, true);\n this._state = GET_INFO;\n } else {\n this._state = DEFER_EVENT;\n setImmediate(() => {\n this.emit('message', data, true);\n this._state = GET_INFO;\n this.startLoop(cb);\n });\n }\n } else {\n const buf = concat(fragments, messageLength);\n\n if (!this._skipUTF8Validation && !isValidUTF8(buf)) {\n const error = this.createError(\n Error,\n 'invalid UTF-8 sequence',\n true,\n 1007,\n 'WS_ERR_INVALID_UTF8'\n );\n\n cb(error);\n return;\n }\n\n if (this._state === INFLATING || this._allowSynchronousEvents) {\n this.emit('message', buf, false);\n this._state = GET_INFO;\n } else {\n this._state = DEFER_EVENT;\n setImmediate(() => {\n this.emit('message', buf, false);\n this._state = GET_INFO;\n this.startLoop(cb);\n });\n }\n }\n }\n\n /**\n * Handles a control message.\n *\n * @param {Buffer} data Data to handle\n * @return {(Error|RangeError|undefined)} A possible error\n * @private\n */\n controlMessage(data, cb) {\n if (this._opcode === 0x08) {\n if (data.length === 0) {\n this._loop = false;\n this.emit('conclude', 1005, EMPTY_BUFFER);\n this.end();\n } else {\n const code = data.readUInt16BE(0);\n\n if (!isValidStatusCode(code)) {\n const error = this.createError(\n RangeError,\n `invalid status code ${code}`,\n true,\n 1002,\n 'WS_ERR_INVALID_CLOSE_CODE'\n );\n\n cb(error);\n return;\n }\n\n const buf = new FastBuffer(\n data.buffer,\n data.byteOffset + 2,\n data.length - 2\n );\n\n if (!this._skipUTF8Validation && !isValidUTF8(buf)) {\n const error = this.createError(\n Error,\n 'invalid UTF-8 sequence',\n true,\n 1007,\n 'WS_ERR_INVALID_UTF8'\n );\n\n cb(error);\n return;\n }\n\n this._loop = false;\n this.emit('conclude', code, buf);\n this.end();\n }\n\n this._state = GET_INFO;\n return;\n }\n\n if (this._allowSynchronousEvents) {\n this.emit(this._opcode === 0x09 ? 'ping' : 'pong', data);\n this._state = GET_INFO;\n } else {\n this._state = DEFER_EVENT;\n setImmediate(() => {\n this.emit(this._opcode === 0x09 ? 'ping' : 'pong', data);\n this._state = GET_INFO;\n this.startLoop(cb);\n });\n }\n }\n\n /**\n * Builds an error object.\n *\n * @param {function(new:Error|RangeError)} ErrorCtor The error constructor\n * @param {String} message The error message\n * @param {Boolean} prefix Specifies whether or not to add a default prefix to\n * `message`\n * @param {Number} statusCode The status code\n * @param {String} errorCode The exposed error code\n * @return {(Error|RangeError)} The error\n * @private\n */\n createError(ErrorCtor, message, prefix, statusCode, errorCode) {\n this._loop = false;\n this._errored = true;\n\n const err = new ErrorCtor(\n prefix ? `Invalid WebSocket frame: ${message}` : message\n );\n\n Error.captureStackTrace(err, this.createError);\n err.code = errorCode;\n err[kStatusCode] = statusCode;\n return err;\n }\n}\n\nmodule.exports = Receiver;\n","/* eslint no-unused-vars: [\"error\", { \"varsIgnorePattern\": \"^Duplex\" }] */\n\n'use strict';\n\nconst { Duplex } = require('stream');\nconst { randomFillSync } = require('crypto');\n\nconst PerMessageDeflate = require('./permessage-deflate');\nconst { EMPTY_BUFFER, kWebSocket, NOOP } = require('./constants');\nconst { isBlob, isValidStatusCode } = require('./validation');\nconst { mask: applyMask, toBuffer } = require('./buffer-util');\n\nconst kByteLength = Symbol('kByteLength');\nconst maskBuffer = Buffer.alloc(4);\nconst RANDOM_POOL_SIZE = 8 * 1024;\nlet randomPool;\nlet randomPoolPointer = RANDOM_POOL_SIZE;\n\nconst DEFAULT = 0;\nconst DEFLATING = 1;\nconst GET_BLOB_DATA = 2;\n\n/**\n * HyBi Sender implementation.\n */\nclass Sender {\n /**\n * Creates a Sender instance.\n *\n * @param {Duplex} socket The connection socket\n * @param {Object} [extensions] An object containing the negotiated extensions\n * @param {Function} [generateMask] The function used to generate the masking\n * key\n */\n constructor(socket, extensions, generateMask) {\n this._extensions = extensions || {};\n\n if (generateMask) {\n this._generateMask = generateMask;\n this._maskBuffer = Buffer.alloc(4);\n }\n\n this._socket = socket;\n\n this._firstFragment = true;\n this._compress = false;\n\n this._bufferedBytes = 0;\n this._queue = [];\n this._state = DEFAULT;\n this.onerror = NOOP;\n this[kWebSocket] = undefined;\n }\n\n /**\n * Frames a piece of data according to the HyBi WebSocket protocol.\n *\n * @param {(Buffer|String)} data The data to frame\n * @param {Object} options Options object\n * @param {Boolean} [options.fin=false] Specifies whether or not to set the\n * FIN bit\n * @param {Function} [options.generateMask] The function used to generate the\n * masking key\n * @param {Boolean} [options.mask=false] Specifies whether or not to mask\n * `data`\n * @param {Buffer} [options.maskBuffer] The buffer used to store the masking\n * key\n * @param {Number} options.opcode The opcode\n * @param {Boolean} [options.readOnly=false] Specifies whether `data` can be\n * modified\n * @param {Boolean} [options.rsv1=false] Specifies whether or not to set the\n * RSV1 bit\n * @return {(Buffer|String)[]} The framed data\n * @public\n */\n static frame(data, options) {\n let mask;\n let merge = false;\n let offset = 2;\n let skipMasking = false;\n\n if (options.mask) {\n mask = options.maskBuffer || maskBuffer;\n\n if (options.generateMask) {\n options.generateMask(mask);\n } else {\n if (randomPoolPointer === RANDOM_POOL_SIZE) {\n /* istanbul ignore else */\n if (randomPool === undefined) {\n //\n // This is lazily initialized because server-sent frames must not\n // be masked so it may never be used.\n //\n randomPool = Buffer.alloc(RANDOM_POOL_SIZE);\n }\n\n randomFillSync(randomPool, 0, RANDOM_POOL_SIZE);\n randomPoolPointer = 0;\n }\n\n mask[0] = randomPool[randomPoolPointer++];\n mask[1] = randomPool[randomPoolPointer++];\n mask[2] = randomPool[randomPoolPointer++];\n mask[3] = randomPool[randomPoolPointer++];\n }\n\n skipMasking = (mask[0] | mask[1] | mask[2] | mask[3]) === 0;\n offset = 6;\n }\n\n let dataLength;\n\n if (typeof data === 'string') {\n if (\n (!options.mask || skipMasking) &&\n options[kByteLength] !== undefined\n ) {\n dataLength = options[kByteLength];\n } else {\n data = Buffer.from(data);\n dataLength = data.length;\n }\n } else {\n dataLength = data.length;\n merge = options.mask && options.readOnly && !skipMasking;\n }\n\n let payloadLength = dataLength;\n\n if (dataLength >= 65536) {\n offset += 8;\n payloadLength = 127;\n } else if (dataLength > 125) {\n offset += 2;\n payloadLength = 126;\n }\n\n const target = Buffer.allocUnsafe(merge ? dataLength + offset : offset);\n\n target[0] = options.fin ? options.opcode | 0x80 : options.opcode;\n if (options.rsv1) target[0] |= 0x40;\n\n target[1] = payloadLength;\n\n if (payloadLength === 126) {\n target.writeUInt16BE(dataLength, 2);\n } else if (payloadLength === 127) {\n target[2] = target[3] = 0;\n target.writeUIntBE(dataLength, 4, 6);\n }\n\n if (!options.mask) return [target, data];\n\n target[1] |= 0x80;\n target[offset - 4] = mask[0];\n target[offset - 3] = mask[1];\n target[offset - 2] = mask[2];\n target[offset - 1] = mask[3];\n\n if (skipMasking) return [target, data];\n\n if (merge) {\n applyMask(data, mask, target, offset, dataLength);\n return [target];\n }\n\n applyMask(data, mask, data, 0, dataLength);\n return [target, data];\n }\n\n /**\n * Sends a close message to the other peer.\n *\n * @param {Number} [code] The status code component of the body\n * @param {(String|Buffer)} [data] The message component of the body\n * @param {Boolean} [mask=false] Specifies whether or not to mask the message\n * @param {Function} [cb] Callback\n * @public\n */\n close(code, data, mask, cb) {\n let buf;\n\n if (code === undefined) {\n buf = EMPTY_BUFFER;\n } else if (typeof code !== 'number' || !isValidStatusCode(code)) {\n throw new TypeError('First argument must be a valid error code number');\n } else if (data === undefined || !data.length) {\n buf = Buffer.allocUnsafe(2);\n buf.writeUInt16BE(code, 0);\n } else {\n const length = Buffer.byteLength(data);\n\n if (length > 123) {\n throw new RangeError('The message must not be greater than 123 bytes');\n }\n\n buf = Buffer.allocUnsafe(2 + length);\n buf.writeUInt16BE(code, 0);\n\n if (typeof data === 'string') {\n buf.write(data, 2);\n } else {\n buf.set(data, 2);\n }\n }\n\n const options = {\n [kByteLength]: buf.length,\n fin: true,\n generateMask: this._generateMask,\n mask,\n maskBuffer: this._maskBuffer,\n opcode: 0x08,\n readOnly: false,\n rsv1: false\n };\n\n if (this._state !== DEFAULT) {\n this.enqueue([this.dispatch, buf, false, options, cb]);\n } else {\n this.sendFrame(Sender.frame(buf, options), cb);\n }\n }\n\n /**\n * Sends a ping message to the other peer.\n *\n * @param {*} data The message to send\n * @param {Boolean} [mask=false] Specifies whether or not to mask `data`\n * @param {Function} [cb] Callback\n * @public\n */\n ping(data, mask, cb) {\n let byteLength;\n let readOnly;\n\n if (typeof data === 'string') {\n byteLength = Buffer.byteLength(data);\n readOnly = false;\n } else if (isBlob(data)) {\n byteLength = data.size;\n readOnly = false;\n } else {\n data = toBuffer(data);\n byteLength = data.length;\n readOnly = toBuffer.readOnly;\n }\n\n if (byteLength > 125) {\n throw new RangeError('The data size must not be greater than 125 bytes');\n }\n\n const options = {\n [kByteLength]: byteLength,\n fin: true,\n generateMask: this._generateMask,\n mask,\n maskBuffer: this._maskBuffer,\n opcode: 0x09,\n readOnly,\n rsv1: false\n };\n\n if (isBlob(data)) {\n if (this._state !== DEFAULT) {\n this.enqueue([this.getBlobData, data, false, options, cb]);\n } else {\n this.getBlobData(data, false, options, cb);\n }\n } else if (this._state !== DEFAULT) {\n this.enqueue([this.dispatch, data, false, options, cb]);\n } else {\n this.sendFrame(Sender.frame(data, options), cb);\n }\n }\n\n /**\n * Sends a pong message to the other peer.\n *\n * @param {*} data The message to send\n * @param {Boolean} [mask=false] Specifies whether or not to mask `data`\n * @param {Function} [cb] Callback\n * @public\n */\n pong(data, mask, cb) {\n let byteLength;\n let readOnly;\n\n if (typeof data === 'string') {\n byteLength = Buffer.byteLength(data);\n readOnly = false;\n } else if (isBlob(data)) {\n byteLength = data.size;\n readOnly = false;\n } else {\n data = toBuffer(data);\n byteLength = data.length;\n readOnly = toBuffer.readOnly;\n }\n\n if (byteLength > 125) {\n throw new RangeError('The data size must not be greater than 125 bytes');\n }\n\n const options = {\n [kByteLength]: byteLength,\n fin: true,\n generateMask: this._generateMask,\n mask,\n maskBuffer: this._maskBuffer,\n opcode: 0x0a,\n readOnly,\n rsv1: false\n };\n\n if (isBlob(data)) {\n if (this._state !== DEFAULT) {\n this.enqueue([this.getBlobData, data, false, options, cb]);\n } else {\n this.getBlobData(data, false, options, cb);\n }\n } else if (this._state !== DEFAULT) {\n this.enqueue([this.dispatch, data, false, options, cb]);\n } else {\n this.sendFrame(Sender.frame(data, options), cb);\n }\n }\n\n /**\n * Sends a data message to the other peer.\n *\n * @param {*} data The message to send\n * @param {Object} options Options object\n * @param {Boolean} [options.binary=false] Specifies whether `data` is binary\n * or text\n * @param {Boolean} [options.compress=false] Specifies whether or not to\n * compress `data`\n * @param {Boolean} [options.fin=false] Specifies whether the fragment is the\n * last one\n * @param {Boolean} [options.mask=false] Specifies whether or not to mask\n * `data`\n * @param {Function} [cb] Callback\n * @public\n */\n send(data, options, cb) {\n const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];\n let opcode = options.binary ? 2 : 1;\n let rsv1 = options.compress;\n\n let byteLength;\n let readOnly;\n\n if (typeof data === 'string') {\n byteLength = Buffer.byteLength(data);\n readOnly = false;\n } else if (isBlob(data)) {\n byteLength = data.size;\n readOnly = false;\n } else {\n data = toBuffer(data);\n byteLength = data.length;\n readOnly = toBuffer.readOnly;\n }\n\n if (this._firstFragment) {\n this._firstFragment = false;\n if (\n rsv1 &&\n perMessageDeflate &&\n perMessageDeflate.params[\n perMessageDeflate._isServer\n ? 'server_no_context_takeover'\n : 'client_no_context_takeover'\n ]\n ) {\n rsv1 = byteLength >= perMessageDeflate._threshold;\n }\n this._compress = rsv1;\n } else {\n rsv1 = false;\n opcode = 0;\n }\n\n if (options.fin) this._firstFragment = true;\n\n const opts = {\n [kByteLength]: byteLength,\n fin: options.fin,\n generateMask: this._generateMask,\n mask: options.mask,\n maskBuffer: this._maskBuffer,\n opcode,\n readOnly,\n rsv1\n };\n\n if (isBlob(data)) {\n if (this._state !== DEFAULT) {\n this.enqueue([this.getBlobData, data, this._compress, opts, cb]);\n } else {\n this.getBlobData(data, this._compress, opts, cb);\n }\n } else if (this._state !== DEFAULT) {\n this.enqueue([this.dispatch, data, this._compress, opts, cb]);\n } else {\n this.dispatch(data, this._compress, opts, cb);\n }\n }\n\n /**\n * Gets the contents of a blob as binary data.\n *\n * @param {Blob} blob The blob\n * @param {Boolean} [compress=false] Specifies whether or not to compress\n * the data\n * @param {Object} options Options object\n * @param {Boolean} [options.fin=false] Specifies whether or not to set the\n * FIN bit\n * @param {Function} [options.generateMask] The function used to generate the\n * masking key\n * @param {Boolean} [options.mask=false] Specifies whether or not to mask\n * `data`\n * @param {Buffer} [options.maskBuffer] The buffer used to store the masking\n * key\n * @param {Number} options.opcode The opcode\n * @param {Boolean} [options.readOnly=false] Specifies whether `data` can be\n * modified\n * @param {Boolean} [options.rsv1=false] Specifies whether or not to set the\n * RSV1 bit\n * @param {Function} [cb] Callback\n * @private\n */\n getBlobData(blob, compress, options, cb) {\n this._bufferedBytes += options[kByteLength];\n this._state = GET_BLOB_DATA;\n\n blob\n .arrayBuffer()\n .then((arrayBuffer) => {\n if (this._socket.destroyed) {\n const err = new Error(\n 'The socket was closed while the blob was being read'\n );\n\n //\n // `callCallbacks` is called in the next tick to ensure that errors\n // that might be thrown in the callbacks behave like errors thrown\n // outside the promise chain.\n //\n process.nextTick(callCallbacks, this, err, cb);\n return;\n }\n\n this._bufferedBytes -= options[kByteLength];\n const data = toBuffer(arrayBuffer);\n\n if (!compress) {\n this._state = DEFAULT;\n this.sendFrame(Sender.frame(data, options), cb);\n this.dequeue();\n } else {\n this.dispatch(data, compress, options, cb);\n }\n })\n .catch((err) => {\n //\n // `onError` is called in the next tick for the same reason that\n // `callCallbacks` above is.\n //\n process.nextTick(onError, this, err, cb);\n });\n }\n\n /**\n * Dispatches a message.\n *\n * @param {(Buffer|String)} data The message to send\n * @param {Boolean} [compress=false] Specifies whether or not to compress\n * `data`\n * @param {Object} options Options object\n * @param {Boolean} [options.fin=false] Specifies whether or not to set the\n * FIN bit\n * @param {Function} [options.generateMask] The function used to generate the\n * masking key\n * @param {Boolean} [options.mask=false] Specifies whether or not to mask\n * `data`\n * @param {Buffer} [options.maskBuffer] The buffer used to store the masking\n * key\n * @param {Number} options.opcode The opcode\n * @param {Boolean} [options.readOnly=false] Specifies whether `data` can be\n * modified\n * @param {Boolean} [options.rsv1=false] Specifies whether or not to set the\n * RSV1 bit\n * @param {Function} [cb] Callback\n * @private\n */\n dispatch(data, compress, options, cb) {\n if (!compress) {\n this.sendFrame(Sender.frame(data, options), cb);\n return;\n }\n\n const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];\n\n this._bufferedBytes += options[kByteLength];\n this._state = DEFLATING;\n perMessageDeflate.compress(data, options.fin, (_, buf) => {\n if (this._socket.destroyed) {\n const err = new Error(\n 'The socket was closed while data was being compressed'\n );\n\n callCallbacks(this, err, cb);\n return;\n }\n\n this._bufferedBytes -= options[kByteLength];\n this._state = DEFAULT;\n options.readOnly = false;\n this.sendFrame(Sender.frame(buf, options), cb);\n this.dequeue();\n });\n }\n\n /**\n * Executes queued send operations.\n *\n * @private\n */\n dequeue() {\n while (this._state === DEFAULT && this._queue.length) {\n const params = this._queue.shift();\n\n this._bufferedBytes -= params[3][kByteLength];\n Reflect.apply(params[0], this, params.slice(1));\n }\n }\n\n /**\n * Enqueues a send operation.\n *\n * @param {Array} params Send operation parameters.\n * @private\n */\n enqueue(params) {\n this._bufferedBytes += params[3][kByteLength];\n this._queue.push(params);\n }\n\n /**\n * Sends a frame.\n *\n * @param {(Buffer | String)[]} list The frame to send\n * @param {Function} [cb] Callback\n * @private\n */\n sendFrame(list, cb) {\n if (list.length === 2) {\n this._socket.cork();\n this._socket.write(list[0]);\n this._socket.write(list[1], cb);\n this._socket.uncork();\n } else {\n this._socket.write(list[0], cb);\n }\n }\n}\n\nmodule.exports = Sender;\n\n/**\n * Calls queued callbacks with an error.\n *\n * @param {Sender} sender The `Sender` instance\n * @param {Error} err The error to call the callbacks with\n * @param {Function} [cb] The first callback\n * @private\n */\nfunction callCallbacks(sender, err, cb) {\n if (typeof cb === 'function') cb(err);\n\n for (let i = 0; i < sender._queue.length; i++) {\n const params = sender._queue[i];\n const callback = params[params.length - 1];\n\n if (typeof callback === 'function') callback(err);\n }\n}\n\n/**\n * Handles a `Sender` error.\n *\n * @param {Sender} sender The `Sender` instance\n * @param {Error} err The error\n * @param {Function} [cb] The first pending callback\n * @private\n */\nfunction onError(sender, err, cb) {\n callCallbacks(sender, err, cb);\n sender.onerror(err);\n}\n","'use strict';\n\nconst { kForOnEventAttribute, kListener } = require('./constants');\n\nconst kCode = Symbol('kCode');\nconst kData = Symbol('kData');\nconst kError = Symbol('kError');\nconst kMessage = Symbol('kMessage');\nconst kReason = Symbol('kReason');\nconst kTarget = Symbol('kTarget');\nconst kType = Symbol('kType');\nconst kWasClean = Symbol('kWasClean');\n\n/**\n * Class representing an event.\n */\nclass Event {\n /**\n * Create a new `Event`.\n *\n * @param {String} type The name of the event\n * @throws {TypeError} If the `type` argument is not specified\n */\n constructor(type) {\n this[kTarget] = null;\n this[kType] = type;\n }\n\n /**\n * @type {*}\n */\n get target() {\n return this[kTarget];\n }\n\n /**\n * @type {String}\n */\n get type() {\n return this[kType];\n }\n}\n\nObject.defineProperty(Event.prototype, 'target', { enumerable: true });\nObject.defineProperty(Event.prototype, 'type', { enumerable: true });\n\n/**\n * Class representing a close event.\n *\n * @extends Event\n */\nclass CloseEvent extends Event {\n /**\n * Create a new `CloseEvent`.\n *\n * @param {String} type The name of the event\n * @param {Object} [options] A dictionary object that allows for setting\n * attributes via object members of the same name\n * @param {Number} [options.code=0] The status code explaining why the\n * connection was closed\n * @param {String} [options.reason=''] A human-readable string explaining why\n * the connection was closed\n * @param {Boolean} [options.wasClean=false] Indicates whether or not the\n * connection was cleanly closed\n */\n constructor(type, options = {}) {\n super(type);\n\n this[kCode] = options.code === undefined ? 0 : options.code;\n this[kReason] = options.reason === undefined ? '' : options.reason;\n this[kWasClean] = options.wasClean === undefined ? false : options.wasClean;\n }\n\n /**\n * @type {Number}\n */\n get code() {\n return this[kCode];\n }\n\n /**\n * @type {String}\n */\n get reason() {\n return this[kReason];\n }\n\n /**\n * @type {Boolean}\n */\n get wasClean() {\n return this[kWasClean];\n }\n}\n\nObject.defineProperty(CloseEvent.prototype, 'code', { enumerable: true });\nObject.defineProperty(CloseEvent.prototype, 'reason', { enumerable: true });\nObject.defineProperty(CloseEvent.prototype, 'wasClean', { enumerable: true });\n\n/**\n * Class representing an error event.\n *\n * @extends Event\n */\nclass ErrorEvent extends Event {\n /**\n * Create a new `ErrorEvent`.\n *\n * @param {String} type The name of the event\n * @param {Object} [options] A dictionary object that allows for setting\n * attributes via object members of the same name\n * @param {*} [options.error=null] The error that generated this event\n * @param {String} [options.message=''] The error message\n */\n constructor(type, options = {}) {\n super(type);\n\n this[kError] = options.error === undefined ? null : options.error;\n this[kMessage] = options.message === undefined ? '' : options.message;\n }\n\n /**\n * @type {*}\n */\n get error() {\n return this[kError];\n }\n\n /**\n * @type {String}\n */\n get message() {\n return this[kMessage];\n }\n}\n\nObject.defineProperty(ErrorEvent.prototype, 'error', { enumerable: true });\nObject.defineProperty(ErrorEvent.prototype, 'message', { enumerable: true });\n\n/**\n * Class representing a message event.\n *\n * @extends Event\n */\nclass MessageEvent extends Event {\n /**\n * Create a new `MessageEvent`.\n *\n * @param {String} type The name of the event\n * @param {Object} [options] A dictionary object that allows for setting\n * attributes via object members of the same name\n * @param {*} [options.data=null] The message content\n */\n constructor(type, options = {}) {\n super(type);\n\n this[kData] = options.data === undefined ? null : options.data;\n }\n\n /**\n * @type {*}\n */\n get data() {\n return this[kData];\n }\n}\n\nObject.defineProperty(MessageEvent.prototype, 'data', { enumerable: true });\n\n/**\n * This provides methods for emulating the `EventTarget` interface. It's not\n * meant to be used directly.\n *\n * @mixin\n */\nconst EventTarget = {\n /**\n * Register an event listener.\n *\n * @param {String} type A string representing the event type to listen for\n * @param {(Function|Object)} handler The listener to add\n * @param {Object} [options] An options object specifies characteristics about\n * the event listener\n * @param {Boolean} [options.once=false] A `Boolean` indicating that the\n * listener should be invoked at most once after being added. If `true`,\n * the listener would be automatically removed when invoked.\n * @public\n */\n addEventListener(type, handler, options = {}) {\n for (const listener of this.listeners(type)) {\n if (\n !options[kForOnEventAttribute] &&\n listener[kListener] === handler &&\n !listener[kForOnEventAttribute]\n ) {\n return;\n }\n }\n\n let wrapper;\n\n if (type === 'message') {\n wrapper = function onMessage(data, isBinary) {\n const event = new MessageEvent('message', {\n data: isBinary ? data : data.toString()\n });\n\n event[kTarget] = this;\n callListener(handler, this, event);\n };\n } else if (type === 'close') {\n wrapper = function onClose(code, message) {\n const event = new CloseEvent('close', {\n code,\n reason: message.toString(),\n wasClean: this._closeFrameReceived && this._closeFrameSent\n });\n\n event[kTarget] = this;\n callListener(handler, this, event);\n };\n } else if (type === 'error') {\n wrapper = function onError(error) {\n const event = new ErrorEvent('error', {\n error,\n message: error.message\n });\n\n event[kTarget] = this;\n callListener(handler, this, event);\n };\n } else if (type === 'open') {\n wrapper = function onOpen() {\n const event = new Event('open');\n\n event[kTarget] = this;\n callListener(handler, this, event);\n };\n } else {\n return;\n }\n\n wrapper[kForOnEventAttribute] = !!options[kForOnEventAttribute];\n wrapper[kListener] = handler;\n\n if (options.once) {\n this.once(type, wrapper);\n } else {\n this.on(type, wrapper);\n }\n },\n\n /**\n * Remove an event listener.\n *\n * @param {String} type A string representing the event type to remove\n * @param {(Function|Object)} handler The listener to remove\n * @public\n */\n removeEventListener(type, handler) {\n for (const listener of this.listeners(type)) {\n if (listener[kListener] === handler && !listener[kForOnEventAttribute]) {\n this.removeListener(type, listener);\n break;\n }\n }\n }\n};\n\nmodule.exports = {\n CloseEvent,\n ErrorEvent,\n Event,\n EventTarget,\n MessageEvent\n};\n\n/**\n * Call an event listener\n *\n * @param {(Function|Object)} listener The listener to call\n * @param {*} thisArg The value to use as `this`` when calling the listener\n * @param {Event} event The event to pass to the listener\n * @private\n */\nfunction callListener(listener, thisArg, event) {\n if (typeof listener === 'object' && listener.handleEvent) {\n listener.handleEvent.call(listener, event);\n } else {\n listener.call(thisArg, event);\n }\n}\n","'use strict';\n\nconst { tokenChars } = require('./validation');\n\n/**\n * Adds an offer to the map of extension offers or a parameter to the map of\n * parameters.\n *\n * @param {Object} dest The map of extension offers or parameters\n * @param {String} name The extension or parameter name\n * @param {(Object|Boolean|String)} elem The extension parameters or the\n * parameter value\n * @private\n */\nfunction push(dest, name, elem) {\n if (dest[name] === undefined) dest[name] = [elem];\n else dest[name].push(elem);\n}\n\n/**\n * Parses the `Sec-WebSocket-Extensions` header into an object.\n *\n * @param {String} header The field value of the header\n * @return {Object} The parsed object\n * @public\n */\nfunction parse(header) {\n const offers = Object.create(null);\n let params = Object.create(null);\n let mustUnescape = false;\n let isEscaping = false;\n let inQuotes = false;\n let extensionName;\n let paramName;\n let start = -1;\n let code = -1;\n let end = -1;\n let i = 0;\n\n for (; i < header.length; i++) {\n code = header.charCodeAt(i);\n\n if (extensionName === undefined) {\n if (end === -1 && tokenChars[code] === 1) {\n if (start === -1) start = i;\n } else if (\n i !== 0 &&\n (code === 0x20 /* ' ' */ || code === 0x09) /* '\\t' */\n ) {\n if (end === -1 && start !== -1) end = i;\n } else if (code === 0x3b /* ';' */ || code === 0x2c /* ',' */) {\n if (start === -1) {\n throw new SyntaxError(`Unexpected character at index ${i}`);\n }\n\n if (end === -1) end = i;\n const name = header.slice(start, end);\n if (code === 0x2c) {\n push(offers, name, params);\n params = Object.create(null);\n } else {\n extensionName = name;\n }\n\n start = end = -1;\n } else {\n throw new SyntaxError(`Unexpected character at index ${i}`);\n }\n } else if (paramName === undefined) {\n if (end === -1 && tokenChars[code] === 1) {\n if (start === -1) start = i;\n } else if (code === 0x20 || code === 0x09) {\n if (end === -1 && start !== -1) end = i;\n } else if (code === 0x3b || code === 0x2c) {\n if (start === -1) {\n throw new SyntaxError(`Unexpected character at index ${i}`);\n }\n\n if (end === -1) end = i;\n push(params, header.slice(start, end), true);\n if (code === 0x2c) {\n push(offers, extensionName, params);\n params = Object.create(null);\n extensionName = undefined;\n }\n\n start = end = -1;\n } else if (code === 0x3d /* '=' */ && start !== -1 && end === -1) {\n paramName = header.slice(start, i);\n start = end = -1;\n } else {\n throw new SyntaxError(`Unexpected character at index ${i}`);\n }\n } else {\n //\n // The value of a quoted-string after unescaping must conform to the\n // token ABNF, so only token characters are valid.\n // Ref: https://tools.ietf.org/html/rfc6455#section-9.1\n //\n if (isEscaping) {\n if (tokenChars[code] !== 1) {\n throw new SyntaxError(`Unexpected character at index ${i}`);\n }\n if (start === -1) start = i;\n else if (!mustUnescape) mustUnescape = true;\n isEscaping = false;\n } else if (inQuotes) {\n if (tokenChars[code] === 1) {\n if (start === -1) start = i;\n } else if (code === 0x22 /* '\"' */ && start !== -1) {\n inQuotes = false;\n end = i;\n } else if (code === 0x5c /* '\\' */) {\n isEscaping = true;\n } else {\n throw new SyntaxError(`Unexpected character at index ${i}`);\n }\n } else if (code === 0x22 && header.charCodeAt(i - 1) === 0x3d) {\n inQuotes = true;\n } else if (end === -1 && tokenChars[code] === 1) {\n if (start === -1) start = i;\n } else if (start !== -1 && (code === 0x20 || code === 0x09)) {\n if (end === -1) end = i;\n } else if (code === 0x3b || code === 0x2c) {\n if (start === -1) {\n throw new SyntaxError(`Unexpected character at index ${i}`);\n }\n\n if (end === -1) end = i;\n let value = header.slice(start, end);\n if (mustUnescape) {\n value = value.replace(/\\\\/g, '');\n mustUnescape = false;\n }\n push(params, paramName, value);\n if (code === 0x2c) {\n push(offers, extensionName, params);\n params = Object.create(null);\n extensionName = undefined;\n }\n\n paramName = undefined;\n start = end = -1;\n } else {\n throw new SyntaxError(`Unexpected character at index ${i}`);\n }\n }\n }\n\n if (start === -1 || inQuotes || code === 0x20 || code === 0x09) {\n throw new SyntaxError('Unexpected end of input');\n }\n\n if (end === -1) end = i;\n const token = header.slice(start, end);\n if (extensionName === undefined) {\n push(offers, token, params);\n } else {\n if (paramName === undefined) {\n push(params, token, true);\n } else if (mustUnescape) {\n push(params, paramName, token.replace(/\\\\/g, ''));\n } else {\n push(params, paramName, token);\n }\n push(offers, extensionName, params);\n }\n\n return offers;\n}\n\n/**\n * Builds the `Sec-WebSocket-Extensions` header field value.\n *\n * @param {Object} extensions The map of extensions and parameters to format\n * @return {String} A string representing the given object\n * @public\n */\nfunction format(extensions) {\n return Object.keys(extensions)\n .map((extension) => {\n let configurations = extensions[extension];\n if (!Array.isArray(configurations)) configurations = [configurations];\n return configurations\n .map((params) => {\n return [extension]\n .concat(\n Object.keys(params).map((k) => {\n let values = params[k];\n if (!Array.isArray(values)) values = [values];\n return values\n .map((v) => (v === true ? k : `${k}=${v}`))\n .join('; ');\n })\n )\n .join('; ');\n })\n .join(', ');\n })\n .join(', ');\n}\n\nmodule.exports = { format, parse };\n","/* eslint no-unused-vars: [\"error\", { \"varsIgnorePattern\": \"^Duplex|Readable$\", \"caughtErrors\": \"none\" }] */\n\n'use strict';\n\nconst EventEmitter = require('events');\nconst https = require('https');\nconst http = require('http');\nconst net = require('net');\nconst tls = require('tls');\nconst { randomBytes, createHash } = require('crypto');\nconst { Duplex, Readable } = require('stream');\nconst { URL } = require('url');\n\nconst PerMessageDeflate = require('./permessage-deflate');\nconst Receiver = require('./receiver');\nconst Sender = require('./sender');\nconst { isBlob } = require('./validation');\n\nconst {\n BINARY_TYPES,\n CLOSE_TIMEOUT,\n EMPTY_BUFFER,\n GUID,\n kForOnEventAttribute,\n kListener,\n kStatusCode,\n kWebSocket,\n NOOP\n} = require('./constants');\nconst {\n EventTarget: { addEventListener, removeEventListener }\n} = require('./event-target');\nconst { format, parse } = require('./extension');\nconst { toBuffer } = require('./buffer-util');\n\nconst kAborted = Symbol('kAborted');\nconst protocolVersions = [8, 13];\nconst readyStates = ['CONNECTING', 'OPEN', 'CLOSING', 'CLOSED'];\nconst subprotocolRegex = /^[!#$%&'*+\\-.0-9A-Z^_`|a-z~]+$/;\n\n/**\n * Class representing a WebSocket.\n *\n * @extends EventEmitter\n */\nclass WebSocket extends EventEmitter {\n /**\n * Create a new `WebSocket`.\n *\n * @param {(String|URL)} address The URL to which to connect\n * @param {(String|String[])} [protocols] The subprotocols\n * @param {Object} [options] Connection options\n */\n constructor(address, protocols, options) {\n super();\n\n this._binaryType = BINARY_TYPES[0];\n this._closeCode = 1006;\n this._closeFrameReceived = false;\n this._closeFrameSent = false;\n this._closeMessage = EMPTY_BUFFER;\n this._closeTimer = null;\n this._errorEmitted = false;\n this._extensions = {};\n this._paused = false;\n this._protocol = '';\n this._readyState = WebSocket.CONNECTING;\n this._receiver = null;\n this._sender = null;\n this._socket = null;\n\n if (address !== null) {\n this._bufferedAmount = 0;\n this._isServer = false;\n this._redirects = 0;\n\n if (protocols === undefined) {\n protocols = [];\n } else if (!Array.isArray(protocols)) {\n if (typeof protocols === 'object' && protocols !== null) {\n options = protocols;\n protocols = [];\n } else {\n protocols = [protocols];\n }\n }\n\n initAsClient(this, address, protocols, options);\n } else {\n this._autoPong = options.autoPong;\n this._closeTimeout = options.closeTimeout;\n this._isServer = true;\n }\n }\n\n /**\n * For historical reasons, the custom \"nodebuffer\" type is used by the default\n * instead of \"blob\".\n *\n * @type {String}\n */\n get binaryType() {\n return this._binaryType;\n }\n\n set binaryType(type) {\n if (!BINARY_TYPES.includes(type)) return;\n\n this._binaryType = type;\n\n //\n // Allow to change `binaryType` on the fly.\n //\n if (this._receiver) this._receiver._binaryType = type;\n }\n\n /**\n * @type {Number}\n */\n get bufferedAmount() {\n if (!this._socket) return this._bufferedAmount;\n\n return this._socket._writableState.length + this._sender._bufferedBytes;\n }\n\n /**\n * @type {String}\n */\n get extensions() {\n return Object.keys(this._extensions).join();\n }\n\n /**\n * @type {Boolean}\n */\n get isPaused() {\n return this._paused;\n }\n\n /**\n * @type {Function}\n */\n /* istanbul ignore next */\n get onclose() {\n return null;\n }\n\n /**\n * @type {Function}\n */\n /* istanbul ignore next */\n get onerror() {\n return null;\n }\n\n /**\n * @type {Function}\n */\n /* istanbul ignore next */\n get onopen() {\n return null;\n }\n\n /**\n * @type {Function}\n */\n /* istanbul ignore next */\n get onmessage() {\n return null;\n }\n\n /**\n * @type {String}\n */\n get protocol() {\n return this._protocol;\n }\n\n /**\n * @type {Number}\n */\n get readyState() {\n return this._readyState;\n }\n\n /**\n * @type {String}\n */\n get url() {\n return this._url;\n }\n\n /**\n * Set up the socket and the internal resources.\n *\n * @param {Duplex} socket The network socket between the server and client\n * @param {Buffer} head The first packet of the upgraded stream\n * @param {Object} options Options object\n * @param {Boolean} [options.allowSynchronousEvents=false] Specifies whether\n * any of the `'message'`, `'ping'`, and `'pong'` events can be emitted\n * multiple times in the same tick\n * @param {Function} [options.generateMask] The function used to generate the\n * masking key\n * @param {Number} [options.maxPayload=0] The maximum allowed message size\n * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or\n * not to skip UTF-8 validation for text and close messages\n * @private\n */\n setSocket(socket, head, options) {\n const receiver = new Receiver({\n allowSynchronousEvents: options.allowSynchronousEvents,\n binaryType: this.binaryType,\n extensions: this._extensions,\n isServer: this._isServer,\n maxPayload: options.maxPayload,\n skipUTF8Validation: options.skipUTF8Validation\n });\n\n const sender = new Sender(socket, this._extensions, options.generateMask);\n\n this._receiver = receiver;\n this._sender = sender;\n this._socket = socket;\n\n receiver[kWebSocket] = this;\n sender[kWebSocket] = this;\n socket[kWebSocket] = this;\n\n receiver.on('conclude', receiverOnConclude);\n receiver.on('drain', receiverOnDrain);\n receiver.on('error', receiverOnError);\n receiver.on('message', receiverOnMessage);\n receiver.on('ping', receiverOnPing);\n receiver.on('pong', receiverOnPong);\n\n sender.onerror = senderOnError;\n\n //\n // These methods may not be available if `socket` is just a `Duplex`.\n //\n if (socket.setTimeout) socket.setTimeout(0);\n if (socket.setNoDelay) socket.setNoDelay();\n\n if (head.length > 0) socket.unshift(head);\n\n socket.on('close', socketOnClose);\n socket.on('data', socketOnData);\n socket.on('end', socketOnEnd);\n socket.on('error', socketOnError);\n\n this._readyState = WebSocket.OPEN;\n this.emit('open');\n }\n\n /**\n * Emit the `'close'` event.\n *\n * @private\n */\n emitClose() {\n if (!this._socket) {\n this._readyState = WebSocket.CLOSED;\n this.emit('close', this._closeCode, this._closeMessage);\n return;\n }\n\n if (this._extensions[PerMessageDeflate.extensionName]) {\n this._extensions[PerMessageDeflate.extensionName].cleanup();\n }\n\n this._receiver.removeAllListeners();\n this._readyState = WebSocket.CLOSED;\n this.emit('close', this._closeCode, this._closeMessage);\n }\n\n /**\n * Start a closing handshake.\n *\n * +----------+ +-----------+ +----------+\n * - - -|ws.close()|-->|close frame|-->|ws.close()|- - -\n * | +----------+ +-----------+ +----------+ |\n * +----------+ +-----------+ |\n * CLOSING |ws.close()|<--|close frame|<--+-----+ CLOSING\n * +----------+ +-----------+ |\n * | | | +---+ |\n * +------------------------+-->|fin| - - - -\n * | +---+ | +---+\n * - - - - -|fin|<---------------------+\n * +---+\n *\n * @param {Number} [code] Status code explaining why the connection is closing\n * @param {(String|Buffer)} [data] The reason why the connection is\n * closing\n * @public\n */\n close(code, data) {\n if (this.readyState === WebSocket.CLOSED) return;\n if (this.readyState === WebSocket.CONNECTING) {\n const msg = 'WebSocket was closed before the connection was established';\n abortHandshake(this, this._req, msg);\n return;\n }\n\n if (this.readyState === WebSocket.CLOSING) {\n if (\n this._closeFrameSent &&\n (this._closeFrameReceived || this._receiver._writableState.errorEmitted)\n ) {\n this._socket.end();\n }\n\n return;\n }\n\n this._readyState = WebSocket.CLOSING;\n this._sender.close(code, data, !this._isServer, (err) => {\n //\n // This error is handled by the `'error'` listener on the socket. We only\n // want to know if the close frame has been sent here.\n //\n if (err) return;\n\n this._closeFrameSent = true;\n\n if (\n this._closeFrameReceived ||\n this._receiver._writableState.errorEmitted\n ) {\n this._socket.end();\n }\n });\n\n setCloseTimer(this);\n }\n\n /**\n * Pause the socket.\n *\n * @public\n */\n pause() {\n if (\n this.readyState === WebSocket.CONNECTING ||\n this.readyState === WebSocket.CLOSED\n ) {\n return;\n }\n\n this._paused = true;\n this._socket.pause();\n }\n\n /**\n * Send a ping.\n *\n * @param {*} [data] The data to send\n * @param {Boolean} [mask] Indicates whether or not to mask `data`\n * @param {Function} [cb] Callback which is executed when the ping is sent\n * @public\n */\n ping(data, mask, cb) {\n if (this.readyState === WebSocket.CONNECTING) {\n throw new Error('WebSocket is not open: readyState 0 (CONNECTING)');\n }\n\n if (typeof data === 'function') {\n cb = data;\n data = mask = undefined;\n } else if (typeof mask === 'function') {\n cb = mask;\n mask = undefined;\n }\n\n if (typeof data === 'number') data = data.toString();\n\n if (this.readyState !== WebSocket.OPEN) {\n sendAfterClose(this, data, cb);\n return;\n }\n\n if (mask === undefined) mask = !this._isServer;\n this._sender.ping(data || EMPTY_BUFFER, mask, cb);\n }\n\n /**\n * Send a pong.\n *\n * @param {*} [data] The data to send\n * @param {Boolean} [mask] Indicates whether or not to mask `data`\n * @param {Function} [cb] Callback which is executed when the pong is sent\n * @public\n */\n pong(data, mask, cb) {\n if (this.readyState === WebSocket.CONNECTING) {\n throw new Error('WebSocket is not open: readyState 0 (CONNECTING)');\n }\n\n if (typeof data === 'function') {\n cb = data;\n data = mask = undefined;\n } else if (typeof mask === 'function') {\n cb = mask;\n mask = undefined;\n }\n\n if (typeof data === 'number') data = data.toString();\n\n if (this.readyState !== WebSocket.OPEN) {\n sendAfterClose(this, data, cb);\n return;\n }\n\n if (mask === undefined) mask = !this._isServer;\n this._sender.pong(data || EMPTY_BUFFER, mask, cb);\n }\n\n /**\n * Resume the socket.\n *\n * @public\n */\n resume() {\n if (\n this.readyState === WebSocket.CONNECTING ||\n this.readyState === WebSocket.CLOSED\n ) {\n return;\n }\n\n this._paused = false;\n if (!this._receiver._writableState.needDrain) this._socket.resume();\n }\n\n /**\n * Send a data message.\n *\n * @param {*} data The message to send\n * @param {Object} [options] Options object\n * @param {Boolean} [options.binary] Specifies whether `data` is binary or\n * text\n * @param {Boolean} [options.compress] Specifies whether or not to compress\n * `data`\n * @param {Boolean} [options.fin=true] Specifies whether the fragment is the\n * last one\n * @param {Boolean} [options.mask] Specifies whether or not to mask `data`\n * @param {Function} [cb] Callback which is executed when data is written out\n * @public\n */\n send(data, options, cb) {\n if (this.readyState === WebSocket.CONNECTING) {\n throw new Error('WebSocket is not open: readyState 0 (CONNECTING)');\n }\n\n if (typeof options === 'function') {\n cb = options;\n options = {};\n }\n\n if (typeof data === 'number') data = data.toString();\n\n if (this.readyState !== WebSocket.OPEN) {\n sendAfterClose(this, data, cb);\n return;\n }\n\n const opts = {\n binary: typeof data !== 'string',\n mask: !this._isServer,\n compress: true,\n fin: true,\n ...options\n };\n\n if (!this._extensions[PerMessageDeflate.extensionName]) {\n opts.compress = false;\n }\n\n this._sender.send(data || EMPTY_BUFFER, opts, cb);\n }\n\n /**\n * Forcibly close the connection.\n *\n * @public\n */\n terminate() {\n if (this.readyState === WebSocket.CLOSED) return;\n if (this.readyState === WebSocket.CONNECTING) {\n const msg = 'WebSocket was closed before the connection was established';\n abortHandshake(this, this._req, msg);\n return;\n }\n\n if (this._socket) {\n this._readyState = WebSocket.CLOSING;\n this._socket.destroy();\n }\n }\n}\n\n/**\n * @constant {Number} CONNECTING\n * @memberof WebSocket\n */\nObject.defineProperty(WebSocket, 'CONNECTING', {\n enumerable: true,\n value: readyStates.indexOf('CONNECTING')\n});\n\n/**\n * @constant {Number} CONNECTING\n * @memberof WebSocket.prototype\n */\nObject.defineProperty(WebSocket.prototype, 'CONNECTING', {\n enumerable: true,\n value: readyStates.indexOf('CONNECTING')\n});\n\n/**\n * @constant {Number} OPEN\n * @memberof WebSocket\n */\nObject.defineProperty(WebSocket, 'OPEN', {\n enumerable: true,\n value: readyStates.indexOf('OPEN')\n});\n\n/**\n * @constant {Number} OPEN\n * @memberof WebSocket.prototype\n */\nObject.defineProperty(WebSocket.prototype, 'OPEN', {\n enumerable: true,\n value: readyStates.indexOf('OPEN')\n});\n\n/**\n * @constant {Number} CLOSING\n * @memberof WebSocket\n */\nObject.defineProperty(WebSocket, 'CLOSING', {\n enumerable: true,\n value: readyStates.indexOf('CLOSING')\n});\n\n/**\n * @constant {Number} CLOSING\n * @memberof WebSocket.prototype\n */\nObject.defineProperty(WebSocket.prototype, 'CLOSING', {\n enumerable: true,\n value: readyStates.indexOf('CLOSING')\n});\n\n/**\n * @constant {Number} CLOSED\n * @memberof WebSocket\n */\nObject.defineProperty(WebSocket, 'CLOSED', {\n enumerable: true,\n value: readyStates.indexOf('CLOSED')\n});\n\n/**\n * @constant {Number} CLOSED\n * @memberof WebSocket.prototype\n */\nObject.defineProperty(WebSocket.prototype, 'CLOSED', {\n enumerable: true,\n value: readyStates.indexOf('CLOSED')\n});\n\n[\n 'binaryType',\n 'bufferedAmount',\n 'extensions',\n 'isPaused',\n 'protocol',\n 'readyState',\n 'url'\n].forEach((property) => {\n Object.defineProperty(WebSocket.prototype, property, { enumerable: true });\n});\n\n//\n// Add the `onopen`, `onerror`, `onclose`, and `onmessage` attributes.\n// See https://html.spec.whatwg.org/multipage/comms.html#the-websocket-interface\n//\n['open', 'error', 'close', 'message'].forEach((method) => {\n Object.defineProperty(WebSocket.prototype, `on${method}`, {\n enumerable: true,\n get() {\n for (const listener of this.listeners(method)) {\n if (listener[kForOnEventAttribute]) return listener[kListener];\n }\n\n return null;\n },\n set(handler) {\n for (const listener of this.listeners(method)) {\n if (listener[kForOnEventAttribute]) {\n this.removeListener(method, listener);\n break;\n }\n }\n\n if (typeof handler !== 'function') return;\n\n this.addEventListener(method, handler, {\n [kForOnEventAttribute]: true\n });\n }\n });\n});\n\nWebSocket.prototype.addEventListener = addEventListener;\nWebSocket.prototype.removeEventListener = removeEventListener;\n\nmodule.exports = WebSocket;\n\n/**\n * Initialize a WebSocket client.\n *\n * @param {WebSocket} websocket The client to initialize\n * @param {(String|URL)} address The URL to which to connect\n * @param {Array} protocols The subprotocols\n * @param {Object} [options] Connection options\n * @param {Boolean} [options.allowSynchronousEvents=true] Specifies whether any\n * of the `'message'`, `'ping'`, and `'pong'` events can be emitted multiple\n * times in the same tick\n * @param {Boolean} [options.autoPong=true] Specifies whether or not to\n * automatically send a pong in response to a ping\n * @param {Number} [options.closeTimeout=30000] Duration in milliseconds to wait\n * for the closing handshake to finish after `websocket.close()` is called\n * @param {Function} [options.finishRequest] A function which can be used to\n * customize the headers of each http request before it is sent\n * @param {Boolean} [options.followRedirects=false] Whether or not to follow\n * redirects\n * @param {Function} [options.generateMask] The function used to generate the\n * masking key\n * @param {Number} [options.handshakeTimeout] Timeout in milliseconds for the\n * handshake request\n * @param {Number} [options.maxPayload=104857600] The maximum allowed message\n * size\n * @param {Number} [options.maxRedirects=10] The maximum number of redirects\n * allowed\n * @param {String} [options.origin] Value of the `Origin` or\n * `Sec-WebSocket-Origin` header\n * @param {(Boolean|Object)} [options.perMessageDeflate=true] Enable/disable\n * permessage-deflate\n * @param {Number} [options.protocolVersion=13] Value of the\n * `Sec-WebSocket-Version` header\n * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or\n * not to skip UTF-8 validation for text and close messages\n * @private\n */\nfunction initAsClient(websocket, address, protocols, options) {\n const opts = {\n allowSynchronousEvents: true,\n autoPong: true,\n closeTimeout: CLOSE_TIMEOUT,\n protocolVersion: protocolVersions[1],\n maxPayload: 100 * 1024 * 1024,\n skipUTF8Validation: false,\n perMessageDeflate: true,\n followRedirects: false,\n maxRedirects: 10,\n ...options,\n socketPath: undefined,\n hostname: undefined,\n protocol: undefined,\n timeout: undefined,\n method: 'GET',\n host: undefined,\n path: undefined,\n port: undefined\n };\n\n websocket._autoPong = opts.autoPong;\n websocket._closeTimeout = opts.closeTimeout;\n\n if (!protocolVersions.includes(opts.protocolVersion)) {\n throw new RangeError(\n `Unsupported protocol version: ${opts.protocolVersion} ` +\n `(supported versions: ${protocolVersions.join(', ')})`\n );\n }\n\n let parsedUrl;\n\n if (address instanceof URL) {\n parsedUrl = address;\n } else {\n try {\n parsedUrl = new URL(address);\n } catch (e) {\n throw new SyntaxError(`Invalid URL: ${address}`);\n }\n }\n\n if (parsedUrl.protocol === 'http:') {\n parsedUrl.protocol = 'ws:';\n } else if (parsedUrl.protocol === 'https:') {\n parsedUrl.protocol = 'wss:';\n }\n\n websocket._url = parsedUrl.href;\n\n const isSecure = parsedUrl.protocol === 'wss:';\n const isIpcUrl = parsedUrl.protocol === 'ws+unix:';\n let invalidUrlMessage;\n\n if (parsedUrl.protocol !== 'ws:' && !isSecure && !isIpcUrl) {\n invalidUrlMessage =\n 'The URL\\'s protocol must be one of \"ws:\", \"wss:\", ' +\n '\"http:\", \"https:\", or \"ws+unix:\"';\n } else if (isIpcUrl && !parsedUrl.pathname) {\n invalidUrlMessage = \"The URL's pathname is empty\";\n } else if (parsedUrl.hash) {\n invalidUrlMessage = 'The URL contains a fragment identifier';\n }\n\n if (invalidUrlMessage) {\n const err = new SyntaxError(invalidUrlMessage);\n\n if (websocket._redirects === 0) {\n throw err;\n } else {\n emitErrorAndClose(websocket, err);\n return;\n }\n }\n\n const defaultPort = isSecure ? 443 : 80;\n const key = randomBytes(16).toString('base64');\n const request = isSecure ? https.request : http.request;\n const protocolSet = new Set();\n let perMessageDeflate;\n\n opts.createConnection =\n opts.createConnection || (isSecure ? tlsConnect : netConnect);\n opts.defaultPort = opts.defaultPort || defaultPort;\n opts.port = parsedUrl.port || defaultPort;\n opts.host = parsedUrl.hostname.startsWith('[')\n ? parsedUrl.hostname.slice(1, -1)\n : parsedUrl.hostname;\n opts.headers = {\n ...opts.headers,\n 'Sec-WebSocket-Version': opts.protocolVersion,\n 'Sec-WebSocket-Key': key,\n Connection: 'Upgrade',\n Upgrade: 'websocket'\n };\n opts.path = parsedUrl.pathname + parsedUrl.search;\n opts.timeout = opts.handshakeTimeout;\n\n if (opts.perMessageDeflate) {\n perMessageDeflate = new PerMessageDeflate(\n opts.perMessageDeflate !== true ? opts.perMessageDeflate : {},\n false,\n opts.maxPayload\n );\n opts.headers['Sec-WebSocket-Extensions'] = format({\n [PerMessageDeflate.extensionName]: perMessageDeflate.offer()\n });\n }\n if (protocols.length) {\n for (const protocol of protocols) {\n if (\n typeof protocol !== 'string' ||\n !subprotocolRegex.test(protocol) ||\n protocolSet.has(protocol)\n ) {\n throw new SyntaxError(\n 'An invalid or duplicated subprotocol was specified'\n );\n }\n\n protocolSet.add(protocol);\n }\n\n opts.headers['Sec-WebSocket-Protocol'] = protocols.join(',');\n }\n if (opts.origin) {\n if (opts.protocolVersion < 13) {\n opts.headers['Sec-WebSocket-Origin'] = opts.origin;\n } else {\n opts.headers.Origin = opts.origin;\n }\n }\n if (parsedUrl.username || parsedUrl.password) {\n opts.auth = `${parsedUrl.username}:${parsedUrl.password}`;\n }\n\n if (isIpcUrl) {\n const parts = opts.path.split(':');\n\n opts.socketPath = parts[0];\n opts.path = parts[1];\n }\n\n let req;\n\n if (opts.followRedirects) {\n if (websocket._redirects === 0) {\n websocket._originalIpc = isIpcUrl;\n websocket._originalSecure = isSecure;\n websocket._originalHostOrSocketPath = isIpcUrl\n ? opts.socketPath\n : parsedUrl.host;\n\n const headers = options && options.headers;\n\n //\n // Shallow copy the user provided options so that headers can be changed\n // without mutating the original object.\n //\n options = { ...options, headers: {} };\n\n if (headers) {\n for (const [key, value] of Object.entries(headers)) {\n options.headers[key.toLowerCase()] = value;\n }\n }\n } else if (websocket.listenerCount('redirect') === 0) {\n const isSameHost = isIpcUrl\n ? websocket._originalIpc\n ? opts.socketPath === websocket._originalHostOrSocketPath\n : false\n : websocket._originalIpc\n ? false\n : parsedUrl.host === websocket._originalHostOrSocketPath;\n\n if (!isSameHost || (websocket._originalSecure && !isSecure)) {\n //\n // Match curl 7.77.0 behavior and drop the following headers. These\n // headers are also dropped when following a redirect to a subdomain.\n //\n delete opts.headers.authorization;\n delete opts.headers.cookie;\n\n if (!isSameHost) delete opts.headers.host;\n\n opts.auth = undefined;\n }\n }\n\n //\n // Match curl 7.77.0 behavior and make the first `Authorization` header win.\n // If the `Authorization` header is set, then there is nothing to do as it\n // will take precedence.\n //\n if (opts.auth && !options.headers.authorization) {\n options.headers.authorization =\n 'Basic ' + Buffer.from(opts.auth).toString('base64');\n }\n\n req = websocket._req = request(opts);\n\n if (websocket._redirects) {\n //\n // Unlike what is done for the `'upgrade'` event, no early exit is\n // triggered here if the user calls `websocket.close()` or\n // `websocket.terminate()` from a listener of the `'redirect'` event. This\n // is because the user can also call `request.destroy()` with an error\n // before calling `websocket.close()` or `websocket.terminate()` and this\n // would result in an error being emitted on the `request` object with no\n // `'error'` event listeners attached.\n //\n websocket.emit('redirect', websocket.url, req);\n }\n } else {\n req = websocket._req = request(opts);\n }\n\n if (opts.timeout) {\n req.on('timeout', () => {\n abortHandshake(websocket, req, 'Opening handshake has timed out');\n });\n }\n\n req.on('error', (err) => {\n if (req === null || req[kAborted]) return;\n\n req = websocket._req = null;\n emitErrorAndClose(websocket, err);\n });\n\n req.on('response', (res) => {\n const location = res.headers.location;\n const statusCode = res.statusCode;\n\n if (\n location &&\n opts.followRedirects &&\n statusCode >= 300 &&\n statusCode < 400\n ) {\n if (++websocket._redirects > opts.maxRedirects) {\n abortHandshake(websocket, req, 'Maximum redirects exceeded');\n return;\n }\n\n req.abort();\n\n let addr;\n\n try {\n addr = new URL(location, address);\n } catch (e) {\n const err = new SyntaxError(`Invalid URL: ${location}`);\n emitErrorAndClose(websocket, err);\n return;\n }\n\n initAsClient(websocket, addr, protocols, options);\n } else if (!websocket.emit('unexpected-response', req, res)) {\n abortHandshake(\n websocket,\n req,\n `Unexpected server response: ${res.statusCode}`\n );\n }\n });\n\n req.on('upgrade', (res, socket, head) => {\n websocket.emit('upgrade', res);\n\n //\n // The user may have closed the connection from a listener of the\n // `'upgrade'` event.\n //\n if (websocket.readyState !== WebSocket.CONNECTING) return;\n\n req = websocket._req = null;\n\n const upgrade = res.headers.upgrade;\n\n if (upgrade === undefined || upgrade.toLowerCase() !== 'websocket') {\n abortHandshake(websocket, socket, 'Invalid Upgrade header');\n return;\n }\n\n const digest = createHash('sha1')\n .update(key + GUID)\n .digest('base64');\n\n if (res.headers['sec-websocket-accept'] !== digest) {\n abortHandshake(websocket, socket, 'Invalid Sec-WebSocket-Accept header');\n return;\n }\n\n const serverProt = res.headers['sec-websocket-protocol'];\n let protError;\n\n if (serverProt !== undefined) {\n if (!protocolSet.size) {\n protError = 'Server sent a subprotocol but none was requested';\n } else if (!protocolSet.has(serverProt)) {\n protError = 'Server sent an invalid subprotocol';\n }\n } else if (protocolSet.size) {\n protError = 'Server sent no subprotocol';\n }\n\n if (protError) {\n abortHandshake(websocket, socket, protError);\n return;\n }\n\n if (serverProt) websocket._protocol = serverProt;\n\n const secWebSocketExtensions = res.headers['sec-websocket-extensions'];\n\n if (secWebSocketExtensions !== undefined) {\n if (!perMessageDeflate) {\n const message =\n 'Server sent a Sec-WebSocket-Extensions header but no extension ' +\n 'was requested';\n abortHandshake(websocket, socket, message);\n return;\n }\n\n let extensions;\n\n try {\n extensions = parse(secWebSocketExtensions);\n } catch (err) {\n const message = 'Invalid Sec-WebSocket-Extensions header';\n abortHandshake(websocket, socket, message);\n return;\n }\n\n const extensionNames = Object.keys(extensions);\n\n if (\n extensionNames.length !== 1 ||\n extensionNames[0] !== PerMessageDeflate.extensionName\n ) {\n const message = 'Server indicated an extension that was not requested';\n abortHandshake(websocket, socket, message);\n return;\n }\n\n try {\n perMessageDeflate.accept(extensions[PerMessageDeflate.extensionName]);\n } catch (err) {\n const message = 'Invalid Sec-WebSocket-Extensions header';\n abortHandshake(websocket, socket, message);\n return;\n }\n\n websocket._extensions[PerMessageDeflate.extensionName] =\n perMessageDeflate;\n }\n\n websocket.setSocket(socket, head, {\n allowSynchronousEvents: opts.allowSynchronousEvents,\n generateMask: opts.generateMask,\n maxPayload: opts.maxPayload,\n skipUTF8Validation: opts.skipUTF8Validation\n });\n });\n\n if (opts.finishRequest) {\n opts.finishRequest(req, websocket);\n } else {\n req.end();\n }\n}\n\n/**\n * Emit the `'error'` and `'close'` events.\n *\n * @param {WebSocket} websocket The WebSocket instance\n * @param {Error} The error to emit\n * @private\n */\nfunction emitErrorAndClose(websocket, err) {\n websocket._readyState = WebSocket.CLOSING;\n //\n // The following assignment is practically useless and is done only for\n // consistency.\n //\n websocket._errorEmitted = true;\n websocket.emit('error', err);\n websocket.emitClose();\n}\n\n/**\n * Create a `net.Socket` and initiate a connection.\n *\n * @param {Object} options Connection options\n * @return {net.Socket} The newly created socket used to start the connection\n * @private\n */\nfunction netConnect(options) {\n options.path = options.socketPath;\n return net.connect(options);\n}\n\n/**\n * Create a `tls.TLSSocket` and initiate a connection.\n *\n * @param {Object} options Connection options\n * @return {tls.TLSSocket} The newly created socket used to start the connection\n * @private\n */\nfunction tlsConnect(options) {\n options.path = undefined;\n\n if (!options.servername && options.servername !== '') {\n options.servername = net.isIP(options.host) ? '' : options.host;\n }\n\n return tls.connect(options);\n}\n\n/**\n * Abort the handshake and emit an error.\n *\n * @param {WebSocket} websocket The WebSocket instance\n * @param {(http.ClientRequest|net.Socket|tls.Socket)} stream The request to\n * abort or the socket to destroy\n * @param {String} message The error message\n * @private\n */\nfunction abortHandshake(websocket, stream, message) {\n websocket._readyState = WebSocket.CLOSING;\n\n const err = new Error(message);\n Error.captureStackTrace(err, abortHandshake);\n\n if (stream.setHeader) {\n stream[kAborted] = true;\n stream.abort();\n\n if (stream.socket && !stream.socket.destroyed) {\n //\n // On Node.js >= 14.3.0 `request.abort()` does not destroy the socket if\n // called after the request completed. See\n // https://github.com/websockets/ws/issues/1869.\n //\n stream.socket.destroy();\n }\n\n process.nextTick(emitErrorAndClose, websocket, err);\n } else {\n stream.destroy(err);\n stream.once('error', websocket.emit.bind(websocket, 'error'));\n stream.once('close', websocket.emitClose.bind(websocket));\n }\n}\n\n/**\n * Handle cases where the `ping()`, `pong()`, or `send()` methods are called\n * when the `readyState` attribute is `CLOSING` or `CLOSED`.\n *\n * @param {WebSocket} websocket The WebSocket instance\n * @param {*} [data] The data to send\n * @param {Function} [cb] Callback\n * @private\n */\nfunction sendAfterClose(websocket, data, cb) {\n if (data) {\n const length = isBlob(data) ? data.size : toBuffer(data).length;\n\n //\n // The `_bufferedAmount` property is used only when the peer is a client and\n // the opening handshake fails. Under these circumstances, in fact, the\n // `setSocket()` method is not called, so the `_socket` and `_sender`\n // properties are set to `null`.\n //\n if (websocket._socket) websocket._sender._bufferedBytes += length;\n else websocket._bufferedAmount += length;\n }\n\n if (cb) {\n const err = new Error(\n `WebSocket is not open: readyState ${websocket.readyState} ` +\n `(${readyStates[websocket.readyState]})`\n );\n process.nextTick(cb, err);\n }\n}\n\n/**\n * The listener of the `Receiver` `'conclude'` event.\n *\n * @param {Number} code The status code\n * @param {Buffer} reason The reason for closing\n * @private\n */\nfunction receiverOnConclude(code, reason) {\n const websocket = this[kWebSocket];\n\n websocket._closeFrameReceived = true;\n websocket._closeMessage = reason;\n websocket._closeCode = code;\n\n if (websocket._socket[kWebSocket] === undefined) return;\n\n websocket._socket.removeListener('data', socketOnData);\n process.nextTick(resume, websocket._socket);\n\n if (code === 1005) websocket.close();\n else websocket.close(code, reason);\n}\n\n/**\n * The listener of the `Receiver` `'drain'` event.\n *\n * @private\n */\nfunction receiverOnDrain() {\n const websocket = this[kWebSocket];\n\n if (!websocket.isPaused) websocket._socket.resume();\n}\n\n/**\n * The listener of the `Receiver` `'error'` event.\n *\n * @param {(RangeError|Error)} err The emitted error\n * @private\n */\nfunction receiverOnError(err) {\n const websocket = this[kWebSocket];\n\n if (websocket._socket[kWebSocket] !== undefined) {\n websocket._socket.removeListener('data', socketOnData);\n\n //\n // On Node.js < 14.0.0 the `'error'` event is emitted synchronously. See\n // https://github.com/websockets/ws/issues/1940.\n //\n process.nextTick(resume, websocket._socket);\n\n websocket.close(err[kStatusCode]);\n }\n\n if (!websocket._errorEmitted) {\n websocket._errorEmitted = true;\n websocket.emit('error', err);\n }\n}\n\n/**\n * The listener of the `Receiver` `'finish'` event.\n *\n * @private\n */\nfunction receiverOnFinish() {\n this[kWebSocket].emitClose();\n}\n\n/**\n * The listener of the `Receiver` `'message'` event.\n *\n * @param {Buffer|ArrayBuffer|Buffer[])} data The message\n * @param {Boolean} isBinary Specifies whether the message is binary or not\n * @private\n */\nfunction receiverOnMessage(data, isBinary) {\n this[kWebSocket].emit('message', data, isBinary);\n}\n\n/**\n * The listener of the `Receiver` `'ping'` event.\n *\n * @param {Buffer} data The data included in the ping frame\n * @private\n */\nfunction receiverOnPing(data) {\n const websocket = this[kWebSocket];\n\n if (websocket._autoPong) websocket.pong(data, !this._isServer, NOOP);\n websocket.emit('ping', data);\n}\n\n/**\n * The listener of the `Receiver` `'pong'` event.\n *\n * @param {Buffer} data The data included in the pong frame\n * @private\n */\nfunction receiverOnPong(data) {\n this[kWebSocket].emit('pong', data);\n}\n\n/**\n * Resume a readable stream\n *\n * @param {Readable} stream The readable stream\n * @private\n */\nfunction resume(stream) {\n stream.resume();\n}\n\n/**\n * The `Sender` error event handler.\n *\n * @param {Error} The error\n * @private\n */\nfunction senderOnError(err) {\n const websocket = this[kWebSocket];\n\n if (websocket.readyState === WebSocket.CLOSED) return;\n if (websocket.readyState === WebSocket.OPEN) {\n websocket._readyState = WebSocket.CLOSING;\n setCloseTimer(websocket);\n }\n\n //\n // `socket.end()` is used instead of `socket.destroy()` to allow the other\n // peer to finish sending queued data. There is no need to set a timer here\n // because `CLOSING` means that it is already set or not needed.\n //\n this._socket.end();\n\n if (!websocket._errorEmitted) {\n websocket._errorEmitted = true;\n websocket.emit('error', err);\n }\n}\n\n/**\n * Set a timer to destroy the underlying raw socket of a WebSocket.\n *\n * @param {WebSocket} websocket The WebSocket instance\n * @private\n */\nfunction setCloseTimer(websocket) {\n websocket._closeTimer = setTimeout(\n websocket._socket.destroy.bind(websocket._socket),\n websocket._closeTimeout\n );\n}\n\n/**\n * The listener of the socket `'close'` event.\n *\n * @private\n */\nfunction socketOnClose() {\n const websocket = this[kWebSocket];\n\n this.removeListener('close', socketOnClose);\n this.removeListener('data', socketOnData);\n this.removeListener('end', socketOnEnd);\n\n websocket._readyState = WebSocket.CLOSING;\n\n //\n // The close frame might not have been received or the `'end'` event emitted,\n // for example, if the socket was destroyed due to an error. Ensure that the\n // `receiver` stream is closed after writing any remaining buffered data to\n // it. If the readable side of the socket is in flowing mode then there is no\n // buffered data as everything has been already written. If instead, the\n // socket is paused, any possible buffered data will be read as a single\n // chunk.\n //\n if (\n !this._readableState.endEmitted &&\n !websocket._closeFrameReceived &&\n !websocket._receiver._writableState.errorEmitted &&\n this._readableState.length !== 0\n ) {\n const chunk = this.read(this._readableState.length);\n\n websocket._receiver.write(chunk);\n }\n\n websocket._receiver.end();\n\n this[kWebSocket] = undefined;\n\n clearTimeout(websocket._closeTimer);\n\n if (\n websocket._receiver._writableState.finished ||\n websocket._receiver._writableState.errorEmitted\n ) {\n websocket.emitClose();\n } else {\n websocket._receiver.on('error', receiverOnFinish);\n websocket._receiver.on('finish', receiverOnFinish);\n }\n}\n\n/**\n * The listener of the socket `'data'` event.\n *\n * @param {Buffer} chunk A chunk of data\n * @private\n */\nfunction socketOnData(chunk) {\n if (!this[kWebSocket]._receiver.write(chunk)) {\n this.pause();\n }\n}\n\n/**\n * The listener of the socket `'end'` event.\n *\n * @private\n */\nfunction socketOnEnd() {\n const websocket = this[kWebSocket];\n\n websocket._readyState = WebSocket.CLOSING;\n websocket._receiver.end();\n this.end();\n}\n\n/**\n * The listener of the socket `'error'` event.\n *\n * @private\n */\nfunction socketOnError() {\n const websocket = this[kWebSocket];\n\n this.removeListener('error', socketOnError);\n this.on('error', NOOP);\n\n if (websocket) {\n websocket._readyState = WebSocket.CLOSING;\n this.destroy();\n }\n}\n","/* eslint no-unused-vars: [\"error\", { \"varsIgnorePattern\": \"^WebSocket$\" }] */\n'use strict';\n\nconst WebSocket = require('./websocket');\nconst { Duplex } = require('stream');\n\n/**\n * Emits the `'close'` event on a stream.\n *\n * @param {Duplex} stream The stream.\n * @private\n */\nfunction emitClose(stream) {\n stream.emit('close');\n}\n\n/**\n * The listener of the `'end'` event.\n *\n * @private\n */\nfunction duplexOnEnd() {\n if (!this.destroyed && this._writableState.finished) {\n this.destroy();\n }\n}\n\n/**\n * The listener of the `'error'` event.\n *\n * @param {Error} err The error\n * @private\n */\nfunction duplexOnError(err) {\n this.removeListener('error', duplexOnError);\n this.destroy();\n if (this.listenerCount('error') === 0) {\n // Do not suppress the throwing behavior.\n this.emit('error', err);\n }\n}\n\n/**\n * Wraps a `WebSocket` in a duplex stream.\n *\n * @param {WebSocket} ws The `WebSocket` to wrap\n * @param {Object} [options] The options for the `Duplex` constructor\n * @return {Duplex} The duplex stream\n * @public\n */\nfunction createWebSocketStream(ws, options) {\n let terminateOnDestroy = true;\n\n const duplex = new Duplex({\n ...options,\n autoDestroy: false,\n emitClose: false,\n objectMode: false,\n writableObjectMode: false\n });\n\n ws.on('message', function message(msg, isBinary) {\n const data =\n !isBinary && duplex._readableState.objectMode ? msg.toString() : msg;\n\n if (!duplex.push(data)) ws.pause();\n });\n\n ws.once('error', function error(err) {\n if (duplex.destroyed) return;\n\n // Prevent `ws.terminate()` from being called by `duplex._destroy()`.\n //\n // - If the `'error'` event is emitted before the `'open'` event, then\n // `ws.terminate()` is a noop as no socket is assigned.\n // - Otherwise, the error is re-emitted by the listener of the `'error'`\n // event of the `Receiver` object. The listener already closes the\n // connection by calling `ws.close()`. This allows a close frame to be\n // sent to the other peer. If `ws.terminate()` is called right after this,\n // then the close frame might not be sent.\n terminateOnDestroy = false;\n duplex.destroy(err);\n });\n\n ws.once('close', function close() {\n if (duplex.destroyed) return;\n\n duplex.push(null);\n });\n\n duplex._destroy = function (err, callback) {\n if (ws.readyState === ws.CLOSED) {\n callback(err);\n process.nextTick(emitClose, duplex);\n return;\n }\n\n let called = false;\n\n ws.once('error', function error(err) {\n called = true;\n callback(err);\n });\n\n ws.once('close', function close() {\n if (!called) callback(err);\n process.nextTick(emitClose, duplex);\n });\n\n if (terminateOnDestroy) ws.terminate();\n };\n\n duplex._final = function (callback) {\n if (ws.readyState === ws.CONNECTING) {\n ws.once('open', function open() {\n duplex._final(callback);\n });\n return;\n }\n\n // If the value of the `_socket` property is `null` it means that `ws` is a\n // client websocket and the handshake failed. In fact, when this happens, a\n // socket is never assigned to the websocket. Wait for the `'error'` event\n // that will be emitted by the websocket.\n if (ws._socket === null) return;\n\n if (ws._socket._writableState.finished) {\n callback();\n if (duplex._readableState.endEmitted) duplex.destroy();\n } else {\n ws._socket.once('finish', function finish() {\n // `duplex` is not destroyed here because the `'end'` event will be\n // emitted on `duplex` after this `'finish'` event. The EOF signaling\n // `null` chunk is, in fact, pushed when the websocket emits `'close'`.\n callback();\n });\n ws.close();\n }\n };\n\n duplex._read = function () {\n if (ws.isPaused) ws.resume();\n };\n\n duplex._write = function (chunk, encoding, callback) {\n if (ws.readyState === ws.CONNECTING) {\n ws.once('open', function open() {\n duplex._write(chunk, encoding, callback);\n });\n return;\n }\n\n ws.send(chunk, callback);\n };\n\n duplex.on('end', duplexOnEnd);\n duplex.on('error', duplexOnError);\n return duplex;\n}\n\nmodule.exports = createWebSocketStream;\n","'use strict';\n\nconst { tokenChars } = require('./validation');\n\n/**\n * Parses the `Sec-WebSocket-Protocol` header into a set of subprotocol names.\n *\n * @param {String} header The field value of the header\n * @return {Set} The subprotocol names\n * @public\n */\nfunction parse(header) {\n const protocols = new Set();\n let start = -1;\n let end = -1;\n let i = 0;\n\n for (i; i < header.length; i++) {\n const code = header.charCodeAt(i);\n\n if (end === -1 && tokenChars[code] === 1) {\n if (start === -1) start = i;\n } else if (\n i !== 0 &&\n (code === 0x20 /* ' ' */ || code === 0x09) /* '\\t' */\n ) {\n if (end === -1 && start !== -1) end = i;\n } else if (code === 0x2c /* ',' */) {\n if (start === -1) {\n throw new SyntaxError(`Unexpected character at index ${i}`);\n }\n\n if (end === -1) end = i;\n\n const protocol = header.slice(start, end);\n\n if (protocols.has(protocol)) {\n throw new SyntaxError(`The \"${protocol}\" subprotocol is duplicated`);\n }\n\n protocols.add(protocol);\n start = end = -1;\n } else {\n throw new SyntaxError(`Unexpected character at index ${i}`);\n }\n }\n\n if (start === -1 || end !== -1) {\n throw new SyntaxError('Unexpected end of input');\n }\n\n const protocol = header.slice(start, i);\n\n if (protocols.has(protocol)) {\n throw new SyntaxError(`The \"${protocol}\" subprotocol is duplicated`);\n }\n\n protocols.add(protocol);\n return protocols;\n}\n\nmodule.exports = { parse };\n","/* eslint no-unused-vars: [\"error\", { \"varsIgnorePattern\": \"^Duplex$\", \"caughtErrors\": \"none\" }] */\n\n'use strict';\n\nconst EventEmitter = require('events');\nconst http = require('http');\nconst { Duplex } = require('stream');\nconst { createHash } = require('crypto');\n\nconst extension = require('./extension');\nconst PerMessageDeflate = require('./permessage-deflate');\nconst subprotocol = require('./subprotocol');\nconst WebSocket = require('./websocket');\nconst { CLOSE_TIMEOUT, GUID, kWebSocket } = require('./constants');\n\nconst keyRegex = /^[+/0-9A-Za-z]{22}==$/;\n\nconst RUNNING = 0;\nconst CLOSING = 1;\nconst CLOSED = 2;\n\n/**\n * Class representing a WebSocket server.\n *\n * @extends EventEmitter\n */\nclass WebSocketServer extends EventEmitter {\n /**\n * Create a `WebSocketServer` instance.\n *\n * @param {Object} options Configuration options\n * @param {Boolean} [options.allowSynchronousEvents=true] Specifies whether\n * any of the `'message'`, `'ping'`, and `'pong'` events can be emitted\n * multiple times in the same tick\n * @param {Boolean} [options.autoPong=true] Specifies whether or not to\n * automatically send a pong in response to a ping\n * @param {Number} [options.backlog=511] The maximum length of the queue of\n * pending connections\n * @param {Boolean} [options.clientTracking=true] Specifies whether or not to\n * track clients\n * @param {Number} [options.closeTimeout=30000] Duration in milliseconds to\n * wait for the closing handshake to finish after `websocket.close()` is\n * called\n * @param {Function} [options.handleProtocols] A hook to handle protocols\n * @param {String} [options.host] The hostname where to bind the server\n * @param {Number} [options.maxPayload=104857600] The maximum allowed message\n * size\n * @param {Boolean} [options.noServer=false] Enable no server mode\n * @param {String} [options.path] Accept only connections matching this path\n * @param {(Boolean|Object)} [options.perMessageDeflate=false] Enable/disable\n * permessage-deflate\n * @param {Number} [options.port] The port where to bind the server\n * @param {(http.Server|https.Server)} [options.server] A pre-created HTTP/S\n * server to use\n * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or\n * not to skip UTF-8 validation for text and close messages\n * @param {Function} [options.verifyClient] A hook to reject connections\n * @param {Function} [options.WebSocket=WebSocket] Specifies the `WebSocket`\n * class to use. It must be the `WebSocket` class or class that extends it\n * @param {Function} [callback] A listener for the `listening` event\n */\n constructor(options, callback) {\n super();\n\n options = {\n allowSynchronousEvents: true,\n autoPong: true,\n maxPayload: 100 * 1024 * 1024,\n skipUTF8Validation: false,\n perMessageDeflate: false,\n handleProtocols: null,\n clientTracking: true,\n closeTimeout: CLOSE_TIMEOUT,\n verifyClient: null,\n noServer: false,\n backlog: null, // use default (511 as implemented in net.js)\n server: null,\n host: null,\n path: null,\n port: null,\n WebSocket,\n ...options\n };\n\n if (\n (options.port == null && !options.server && !options.noServer) ||\n (options.port != null && (options.server || options.noServer)) ||\n (options.server && options.noServer)\n ) {\n throw new TypeError(\n 'One and only one of the \"port\", \"server\", or \"noServer\" options ' +\n 'must be specified'\n );\n }\n\n if (options.port != null) {\n this._server = http.createServer((req, res) => {\n const body = http.STATUS_CODES[426];\n\n res.writeHead(426, {\n 'Content-Length': body.length,\n 'Content-Type': 'text/plain'\n });\n res.end(body);\n });\n this._server.listen(\n options.port,\n options.host,\n options.backlog,\n callback\n );\n } else if (options.server) {\n this._server = options.server;\n }\n\n if (this._server) {\n const emitConnection = this.emit.bind(this, 'connection');\n\n this._removeListeners = addListeners(this._server, {\n listening: this.emit.bind(this, 'listening'),\n error: this.emit.bind(this, 'error'),\n upgrade: (req, socket, head) => {\n this.handleUpgrade(req, socket, head, emitConnection);\n }\n });\n }\n\n if (options.perMessageDeflate === true) options.perMessageDeflate = {};\n if (options.clientTracking) {\n this.clients = new Set();\n this._shouldEmitClose = false;\n }\n\n this.options = options;\n this._state = RUNNING;\n }\n\n /**\n * Returns the bound address, the address family name, and port of the server\n * as reported by the operating system if listening on an IP socket.\n * If the server is listening on a pipe or UNIX domain socket, the name is\n * returned as a string.\n *\n * @return {(Object|String|null)} The address of the server\n * @public\n */\n address() {\n if (this.options.noServer) {\n throw new Error('The server is operating in \"noServer\" mode');\n }\n\n if (!this._server) return null;\n return this._server.address();\n }\n\n /**\n * Stop the server from accepting new connections and emit the `'close'` event\n * when all existing connections are closed.\n *\n * @param {Function} [cb] A one-time listener for the `'close'` event\n * @public\n */\n close(cb) {\n if (this._state === CLOSED) {\n if (cb) {\n this.once('close', () => {\n cb(new Error('The server is not running'));\n });\n }\n\n process.nextTick(emitClose, this);\n return;\n }\n\n if (cb) this.once('close', cb);\n\n if (this._state === CLOSING) return;\n this._state = CLOSING;\n\n if (this.options.noServer || this.options.server) {\n if (this._server) {\n this._removeListeners();\n this._removeListeners = this._server = null;\n }\n\n if (this.clients) {\n if (!this.clients.size) {\n process.nextTick(emitClose, this);\n } else {\n this._shouldEmitClose = true;\n }\n } else {\n process.nextTick(emitClose, this);\n }\n } else {\n const server = this._server;\n\n this._removeListeners();\n this._removeListeners = this._server = null;\n\n //\n // The HTTP/S server was created internally. Close it, and rely on its\n // `'close'` event.\n //\n server.close(() => {\n emitClose(this);\n });\n }\n }\n\n /**\n * See if a given request should be handled by this server instance.\n *\n * @param {http.IncomingMessage} req Request object to inspect\n * @return {Boolean} `true` if the request is valid, else `false`\n * @public\n */\n shouldHandle(req) {\n if (this.options.path) {\n const index = req.url.indexOf('?');\n const pathname = index !== -1 ? req.url.slice(0, index) : req.url;\n\n if (pathname !== this.options.path) return false;\n }\n\n return true;\n }\n\n /**\n * Handle a HTTP Upgrade request.\n *\n * @param {http.IncomingMessage} req The request object\n * @param {Duplex} socket The network socket between the server and client\n * @param {Buffer} head The first packet of the upgraded stream\n * @param {Function} cb Callback\n * @public\n */\n handleUpgrade(req, socket, head, cb) {\n socket.on('error', socketOnError);\n\n const key = req.headers['sec-websocket-key'];\n const upgrade = req.headers.upgrade;\n const version = +req.headers['sec-websocket-version'];\n\n if (req.method !== 'GET') {\n const message = 'Invalid HTTP method';\n abortHandshakeOrEmitwsClientError(this, req, socket, 405, message);\n return;\n }\n\n if (upgrade === undefined || upgrade.toLowerCase() !== 'websocket') {\n const message = 'Invalid Upgrade header';\n abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);\n return;\n }\n\n if (key === undefined || !keyRegex.test(key)) {\n const message = 'Missing or invalid Sec-WebSocket-Key header';\n abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);\n return;\n }\n\n if (version !== 13 && version !== 8) {\n const message = 'Missing or invalid Sec-WebSocket-Version header';\n abortHandshakeOrEmitwsClientError(this, req, socket, 400, message, {\n 'Sec-WebSocket-Version': '13, 8'\n });\n return;\n }\n\n if (!this.shouldHandle(req)) {\n abortHandshake(socket, 400);\n return;\n }\n\n const secWebSocketProtocol = req.headers['sec-websocket-protocol'];\n let protocols = new Set();\n\n if (secWebSocketProtocol !== undefined) {\n try {\n protocols = subprotocol.parse(secWebSocketProtocol);\n } catch (err) {\n const message = 'Invalid Sec-WebSocket-Protocol header';\n abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);\n return;\n }\n }\n\n const secWebSocketExtensions = req.headers['sec-websocket-extensions'];\n const extensions = {};\n\n if (\n this.options.perMessageDeflate &&\n secWebSocketExtensions !== undefined\n ) {\n const perMessageDeflate = new PerMessageDeflate(\n this.options.perMessageDeflate,\n true,\n this.options.maxPayload\n );\n\n try {\n const offers = extension.parse(secWebSocketExtensions);\n\n if (offers[PerMessageDeflate.extensionName]) {\n perMessageDeflate.accept(offers[PerMessageDeflate.extensionName]);\n extensions[PerMessageDeflate.extensionName] = perMessageDeflate;\n }\n } catch (err) {\n const message =\n 'Invalid or unacceptable Sec-WebSocket-Extensions header';\n abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);\n return;\n }\n }\n\n //\n // Optionally call external client verification handler.\n //\n if (this.options.verifyClient) {\n const info = {\n origin:\n req.headers[`${version === 8 ? 'sec-websocket-origin' : 'origin'}`],\n secure: !!(req.socket.authorized || req.socket.encrypted),\n req\n };\n\n if (this.options.verifyClient.length === 2) {\n this.options.verifyClient(info, (verified, code, message, headers) => {\n if (!verified) {\n return abortHandshake(socket, code || 401, message, headers);\n }\n\n this.completeUpgrade(\n extensions,\n key,\n protocols,\n req,\n socket,\n head,\n cb\n );\n });\n return;\n }\n\n if (!this.options.verifyClient(info)) return abortHandshake(socket, 401);\n }\n\n this.completeUpgrade(extensions, key, protocols, req, socket, head, cb);\n }\n\n /**\n * Upgrade the connection to WebSocket.\n *\n * @param {Object} extensions The accepted extensions\n * @param {String} key The value of the `Sec-WebSocket-Key` header\n * @param {Set} protocols The subprotocols\n * @param {http.IncomingMessage} req The request object\n * @param {Duplex} socket The network socket between the server and client\n * @param {Buffer} head The first packet of the upgraded stream\n * @param {Function} cb Callback\n * @throws {Error} If called more than once with the same socket\n * @private\n */\n completeUpgrade(extensions, key, protocols, req, socket, head, cb) {\n //\n // Destroy the socket if the client has already sent a FIN packet.\n //\n if (!socket.readable || !socket.writable) return socket.destroy();\n\n if (socket[kWebSocket]) {\n throw new Error(\n 'server.handleUpgrade() was called more than once with the same ' +\n 'socket, possibly due to a misconfiguration'\n );\n }\n\n if (this._state > RUNNING) return abortHandshake(socket, 503);\n\n const digest = createHash('sha1')\n .update(key + GUID)\n .digest('base64');\n\n const headers = [\n 'HTTP/1.1 101 Switching Protocols',\n 'Upgrade: websocket',\n 'Connection: Upgrade',\n `Sec-WebSocket-Accept: ${digest}`\n ];\n\n const ws = new this.options.WebSocket(null, undefined, this.options);\n\n if (protocols.size) {\n //\n // Optionally call external protocol selection handler.\n //\n const protocol = this.options.handleProtocols\n ? this.options.handleProtocols(protocols, req)\n : protocols.values().next().value;\n\n if (protocol) {\n headers.push(`Sec-WebSocket-Protocol: ${protocol}`);\n ws._protocol = protocol;\n }\n }\n\n if (extensions[PerMessageDeflate.extensionName]) {\n const params = extensions[PerMessageDeflate.extensionName].params;\n const value = extension.format({\n [PerMessageDeflate.extensionName]: [params]\n });\n headers.push(`Sec-WebSocket-Extensions: ${value}`);\n ws._extensions = extensions;\n }\n\n //\n // Allow external modification/inspection of handshake headers.\n //\n this.emit('headers', headers, req);\n\n socket.write(headers.concat('\\r\\n').join('\\r\\n'));\n socket.removeListener('error', socketOnError);\n\n ws.setSocket(socket, head, {\n allowSynchronousEvents: this.options.allowSynchronousEvents,\n maxPayload: this.options.maxPayload,\n skipUTF8Validation: this.options.skipUTF8Validation\n });\n\n if (this.clients) {\n this.clients.add(ws);\n ws.on('close', () => {\n this.clients.delete(ws);\n\n if (this._shouldEmitClose && !this.clients.size) {\n process.nextTick(emitClose, this);\n }\n });\n }\n\n cb(ws, req);\n }\n}\n\nmodule.exports = WebSocketServer;\n\n/**\n * Add event listeners on an `EventEmitter` using a map of <event, listener>\n * pairs.\n *\n * @param {EventEmitter} server The event emitter\n * @param {Object.<String, Function>} map The listeners to add\n * @return {Function} A function that will remove the added listeners when\n * called\n * @private\n */\nfunction addListeners(server, map) {\n for (const event of Object.keys(map)) server.on(event, map[event]);\n\n return function removeListeners() {\n for (const event of Object.keys(map)) {\n server.removeListener(event, map[event]);\n }\n };\n}\n\n/**\n * Emit a `'close'` event on an `EventEmitter`.\n *\n * @param {EventEmitter} server The event emitter\n * @private\n */\nfunction emitClose(server) {\n server._state = CLOSED;\n server.emit('close');\n}\n\n/**\n * Handle socket errors.\n *\n * @private\n */\nfunction socketOnError() {\n this.destroy();\n}\n\n/**\n * Close the connection when preconditions are not fulfilled.\n *\n * @param {Duplex} socket The socket of the upgrade request\n * @param {Number} code The HTTP response status code\n * @param {String} [message] The HTTP response body\n * @param {Object} [headers] Additional HTTP response headers\n * @private\n */\nfunction abortHandshake(socket, code, message, headers) {\n //\n // The socket is writable unless the user destroyed or ended it before calling\n // `server.handleUpgrade()` or in the `verifyClient` function, which is a user\n // error. Handling this does not make much sense as the worst that can happen\n // is that some of the data written by the user might be discarded due to the\n // call to `socket.end()` below, which triggers an `'error'` event that in\n // turn causes the socket to be destroyed.\n //\n message = message || http.STATUS_CODES[code];\n headers = {\n Connection: 'close',\n 'Content-Type': 'text/html',\n 'Content-Length': Buffer.byteLength(message),\n ...headers\n };\n\n socket.once('finish', socket.destroy);\n\n socket.end(\n `HTTP/1.1 ${code} ${http.STATUS_CODES[code]}\\r\\n` +\n Object.keys(headers)\n .map((h) => `${h}: ${headers[h]}`)\n .join('\\r\\n') +\n '\\r\\n\\r\\n' +\n message\n );\n}\n\n/**\n * Emit a `'wsClientError'` event on a `WebSocketServer` if there is at least\n * one listener for it, otherwise call `abortHandshake()`.\n *\n * @param {WebSocketServer} server The WebSocket server\n * @param {http.IncomingMessage} req The request object\n * @param {Duplex} socket The socket of the upgrade request\n * @param {Number} code The HTTP response status code\n * @param {String} message The HTTP response body\n * @param {Object} [headers] The HTTP response headers\n * @private\n */\nfunction abortHandshakeOrEmitwsClientError(\n server,\n req,\n socket,\n code,\n message,\n headers\n) {\n if (server.listenerCount('wsClientError')) {\n const err = new Error(message);\n Error.captureStackTrace(err, abortHandshakeOrEmitwsClientError);\n\n server.emit('wsClientError', err, socket, req);\n } else {\n abortHandshake(socket, code, message, headers);\n }\n}\n","import type { ChainProviderEntry, TownhouseConfig } from './schema.js';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { DEFAULT_CONNECTOR_IMAGE } from '../constants.js';\n\n/**\n * Default chain-provider entry for HS-mode boots without explicit config.\n *\n * These are dev-Anvil deterministic addresses paired with a dead RPC URL —\n * the connector's settlement subsystem (AccountManager + ClaimReceiver)\n * initializes successfully (SQLite-backed) even when the RPC never resolves,\n * because the chain calls are lazy and the failure path is non-fatal.\n *\n * The single concrete value-add: with these defaults present, the connector\n * wires the ClaimReceiver into the AdminServer at boot and\n * `GET /admin/earnings.json` returns 200 instead of 503 (Epic 47 BUG-1).\n *\n * Production operators override this in `config.yaml`:\n *\n * chainProviders:\n * - chainType: evm\n * chainId: evm:base:8453 # Base mainnet\n * rpcUrl: https://mainnet.base.org\n * registryAddress: 0x…\n * tokenAddress: 0x…\n * keyId: 0x… # operator-managed key\n */\nexport const DEFAULT_HS_CHAIN_PROVIDERS: readonly ChainProviderEntry[] = [\n Object.freeze({\n chainType: 'evm',\n chainId: 'evm:base:31337',\n rpcUrl: 'http://127.0.0.1:19999',\n registryAddress: '0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512',\n tokenAddress: '0x5FbDB2315678afecb367f032d93F642f64180aa3',\n keyId: '0x7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6',\n }),\n] as const;\n\n/**\n * Sensible default configuration. All nodes disabled by default —\n * operator must explicitly enable what they want to run.\n */\nexport function getDefaultConfig(): TownhouseConfig {\n return {\n nodes: {\n town: { enabled: false },\n mill: { enabled: false },\n dvm: { enabled: false },\n },\n wallet: {\n encrypted_path: join(homedir(), '.townhouse', 'wallet.enc'),\n },\n connector: {\n image: DEFAULT_CONNECTOR_IMAGE,\n adminPort: 9401,\n },\n transport: {\n mode: 'direct',\n },\n api: {\n port: 9400,\n host: '127.0.0.1',\n },\n logging: {\n level: 'info',\n },\n };\n}\n","/**\n * Runtime validation for Townhouse configuration.\n * Validates shape, narrows types, returns typed config or throws.\n */\n\nimport type { ChainProviderEntry, TownhouseConfig } from './schema.js';\n\nconst VALID_CHAIN_TYPES = new Set(['evm']);\nconst HEX_ADDRESS = /^0x[a-fA-F0-9]+$/;\n\nclass ConfigValidationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ConfigValidationError';\n }\n}\n\nfunction assertObject(\n value: unknown,\n path: string\n): asserts value is Record<string, unknown> {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new ConfigValidationError(`${path} must be a non-null object`);\n }\n}\n\nfunction assertBoolean(value: unknown, path: string): asserts value is boolean {\n if (typeof value !== 'boolean') {\n throw new ConfigValidationError(`${path} must be a boolean`);\n }\n}\n\nfunction assertString(value: unknown, path: string): asserts value is string {\n if (typeof value !== 'string') {\n throw new ConfigValidationError(`${path} must be a string`);\n }\n}\n\nfunction assertNumber(value: unknown, path: string): asserts value is number {\n if (typeof value !== 'number' || !Number.isFinite(value)) {\n throw new ConfigValidationError(`${path} must be a finite number`);\n }\n}\n\nfunction assertPort(value: number, path: string): void {\n if (!Number.isInteger(value) || value < 0 || value > 65535) {\n throw new ConfigValidationError(\n `${path} must be an integer in range 0..65535`\n );\n }\n}\n\nconst VALID_LOG_LEVELS = new Set(['debug', 'info', 'warn', 'error']);\nconst VALID_TRANSPORT_MODES = new Set(['ator', 'direct']);\n\nfunction validateNodeConfig(\n raw: unknown,\n path: string\n): { enabled: boolean } & Record<string, unknown> {\n assertObject(raw, path);\n assertBoolean(raw['enabled'], `${path}.enabled`);\n\n if (raw['feePerEvent'] !== undefined) {\n assertNumber(raw['feePerEvent'], `${path}.feePerEvent`);\n }\n if (raw['feeBasisPoints'] !== undefined) {\n assertNumber(raw['feeBasisPoints'], `${path}.feeBasisPoints`);\n }\n if (raw['feePerJob'] !== undefined) {\n assertNumber(raw['feePerJob'], `${path}.feePerJob`);\n }\n if (raw['kindPricing'] !== undefined) {\n assertObject(raw['kindPricing'], `${path}.kindPricing`);\n for (const [k, v] of Object.entries(\n raw['kindPricing'] as Record<string, unknown>\n )) {\n // Keys must be positive-integer strings — prevents prototype-key\n // pollution (`__proto__`, `constructor`) and env-var key injection\n // (newlines / spaces) when the orchestrator emits KIND_PRICING_<k>.\n if (!/^[0-9]+$/.test(k)) {\n throw new ConfigValidationError(\n `${path}.kindPricing has invalid key \"${k}\" — must be a positive-integer string`\n );\n }\n assertNumber(v, `${path}.kindPricing.${k}`);\n if (!Number.isInteger(v as number) || (v as number) < 0) {\n throw new ConfigValidationError(\n `${path}.kindPricing.${k} must be a non-negative integer`\n );\n }\n if ((v as number) > Number.MAX_SAFE_INTEGER) {\n throw new ConfigValidationError(\n `${path}.kindPricing.${k} exceeds Number.MAX_SAFE_INTEGER`\n );\n }\n }\n }\n if (raw['image'] !== undefined) {\n assertString(raw['image'], `${path}.image`);\n }\n\n return raw as { enabled: boolean } & Record<string, unknown>;\n}\n\n/**\n * Validate raw input and return a typed TownhouseConfig.\n * Throws ConfigValidationError with descriptive messages on invalid input.\n */\nexport function validateConfig(raw: unknown): TownhouseConfig {\n assertObject(raw, 'config');\n\n // nodes\n assertObject(raw['nodes'], 'config.nodes');\n const nodes = raw['nodes'] as Record<string, unknown>;\n const town = validateNodeConfig(nodes['town'], 'config.nodes.town');\n const mill = validateNodeConfig(nodes['mill'], 'config.nodes.mill');\n const dvm = validateNodeConfig(nodes['dvm'], 'config.nodes.dvm');\n\n // wallet\n assertObject(raw['wallet'], 'config.wallet');\n const wallet = raw['wallet'] as Record<string, unknown>;\n assertString(wallet['encrypted_path'], 'config.wallet.encrypted_path');\n\n // connector\n assertObject(raw['connector'], 'config.connector');\n const connector = raw['connector'] as Record<string, unknown>;\n assertString(connector['image'], 'config.connector.image');\n assertNumber(connector['adminPort'], 'config.connector.adminPort');\n assertPort(connector['adminPort'] as number, 'config.connector.adminPort');\n\n // transport\n assertObject(raw['transport'], 'config.transport');\n const transport = raw['transport'] as Record<string, unknown>;\n assertString(transport['mode'], 'config.transport.mode');\n if (!VALID_TRANSPORT_MODES.has(transport['mode'] as string)) {\n throw new ConfigValidationError(\n `config.transport.mode must be one of: ${[...VALID_TRANSPORT_MODES].join(', ')}`\n );\n }\n if (transport['socksProxy'] !== undefined) {\n assertString(transport['socksProxy'], 'config.transport.socksProxy');\n }\n if (transport['externalUrl'] !== undefined) {\n assertString(transport['externalUrl'], 'config.transport.externalUrl');\n }\n // hiddenService is optional and only meaningful when mode='ator'. We\n // validate the inner shape unconditionally if present (a hiddenService\n // block under mode='direct' is operator confusion, not silent acceptance).\n if (transport['hiddenService'] !== undefined) {\n assertObject(transport['hiddenService'], 'config.transport.hiddenService');\n const hs = transport['hiddenService'] as Record<string, unknown>;\n assertString(hs['dir'], 'config.transport.hiddenService.dir');\n assertNumber(hs['port'], 'config.transport.hiddenService.port');\n assertPort(hs['port'] as number, 'config.transport.hiddenService.port');\n if (hs['externalUrl'] !== undefined) {\n assertString(\n hs['externalUrl'],\n 'config.transport.hiddenService.externalUrl'\n );\n }\n if (hs['startupTimeoutMs'] !== undefined) {\n assertNumber(\n hs['startupTimeoutMs'],\n 'config.transport.hiddenService.startupTimeoutMs'\n );\n }\n if (hs['stopTimeoutMs'] !== undefined) {\n assertNumber(\n hs['stopTimeoutMs'],\n 'config.transport.hiddenService.stopTimeoutMs'\n );\n }\n if (transport['mode'] !== 'ator') {\n throw new ConfigValidationError(\n 'config.transport.hiddenService is only valid when config.transport.mode is \"ator\"'\n );\n }\n }\n // relayHiddenService is the second optional HS — when set, the orchestrator\n // launches a parallel sidecar that forwards inbound .anyone traffic to the\n // town container's port 7100 so external Nostr clients can read the relay\n // without routing through ILP/BTP. Reuses HiddenServiceConfig shape.\n if (transport['relayHiddenService'] !== undefined) {\n assertObject(\n transport['relayHiddenService'],\n 'config.transport.relayHiddenService'\n );\n const hs = transport['relayHiddenService'] as Record<string, unknown>;\n assertString(hs['dir'], 'config.transport.relayHiddenService.dir');\n assertNumber(hs['port'], 'config.transport.relayHiddenService.port');\n assertPort(\n hs['port'] as number,\n 'config.transport.relayHiddenService.port'\n );\n if (hs['externalUrl'] !== undefined) {\n assertString(\n hs['externalUrl'],\n 'config.transport.relayHiddenService.externalUrl'\n );\n }\n if (hs['startupTimeoutMs'] !== undefined) {\n assertNumber(\n hs['startupTimeoutMs'],\n 'config.transport.relayHiddenService.startupTimeoutMs'\n );\n }\n if (hs['stopTimeoutMs'] !== undefined) {\n assertNumber(\n hs['stopTimeoutMs'],\n 'config.transport.relayHiddenService.stopTimeoutMs'\n );\n }\n }\n\n // mode='ator' requires SOMETHING to advertise: either explicit externalUrl\n // OR hiddenService (which makes externalUrl='auto' implicit). Without one\n // of these, the connector's socks5 transport rejects with \"missing\n // required field: transport.externalUrl\" and the connector fails to boot.\n if (\n transport['mode'] === 'ator' &&\n transport['externalUrl'] === undefined &&\n transport['hiddenService'] === undefined\n ) {\n throw new ConfigValidationError(\n 'config.transport.mode=\"ator\" requires either config.transport.externalUrl ' +\n '(operator-managed anon binary) or config.transport.hiddenService ' +\n '(connector-managed anon binary). Without one of these, the underlying ' +\n 'connector will reject the manifest at boot.'\n );\n }\n\n // chainProviders (optional)\n let chainProviders: ChainProviderEntry[] | undefined;\n if (raw['chainProviders'] !== undefined) {\n if (!Array.isArray(raw['chainProviders'])) {\n throw new ConfigValidationError(\n 'config.chainProviders must be an array of ChainProviderEntry'\n );\n }\n chainProviders = (raw['chainProviders'] as unknown[]).map((entry, idx) => {\n const path = `config.chainProviders[${idx}]`;\n assertObject(entry, path);\n assertString(entry['chainType'], `${path}.chainType`);\n if (!VALID_CHAIN_TYPES.has(entry['chainType'] as string)) {\n throw new ConfigValidationError(\n `${path}.chainType must be one of: ${[...VALID_CHAIN_TYPES].join(', ')}`\n );\n }\n assertString(entry['chainId'], `${path}.chainId`);\n assertString(entry['rpcUrl'], `${path}.rpcUrl`);\n assertString(entry['registryAddress'], `${path}.registryAddress`);\n if (!HEX_ADDRESS.test(entry['registryAddress'] as string)) {\n throw new ConfigValidationError(\n `${path}.registryAddress must match /^0x[a-fA-F0-9]+$/`\n );\n }\n assertString(entry['tokenAddress'], `${path}.tokenAddress`);\n if (!HEX_ADDRESS.test(entry['tokenAddress'] as string)) {\n throw new ConfigValidationError(\n `${path}.tokenAddress must match /^0x[a-fA-F0-9]+$/`\n );\n }\n assertString(entry['keyId'], `${path}.keyId`);\n if (!HEX_ADDRESS.test(entry['keyId'] as string)) {\n throw new ConfigValidationError(\n `${path}.keyId must match /^0x[a-fA-F0-9]+$/`\n );\n }\n return {\n chainType: entry['chainType'] as 'evm',\n chainId: entry['chainId'] as string,\n rpcUrl: entry['rpcUrl'] as string,\n registryAddress: entry['registryAddress'] as string,\n tokenAddress: entry['tokenAddress'] as string,\n keyId: entry['keyId'] as string,\n };\n });\n }\n\n // api\n assertObject(raw['api'], 'config.api');\n const api = raw['api'] as Record<string, unknown>;\n assertNumber(api['port'], 'config.api.port');\n assertPort(api['port'] as number, 'config.api.port');\n assertString(api['host'], 'config.api.host');\n\n // logging\n assertObject(raw['logging'], 'config.logging');\n const logging = raw['logging'] as Record<string, unknown>;\n assertString(logging['level'], 'config.logging.level');\n if (!VALID_LOG_LEVELS.has(logging['level'] as string)) {\n throw new ConfigValidationError(\n `config.logging.level must be one of: ${[...VALID_LOG_LEVELS].join(', ')}`\n );\n }\n\n return {\n nodes: {\n town: {\n enabled: town['enabled'] as boolean,\n ...pickOptional(town, ['feePerEvent', 'image']),\n },\n mill: {\n enabled: mill['enabled'] as boolean,\n ...pickOptional(mill, ['feeBasisPoints', 'image']),\n },\n dvm: {\n enabled: dvm['enabled'] as boolean,\n ...pickOptional(dvm, ['feePerJob', 'kindPricing', 'image']),\n },\n },\n wallet: { encrypted_path: wallet['encrypted_path'] as string },\n connector: {\n image: connector['image'] as string,\n adminPort: connector['adminPort'] as number,\n },\n transport: {\n mode: transport['mode'] as 'ator' | 'direct',\n ...(transport['socksProxy'] !== undefined\n ? { socksProxy: transport['socksProxy'] as string }\n : {}),\n ...(transport['externalUrl'] !== undefined\n ? { externalUrl: transport['externalUrl'] as string }\n : {}),\n ...(transport['hiddenService'] !== undefined\n ? {\n hiddenService: transport['hiddenService'] as {\n dir: string;\n port: number;\n externalUrl?: string;\n startupTimeoutMs?: number;\n stopTimeoutMs?: number;\n },\n }\n : {}),\n ...(transport['relayHiddenService'] !== undefined\n ? {\n relayHiddenService: transport['relayHiddenService'] as {\n dir: string;\n port: number;\n externalUrl?: string;\n startupTimeoutMs?: number;\n stopTimeoutMs?: number;\n },\n }\n : {}),\n },\n api: {\n port: api['port'] as number,\n host: api['host'] as string,\n },\n logging: {\n level: logging['level'] as 'debug' | 'info' | 'warn' | 'error',\n },\n ...(chainProviders !== undefined ? { chainProviders } : {}),\n };\n}\n\nfunction pickOptional(\n obj: Record<string, unknown>,\n keys: string[]\n): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n for (const key of keys) {\n if (obj[key] !== undefined) {\n result[key] = obj[key];\n }\n }\n return result;\n}\n\nexport { ConfigValidationError };\n","/**\n * Config file loader — reads YAML, validates, writes.\n */\n\nimport { readFileSync, writeFileSync, renameSync } from 'node:fs';\nimport { parse, stringify } from 'yaml';\nimport { validateConfig } from './validator.js';\nimport { getDefaultConfig } from './defaults.js';\nimport type { TownhouseConfig } from './schema.js';\n\n/**\n * Load and validate a Townhouse config from a YAML file.\n * Environment variables override YAML values for key settings:\n * - TOWNHOUSE_API_PORT\n * - TOWNHOUSE_TRANSPORT_MODE\n * - TOWNHOUSE_LOG_LEVEL\n */\nexport function loadConfig(configPath: string): TownhouseConfig {\n let rawText: string;\n try {\n rawText = readFileSync(configPath, 'utf-8');\n } catch (err: unknown) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === 'ENOENT') {\n throw new Error(`Config file not found: ${configPath}`);\n }\n throw err;\n }\n\n let parsed: unknown;\n try {\n parsed = parse(rawText);\n } catch {\n throw new Error(`Failed to parse YAML config at ${configPath}`);\n }\n\n // An empty YAML file parses to null — treat as empty object so defaults apply\n if (parsed === null || parsed === undefined) {\n parsed = {};\n }\n\n if (typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new Error(\n `Config at ${configPath} must be a YAML mapping (object), not ${Array.isArray(parsed) ? 'an array' : typeof parsed}`\n );\n }\n\n // Deep-merge with defaults so partial configs work\n const defaults = getDefaultConfig();\n const merged = deepMerge(\n defaults as unknown as Record<string, unknown>,\n parsed as Record<string, unknown>\n );\n\n // Apply environment variable overrides\n applyEnvOverrides(merged);\n\n return validateConfig(merged);\n}\n\nfunction applyEnvOverrides(config: Record<string, unknown>): void {\n const env = process.env;\n\n if (env['TOWNHOUSE_API_PORT']) {\n const port = parseInt(env['TOWNHOUSE_API_PORT'], 10);\n if (!Number.isFinite(port) || port < 0 || port > 65535) {\n throw new Error('TOWNHOUSE_API_PORT must be 0..65535');\n }\n const api = config['api'] as Record<string, unknown> | undefined;\n if (api) {\n api['port'] = port;\n }\n }\n\n if (env['TOWNHOUSE_TRANSPORT_MODE']) {\n const mode = env['TOWNHOUSE_TRANSPORT_MODE'];\n if (mode !== 'ator' && mode !== 'direct') {\n throw new Error('TOWNHOUSE_TRANSPORT_MODE must be \"ator\" or \"direct\"');\n }\n const transport = config['transport'] as\n | Record<string, unknown>\n | undefined;\n if (transport) {\n transport['mode'] = mode;\n }\n }\n\n if (env['TOWNHOUSE_LOG_LEVEL']) {\n const level = env['TOWNHOUSE_LOG_LEVEL'];\n if (!['debug', 'info', 'warn', 'error'].includes(level)) {\n throw new Error(\n 'TOWNHOUSE_LOG_LEVEL must be one of: debug, info, warn, error'\n );\n }\n const logging = config['logging'] as Record<string, unknown> | undefined;\n if (logging) {\n logging['level'] = level;\n }\n }\n}\n\n/** Keys that must never be merged — prevents prototype pollution (CWE-1321). */\nconst DANGEROUS_KEYS = new Set(['__proto__', 'constructor', 'prototype']);\n\n/**\n * Simple deep merge — target values are overwritten by source values.\n * Arrays are replaced, not concatenated.\n * Skips dangerous keys to prevent prototype pollution from crafted YAML.\n */\nfunction deepMerge(\n target: Record<string, unknown>,\n source: Record<string, unknown>\n): Record<string, unknown> {\n const result: Record<string, unknown> = { ...target };\n for (const key of Object.keys(source)) {\n if (DANGEROUS_KEYS.has(key)) {\n continue;\n }\n const sv = source[key];\n const tv = target[key];\n if (\n typeof sv === 'object' &&\n sv !== null &&\n !Array.isArray(sv) &&\n typeof tv === 'object' &&\n tv !== null &&\n !Array.isArray(tv)\n ) {\n result[key] = deepMerge(\n tv as Record<string, unknown>,\n sv as Record<string, unknown>\n );\n } else {\n result[key] = sv;\n }\n }\n return result;\n}\n\n/**\n * Save a config to a YAML file atomically.\n * Writes to a temp file first, then renames to the target (atomic on POSIX).\n */\nexport function saveConfig(configPath: string, config: TownhouseConfig): void {\n // Validate before saving\n const validated = validateConfig(config);\n\n // Serialize to YAML\n const yaml = stringify(validated);\n\n // Write to temp file first\n const tmpPath = configPath + '.tmp';\n writeFileSync(tmpPath, yaml, 'utf-8');\n\n // Atomic rename\n renameSync(tmpPath, configPath);\n}\n","/**\n * Connector Config Generator for Townhouse (Story 21.3).\n *\n * Generates runtime configuration for the standalone ILP connector\n * based on the Townhouse config and currently active nodes.\n */\n\nimport { stringify as yamlStringify } from 'yaml';\n\nimport type { TownhouseConfig } from '../config/schema.js';\nimport type { NodeType } from '../docker/types.js';\nimport { CONTAINER_PREFIX, NODE_BTP_PORT } from '../constants.js';\nimport type { ConnectorRuntimeConfig, PeerEntry } from './types.js';\n\n/** Default ILP address for the Townhouse connector */\nconst DEFAULT_ILP_ADDRESS = 'g.townhouse';\n\n/** Default ATOR SOCKS proxy address */\nexport const DEFAULT_ATOR_PROXY = 'socks5h://proxy.ator.io:9050';\n\n/** Default asset configuration for ILP peers */\nconst DEFAULT_ASSET_CODE = 'USD';\nconst DEFAULT_ASSET_SCALE = 6;\n\n/**\n * ConnectorConfigGenerator produces runtime configuration for the standalone\n * ILP connector based on Townhouse config and active node list.\n *\n * Key design: peer BTP URLs are deterministic Docker DNS names, so nodes\n * don't need to be running for config generation to work.\n */\nexport class ConnectorConfigGenerator {\n private readonly config: TownhouseConfig;\n\n constructor(config: TownhouseConfig) {\n this.config = config;\n }\n\n /**\n * Generate a ConnectorRuntimeConfig for the given set of active nodes.\n *\n * @param activeNodes - Node types currently running or about to start\n * @returns Typed configuration object (not serialized)\n */\n generate(activeNodes: NodeType[]): ConnectorRuntimeConfig {\n const peers = this.generatePeerList(activeNodes);\n const transport = this.generateTransportConfig();\n\n return {\n adminPort: this.config.connector.adminPort,\n ilpAddress: DEFAULT_ILP_ADDRESS,\n peers,\n transport,\n };\n }\n\n /**\n * Serialize a ConnectorRuntimeConfig into environment variable key-value pairs.\n *\n * @returns Record of env var name to string value\n */\n toEnvVars(runtimeConfig: ConnectorRuntimeConfig): Record<string, string> {\n const env: Record<string, string> = {\n CONNECTOR_ADMIN_PORT: String(runtimeConfig.adminPort),\n CONNECTOR_ILP_ADDRESS: runtimeConfig.ilpAddress,\n CONNECTOR_PEERS: JSON.stringify(runtimeConfig.peers),\n TRANSPORT_MODE: runtimeConfig.transport.mode,\n };\n\n if (runtimeConfig.transport.socksProxy) {\n env['SOCKS_PROXY'] = runtimeConfig.transport.socksProxy;\n }\n\n // Hidden-service env vars surface for tooling that wants the .anyone\n // address out-of-band (e.g., `townhouse status` reading the hostname\n // file). The connector itself reads these from the YAML config block\n // produced by toYaml(), not env.\n if (runtimeConfig.transport.hiddenService) {\n env['TRANSPORT_HIDDEN_SERVICE_DIR'] =\n runtimeConfig.transport.hiddenService.dir;\n env['TRANSPORT_HIDDEN_SERVICE_PORT'] = String(\n runtimeConfig.transport.hiddenService.port\n );\n }\n\n return env;\n }\n\n /**\n * Convert a ConnectorRuntimeConfig into the string[] format expected by\n * dockerode's container create API (Env option: ['KEY=VALUE', ...]).\n *\n * @returns Array of 'KEY=VALUE' strings\n */\n toEnvArray(runtimeConfig: ConnectorRuntimeConfig): string[] {\n const envVars = this.toEnvVars(runtimeConfig);\n return Object.entries(envVars).map(([key, value]) => `${key}=${value}`);\n }\n\n /**\n * Render a connector YAML config string the connector image at 3.3.x can\n * load via its `CONFIG_FILE` env var (default `./config.yaml`).\n *\n * The shape mirrors `docker/configs/townhouse-dev-connector.yaml` (the\n * working dev fixture) — peers list is empty because child nodes dial\n * INTO the connector at startup; the connector accepts BTP connections\n * (no-auth in dev) without needing pre-configured peer entries.\n *\n * Added in the orchestrator-bug-fix: env vars set on the container were\n * silently ignored by the connector image, which only reads from this\n * YAML file. Caller writes the returned string to disk and mounts it\n * at `/config/connector.yaml` in the container.\n */\n toYaml(runtimeConfig: ConnectorRuntimeConfig): string {\n // Translate operator-facing `mode: 'ator' | 'direct'` into the\n // connector's internal discriminated union (Epic 35 / Story 35.3):\n // { type: 'direct' }\n // → unchanged direct TCP\n // { type: 'socks5', socksProxy, externalUrl, managed, managedOptions? }\n // → SOCKS5 outbound + (optionally) managed inbound hidden service\n //\n // Historical bug we're fixing here: the previous shape was\n // `{ mode: 'ator', socksProxy }`, which the connector at 3.3.x\n // does NOT recognize. The connector's validateTransport() saw an\n // unknown `mode` field, defaulted `type` to 'direct', and silently\n // discarded socksProxy. Operators toggling mode='ator' got direct\n // traffic anyway. The new shape is what the connector actually reads.\n const transportBlock = this.buildConnectorTransportBlock(\n runtimeConfig.transport\n );\n\n const yamlObj: Record<string, unknown> = {\n nodeId: runtimeConfig.ilpAddress,\n btpServerPort: NODE_BTP_PORT,\n healthCheckPort: 8080,\n environment: 'development',\n deploymentMode: 'standalone',\n logLevel: this.config.logging?.level ?? 'info',\n adminApi: {\n enabled: true,\n port: runtimeConfig.adminPort,\n host: '0.0.0.0',\n // Permissive for the demo loopback environment. Production deployments\n // would lock this to specific operator IPs.\n allowedIPs: ['0.0.0.0/0'],\n },\n transport: transportBlock,\n peers: [],\n routes: [],\n };\n\n // Epic 47 BUG-1 fix (D2): emit chainProviders when configured so the\n // connector's settlement subsystem initializes. Without it,\n // /admin/earnings.json returns 503 and Townhouse's earnings data plane\n // (Epic 47.1–47.5) silently degrades. `hs-config-writer.ts` injects\n // `DEFAULT_HS_CHAIN_PROVIDERS` when the operator hasn't configured this.\n if (\n this.config.chainProviders !== undefined &&\n this.config.chainProviders.length > 0\n ) {\n yamlObj['chainProviders'] = this.config.chainProviders.map((p) => ({\n chainType: p.chainType,\n chainId: p.chainId,\n rpcUrl: p.rpcUrl,\n registryAddress: p.registryAddress,\n tokenAddress: p.tokenAddress,\n keyId: p.keyId,\n }));\n }\n\n return yamlStringify(yamlObj);\n }\n\n // ── Private helpers ──\n\n /**\n * Translate the runtime config's transport block into the discriminated-\n * union shape the connector expects. See toYaml's note for why this was\n * silently broken before.\n */\n private buildConnectorTransportBlock(\n transport: ConnectorRuntimeConfig['transport']\n ): Record<string, unknown> {\n if (transport.mode === 'direct') {\n return { type: 'direct' };\n }\n const block: Record<string, unknown> = {\n type: 'socks5',\n socksProxy: transport.socksProxy ?? DEFAULT_ATOR_PROXY,\n };\n if (transport.hiddenService) {\n // Connector-managed inbound hidden service (Story 35.5). When\n // hiddenService is set, the connector spawns the anon binary itself\n // and resolves externalUrl from `${dir}/hostname` at boot.\n block['externalUrl'] = transport.externalUrl ?? 'auto';\n block['managed'] = true;\n const managedOptions: Record<string, unknown> = {\n hiddenServiceDir: transport.hiddenService.dir,\n hiddenServicePort: transport.hiddenService.port,\n };\n if (transport.hiddenService.startupTimeoutMs !== undefined) {\n managedOptions['startupTimeoutMs'] =\n transport.hiddenService.startupTimeoutMs;\n }\n if (transport.hiddenService.stopTimeoutMs !== undefined) {\n managedOptions['stopTimeoutMs'] = transport.hiddenService.stopTimeoutMs;\n }\n block['managedOptions'] = managedOptions;\n } else {\n // Operator manages anon externally — externalUrl is non-optional in\n // this branch; the validator enforces it upstream, so we trust it.\n // The connector also requires `managed: false` here.\n block['externalUrl'] = transport.externalUrl;\n block['managed'] = false;\n }\n return block;\n }\n\n /**\n * Generate PeerEntry list for each active node type.\n * BTP URLs use Docker DNS: btp+ws://townhouse-{type}:3000 // nosemgrep: javascript.lang.security.detect-insecure-websocket.detect-insecure-websocket\n */\n private generatePeerList(activeNodes: NodeType[]): PeerEntry[] {\n return activeNodes.map((type) => ({\n id: type,\n relation: 'child' as const,\n // nosemgrep: javascript.lang.security.detect-insecure-websocket.detect-insecure-websocket -- Docker-internal BTP URL, TLS unnecessary\n btpUrl: `btp+ws://${CONTAINER_PREFIX}${type}:${NODE_BTP_PORT}`,\n assetCode: DEFAULT_ASSET_CODE,\n assetScale: DEFAULT_ASSET_SCALE,\n }));\n }\n\n /**\n * Generate transport config from Townhouse config.\n * When mode is 'ator', includes SOCKS proxy (uses default if not configured).\n * Carries forward externalUrl + hiddenService when set; downstream\n * buildConnectorTransportBlock handles translation to the connector's\n * wire shape.\n */\n private generateTransportConfig(): ConnectorRuntimeConfig['transport'] {\n if (this.config.transport.mode === 'ator') {\n const transport: ConnectorRuntimeConfig['transport'] = {\n mode: 'ator',\n socksProxy: this.config.transport.socksProxy ?? DEFAULT_ATOR_PROXY,\n };\n if (this.config.transport.externalUrl !== undefined) {\n transport.externalUrl = this.config.transport.externalUrl;\n }\n if (this.config.transport.hiddenService !== undefined) {\n const hs = this.config.transport.hiddenService;\n transport.hiddenService = {\n dir: hs.dir,\n port: hs.port,\n ...(hs.externalUrl !== undefined\n ? { externalUrl: hs.externalUrl }\n : {}),\n ...(hs.startupTimeoutMs !== undefined\n ? { startupTimeoutMs: hs.startupTimeoutMs }\n : {}),\n ...(hs.stopTimeoutMs !== undefined\n ? { stopTimeoutMs: hs.stopTimeoutMs }\n : {}),\n };\n }\n return transport;\n }\n\n return { mode: 'direct' };\n }\n}\n","/**\n * Connector Admin Client for Townhouse (Story 21.3, contract aligned in 21.7.5).\n *\n * HTTP client for the connector's admin API endpoints. Paths and response\n * shapes mirror the connector source-of-truth — see\n * `@toon-protocol/connector` `packages/connector/src/http/{types,admin-api}.ts`.\n *\n * Uses Node.js native fetch (available in Node 20+).\n *\n * Two distinct HTTP servers live on the connector image:\n * - healthCheckPort serves /health and /health/{live,ready}\n * - adminApi.port serves /admin/* (peers, metrics.json, routes, channels, …)\n *\n * The base URL passed to this client must point at whichever server hosts\n * the endpoint being called: pass the healthCheckPort base for `getHealth`\n * and the adminApi.port base for `getPeers` / `getMetrics`. In practice\n * Townhouse currently runs both ports on the same host, so callers either\n * construct two clients or hit a shared base URL when the ports overlap.\n */\n\nimport type {\n ChannelSummary,\n ConnectorFeeEntry,\n EarningsResponse,\n EarningsTimestamp,\n HealthResponse,\n HsHostnameResponse,\n MetricsResponse,\n PeerEarnings,\n PeerStatus,\n PeersResponse,\n PacketLogFilter,\n PacketLogEntry,\n PacketLogResponse,\n RecentClaim,\n} from './types.js';\n\n/** Default request timeout in milliseconds (5 seconds) */\nconst DEFAULT_TIMEOUT_MS = 5000;\n\nexport class ConnectorAdminClient {\n private readonly baseUrl: string;\n private readonly timeoutMs: number;\n\n /**\n * @param baseUrl - Base URL for the connector admin API (e.g., 'http://localhost:9402')\n * @param timeoutMs - Request timeout in milliseconds (default: 5000)\n */\n constructor(baseUrl: string, timeoutMs: number = DEFAULT_TIMEOUT_MS) {\n // Strip trailing slash to avoid double-slash in URL construction\n this.baseUrl = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;\n this.timeoutMs = timeoutMs;\n }\n\n /** Public read of the configured base URL (used by drill-command probes to derive a sibling client). */\n getBaseUrl(): string {\n return this.baseUrl;\n }\n\n /**\n * GET /health on the admin-API port — checks HTTP reachability of the\n * connector without validating the rich HealthStatus shape. Use this from\n * the drill-command health probe when only the admin URL is available\n * (port 9401), not the healthCheckPort (8080). The admin server's /health\n * returns `{status:'healthy', service:'admin-api', nodeId, timestamp}` —\n * a different shape from `getHealth()`'s validator. This method returns\n * a coarse status from a 200 response and reads `nodeId` if present.\n *\n * @throws Error when connector is unreachable or returns non-2xx.\n */\n async pingAdminLive(): Promise<{ status: 'healthy'; nodeId?: string }> {\n const response = await this.fetch('/health');\n const body: unknown = await response.json().catch(() => ({}));\n const nodeId =\n typeof body === 'object' &&\n body !== null &&\n typeof (body as Record<string, unknown>)['nodeId'] === 'string'\n ? ((body as Record<string, unknown>)['nodeId'] as string)\n : undefined;\n return nodeId !== undefined\n ? { status: 'healthy', nodeId }\n : { status: 'healthy' };\n }\n\n /**\n * GET /health — returns the connector's HealthStatus from the healthCheckPort server.\n *\n * @throws Error when connector is not running, returns non-200, or shape is invalid\n */\n async getHealth(): Promise<HealthResponse> {\n const response = await this.fetch('/health');\n const body: unknown = await response.json();\n if (typeof body !== 'object' || body === null) {\n throw new Error('Connector admin API: invalid health response shape');\n }\n const obj = body as Record<string, unknown>;\n const status = obj['status'];\n if (\n status !== 'healthy' &&\n status !== 'unhealthy' &&\n status !== 'starting' &&\n status !== 'degraded'\n ) {\n throw new Error('Connector admin API: invalid health response shape');\n }\n if (\n typeof obj['uptime'] !== 'number' ||\n typeof obj['peersConnected'] !== 'number' ||\n typeof obj['totalPeers'] !== 'number' ||\n typeof obj['timestamp'] !== 'string'\n ) {\n throw new Error('Connector admin API: invalid health response shape');\n }\n return body as HealthResponse;\n }\n\n /**\n * GET /admin/hs-hostname — returns the connector's published .anyone hidden-service\n * hostname (Epic 45 / Story 44.1). Returns 200 with {hostname, publishedAt} both\n * possibly null while bootstrap is in progress, both non-null once anon publishes.\n * Returns 503 when the connector is anon-disabled (anon.enabled: false in config).\n *\n * @throws Error('connector is anon-disabled (HTTP 503)') on 503 — caller can match\n * on this exact prefix for actionable diagnostics.\n * @throws Error on non-200/503 status, network error, or shape-validation failure.\n */\n async getHsHostname(): Promise<HsHostnameResponse> {\n const url = `${this.baseUrl}/admin/hs-hostname`;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n let body: unknown;\n try {\n let response: Response;\n try {\n response = await fetch(url, { signal: controller.signal });\n } catch (error: unknown) {\n if (error instanceof Error && error.name === 'AbortError') {\n throw new Error(\n `Connector admin API request timeout after ${this.timeoutMs}ms: ${url}`\n );\n }\n const msg = error instanceof Error ? error.message : String(error);\n throw new Error(`Connector admin API connection refused: ${msg}`);\n }\n if (response.status === 503) {\n throw new Error('connector is anon-disabled (HTTP 503)');\n }\n // fast-fail on unexpected non-200/503 status codes. Retrying a 404\n // (pre-v3.5.0 connector image) for 120 s silently burns the full readiness\n // timeout. Only 200 (success) and 503 (anon-disabled, caught above) are\n // expected — everything else is an immediate fatal error.\n if (!response.ok) {\n throw new Error(\n `Connector admin API unexpected status ${response.status} on /admin/hs-hostname — ` +\n `expected 200 or 503 (connector image may be too old or misconfigured)`\n );\n }\n // Body read MUST happen inside the AbortSignal-protected try so the\n // request timeout covers a slow / streaming JSON body. An AbortError\n // here means the body read itself timed out; surface as a timeout so\n // the readiness loop's diagnostics distinguish it from a malformed\n // body. Other JSON errors (true SyntaxError on non-JSON content)\n // re-throw as a shape error.\n try {\n body = await response.json();\n } catch (error: unknown) {\n if (error instanceof Error && error.name === 'AbortError') {\n throw new Error(\n `Connector admin API request timeout after ${this.timeoutMs}ms: ${url}`\n );\n }\n const msg = error instanceof Error ? error.message : String(error);\n throw new Error(\n `Connector admin API: invalid JSON in hs-hostname response: ${msg}`\n );\n }\n } finally {\n clearTimeout(timer);\n }\n if (typeof body !== 'object' || body === null) {\n throw new Error(\n 'Connector admin API: invalid hs-hostname response shape'\n );\n }\n const obj = body as Record<string, unknown>;\n const hostname = obj['hostname'];\n const publishedAt = obj['publishedAt'];\n if (\n (hostname !== null && typeof hostname !== 'string') ||\n (publishedAt !== null && typeof publishedAt !== 'string')\n ) {\n throw new Error(\n 'Connector admin API: invalid hs-hostname response shape'\n );\n }\n // Empty-string hostname / publishedAt are server-side bugs — reject so\n // the readiness loop fails fast instead of returning \"ready\" with an\n // unusable address or empty timestamp.\n if (typeof hostname === 'string' && hostname.length === 0) {\n throw new Error(\n 'Connector admin API: invalid hs-hostname response shape'\n );\n }\n if (typeof publishedAt === 'string' && publishedAt.length === 0) {\n throw new Error(\n 'Connector admin API: invalid hs-hostname response shape'\n );\n }\n // Enforce the `.anon` suffix at the trust boundary: the ATOR network uses\n // `.anon` as the hidden-service TLD (analogous to Tor's `.onion`). A\n // non-`.anon` value indicates connector-side misconfiguration and would\n // propagate as an unusable address through Story 45.4's CLI.\n if (typeof hostname === 'string' && !hostname.endsWith('.anon')) {\n throw new Error(\n 'Connector admin API: invalid hs-hostname response shape'\n );\n }\n return body as HsHostnameResponse;\n }\n\n /**\n * GET /admin/metrics.json — returns the connector's per-peer ILP counters\n * with an aggregate rollup, mirroring `AdminMetricsJsonResponse`.\n *\n * @throws Error when connector is not running, returns non-200, or shape is invalid\n */\n async getMetrics(): Promise<MetricsResponse> {\n const response = await this.fetch('/admin/metrics.json');\n const body: unknown = await response.json();\n if (typeof body !== 'object' || body === null) {\n throw new Error('Connector admin API: invalid metrics response shape');\n }\n const obj = body as Record<string, unknown>;\n const aggregate = obj['aggregate'];\n if (\n typeof obj['uptimeSeconds'] !== 'number' ||\n typeof aggregate !== 'object' ||\n aggregate === null ||\n !Array.isArray(obj['peers']) ||\n typeof obj['timestamp'] !== 'string'\n ) {\n throw new Error('Connector admin API: invalid metrics response shape');\n }\n const agg = aggregate as Record<string, unknown>;\n if (\n typeof agg['packetsForwarded'] !== 'number' ||\n typeof agg['packetsRejected'] !== 'number' ||\n typeof agg['bytesSent'] !== 'number'\n ) {\n throw new Error('Connector admin API: invalid metrics response shape');\n }\n return body as MetricsResponse;\n }\n\n /**\n * GET /admin/earnings.json — returns the connector's per-peer per-asset\n * earnings projection, mirroring `AdminEarningsJsonResponse` (connector v3.2.0+).\n *\n * Source of truth: @toon-protocol/connector\n * packages/connector/src/http/admin-api.ts:1864-1945\n *\n * Returns HTTP 503 when the connector is started without settlement config\n * (accountManager / claimReceiver not wired). Townhouse's apex always wires\n * both; 503 in production indicates connector misconfiguration.\n *\n * Wire-shape adaptation: the connector's `timestamp: string` field is\n * wrapped into `{ iso: string }` on the way out (EarningsTimestamp).\n *\n * @throws Error when connector is not running, returns non-200, or shape is invalid\n */\n async getEarnings(): Promise<EarningsResponse> {\n const response = await this.fetch('/admin/earnings.json');\n const body: unknown = await response.json();\n if (typeof body !== 'object' || body === null) {\n throw new Error('Connector admin API: invalid earnings response shape');\n }\n const obj = body as Record<string, unknown>;\n if (\n typeof obj['uptimeSeconds'] !== 'number' ||\n !Array.isArray(obj['peers']) ||\n !Array.isArray(obj['connectorFees']) ||\n !Array.isArray(obj['recentClaims']) ||\n typeof obj['timestamp'] !== 'string'\n ) {\n throw new Error('Connector admin API: invalid earnings response shape');\n }\n // Inner-element shape validation — AC #3 drift coverage for named fields\n // (peers[].byAsset[].claimsReceivedTotal, connectorFees[].assetCode, recentClaims[].direction).\n const peers = obj['peers'] as unknown[];\n for (const peer of peers) {\n if (typeof peer !== 'object' || peer === null) {\n throw new Error('Connector admin API: invalid earnings response shape');\n }\n const p = peer as Record<string, unknown>;\n if (typeof p['peerId'] !== 'string' || !Array.isArray(p['byAsset'])) {\n throw new Error('Connector admin API: invalid earnings response shape');\n }\n for (const asset of p['byAsset'] as unknown[]) {\n if (typeof asset !== 'object' || asset === null) {\n throw new Error(\n 'Connector admin API: invalid earnings response shape'\n );\n }\n const a = asset as Record<string, unknown>;\n if (\n typeof a['assetCode'] !== 'string' ||\n typeof a['assetScale'] !== 'number' ||\n typeof a['claimsReceivedTotal'] !== 'string' ||\n typeof a['claimsSentTotal'] !== 'string' ||\n typeof a['netBalance'] !== 'string' ||\n (a['lastClaimAt'] !== null && typeof a['lastClaimAt'] !== 'string')\n ) {\n throw new Error(\n 'Connector admin API: invalid earnings response shape'\n );\n }\n }\n }\n const fees = obj['connectorFees'] as unknown[];\n for (const fee of fees) {\n if (typeof fee !== 'object' || fee === null) {\n throw new Error('Connector admin API: invalid earnings response shape');\n }\n const f = fee as Record<string, unknown>;\n if (\n typeof f['assetCode'] !== 'string' ||\n typeof f['assetScale'] !== 'number' ||\n typeof f['total'] !== 'string'\n ) {\n throw new Error('Connector admin API: invalid earnings response shape');\n }\n }\n const claims = obj['recentClaims'] as unknown[];\n for (const claim of claims) {\n if (typeof claim !== 'object' || claim === null) {\n throw new Error('Connector admin API: invalid earnings response shape');\n }\n const c = claim as Record<string, unknown>;\n if (\n typeof c['peerId'] !== 'string' ||\n typeof c['assetCode'] !== 'string' ||\n typeof c['assetScale'] !== 'number' ||\n typeof c['amount'] !== 'string' ||\n (c['direction'] !== 'inbound' && c['direction'] !== 'outbound') ||\n typeof c['at'] !== 'string'\n ) {\n throw new Error('Connector admin API: invalid earnings response shape');\n }\n }\n const timestamp: EarningsTimestamp = { iso: obj['timestamp'] as string };\n // Explicit construction (not spread) — prevents forward-compat wire fields\n // from leaking through the typed surface.\n return {\n uptimeSeconds: obj['uptimeSeconds'] as number,\n peers: peers as PeerEarnings[],\n connectorFees: fees as ConnectorFeeEntry[],\n recentClaims: claims as RecentClaim[],\n timestamp,\n };\n }\n\n /**\n * GET /admin/peers — returns the connector's peer roster with route counts\n * and ILP addresses. Returns the unwrapped peers array (the wrapper's\n * nodeId / peerCount / connectedCount fields are dropped).\n *\n * @throws Error when connector is not running, returns non-200, or shape is invalid\n */\n async getPeers(): Promise<PeerStatus[]> {\n const response = await this.fetch('/admin/peers');\n const body: unknown = await response.json();\n if (typeof body !== 'object' || body === null) {\n throw new Error('Connector admin API: invalid peers response shape');\n }\n const obj = body as Record<string, unknown>;\n if (!Array.isArray(obj['peers'])) {\n throw new Error('Connector admin API: invalid peers response shape');\n }\n return (body as PeersResponse).peers;\n }\n\n /**\n * GET /admin/channels — returns the connector's payment-channel summaries\n * across all registered chain providers. Multi-chain: one entry per channel\n * regardless of chain.\n *\n * @throws Error when connector is not running, returns non-200, or shape is invalid\n */\n async getChannels(): Promise<ChannelSummary[]> {\n const response = await this.fetch('/admin/channels');\n const body: unknown = await response.json();\n if (!Array.isArray(body)) {\n throw new Error('Connector admin API: invalid channels response shape');\n }\n for (const entry of body) {\n if (typeof entry !== 'object' || entry === null) {\n throw new Error('Connector admin API: invalid channels response shape');\n }\n const e = entry as Record<string, unknown>;\n if (\n typeof e['channelId'] !== 'string' ||\n typeof e['peerId'] !== 'string' ||\n typeof e['chain'] !== 'string' ||\n typeof e['status'] !== 'string' ||\n typeof e['deposit'] !== 'string' ||\n typeof e['lastActivity'] !== 'string'\n ) {\n throw new Error('Connector admin API: invalid channels response shape');\n }\n }\n return body as ChannelSummary[];\n }\n\n /**\n * POST /admin/peers — register (or re-register, idempotent) a child peer\n * with the connector. Used by the boot reconciler (Story 46.1) to\n * re-register peers present in `nodes.yaml` but missing from the\n * connector's runtime peer roster (e.g., after a connector restart).\n *\n * The connector's POST /admin/peers handler treats a POST whose `id`\n * matches an existing peer as a re-registration (no-op for the peer\n * itself; routes are appended). A POST with a new `id` triggers\n * `addPeer()` and BTP connection setup.\n *\n * @param input.id - peer identifier (matches `nodes.yaml`'s `peerId` and\n * the connector's `PeerStatus.id`).\n * @param input.url - BTP WebSocket URL the connector dials. MUST start\n * with `ws://` or `wss://` (the connector validates this).\n * @param input.authToken - shared auth token; pass empty string for\n * internal Townhouse peers (no auth).\n * @param input.routes - optional ILP route prefixes to register against\n * this peer. The reconciler passes the peer's ilpAddress.\n * @param input.transport - optional per-peer transport selection\n * (connector >= 3.6.2). `'direct'` forces the connector to bypass the\n * global SOCKS5 transport for this peer, even when the apex itself\n * runs in `transport.type: socks5` mode. Required for Docker-sibling\n * peers in HS mode — the anon SOCKS5 proxy cannot resolve internal\n * Docker hostnames. When omitted, the peer inherits the connector's\n * global transport (back-compat with pre-3.6.2 connectors).\n *\n * @throws Error on non-2xx response, timeout, or connection refused.\n */\n async registerPeer(input: {\n id: string;\n url: string;\n authToken: string;\n routes?: { prefix: string; priority?: number }[];\n transport?: 'direct' | 'socks5';\n // Peer relation. `'child'` marks an apex-owned downstream node (town relay,\n // mill, dvm): the connector's `requiresSettlementClaim()` returns false for\n // children, so the apex routes to them for FREE (no settlement channel) —\n // children don't pay each other. Forwarded verbatim to POST /admin/peers,\n // which calls setPeerRelation(id, relation). On an existing (inbound-dialed)\n // peer the connector treats this as an update: no re-dial, just the\n // relation + any routes are (re)applied.\n relation?: 'parent' | 'peer' | 'child';\n }): Promise<void> {\n if (!input.url.startsWith('ws://') && !input.url.startsWith('wss://')) {\n throw new Error(\n `Connector admin API: registerPeer.url must start with ws:// or wss:// (got: ${input.url})`\n );\n }\n const url = `${this.baseUrl}/admin/peers`;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n try {\n let response: Response;\n try {\n response = await fetch(url, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(input),\n signal: controller.signal,\n });\n } catch (error: unknown) {\n if (error instanceof Error && error.name === 'AbortError') {\n throw new Error(\n `Connector admin API request timeout after ${this.timeoutMs}ms: POST ${url}`\n );\n }\n const msg = error instanceof Error ? error.message : String(error);\n throw new Error(\n `Connector admin API request failed: POST ${url} — ${msg}`\n );\n }\n if (!response.ok) {\n // Body read MUST happen inside the AbortSignal-protected try so a slow\n // response body cannot hang past the request timeout.\n let body = '';\n try {\n body = await response.text();\n } catch (error: unknown) {\n if (error instanceof Error && error.name === 'AbortError') {\n throw new Error(\n `Connector admin API request timeout after ${this.timeoutMs}ms: POST ${url} (body read)`\n );\n }\n /* best-effort: leave body empty */\n }\n throw new Error(\n `Connector admin API error: POST /admin/peers returned ${response.status} ${response.statusText}${body ? ` — ${body}` : ''}`\n );\n }\n } finally {\n clearTimeout(timer);\n }\n }\n\n /**\n * DELETE /admin/peers/:peerId?removeRoutes=true — deregister a child peer.\n *\n * Idempotent: a 404 from the connector (peer already removed) is treated as\n * success so callers can safely use this as a rollback step without knowing\n * whether the peer was ever registered.\n *\n * `removeRoutes=true` is always sent so the connector drops the ILP routing\n * entries for this peer along with the BTP connection config.\n *\n * @throws Error on empty peerId (rejected at client, no network request made)\n * @throws Error on non-2xx/404 response, timeout, or connection refused\n */\n async removePeer(peerId: string): Promise<void> {\n if (!peerId) {\n throw new Error(\n 'Connector admin API: removePeer requires a non-empty peerId'\n );\n }\n const url = `${this.baseUrl}/admin/peers/${encodeURIComponent(peerId)}?removeRoutes=true`;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n try {\n let response: Response;\n try {\n response = await fetch(url, {\n method: 'DELETE',\n signal: controller.signal,\n });\n } catch (error: unknown) {\n if (error instanceof Error && error.name === 'AbortError') {\n throw new Error(\n `Connector admin API request timeout after ${this.timeoutMs}ms: DELETE ${url}`\n );\n }\n const msg = error instanceof Error ? error.message : String(error);\n throw new Error(\n `Connector admin API request failed: DELETE ${url} — ${msg}`\n );\n }\n // 404 means peer already gone — idempotent success.\n if (response.status === 404) {\n return;\n }\n if (!response.ok) {\n let body = '';\n try {\n body = await response.text();\n } catch (error: unknown) {\n if (error instanceof Error && error.name === 'AbortError') {\n throw new Error(\n `Connector admin API request timeout after ${this.timeoutMs}ms: DELETE ${url} (body read)`\n );\n }\n /* best-effort: leave body empty */\n }\n throw new Error(\n `Connector admin API error: DELETE /admin/peers/${peerId} returned ${response.status} ${response.statusText}${body ? ` — ${body}` : ''}`\n );\n }\n } finally {\n clearTimeout(timer);\n }\n }\n\n /**\n * GET /packets — returns the connector's raw packet log filtered by the\n * given criteria. Used by the timeseries aggregation route (story 21.10).\n *\n * Townhouse-Side Contract: see packages/sdk/CONNECTOR_MIGRATION.md §getPacketLog.\n * If the connector image does not expose GET /packets, this method throws\n * with a `ConnectorEndpointNotFound` error code so the route can return 503.\n *\n * @throws Error with code='ConnectorEndpointNotFound' when connector returns 404\n * @throws Error when connector is not running, returns non-200, or shape is invalid\n */\n async getPacketLog(filter: PacketLogFilter = {}): Promise<PacketLogEntry[]> {\n const params = new URLSearchParams();\n if (filter.ilpAddress !== undefined)\n params.set('ilpAddress', filter.ilpAddress);\n if (filter.since !== undefined) params.set('since', String(filter.since));\n if (filter.limit !== undefined) params.set('limit', String(filter.limit));\n const path = params.toString()\n ? `/packets?${params.toString()}`\n : '/packets';\n\n let response: Response;\n try {\n response = await this.fetch(path);\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n if (msg.includes('404')) {\n const err = new Error(\n 'Connector does not expose GET /packets — endpoint not found'\n );\n (err as NodeJS.ErrnoException).code = 'ConnectorEndpointNotFound';\n throw err;\n }\n throw error;\n }\n\n const body: unknown = await response.json();\n if (!Array.isArray(body)) {\n throw new Error(\n 'Connector admin API: invalid packet log response shape — expected array'\n );\n }\n return body as PacketLogResponse;\n }\n\n // ── Private helpers ──\n\n /**\n * Perform an HTTP GET request to the connector admin API.\n * Wraps fetch with error handling for connection refused and non-200 responses.\n */\n private async fetch(path: string): Promise<Response> {\n const url = `${this.baseUrl}${path}`;\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n\n try {\n let response: Response;\n try {\n response = await fetch(url, { signal: controller.signal });\n } catch (error: unknown) {\n if (error instanceof Error && error.name === 'AbortError') {\n throw new Error(\n `Connector admin API request timeout after ${this.timeoutMs}ms: ${url}`\n );\n }\n const msg = error instanceof Error ? error.message : String(error);\n throw new Error(`Connector admin API connection refused: ${msg}`);\n }\n\n if (!response.ok) {\n throw new Error(\n `Connector admin API error: ${response.status} ${response.statusText}`\n );\n }\n\n return response;\n } finally {\n clearTimeout(timer);\n }\n }\n}\n","/**\n * Docker Orchestration Engine for Townhouse (Story 21.2).\n *\n * Manages the full container lifecycle: network creation, image pulling,\n * container creation/start/stop/removal, and health check polling.\n * Uses dockerode for programmatic Docker control with DI for testability.\n */\n\nimport { EventEmitter } from 'node:events';\nimport { spawn } from 'node:child_process';\nimport { existsSync, mkdirSync, writeFileSync } from 'node:fs';\nimport { dirname, isAbsolute, join } from 'node:path';\nimport type Docker from 'dockerode';\nimport type { TownhouseConfig } from '../config/schema.js';\nimport { ConnectorConfigGenerator } from '../connector/config-generator.js';\nimport { ConnectorAdminClient } from '../connector/admin-client.js';\nimport type { ComposeProfile } from '../compose-loader.js';\nimport {\n CONTAINER_PREFIX,\n TOWN_HEALTH_PORT,\n MILL_HEALTH_PORT,\n DVM_HEALTH_PORT,\n} from '../constants.js';\nimport type { NodeType, HealthCheckOptions, BandwidthStats } from './types.js';\nimport type { WalletManager } from '../wallet/index.js';\n\ninterface RunDockerOptions {\n timeout?: number;\n maxBuffer?: number;\n inheritStdio?: boolean;\n /** Override the subprocess env. Defaults to process.env when omitted. */\n env?: NodeJS.ProcessEnv;\n}\n\n/**\n * Run `docker <args>` as a child process and resolve with captured stderr/stdout.\n *\n * `inheritStdio: true` pipes the child's stdout straight to the parent's TTY so\n * the operator sees `docker pull` progress during a multi-minute first-time\n * pull (Story 45.3 AC #10). Stderr is always captured so we can surface\n * compose failure diagnostics through the `containerState` event channel.\n */\nfunction runDockerCompose(\n file: string,\n args: readonly string[],\n options: RunDockerOptions = {}\n): Promise<{ stdout: string; stderr: string }> {\n const {\n timeout,\n maxBuffer = 16 * 1024 * 1024,\n inheritStdio = false,\n env,\n } = options;\n return new Promise((resolve, reject) => {\n const child = spawn(file, Array.from(args), {\n stdio: inheritStdio\n ? ['ignore', 'inherit', 'pipe']\n : ['ignore', 'pipe', 'pipe'],\n ...(env !== undefined ? { env } : {}),\n });\n const stderrChunks: Buffer[] = [];\n const stdoutChunks: Buffer[] = [];\n let stderrLen = 0;\n let stdoutLen = 0;\n let timedOut = false;\n const timer =\n timeout !== undefined && timeout > 0\n ? setTimeout(() => {\n timedOut = true;\n child.kill('SIGTERM');\n // Force-kill if SIGTERM is ignored (5 s grace).\n setTimeout(() => {\n if (!child.killed) child.kill('SIGKILL');\n }, 5_000).unref();\n }, timeout)\n : null;\n child.stderr?.on('data', (chunk: Buffer) => {\n if (stderrLen < maxBuffer) {\n stderrChunks.push(chunk);\n stderrLen += chunk.length;\n }\n });\n // Capture stdout when piped (inheritStdio: false). When stdio inherits to\n // the parent TTY, child.stdout is null and we leave stdoutChunks empty —\n // the operator sees the output directly.\n child.stdout?.on('data', (chunk: Buffer) => {\n if (stdoutLen < maxBuffer) {\n stdoutChunks.push(chunk);\n stdoutLen += chunk.length;\n }\n });\n child.on('error', (err: NodeJS.ErrnoException) => {\n if (timer) clearTimeout(timer);\n reject(err);\n });\n child.on('close', (code, signal) => {\n if (timer) clearTimeout(timer);\n const stderr = Buffer.concat(stderrChunks).toString('utf8');\n const stdout = Buffer.concat(stdoutChunks).toString('utf8');\n if (timedOut) {\n const err = new Error(\n `docker subprocess timed out after ${timeout}ms`\n ) as Error & {\n stdout?: string;\n stderr?: string;\n code?: number | string;\n signal?: NodeJS.Signals | null;\n };\n err.stdout = stdout;\n err.stderr = stderr;\n err.code = 'ETIMEDOUT';\n err.signal = signal;\n return reject(err);\n }\n if (code === 0) {\n return resolve({ stdout, stderr });\n }\n const err = new Error(\n `docker subprocess exited with ${code !== null ? `code ${code}` : `signal ${signal}`}`\n ) as Error & {\n stdout?: string;\n stderr?: string;\n code?: number | string;\n signal?: NodeJS.Signals | null;\n };\n err.stdout = stdout;\n err.stderr = stderr;\n if (code !== null) err.code = code;\n if (signal !== null) err.signal = signal;\n reject(err);\n });\n });\n}\n\ntype ExecFileAsyncSignature = (\n file: string,\n args: readonly string[],\n options?: RunDockerOptions\n) => Promise<{ stdout: string; stderr: string }>;\n\n/** Nostr relay WebSocket port on Town containers (fixed by Dockerfile) */\nconst TOWN_RELAY_PORT = 7100;\n\n/** Container stats cache TTL in milliseconds */\nconst STATS_CACHE_TTL_MS = 5_000;\n\ninterface CachedStats {\n data: BandwidthStats | null;\n cachedAt: number;\n}\n\n/** Docker bridge network name */\nconst NETWORK_NAME = 'townhouse-net';\n\n/** Default images for node types (used when not overridden in config) */\nconst DEFAULT_NODE_IMAGES: Record<NodeType, string> = {\n town: 'toon:town',\n mill: 'toon:mill',\n dvm: 'toon:dvm',\n};\n\n/** Maximum number of start retries per container */\nconst MAX_START_RETRIES = 3;\n\n/** Internal connector port (Docker-internal, not exposed to host) */\nconst CONNECTOR_INTERNAL_PORT = 3000;\n\n/** Default ator sidecar image tag for relay hidden service publication. */\nconst RELAY_ATOR_SIDECAR_IMAGE = 'toon:townhouse-ator-sidecar';\n\n/**\n * SOCKS port for the relay-side ator sidecar. Distinct from the connector\n * HS sidecar's 9050 so the two can coexist on the same Docker network if\n * both transports are enabled.\n */\nconst RELAY_ATOR_SOCKS_PORT = 9051;\n\n/**\n * Error thrown by DockerOrchestrator HS-path failures (Story 45.3).\n * Carries the failed-service name + subprocess diagnostics so CLI consumers\n * (Story 45.4) can render Sally's failure-state copy library (UX-DR5).\n */\nexport class OrchestratorError extends Error {\n readonly service?: string;\n readonly exitCode?: number;\n readonly stderr?: string;\n constructor(\n message: string,\n options: {\n service?: string;\n exitCode?: number;\n stderr?: string;\n cause?: Error;\n } = {}\n ) {\n super(message, options.cause ? { cause: options.cause } : undefined);\n this.name = 'OrchestratorError';\n if (options.service !== undefined) this.service = options.service;\n if (options.exitCode !== undefined) this.exitCode = options.exitCode;\n if (options.stderr !== undefined) this.stderr = options.stderr;\n }\n}\n\n/**\n * Strip secret-name env assignments from compose stderr before it becomes\n * part of an error message (Story 46.2 P5). Compose stderr can echo env\n * interpolation, including injected secrets. Conservative: keep the KEY so\n * operators see which secret was involved; redact only the VALUE up to the\n * next whitespace, quote, or newline.\n */\nfunction redactSecretsInComposeStderr(stderr: string): string {\n const SECRET_KEYS = [\n 'TOWN_SECRET_KEY',\n 'MILL_SECRET_KEY',\n 'DVM_SECRET_KEY',\n 'TOWN_SETTLEMENT_PRIVATE_KEY',\n 'MILL_SETTLEMENT_PRIVATE_KEY',\n 'DVM_SETTLEMENT_PRIVATE_KEY',\n 'MILL_MNEMONIC',\n 'TOWNHOUSE_WALLET_PASSWORD',\n ];\n const pattern = new RegExp(`(${SECRET_KEYS.join('|')})=[^\\\\s\"'\\\\n\\\\r]+`, 'g');\n return stderr.replace(pattern, '$1=[REDACTED]');\n}\n\n/**\n * Normalize a Docker image reference to include an explicit tag.\n * Docker defaults to `:latest` when no tag is specified, but\n * `listImages()` RepoTags always include the explicit tag.\n * Without normalization, an untagged image like `nginx` would not\n * match `nginx:latest` in the local image cache check.\n */\nfunction normalizeImageTag(image: string): string {\n // If there's already a tag (contains ':' after the last '/'), return as-is.\n // Handle registry prefixes like ghcr.io/org/image:tag\n const lastSlash = image.lastIndexOf('/');\n const nameAndTag = lastSlash >= 0 ? image.slice(lastSlash + 1) : image;\n if (nameAndTag.includes(':')) {\n return image;\n }\n return `${image}:latest`;\n}\n\n/**\n * DockerOrchestrator manages the lifecycle of Townhouse containers.\n *\n * Constructor accepts a dockerode instance (DI for testability) and config.\n * Emits typed events defined in OrchestratorEvents: pullProgress,\n * containerState, and healthCheck.\n */\nexport class DockerOrchestrator extends EventEmitter {\n private readonly docker: Docker;\n private readonly config: TownhouseConfig;\n private readonly configGenerator: ConnectorConfigGenerator;\n private readonly walletManager: WalletManager | undefined;\n private activeNodes: NodeType[] = [];\n private readonly statsCache = new Map<string, CachedStats>();\n private readonly profile: ComposeProfile;\n private readonly composePath: string | undefined;\n private readonly execFileAsync: ExecFileAsyncSignature;\n private readonly adminClientFactory: (\n baseUrl: string,\n timeoutMs: number\n ) => ConnectorAdminClient;\n\n constructor(\n docker: Docker,\n config: TownhouseConfig,\n walletManager?: WalletManager,\n options: {\n profile?: ComposeProfile;\n composePath?: string;\n execFileAsync?: ExecFileAsyncSignature;\n adminClientFactory?: (\n baseUrl: string,\n timeoutMs: number\n ) => ConnectorAdminClient;\n } = {}\n ) {\n super();\n this.docker = docker;\n this.config = config;\n this.configGenerator = new ConnectorConfigGenerator(config);\n this.walletManager = walletManager;\n this.profile = options.profile ?? 'dev';\n // Trim composePath so a whitespace-only string trips the same validation\n // as undefined / empty string (otherwise ` ` would slip past the falsy\n // check below and be passed verbatim to docker).\n const trimmedComposePath = options.composePath?.trim();\n this.composePath =\n trimmedComposePath !== undefined && trimmedComposePath.length > 0\n ? trimmedComposePath\n : undefined;\n this.execFileAsync = options.execFileAsync ?? runDockerCompose;\n this.adminClientFactory =\n options.adminClientFactory ??\n ((url, t) => new ConnectorAdminClient(url, t));\n\n if (this.profile === 'hs' && !this.composePath) {\n throw new OrchestratorError(\n `profile: 'hs' requires a non-empty composePath. Pass options.composePath ` +\n `pointing at the rendered HS template (typically the composePath ` +\n `returned by materializeComposeTemplate('hs')).`\n );\n }\n }\n\n /**\n * Orchestrate full startup sequence. Branches on profile:\n * - 'dev' (default): dockerode-based, preserves existing dev-stack behavior\n * - 'hs': docker compose subprocess + HS hostname readiness gate\n */\n async up(profiles: NodeType[]): Promise<void> {\n if (this.profile === 'hs') {\n await this.upHs(profiles);\n // defer activeNodes mutation until after upHs succeeds so a\n // failed/timed-out upHs does not leave phantom state.\n this.activeNodes = [...profiles];\n } else {\n this.activeNodes = [...profiles];\n await this.upDev(profiles);\n }\n }\n\n private async upDev(profiles: NodeType[]): Promise<void> {\n await this.ensureNetwork();\n await this.pullImages(profiles);\n await this.startConnector();\n await this.waitForHealth('townhouse-connector');\n\n // Start all node containers in parallel\n await Promise.all(profiles.map((type) => this.startNode(type)));\n\n // Optional: bring up the relay-side ator sidecar after town is started.\n // It forwards inbound HS traffic to the town container's relay WS port,\n // so it must be created after the town container exists in DNS.\n if (profiles.includes('town') && this.config.transport.relayHiddenService) {\n await this.startRelayAtorSidecar();\n }\n }\n\n /**\n * Narrow `this.composePath` to a definite string. The constructor enforces\n * this invariant for `profile: 'hs'`; this helper exists so the HS-path\n * methods don't need a non-null assertion (lint-clean) and so a constructor\n * regression surfaces as an `OrchestratorError` rather than a `TypeError`.\n */\n private requireComposePath(): string {\n if (!this.composePath) {\n throw new OrchestratorError(\n `internal: composePath unset for HS profile (constructor invariant violated)`\n );\n }\n return this.composePath;\n }\n\n /**\n * validate that composePath is absolute and exists on disk before\n * passing it to any subprocess call. Defence-in-depth — callers pass paths\n * from materializeComposeTemplate so this should never fire in normal use.\n */\n private validateComposePath(composePath: string): void {\n if (!isAbsolute(composePath)) {\n throw new OrchestratorError(\n `composePath must be an absolute path, got: ${composePath}`\n );\n }\n if (!existsSync(composePath)) {\n throw new OrchestratorError(\n `composePath does not exist on disk: ${composePath}`\n );\n }\n }\n\n /** HS-mode startup: shell out to `docker compose up -d`, wait for HS hostname. */\n private async upHs(profiles: NodeType[]): Promise<void> {\n const composePath = this.requireComposePath();\n this.validateComposePath(composePath);\n // Profile flags MUST come BEFORE the subcommand per Docker Compose CLI grammar.\n // Deterministic order: town → mill → dvm (matches AC #4).\n const PROFILE_ORDER: NodeType[] = ['town', 'mill', 'dvm'];\n // Reject unknown profile types up-front rather than silently dropping them\n // (they would otherwise fail the `PROFILE_ORDER.includes()` check below\n // and start no containers).\n for (const p of profiles) {\n if (!PROFILE_ORDER.includes(p)) {\n throw new OrchestratorError(\n `Unknown profile '${String(p)}'. Expected one of: ${PROFILE_ORDER.join(', ')}.`\n );\n }\n }\n const args = ['compose', '-f', composePath];\n for (const type of PROFILE_ORDER) {\n if (profiles.includes(type)) {\n args.push('--profile', type);\n }\n }\n args.push('up', '-d');\n\n try {\n await this.execFileAsync('docker', args, {\n timeout: 180_000,\n maxBuffer: 16 * 1024 * 1024,\n inheritStdio: true,\n });\n } catch (err: unknown) {\n const e = err as NodeJS.ErrnoException & {\n stderr?: string;\n stdout?: string;\n code?: number | string;\n signal?: string | null;\n };\n const stderr = String(e.stderr ?? '');\n const numericExit = typeof e.code === 'number' ? e.code : undefined;\n const codeLabel = String(e.code ?? e.signal ?? 'unknown');\n let message: string;\n if (e.code === 'ENOENT') {\n message = `docker CLI not found on PATH (ENOENT): ${stderr.trim().slice(0, 2000)}`;\n } else if (e.code === 'ETIMEDOUT') {\n message = `docker compose up timed out after 180000ms: ${stderr.trim().slice(0, 2000)}`;\n } else {\n message = `docker compose up failed (exit ${codeLabel}): ${stderr.trim().slice(0, 2000)}`;\n }\n this.surfaceComposeFailure(stderr);\n throw new OrchestratorError(message, {\n ...(numericExit !== undefined ? { exitCode: numericExit } : {}),\n stderr,\n cause: err instanceof Error ? err : undefined,\n });\n }\n\n // roll back containers when waitForHsHostname times out or throws,\n // so the operator can retry without a manual `townhouse hs down`.\n try {\n await this.waitForHsHostname();\n } catch (err: unknown) {\n await this.downHs().catch(() => {\n // Best-effort rollback — ignore teardown errors so the original error\n // propagates to the caller unchanged.\n });\n throw err;\n }\n }\n\n /**\n * Parse Docker Compose stderr for failed-service names and emit a\n * containerState event per failed service so callers see the failure via\n * the same channel dev-mode uses (AC #6 — \"for each failed service\n * identified, it emits...\"). When no pattern matches, emit a single\n * fallback event with name `'compose-up'`.\n */\n private surfaceComposeFailure(stderr: string): void {\n // Patterns 1 + 2 capture the SERVICE name from quoted Compose v2 stderr\n // (e.g., `failed to start \"townhouse-api\"`).\n // Pattern 3 was hardcoded to `townhouse-hs-` which would miss\n // Epic 46 containers (town-*, mill-*, dvm-*). The generic Compose container\n // name format is `<project>-<service>-<N>`. Capture the service name by\n // matching `Container <word>-<service>-<N> Error` without a fixed prefix.\n const patterns = [\n /failed to start (?:service\\s+)?[\"']([^\"']+)[\"']/gi,\n /service\\s+[\"']([^\"']+)[\"']\\s+failed/gi,\n /Container\\s+[\\w-]+-([a-z][\\w-]*?)(?:-\\d+)?\\s+Error/gi,\n ];\n const detail = stderr.trim().slice(0, 2000);\n const seen = new Set<string>();\n for (const pattern of patterns) {\n for (const match of stderr.matchAll(pattern)) {\n const name = match[1];\n if (name && !seen.has(name)) {\n seen.add(name);\n this.emit('containerState', { name, state: 'error', detail });\n }\n }\n }\n if (seen.size === 0) {\n this.emit('containerState', {\n name: 'compose-up',\n state: 'error',\n detail,\n });\n }\n }\n\n private async waitForHsHostname(): Promise<void> {\n const adminUrl = `http://127.0.0.1:${this.config.connector.adminPort}`;\n const client = this.adminClientFactory(adminUrl, 5_000);\n // use process.hrtime.bigint() (monotonic) instead of Date.now() so\n // laptop suspend/resume cannot jump the clock backward and extend the timeout\n // indefinitely. 120_000ms → 120_000_000_000n nanoseconds.\n const deadlineNs = process.hrtime.bigint() + 120_000_000_000n;\n const pollInterval = 2_000;\n let lastResponse:\n | { hostname: string | null; publishedAt: string | null }\n | undefined;\n let lastError: Error | undefined;\n while (process.hrtime.bigint() < deadlineNs) {\n try {\n lastResponse = await client.getHsHostname();\n lastError = undefined;\n if (\n lastResponse.hostname !== null &&\n lastResponse.publishedAt !== null\n ) {\n return;\n }\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n lastError = err instanceof Error ? err : new Error(String(err));\n // 503 anon-disabled is fatal — do NOT keep polling.\n if (msg.includes('anon-disabled')) {\n throw new OrchestratorError(\n `connector is anon-disabled — set anon.enabled: true in the connector config`,\n { cause: err instanceof Error ? err : undefined }\n );\n }\n // Connector returned 200 with malformed body (or non-JSON) — fatal.\n // Retrying for 120 s would mask a real connector regression behind a\n // generic timeout. Surface the shape error immediately.\n if (\n msg.includes('invalid hs-hostname response shape') ||\n msg.includes('invalid JSON in hs-hostname response')\n ) {\n throw new OrchestratorError(\n `connector returned a malformed /admin/hs-hostname response: ${msg}`,\n { cause: err instanceof Error ? err : undefined }\n );\n }\n // fast-fail on unexpected status codes (e.g. 404 from a pre-v3.5.0\n // connector image). The admin-client throws with 'unexpected status' in the\n // message; retrying these would burn the full 120 s budget silently.\n if (msg.includes('unexpected status')) {\n throw new OrchestratorError(msg, {\n cause: err instanceof Error ? err : undefined,\n });\n }\n // Network errors (ECONNREFUSED, request timeout, etc.) — retry within\n // budget. lastError preserves the most recent diagnostic for the\n // timeout message below.\n }\n await new Promise((r) => setTimeout(r, pollInterval));\n }\n // Build a timeout message that prefers the most recent observation. If the\n // last poll *failed* (e.g., connector died mid-bootstrap), report that —\n // not a stale earlier success — so the operator sees connection death\n // instead of \"anon bootstrap is still in progress\" misdiagnosis.\n const tail = lastError\n ? ` (last error: ${lastError.message})`\n : lastResponse\n ? ` (last response: ${JSON.stringify(lastResponse)})`\n : ' (no successful response received)';\n throw new OrchestratorError(\n `HS hostname publication timeout after 120000ms` + tail,\n lastError ? { cause: lastError } : {}\n );\n }\n\n /**\n * Regenerate connector config and restart the connector container\n * with updated environment variables (peer list).\n *\n * Sequence: emit connectorRestarting -> stop -> remove -> create -> start -> health -> emit connectorRestarted\n */\n async regenerateConnectorConfig(activeNodes: NodeType[]): Promise<void> {\n this.activeNodes = [...activeNodes];\n\n this.emit('connectorRestarting', { reason: 'peer list updated' });\n\n // Stop and remove existing connector.\n // Use separate try-catch blocks so a failed stop() (e.g. container in\n // Created/exited state) still allows remove() to run and clear the name.\n const connectorName = `${CONTAINER_PREFIX}connector`;\n const existingContainer = this.docker.getContainer(connectorName);\n try {\n await existingContainer.stop({ t: 5 });\n } catch {\n /* not running */\n }\n try {\n await existingContainer.remove();\n } catch {\n /* may not exist */\n }\n\n // Ensure the network exists — regenerate can be called independently of\n // up() (e.g. fee change), so we cannot assume ensureNetwork() already ran.\n await this.ensureNetwork();\n\n // Start new connector with updated config. Always emit connectorRestarted in\n // a finally block so WS clients clear the restarting state even when the\n // connector fails to start (prevents isRestarting stuck indefinitely in UI).\n try {\n await this.startConnector();\n await this.waitForHealth(connectorName);\n } finally {\n this.emit('connectorRestarted', { peers: activeNodes });\n }\n }\n\n /**\n * Hot-add a node after initial startup.\n * Starts the node container, then restarts the connector with updated peer list.\n */\n async addNode(type: NodeType): Promise<void> {\n if (!this.activeNodes.includes(type)) {\n this.activeNodes.push(type);\n }\n\n await this.startNode(type);\n await this.regenerateConnectorConfig(this.activeNodes);\n }\n\n /**\n * Hot-remove a node.\n * Stops the node container, then restarts the connector with updated peer list.\n */\n async removeNode(type: NodeType): Promise<void> {\n this.activeNodes = this.activeNodes.filter((n) => n !== type);\n\n const containerName = `${CONTAINER_PREFIX}${type}`;\n await this.stopAndRemove(containerName);\n await this.regenerateConnectorConfig(this.activeNodes);\n }\n\n /**\n * Graceful shutdown. Branches on profile:\n * - 'dev' (default): dockerode-based teardown\n * - 'hs': docker compose subprocess\n */\n async down(): Promise<void> {\n if (this.profile === 'hs') {\n await this.downHs();\n } else {\n await this.downDev();\n }\n }\n\n private async downDev(): Promise<void> {\n const containers = await this.docker.listContainers({ all: true });\n\n // Find all townhouse containers\n const nodeContainerNames: string[] = [];\n let connectorName: string | undefined;\n\n for (const info of containers) {\n for (const name of info.Names) {\n const cleanName = name.startsWith('/') ? name.slice(1) : name;\n if (!cleanName.startsWith(CONTAINER_PREFIX)) continue;\n\n if (cleanName === `${CONTAINER_PREFIX}connector`) {\n connectorName = cleanName;\n } else {\n nodeContainerNames.push(cleanName);\n }\n }\n }\n\n // Stop nodes first (parallel)\n await Promise.all(\n nodeContainerNames.map((name) => this.stopAndRemove(name))\n );\n\n // Then stop connector\n if (connectorName) {\n await this.stopAndRemove(connectorName);\n }\n\n // Remove network\n await this.removeNetwork();\n }\n\n private async downHs(): Promise<void> {\n const composePath = this.requireComposePath();\n const args = ['compose', '-f', composePath, 'down'];\n // NO -v flag — preserves the townhouse-hs-anon volume so the .anyone\n // address survives `down` (Story 45.4 AC).\n try {\n // 120s timeout (up from 60s) to accommodate Epic 46 multi-container\n // stacks where town+mill+dvm each need a SIGTERM grace period.\n await this.execFileAsync('docker', args, {\n timeout: 120_000,\n maxBuffer: 16 * 1024 * 1024,\n });\n } catch (err: unknown) {\n const e = err as NodeJS.ErrnoException & {\n stderr?: string;\n code?: number | string;\n signal?: string | null;\n };\n const stderr = String(e.stderr ?? '');\n const numericExit = typeof e.code === 'number' ? e.code : undefined;\n const codeLabel = String(e.code ?? e.signal ?? 'unknown');\n let message: string;\n if (e.code === 'ENOENT') {\n message = `docker CLI not found on PATH (ENOENT): ${stderr.trim().slice(0, 2000)}`;\n } else if (e.code === 'ETIMEDOUT') {\n message = `docker compose down timed out after 120000ms: ${stderr.trim().slice(0, 2000)}`;\n } else {\n // Compose down returns non-zero when nothing is running on some\n // Compose versions. Treat an empty-stack teardown (no containers / no\n // such service) as success so `townhouse hs down` is idempotent.\n if (\n stderr.includes('no such service') ||\n stderr.includes('no containers to remove') ||\n stderr.includes('No such container') ||\n (stderr.includes('network') && stderr.includes('not found'))\n ) {\n return;\n }\n message = `docker compose down failed (exit ${codeLabel}): ${stderr.trim().slice(0, 2000)}`;\n }\n throw new OrchestratorError(message, {\n ...(numericExit !== undefined ? { exitCode: numericExit } : {}),\n stderr,\n cause: err instanceof Error ? err : undefined,\n });\n }\n }\n\n /**\n * Resolve the Nostr relay WebSocket URL for a Town node instance.\n *\n * Inspects the container's port bindings to get the host-bound port for\n * the relay WebSocket (7100/tcp). Falls back to the Docker-internal URL\n * when the server is running inside the Docker network or bindings are absent.\n *\n * @param nodeId - The `NodeInfo.id` value (e.g. 'town', 'dev-town-01')\n */\n async getNodeRelayEndpoint(nodeId: string): Promise<string> {\n const containerName = `${CONTAINER_PREFIX}${nodeId}`;\n try {\n const container = this.docker.getContainer(containerName);\n const info = await container.inspect();\n const portBindings = info.HostConfig?.PortBindings as\n | Record<string, { HostIp?: string; HostPort?: string }[] | null>\n | undefined;\n const binding = portBindings?.[`${TOWN_RELAY_PORT}/tcp`]?.[0];\n if (binding?.HostPort) {\n const host =\n binding.HostIp && binding.HostIp !== '0.0.0.0'\n ? binding.HostIp\n : '127.0.0.1';\n // nosemgrep: javascript.lang.security.detect-insecure-websocket -- operator-controlled host, not user input\n return `ws://${host}:${binding.HostPort}`;\n }\n } catch {\n // Container not found or inspect failed — fall through to Docker-internal fallback\n }\n // Docker-internal fallback (server running inside Docker network)\n // nosemgrep: javascript.lang.security.detect-insecure-websocket -- Docker-internal, TLS unnecessary\n return `ws://${containerName}:${TOWN_RELAY_PORT}`;\n }\n\n /**\n * Resolve the BLS health HTTP URL for a node instance.\n *\n * Inspects the container's port bindings to find the host-bound port for the\n * node's health endpoint. Falls back to Docker-internal URL when running\n * inside the Docker network or when bindings are absent.\n *\n * @param nodeId - The `NodeInfo.id` value (e.g. 'mill', 'dev-mill-01')\n * @param type - Node type (determines which internal port to use)\n */\n async getNodeHealthEndpoint(\n nodeId: string,\n type: 'town' | 'mill' | 'dvm'\n ): Promise<string> {\n const port =\n type === 'town'\n ? TOWN_HEALTH_PORT\n : type === 'mill'\n ? MILL_HEALTH_PORT\n : DVM_HEALTH_PORT;\n const containerName = `${CONTAINER_PREFIX}${nodeId}`;\n try {\n const container = this.docker.getContainer(containerName);\n const info = await container.inspect();\n const portBindings = info.HostConfig?.PortBindings as\n | Record<string, { HostIp?: string; HostPort?: string }[] | null>\n | undefined;\n const binding = portBindings?.[`${port}/tcp`]?.[0];\n if (binding?.HostPort) {\n const host =\n binding.HostIp && binding.HostIp !== '0.0.0.0'\n ? binding.HostIp\n : '127.0.0.1';\n return `http://${host}:${binding.HostPort}`;\n }\n } catch {\n // Container not found or inspect failed — fall through to Docker-internal fallback\n }\n return `http://${containerName}:${port}`;\n }\n\n /**\n * Fetch network I/O stats for a container.\n * Results are cached for 5 seconds to avoid per-request Docker API overhead.\n *\n * @param containerName - Full container name (e.g. 'townhouse-town')\n * @returns Bandwidth stats or null when container is not running\n */\n async getContainerStats(\n containerName: string\n ): Promise<BandwidthStats | null> {\n const cached = this.statsCache.get(containerName);\n if (cached && Date.now() - cached.cachedAt < STATS_CACHE_TTL_MS) {\n return cached.data;\n }\n\n try {\n const container = this.docker.getContainer(containerName);\n const stats = (await container.stats({\n stream: false,\n })) as unknown as Record<string, unknown>;\n\n const networks = stats['networks'] as\n | Record<string, { rx_bytes?: number; tx_bytes?: number }>\n | undefined;\n\n if (!networks) {\n const result: BandwidthStats = {\n bytesIn: 0,\n bytesOut: 0,\n sampleAt: Date.now(),\n };\n this.statsCache.set(containerName, {\n data: result,\n cachedAt: Date.now(),\n });\n return result;\n }\n\n let bytesIn = 0;\n let bytesOut = 0;\n for (const iface of Object.values(networks)) {\n bytesIn += iface.rx_bytes ?? 0;\n bytesOut += iface.tx_bytes ?? 0;\n }\n\n const result: BandwidthStats = {\n bytesIn,\n bytesOut,\n sampleAt: Date.now(),\n };\n this.statsCache.set(containerName, {\n data: result,\n cachedAt: Date.now(),\n });\n return result;\n } catch {\n // Container not running or stats unavailable\n this.statsCache.set(containerName, { data: null, cachedAt: Date.now() });\n return null;\n }\n }\n\n /**\n * Return status for all townhouse containers.\n *\n * Discovers both single-instance (townhouse-<type>) and multi-instance\n * (townhouse-<prefix>-<type>-<n>) containers. Multi-instance containers\n * are returned with a `name` matching their instance suffix so callers\n * can build per-instance NodeInfo entries (e.g. \"dev-town-01\").\n */\n async status(): Promise<\n {\n name: string;\n type: 'connector' | 'town' | 'mill' | 'dvm';\n state: string;\n health?: string;\n startedAt?: string;\n }[]\n > {\n const containers = await this.docker.listContainers({ all: true });\n const nodeTypes = ['town', 'mill', 'dvm'] as const;\n const allTypes = ['connector', ...nodeTypes] as const;\n\n // Collect all containers that belong to this townhouse instance\n const matching: {\n containerName: string;\n type: (typeof allTypes)[number];\n info: (typeof containers)[number];\n }[] = [];\n\n for (const c of containers) {\n for (const rawName of c.Names) {\n const name = rawName.startsWith('/') ? rawName.slice(1) : rawName;\n if (!name.startsWith(CONTAINER_PREFIX)) continue;\n const suffix = name.slice(CONTAINER_PREFIX.length); // e.g. \"town\", \"dev-town-01\"\n for (const type of allTypes) {\n if (\n suffix === type ||\n suffix.endsWith(`-${type}`) ||\n suffix.includes(`-${type}-`)\n ) {\n matching.push({ containerName: name, type, info: c });\n break;\n }\n }\n }\n }\n\n const results: {\n name: string;\n type: (typeof allTypes)[number];\n state: string;\n health?: string;\n startedAt?: string;\n }[] = [];\n\n for (const { containerName, type, info } of matching) {\n const suffix = containerName.slice(CONTAINER_PREFIX.length); // e.g. \"town\", \"dev-town-01\"\n let health: string | undefined;\n let startedAt: string | undefined;\n try {\n const container = this.docker.getContainer(containerName);\n const detail = await container.inspect();\n health = detail.State?.Health?.Status ?? undefined;\n startedAt = detail.State?.StartedAt ?? undefined;\n } catch {\n // Inspect may fail if container is being removed — skip health and startedAt\n }\n results.push({\n name: suffix, // \"town\" for single-instance, \"dev-town-01\" for multi\n type,\n state: info.State ?? 'stopped',\n ...(health !== undefined ? { health } : {}),\n ...(startedAt !== undefined ? { startedAt } : {}),\n });\n }\n\n // Ensure every type has at least one entry (stopped placeholder)\n for (const type of allTypes) {\n if (!results.some((r) => r.type === type)) {\n results.push({ name: type, type, state: 'stopped' });\n }\n }\n\n return results;\n }\n\n /**\n * Pull required images before starting containers.\n * Skips images that already exist locally.\n * Emits pullProgress events during download.\n */\n async pullImages(profiles: NodeType[]): Promise<void> {\n const imagesToPull = new Set<string>();\n\n // Always need the connector image\n imagesToPull.add(normalizeImageTag(this.config.connector.image));\n\n // Add node images\n for (const type of profiles) {\n const nodeConfig = this.config.nodes[type];\n const image = nodeConfig.image ?? DEFAULT_NODE_IMAGES[type];\n imagesToPull.add(normalizeImageTag(image));\n }\n\n // Pull the relay ator sidecar when the operator opted in. Built locally\n // by docker/townhouse-ator-sidecar — pull may 404, which is fine; the\n // operator must have built it before `townhouse up` (see README).\n if (profiles.includes('town') && this.config.transport.relayHiddenService) {\n imagesToPull.add(normalizeImageTag(RELAY_ATOR_SIDECAR_IMAGE));\n }\n\n // Delegate per-image pull to the public pullImage method (reuses skip-if-exists logic).\n for (const image of imagesToPull) {\n await this.pullImage(image);\n }\n }\n\n /**\n * Pull a single image by its reference (tag or digest form).\n *\n * Skips the pull when the image already exists locally (matches against\n * both RepoTags and RepoDigests so digest-form refs like\n * `ghcr.io/toon-protocol/town@sha256:abc...` are found correctly).\n * Throws `OrchestratorError` on pull failure.\n */\n async pullImage(image: string): Promise<void> {\n const existingImages = await this.docker.listImages();\n const existingRefs = new Set<string>();\n for (const img of existingImages) {\n for (const tag of img.RepoTags ?? []) existingRefs.add(tag);\n for (const digest of img.RepoDigests ?? []) existingRefs.add(digest);\n }\n if (existingRefs.has(image)) {\n return;\n }\n try {\n const stream = await this.docker.pull(image);\n await this.followPullProgress(image, stream);\n } catch (err: unknown) {\n throw new OrchestratorError(\n `Failed to pull image ${image}: ${err instanceof Error ? err.message : String(err)}`,\n { cause: err instanceof Error ? err : undefined }\n );\n }\n }\n\n /**\n * Start a child peer node via `docker compose --profile <type> up -d <type>`.\n *\n * HS-profile only — throws `OrchestratorError` when called on the dev profile.\n *\n * The `env` parameter supplies the per-node wallet secrets (e.g.\n * `TOWN_SECRET_KEY`, `MILL_MNEMONIC`). It is layered on top of `process.env`\n * so that PATH, HOME, and other process-level env vars are preserved for the\n * docker CLI subprocess.\n *\n * Logging guard: the caller (nodes-lifecycle route) must NOT log the `env`\n * argument — it contains secret keys and the wallet mnemonic.\n */\n async startNodeViaCompose(\n type: NodeType,\n env: Record<string, string>\n ): Promise<void> {\n if (this.profile === 'dev') {\n throw new OrchestratorError(\n `startNodeViaCompose is only available in HS profile; current profile is 'dev'`\n );\n }\n const composePath = this.requireComposePath();\n this.validateComposePath(composePath);\n\n const args = [\n 'compose',\n '-f',\n composePath,\n '--profile',\n type,\n 'up',\n '-d',\n type,\n ] as const;\n\n try {\n await this.execFileAsync('docker', args, {\n timeout: 180_000,\n maxBuffer: 16 * 1024 * 1024,\n inheritStdio: true,\n // Layer node secrets on top of process.env — preserves PATH, HOME, etc.\n env: { ...process.env, ...env },\n });\n } catch (err: unknown) {\n const e = err as NodeJS.ErrnoException & {\n stderr?: string;\n code?: number | string;\n signal?: string | null;\n };\n // P5: redact env-value secrets before stderr ever reaches an error\n // message or response body.\n const stderr = redactSecretsInComposeStderr(String(e.stderr ?? ''));\n const numericExit = typeof e.code === 'number' ? e.code : undefined;\n const codeLabel = String(e.code ?? e.signal ?? 'unknown');\n let message: string;\n if (e.code === 'ENOENT') {\n message = `docker CLI not found on PATH (ENOENT): ${stderr.trim().slice(0, 2000)}`;\n } else if (e.code === 'ETIMEDOUT') {\n message = `docker compose up timed out after 180000ms: ${stderr.trim().slice(0, 2000)}`;\n } else {\n message = `docker compose up failed (exit ${codeLabel}): ${stderr.trim().slice(0, 2000)}`;\n }\n this.surfaceComposeFailure(stderr);\n throw new OrchestratorError(message, {\n ...(numericExit !== undefined ? { exitCode: numericExit } : {}),\n stderr,\n cause: err instanceof Error ? err : undefined,\n });\n }\n }\n\n /**\n * Stop and remove a child peer node via `docker compose stop` + `rm -f`.\n *\n * HS-profile only — throws `OrchestratorError` when called on the dev profile.\n * Idempotent: stderr patterns indicating the service/container is already gone\n * (`'no such service'`, `'no containers to remove'`, `'No such container'`)\n * are treated as success so callers can run this as a rollback without\n * worrying about the container's prior state.\n */\n async stopNodeViaCompose(type: NodeType): Promise<void> {\n if (this.profile === 'dev') {\n throw new OrchestratorError(\n `stopNodeViaCompose is only available in HS profile; current profile is 'dev'`\n );\n }\n const composePath = this.requireComposePath();\n\n const idempotentStderr = (stderr: string): boolean =>\n stderr.includes('no such service') ||\n stderr.includes('no containers to remove') ||\n stderr.includes('No such container');\n\n // Step 1: stop the service\n try {\n await this.execFileAsync(\n 'docker',\n ['compose', '-f', composePath, '--profile', type, 'stop', type],\n { timeout: 60_000, maxBuffer: 16 * 1024 * 1024 }\n );\n } catch (err: unknown) {\n const e = err as NodeJS.ErrnoException & {\n stderr?: string;\n code?: number | string;\n };\n const stderr = redactSecretsInComposeStderr(String(e.stderr ?? ''));\n if (!idempotentStderr(stderr)) {\n // P11: wrap raw error in OrchestratorError for parity with startNodeViaCompose.\n const numericExit = typeof e.code === 'number' ? e.code : undefined;\n const codeLabel = String(e.code ?? 'unknown');\n throw new OrchestratorError(\n `docker compose stop failed (exit ${codeLabel}): ${stderr.trim().slice(0, 2000)}`,\n {\n ...(numericExit !== undefined ? { exitCode: numericExit } : {}),\n stderr,\n cause: err instanceof Error ? err : undefined,\n }\n );\n }\n }\n\n // Step 2: remove the stopped container so a future `up` re-creates it cleanly\n try {\n await this.execFileAsync(\n 'docker',\n ['compose', '-f', composePath, '--profile', type, 'rm', '-f', type],\n { timeout: 60_000, maxBuffer: 16 * 1024 * 1024 }\n );\n } catch (err: unknown) {\n const e = err as NodeJS.ErrnoException & {\n stderr?: string;\n code?: number | string;\n };\n const stderr = redactSecretsInComposeStderr(String(e.stderr ?? ''));\n if (!idempotentStderr(stderr)) {\n const numericExit = typeof e.code === 'number' ? e.code : undefined;\n const codeLabel = String(e.code ?? 'unknown');\n throw new OrchestratorError(\n `docker compose rm failed (exit ${codeLabel}): ${stderr.trim().slice(0, 2000)}`,\n {\n ...(numericExit !== undefined ? { exitCode: numericExit } : {}),\n stderr,\n cause: err instanceof Error ? err : undefined,\n }\n );\n }\n }\n }\n\n /**\n * Poll container health status via inspect().\n * Retries at configurable interval, throws on timeout.\n */\n async healthCheck(\n containerName: string,\n options?: HealthCheckOptions\n ): Promise<string> {\n const interval = options?.interval ?? 2000;\n const timeout = options?.timeout ?? 60000;\n const startTime = Date.now();\n let attempt = 0;\n\n while (Date.now() - startTime < timeout) {\n attempt++;\n try {\n const container = this.docker.getContainer(containerName);\n const info = await container.inspect();\n\n const healthStatus = info.State?.Health?.Status ?? 'none';\n\n this.emit('healthCheck', {\n name: containerName,\n status: healthStatus,\n attempt,\n });\n\n if (healthStatus === 'healthy') {\n return 'healthy';\n }\n } catch {\n // Transient inspect failure (Docker daemon hiccup) — retry within timeout\n this.emit('healthCheck', {\n name: containerName,\n status: 'error',\n attempt,\n });\n }\n\n // Wait before next poll\n await new Promise((resolve) => setTimeout(resolve, interval));\n }\n\n throw new Error(\n `Health check timeout: ${containerName} did not become healthy within ${timeout}ms`\n );\n }\n\n // ── Private helpers ──\n\n /**\n * Create the townhouse-net bridge network if it doesn't exist.\n */\n private async ensureNetwork(): Promise<void> {\n try {\n // Docker's name filter does substring matching, so we post-filter\n // with an exact Name comparison to avoid false positives.\n const networks = await this.docker.listNetworks({\n filters: { name: [NETWORK_NAME] },\n });\n\n const exists = networks.some(\n (n: { Name: string }) => n.Name === NETWORK_NAME\n );\n if (exists) return;\n\n await this.docker.createNetwork({\n Name: NETWORK_NAME,\n Driver: 'bridge',\n });\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n if (\n msg.includes('ENOENT') ||\n msg.includes('ECONNREFUSED') ||\n msg.includes('socket')\n ) {\n throw new Error(\n `Docker is not running or not available. Please start Docker and try again. (${msg})`\n );\n }\n throw error;\n }\n }\n\n /**\n * Start the connector container — always runs first.\n *\n * The connector image at 3.3.x reads its config from a YAML file pointed\n * to by the `CONFIG_FILE` env var (default `./config.yaml`). We write the\n * generated YAML to `<configDir>/connector.yaml` (sibling to wallet.enc),\n * mount it as `/config/connector.yaml`, and set CONFIG_FILE accordingly.\n *\n * (Env-var-based config was set on the container historically but the\n * connector image silently ignored them — see the YAML fix landing with\n * this comment block.)\n */\n private async startConnector(): Promise<void> {\n const name = `${CONTAINER_PREFIX}connector`;\n const env = this.buildConnectorEnv();\n env.push('CONFIG_FILE=/config/connector.yaml');\n\n // Write the YAML config beside the wallet so it's stable across restarts.\n const configDir = dirname(this.config.wallet.encrypted_path);\n mkdirSync(configDir, { recursive: true, mode: 0o700 });\n const yamlPath = join(configDir, 'connector.yaml');\n const runtimeConfig = this.configGenerator.generate(this.activeNodes);\n writeFileSync(yamlPath, this.configGenerator.toYaml(runtimeConfig), {\n encoding: 'utf-8',\n mode: 0o600,\n });\n\n this.emit('containerState', { name, state: 'creating' });\n\n const container = await this.docker.createContainer({\n name,\n Image: this.config.connector.image,\n Env: env,\n ExposedPorts: {\n [`${CONNECTOR_INTERNAL_PORT}/tcp`]: {},\n [`${this.config.connector.adminPort}/tcp`]: {},\n },\n HostConfig: {\n NetworkMode: NETWORK_NAME,\n Binds: [`${yamlPath}:/config/connector.yaml:ro`],\n PortBindings: {\n [`${this.config.connector.adminPort}/tcp`]: [\n {\n HostIp: '127.0.0.1',\n HostPort: String(this.config.connector.adminPort),\n },\n ],\n },\n },\n });\n\n this.emit('containerState', { name, state: 'starting' });\n await container.start();\n this.emit('containerState', { name, state: 'running' });\n }\n\n /**\n * Start a node container (town, mill, or dvm).\n * Retries up to MAX_START_RETRIES on failure.\n */\n private async startNode(type: NodeType): Promise<void> {\n const name = `${CONTAINER_PREFIX}${type}`;\n const nodeConfig = this.config.nodes[type];\n const image = nodeConfig.image ?? DEFAULT_NODE_IMAGES[type];\n const env = await this.buildNodeEnv(type);\n\n let lastError: Error | undefined;\n\n for (let attempt = 1; attempt <= MAX_START_RETRIES; attempt++) {\n try {\n this.emit('containerState', { name, state: 'creating' });\n\n const container = await this.docker.createContainer({\n name,\n Image: image,\n Env: env,\n HostConfig: {\n NetworkMode: NETWORK_NAME,\n },\n });\n\n this.emit('containerState', { name, state: 'starting' });\n await container.start();\n this.emit('containerState', { name, state: 'running' });\n return;\n } catch (error: unknown) {\n lastError = error instanceof Error ? error : new Error(String(error));\n this.emit('containerState', { name, state: 'error' });\n\n // Clean up failed container before retry\n try {\n const existing = this.docker.getContainer(name);\n await existing.remove({ force: true });\n } catch {\n // Container may not exist, ignore\n }\n }\n }\n\n throw new Error(\n `Failed to start container ${name} after ${MAX_START_RETRIES} restart attempts: ${lastError?.message}`\n );\n }\n\n /**\n * Wait for a container's health check to pass.\n */\n private async waitForHealth(containerName: string): Promise<void> {\n await this.healthCheck(containerName);\n }\n\n /**\n * Start the relay-side ator sidecar that publishes a v3 hidden service\n * forwarding inbound traffic to the town container's Nostr WebSocket port.\n *\n * The keypair directory is mounted read-write because the sidecar's\n * entrypoint writes the `hostname` file on first boot (see\n * docker/townhouse-ator-sidecar/Dockerfile). The town container picks up\n * the resulting .anyone URL via the operator-set externalUrl field.\n */\n private async startRelayAtorSidecar(): Promise<void> {\n const hsConfig = this.config.transport.relayHiddenService;\n if (!hsConfig) return;\n\n const name = `${CONTAINER_PREFIX}ator-sidecar-relay`;\n const env = [\n `HS_TARGET_HOST=${CONTAINER_PREFIX}town`,\n `HS_TARGET_PORT=${TOWN_RELAY_PORT}`,\n `HS_PORT=${hsConfig.port}`,\n `SOCKS_PORT=${RELAY_ATOR_SOCKS_PORT}`,\n ];\n\n this.emit('containerState', { name, state: 'creating' });\n\n const container = await this.docker.createContainer({\n name,\n Image: RELAY_ATOR_SIDECAR_IMAGE,\n Env: env,\n HostConfig: {\n NetworkMode: NETWORK_NAME,\n Binds: [`${hsConfig.dir}:/var/lib/anon/hs:rw`],\n },\n });\n\n this.emit('containerState', { name, state: 'starting' });\n await container.start();\n this.emit('containerState', { name, state: 'running' });\n }\n\n /**\n * Stop and remove a single container.\n */\n private async stopAndRemove(containerName: string): Promise<void> {\n try {\n const container = this.docker.getContainer(containerName);\n this.emit('containerState', { name: containerName, state: 'stopping' });\n await container.stop({ t: 10 });\n await container.remove();\n this.emit('containerState', { name: containerName, state: 'stopped' });\n } catch (error: unknown) {\n // Container may already be stopped/removed — only swallow expected errors\n const msg = error instanceof Error ? error.message : String(error);\n if (\n msg.includes('already stopped') ||\n msg.includes('not running') ||\n msg.includes('No such container') ||\n msg.includes('is not running') ||\n msg.includes('removal')\n ) {\n return;\n }\n // Emit error state with detail but don't throw — best-effort cleanup during shutdown\n this.emit('containerState', {\n name: containerName,\n state: 'error',\n detail: msg,\n });\n }\n }\n\n /**\n * Remove the townhouse-net network if it exists.\n */\n private async removeNetwork(): Promise<void> {\n try {\n const networks = await this.docker.listNetworks({\n filters: { name: [NETWORK_NAME] },\n });\n const netInfo = networks.find(\n (n: { Name: string }) => n.Name === NETWORK_NAME\n );\n if (netInfo) {\n const network = this.docker.getNetwork(netInfo.Id ?? netInfo.Name);\n await network.remove();\n }\n } catch {\n // Network may not exist, ignore\n }\n }\n\n /**\n * Build environment variables for the connector container.\n * Delegates to ConnectorConfigGenerator for consistent config generation.\n */\n private buildConnectorEnv(): string[] {\n const runtimeConfig = this.configGenerator.generate(this.activeNodes);\n return this.configGenerator.toEnvArray(runtimeConfig);\n }\n\n /**\n * Build environment variables for a node container.\n * If a WalletManager is provided, injects per-node identity keys.\n *\n * Async because the DVM path may need to derive an RSA-4096 Arweave key\n * via `walletManager.ensureArweaveKey('dvm')` — that derivation takes\n * 5–30s on first call per unlocked wallet (cached thereafter).\n */\n private async buildNodeEnv(type: NodeType): Promise<string[]> {\n // nosemgrep: javascript.lang.security.detect-insecure-websocket.detect-insecure-websocket -- Docker-internal container-to-container URL, TLS unnecessary\n const connectorUrl = `ws://${CONTAINER_PREFIX}connector:${CONNECTOR_INTERNAL_PORT}`;\n const env: string[] = [`CONNECTOR_URL=${connectorUrl}`];\n\n switch (type) {\n case 'town': {\n const feePerEvent = this.config.nodes.town.feePerEvent;\n if (feePerEvent !== undefined) {\n env.push(`FEE_PER_EVENT=${feePerEvent}`);\n }\n // When the operator opts into a relay hidden service, the .anyone URL\n // is advertised via packages/town/src/cli.ts which reads\n // TOON_EXTERNAL_RELAY_URL into config.externalRelayUrl. We deliberately\n // do NOT set config.ator.enabled — that would bind the relay to\n // 127.0.0.1 inside the container, which the sidecar (reaching town via\n // Docker DNS) cannot then forward to.\n const relayHs = this.config.transport.relayHiddenService;\n if (relayHs?.externalUrl) {\n env.push(`TOON_EXTERNAL_RELAY_URL=${relayHs.externalUrl}`);\n }\n break;\n }\n case 'mill': {\n const feeBasisPoints = this.config.nodes.mill.feeBasisPoints;\n if (feeBasisPoints !== undefined) {\n env.push(`FEE_BASIS_POINTS=${feeBasisPoints}`);\n }\n break;\n }\n case 'dvm': {\n const feePerJob = this.config.nodes.dvm.feePerJob;\n if (feePerJob !== undefined) {\n env.push(`FEE_PER_JOB=${feePerJob}`);\n }\n const kindPricing = this.config.nodes.dvm.kindPricing;\n if (kindPricing) {\n for (const [kind, value] of Object.entries(kindPricing)) {\n env.push(`KIND_PRICING_${kind}=${value}`);\n }\n }\n // Arweave DVM (kind:5094) requires Arweave credit credentials for\n // authenticated uploads. Two paths are wired here:\n // 1. Preferred (Phase 4): pipe the wallet-derived AR JWK as\n // DVM_ARWEAVE_JWK_B64 = base64(JSON(jwk)). Container picks\n // this up first and constructs an ArweaveSigner.\n // 2. Legacy: pass through TURBO_TOKEN (raw JWK JSON env var)\n // so existing operators are not broken.\n // Without either, the entrypoint installs a stub adapter that\n // throws on first upload with a `townhouse credits buy` CTA.\n //\n // NOTE: the JWK is secret material — DO NOT log the env-var value\n // anywhere in this path. The orchestrator's existing logging only\n // surfaces container names / lifecycle events, not env arrays.\n if (this.walletManager) {\n try {\n // Surface the 5–30s blocking call so operators know why their\n // boot is paused. Logged BEFORE the await so the message lands\n // even if derivation is slow.\n console.log(\n '[orchestrator] Deriving DVM Arweave key (first boot, this can take 5-30s)...'\n );\n await this.walletManager.ensureArweaveKey('dvm');\n const jwk = this.walletManager.getArweaveJwk('dvm');\n const jwkB64 = Buffer.from(JSON.stringify(jwk), 'utf-8').toString(\n 'base64'\n );\n env.push(`DVM_ARWEAVE_JWK_B64=${jwkB64}`);\n } catch {\n // Wallet locked, unsupported platform, or derivation failed —\n // skip the preferred path silently. The legacy TURBO_TOKEN\n // path below (or the stub adapter with CTA) takes over.\n }\n }\n const turboToken = process.env['TURBO_TOKEN'];\n if (turboToken) {\n env.push(`TURBO_TOKEN=${turboToken}`);\n }\n break;\n }\n }\n\n // Inject wallet-derived identity keys if available\n if (this.walletManager) {\n try {\n const keys = this.walletManager.getNodeKeys(type);\n env.push(`NODE_NOSTR_PUBKEY=${keys.nostrPubkey}`);\n env.push(`NODE_EVM_ADDRESS=${keys.evmAddress}`);\n // Secret key as hex for the container to use for event signing\n const secretHex = Buffer.from(keys.nostrSecretKey).toString('hex');\n env.push(`NODE_NOSTR_SECRET_KEY=${secretHex}`);\n } catch {\n // Wallet not initialized — skip key injection (backward compatible)\n }\n }\n\n return env;\n }\n\n /**\n * Follow a Docker pull stream and emit progress events.\n */\n private async followPullProgress(\n image: string,\n stream: NodeJS.ReadableStream\n ): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n this.docker.modem.followProgress(\n stream,\n (err: Error | null) => {\n if (err) {\n reject(err);\n } else {\n resolve();\n }\n },\n (event: { status?: string; id?: string; progress?: string }) => {\n this.emit('pullProgress', {\n image,\n status: event.status ?? '',\n id: event.id,\n progress: event.progress,\n });\n }\n );\n });\n }\n}\n","/**\n * ATOR transport probe — periodically TCP-connects to the configured SOCKS5\n * proxy host:port and measures direct HTTPS latency for comparison.\n *\n * The probe answers a single operator question: \"is my configured ATOR proxy\n * contactable from this host?\" TCP connect is the right granularity — if the\n * TCP listener is up, the connector's real BTP traffic will succeed.\n *\n * The probe NEVER makes a real SOCKS5 handshake or proxied request — only a\n * plain TCP connect to the proxy host:port.\n */\n\nimport * as net from 'node:net';\nimport * as https from 'node:https';\nimport * as http from 'node:http';\n\nconst DEFAULT_INTERVAL_MS = 30_000;\nconst MIN_INTERVAL_MS = 1_000;\nconst PROBE_TIMEOUT_MS = 3_000;\nconst DEFAULT_DIRECT_PROBE_URL = 'https://1.1.1.1/';\n\nexport interface TransportProbeOptions {\n proxyUrl: string;\n intervalMs?: number;\n /** Override the direct-latency probe URL (for tests — avoids real network). */\n directProbeUrl?: string;\n}\n\nexport interface TransportProbeStatus {\n reachable: boolean;\n latencyProxyMs: number | null;\n latencyDirectMs: number | null;\n lastProbedAt: number;\n probeError: string | null;\n}\n\nexport class TransportProbe {\n private proxyUrl: string;\n private readonly intervalMs: number;\n private readonly directProbeUrl: string;\n\n private running = false;\n private timer: ReturnType<typeof setInterval> | null = null;\n\n private status: TransportProbeStatus = {\n reachable: true,\n latencyProxyMs: null,\n latencyDirectMs: null,\n lastProbedAt: 0,\n probeError: null,\n };\n\n constructor(opts: TransportProbeOptions) {\n this.proxyUrl = opts.proxyUrl;\n this.intervalMs = Math.max(\n MIN_INTERVAL_MS,\n opts.intervalMs ?? DEFAULT_INTERVAL_MS\n );\n this.directProbeUrl = opts.directProbeUrl ?? DEFAULT_DIRECT_PROBE_URL;\n }\n\n /** Start the probe loop. Idempotent — calling twice while running is a no-op. */\n start(): void {\n if (this.running) return;\n this.running = true;\n const tick = (): void => {\n void this.tick().catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : String(err);\n console.warn(`[TransportProbe] tick failed: ${msg}`);\n });\n };\n // Run immediately on start, then on interval\n tick();\n this.timer = setInterval(tick, this.intervalMs);\n }\n\n /** Stop the probe loop. Idempotent. */\n stop(): void {\n this.running = false;\n if (this.timer !== null) {\n clearInterval(this.timer);\n this.timer = null;\n }\n }\n\n /** Returns the latest probe snapshot synchronously. Never blocks. */\n getStatus(): TransportProbeStatus {\n return { ...this.status };\n }\n\n /**\n * Update the target proxy URL.\n * The next tick will use the new URL; the current tick may complete against the old URL.\n */\n setProxyUrl(url: string): void {\n this.proxyUrl = url;\n }\n\n // ── Private probe logic ──────────────────────────────────────────────────\n\n private async tick(): Promise<void> {\n if (!this.running) return;\n\n const directMs = await this.probeDirectLatency();\n if (!this.running) return;\n\n if (!this.proxyUrl) {\n // Direct-mode: no proxy to probe\n const prev = this.status;\n this.status = {\n reachable: true,\n latencyProxyMs: null,\n latencyDirectMs: directMs,\n lastProbedAt: Date.now(),\n probeError: null,\n };\n this.logTransition(prev, this.status);\n return;\n }\n\n let host: string;\n let port: number;\n let hostPort: string;\n try {\n const parsed = new URL(this.proxyUrl);\n host = parsed.hostname;\n const rawPort = Number(parsed.port);\n if (\n !parsed.port ||\n !Number.isInteger(rawPort) ||\n rawPort < 1 ||\n rawPort > 65535\n ) {\n throw new Error('missing or invalid port');\n }\n port = rawPort;\n hostPort = `${host}:${port}`;\n } catch {\n const prev = this.status;\n this.status = {\n reachable: false,\n latencyProxyMs: null,\n latencyDirectMs: directMs,\n lastProbedAt: Date.now(),\n probeError: 'invalid_proxy_url',\n };\n this.logTransition(prev, this.status);\n return;\n }\n\n const { reachable, latencyMs, error } = await this.probeTcp(host, port);\n if (!this.running) return;\n const prev = this.status;\n this.status = {\n reachable,\n latencyProxyMs: latencyMs,\n latencyDirectMs: directMs,\n lastProbedAt: Date.now(),\n probeError: error ?? null,\n };\n this.logTransition(prev, this.status, hostPort);\n }\n\n private probeTcp(\n host: string,\n port: number\n ): Promise<{ reachable: boolean; latencyMs: number | null; error?: string }> {\n return new Promise((resolve) => {\n const start = Date.now();\n const socket = net.createConnection({ host, port });\n let settled = false;\n\n const settle = (result: {\n reachable: boolean;\n latencyMs: number | null;\n error?: string;\n }) => {\n if (settled) return;\n settled = true;\n try {\n socket.destroy();\n } catch {\n /* best-effort */\n }\n resolve(result);\n };\n\n const timeout = setTimeout(() => {\n settle({ reachable: false, latencyMs: null, error: 'timeout' });\n }, PROBE_TIMEOUT_MS);\n\n socket.once('connect', () => {\n clearTimeout(timeout);\n settle({ reachable: true, latencyMs: Date.now() - start });\n });\n\n socket.once('error', (err: NodeJS.ErrnoException) => {\n clearTimeout(timeout);\n settle({\n reachable: false,\n latencyMs: null,\n error: err.code ?? err.message,\n });\n });\n });\n }\n\n private probeDirectLatency(): Promise<number | null> {\n return new Promise((resolve) => {\n const start = Date.now();\n let settled = false;\n\n const settle = (ms: number | null) => {\n if (settled) return;\n settled = true;\n resolve(ms);\n };\n\n // Support both http:// (tests) and https:// (production)\n const isHttps = this.directProbeUrl.startsWith('https://');\n const requester = isHttps ? https : http;\n\n let req: http.ClientRequest | undefined;\n const timeout = setTimeout(() => {\n try {\n req?.destroy();\n } catch {\n /* best-effort */\n }\n settle(null);\n }, PROBE_TIMEOUT_MS);\n\n try {\n req = requester.request(\n this.directProbeUrl,\n { method: 'HEAD' },\n (res) => {\n clearTimeout(timeout);\n res.resume();\n settle(Date.now() - start);\n }\n );\n\n req.once('error', (err: NodeJS.ErrnoException) => {\n clearTimeout(timeout);\n console.debug(\n `[TransportProbe] direct latency probe failed: ${err.code ?? err.message}`\n );\n settle(null);\n });\n\n req.end();\n } catch (err) {\n clearTimeout(timeout);\n const msg = err instanceof Error ? err.message : String(err);\n console.debug(`[TransportProbe] direct latency probe threw: ${msg}`);\n settle(null);\n }\n });\n }\n\n private logTransition(\n prev: TransportProbeStatus,\n next: TransportProbeStatus,\n hostPort?: string\n ): void {\n // Suppress reachable→unreachable transition log on the very first probe;\n // the default initial state is reachable=true even before any probe ran.\n if (prev.lastProbedAt === 0) return;\n\n const target = hostPort ? ` (${hostPort})` : '';\n if (prev.reachable && !next.reachable) {\n console.warn(\n `[TransportProbe] proxy became unreachable${target}: ${next.probeError ?? 'unknown'}`\n );\n } else if (!prev.reachable && next.reachable) {\n console.debug(`[TransportProbe] proxy reachable${target}`);\n }\n // No per-tick info logs for the proxy host (see AC-22)\n }\n}\n","/**\n * HS connector config writer for `townhouse hs up` (Story 45.4, Task 3).\n *\n * Generates ~/.townhouse/connector.yaml with anon.enabled: true and the\n * managed hidden-service transport block so the connector spawns the anon\n * binary and publishes a .anyone v3 hidden service automatically.\n *\n * Idempotency: if the file already exists and contains `anon.enabled: true`,\n * it is reused verbatim (preserves operator edits). Pass `force: true` to\n * overwrite unconditionally.\n */\n\nimport { existsSync, readFileSync, writeFileSync, chmodSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { parse, stringify as yamlStringify } from 'yaml';\nimport { ConnectorConfigGenerator } from './config-generator.js';\nimport type { TownhouseConfig } from '../config/schema.js';\nimport { DEFAULT_HS_CHAIN_PROVIDERS } from '../config/defaults.js';\nimport type { ConnectorRuntimeConfig } from './types.js';\n\n/** Absolute path inside the connector container where anon stores the keypair. */\nconst HS_DIR = '/var/lib/anon/hs';\n\n/** Port the connector's BTP server listens on (inside the container). */\nconst HS_PORT = 3000;\n\n// HS detection: parse the YAML and check anon.enabled === true. This avoids\n// false negatives from YAML formatting differences (dotted key vs. nested block).\n\nexport interface WriteHsConnectorConfigResult {\n yamlPath: string;\n /** true if the file was freshly written; false if an existing HS file was reused. */\n created: boolean;\n}\n\n/**\n * Write (or reuse) `~/.townhouse/connector.yaml` with HS-specific overrides.\n *\n * @param configDir - The townhouse home directory (e.g. `~/.townhouse/`).\n * @param config - Loaded `TownhouseConfig` (provides adminPort, ilpAddress, etc.).\n * @param options.force - When true, overwrite even if an HS file already exists.\n */\nexport function writeHsConnectorConfig(\n configDir: string,\n config: TownhouseConfig,\n options: { force?: boolean } = {}\n): WriteHsConnectorConfigResult {\n const yamlPath = join(configDir, 'connector.yaml');\n\n // Idempotency check: if the file exists and was written by a prior hs up,\n // reuse it verbatim so operator edits (e.g. log level) are preserved.\n if (!options.force && existsSync(yamlPath)) {\n try {\n const existing = parse(readFileSync(yamlPath, 'utf-8')) as Record<\n string,\n unknown\n >;\n const anon = existing['anon'] as Record<string, unknown> | undefined;\n if (anon?.['enabled'] === true) {\n return { yamlPath, created: false };\n }\n } catch {\n // Unparseable existing file — fall through to overwrite.\n }\n // Existing file lacks anon.enabled: true — treat as legacy non-HS config.\n // Fall through to overwrite.\n }\n\n // Epic 47 BUG-1 product fix (D2): inject DEFAULT_HS_CHAIN_PROVIDERS when\n // the operator has not configured chainProviders. Without at least one\n // entry, the connector's settlement subsystem (AccountManager +\n // ClaimReceiver) does not initialize, so `/admin/earnings.json` returns\n // 503 and Townhouse's earnings data plane (Epic 47) breaks. The defaults\n // are dev-Anvil deterministic addresses paired with a dead RPC; they\n // initialize the subsystem successfully but never resolve on-chain calls.\n // Operators running on real chains override chainProviders in config.yaml.\n const hsConfig: TownhouseConfig =\n config.chainProviders !== undefined && config.chainProviders.length > 0\n ? config\n : { ...config, chainProviders: [...DEFAULT_HS_CHAIN_PROVIDERS] };\n\n // Build the HS runtime config by extending the base generated config.\n const generator = new ConnectorConfigGenerator(hsConfig);\n const baseConfig = generator.generate([]); // apex-only, no peers\n\n // Managed mode: the connector spawns the anon daemon in-process. The SOCKS\n // proxy port binds locally at 127.0.0.1:9050 — use the local address so the\n // connector's TCP-readiness check waits for the right host, not the external\n // public ATOR proxy (proxy.ator.io:9050) which can never bind locally.\n const HS_LOCAL_SOCKS_PROXY = 'socks5h://127.0.0.1:9050';\n\n const hsRuntimeConfig: ConnectorRuntimeConfig = {\n ...baseConfig,\n transport: {\n mode: 'ator',\n socksProxy: HS_LOCAL_SOCKS_PROXY,\n externalUrl: 'auto',\n hiddenService: {\n dir: HS_DIR,\n port: HS_PORT,\n // The orchestrator polls getHsHostname() for up to 120s; give the\n // connector the same budget so the internal timeout doesn't fire first.\n startupTimeoutMs: 120_000,\n },\n },\n };\n\n // Render the base YAML, then add `anon: { enabled: true }` as a top-level field.\n const baseYaml = generator.toYaml(hsRuntimeConfig);\n const parsed = parse(baseYaml) as Record<string, unknown>;\n parsed['anon'] = { enabled: true };\n const finalYaml = yamlStringify(parsed);\n\n // Write atomically: writeFileSync is not atomic on all platforms, but since\n // we set mode on creation and then defensively chmod, this is consistent with\n // the pattern used by materializeComposeTemplate (Story 45.2).\n writeFileSync(yamlPath, finalYaml, { mode: 0o600, encoding: 'utf-8' });\n chmodSync(yamlPath, 0o600);\n\n return { yamlPath, created: true };\n}\n","import {\n readFileSync,\n writeFileSync,\n mkdirSync,\n chmodSync,\n statSync,\n lstatSync,\n existsSync,\n} from 'node:fs';\nimport { dirname, join, resolve, isAbsolute } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { homedir } from 'node:os';\n\nexport type ComposeProfile = 'dev' | 'hs';\nconst VALID_PROFILES: readonly ComposeProfile[] = ['dev', 'hs'] as const;\n\nexport interface ComposeLoaderOptions {\n /** Override default `~/.townhouse/` write target. Used by tests. */\n townhouseHome?: string;\n /** Override the package-relative dist directory the loader reads from.\n * Defaults to the `dist/` adjacent to compose-loader.js at runtime.\n * Tests use this to point at fixture directories. */\n distDir?: string;\n}\n\nexport class ComposeLoaderError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ComposeLoaderError';\n }\n}\n\nfunction defaultDistDir(): string {\n // Resolves to `dist/` adjacent to the bundled output at runtime.\n // When bundled by tsup, import.meta.url is the path of dist/index.js,\n // so dirname = <package>/dist. resolve(<package>/dist, '..', 'dist') = <package>/dist.\n // When running via tsx/ts-node from src/, dirname = <package>/src,\n // so resolve(<package>/src, '..', 'dist') = <package>/dist. Both work.\n const here = dirname(fileURLToPath(import.meta.url));\n return resolve(here, '..', 'dist');\n}\n\nfunction assertValidProfile(\n profile: string\n): asserts profile is ComposeProfile {\n if (!(VALID_PROFILES as readonly string[]).includes(profile)) {\n throw new ComposeLoaderError(\n `invalid compose profile: '${profile}'. Must be one of: ${VALID_PROFILES.join(', ')}.`\n );\n }\n}\n\n// Reject townhouseHome paths that target system directories. Internal callers\n// (CLI, API, Story 45.4) pass `~/.townhouse` or test tmpdirs; an attacker\n// reaching this code path with `townhouseHome: '/etc'` would otherwise write\n// `/etc/compose/townhouse-hs.yml` and `chmod /etc 0o700`. The list is a\n// belt-and-suspenders defense — it doesn't replace caller validation, but it\n// turns a silent privilege escalation into a loud error.\nconst SYSTEM_PATH_PREFIXES = [\n '/etc',\n '/usr',\n '/bin',\n '/sbin',\n '/lib',\n '/lib64',\n '/proc',\n '/sys',\n '/dev',\n '/boot',\n '/root',\n] as const;\n\nfunction assertValidTownhouseHome(home: string): void {\n if (!home) {\n throw new ComposeLoaderError(\n 'townhouseHome resolved to an empty path. Set $HOME or pass options.townhouseHome explicitly.'\n );\n }\n if (!isAbsolute(home)) {\n throw new ComposeLoaderError(\n `townhouseHome must be an absolute path; got '${home}'.`\n );\n }\n if (home === '/' || home === '\\\\') {\n throw new ComposeLoaderError(\n `townhouseHome must not be the filesystem root; got '${home}'. ` +\n `This usually means $HOME is unset and homedir() returned '/'.`\n );\n }\n for (const prefix of SYSTEM_PATH_PREFIXES) {\n if (home === prefix || home.startsWith(prefix + '/')) {\n throw new ComposeLoaderError(\n `townhouseHome must not target a system directory; got '${home}'. ` +\n `Allowed paths: under $HOME, under tmpdir(), or any user-writable location.`\n );\n }\n }\n}\n\n// Refuse to write through a symlink at composePath/manifestPath. The dir-level\n// guard above only protects the directory itself; this protects the file path.\nfunction assertNotSymlink(filePath: string): void {\n try {\n const lst = lstatSync(filePath);\n if (lst.isSymbolicLink()) {\n throw new ComposeLoaderError(\n `${filePath} is a symlink; refusing to write through it. ` +\n `If this is intentional, remove the symlink and re-run.`\n );\n }\n } catch (err) {\n // ENOENT is expected (file doesn't exist yet — fresh write); rethrow others.\n const code = (err as NodeJS.ErrnoException).code;\n if (code !== 'ENOENT') throw err;\n }\n}\n\n/**\n * Returns the rendered compose YAML for the requested profile.\n * For 'hs', digest substitutions are already applied (resolved at build time).\n * For 'dev', the YAML is returned verbatim (uses local `toon:*` image tags).\n * Throws `ComposeLoaderError` if the requested profile's YAML is unreadable.\n */\nexport function loadComposeTemplate(\n profile: ComposeProfile,\n options: ComposeLoaderOptions = {}\n): string {\n assertValidProfile(profile);\n const distDir = options.distDir ?? defaultDistDir();\n const composePath = join(distDir, 'compose', `townhouse-${profile}.yml`);\n if (!existsSync(composePath)) {\n throw new ComposeLoaderError(\n `compose template not found: ${composePath}. ` +\n `Did you run 'pnpm --filter @toon-protocol/townhouse build' first?`\n );\n }\n return readFileSync(composePath, 'utf-8');\n}\n\n/**\n * Writes the resolved compose YAML to `<townhouseHome>/compose/<profile>.yml`\n * and copies `dist/image-manifest.json` to `<townhouseHome>/image-manifest.json`.\n * BOTH output files are written with mode 0o600 (NFR8 — operator-secret file mode).\n * Returns the absolute paths of the two files written.\n */\nexport function materializeComposeTemplate(\n profile: ComposeProfile,\n options: ComposeLoaderOptions = {}\n): { composePath: string; manifestPath: string } {\n assertValidProfile(profile);\n const home = options.townhouseHome || join(homedir(), '.townhouse');\n assertValidTownhouseHome(home);\n\n const distDir = options.distDir ?? defaultDistDir();\n const manifestSrc = join(distDir, 'image-manifest.json');\n\n // Validate inputs BEFORE any writes so a failure leaves disk untouched.\n // HS profile cannot succeed without a manifest — fail loudly up-front\n // rather than after writing a stale/torn compose file.\n if (profile === 'hs' && !existsSync(manifestSrc)) {\n throw new ComposeLoaderError(\n `image-manifest.json not found at ${manifestSrc}. ` +\n `HS mode requires a digest-pinned image manifest. ` +\n `Reinstall @toon-protocol/townhouse from npm to restore the manifest.`\n );\n }\n // loadComposeTemplate also throws ENOENT if the source is missing — surface\n // it now (read-only) so we don't mkdir or chmod for a doomed call.\n const yaml = loadComposeTemplate(profile, options);\n\n const composeDir = join(home, 'compose');\n // Pass mode: 0o700 so newly-created intermediates start tight (closes the\n // mkdir → chmod TOCTOU window that allowed brief world-readable state).\n mkdirSync(composeDir, { recursive: true, mode: 0o700 });\n\n // Refuse to chmod symlink targets — operator may have placed `~/.townhouse`\n // as a symlink to an encrypted volume, and we should not silently flip the\n // mode of a path we did not create. lstatSync inspects the link itself.\n for (const dir of [home, composeDir]) {\n const lst = lstatSync(dir);\n if (lst.isSymbolicLink()) {\n // Resolve the link target and confirm it's a directory; do not chmod.\n const target = statSync(dir);\n if (!target.isDirectory()) {\n throw new ComposeLoaderError(\n `${dir} is a symlink to a non-directory; refusing to materialize.`\n );\n }\n continue;\n }\n // Only narrow the mode if it is currently broader than 0o700. Operators\n // who deliberately set 0o700 OR tighter (e.g. 0o500) keep their setting.\n // Bug fix R2: previous `!== 0o700` widened 0o500 to 0o700 — now we only\n // chmod if the existing mode grants any permission outside the owner.\n const currentMode = lst.mode & 0o777;\n if ((currentMode & 0o077) !== 0) {\n chmodSync(dir, 0o700);\n }\n }\n\n const composePath = join(composeDir, `townhouse-${profile}.yml`);\n // R2 file-symlink guard — refuse to write through a planted symlink.\n assertNotSymlink(composePath);\n writeFileSync(composePath, yaml, { mode: 0o600, encoding: 'utf-8' });\n // Defensive re-chmod: writeFileSync's mode option is honored only on file\n // creation — if composePath already existed at e.g. 0o644 (stale state from\n // a prior interrupted run), the mode is unchanged by writeFileSync.\n // chmodSync corrects both that case AND the WSL2 umask-masking edge case.\n chmodSync(composePath, 0o600);\n\n const manifestPath = join(home, 'image-manifest.json');\n if (existsSync(manifestSrc)) {\n assertNotSymlink(manifestPath);\n const manifest = readFileSync(manifestSrc, 'utf-8');\n writeFileSync(manifestPath, manifest, { mode: 0o600, encoding: 'utf-8' });\n chmodSync(manifestPath, 0o600);\n }\n // (Manifest absence for 'dev' profile is silently tolerated — dev mode\n // doesn't need digest pinning. HS profile already failed at the entry guard.)\n\n return { composePath, manifestPath };\n}\n","/**\n * `nodes.yaml` schema + read/write helpers (Story 46.1).\n *\n * `~/.townhouse/nodes.yaml` is the operator-managed source of truth for\n * enabled child nodes. The reconciler (see `../reconciler.ts`) converges\n * connector peer state to this file on every `townhouse hs up`.\n *\n * Architectural rule (Epic 46.2 dependency): yaml writes happen BEFORE\n * connector registration. The drift window resolves in the safe direction —\n * a yaml entry without a connector peer is re-registered on next boot; a\n * connector peer without a yaml entry is treated as `'external'` and left\n * alone.\n */\n\nimport { promises as fs } from 'node:fs';\nimport { dirname } from 'node:path';\nimport { parse as yamlParse, stringify as yamlStringify } from 'yaml';\nimport { z } from 'zod';\n\nconst NodesYamlEntrySchema = z\n .object({\n id: z.string().min(1),\n type: z.enum(['town', 'mill', 'dvm']),\n peerId: z.string().min(1),\n ilpAddress: z.string().min(1),\n derivationIndex: z.number().int().nonnegative(),\n enabledAt: z.string().datetime({ offset: true }),\n lastSeenAt: z.string().datetime({ offset: true }).nullable(),\n })\n .strict();\n\nexport const NodesYamlSchema = z\n .object({\n entries: z.array(NodesYamlEntrySchema),\n })\n .strict()\n .superRefine((data, ctx) => {\n const seenPeerIds = new Set<string>();\n const seenDerivationIndexes = new Set<number>();\n for (const [i, e] of data.entries.entries()) {\n if (seenPeerIds.has(e.peerId)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['entries', i, 'peerId'],\n message: `duplicate peerId: ${e.peerId}`,\n });\n }\n seenPeerIds.add(e.peerId);\n if (seenDerivationIndexes.has(e.derivationIndex)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['entries', i, 'derivationIndex'],\n message: `duplicate derivationIndex: ${e.derivationIndex}`,\n });\n }\n seenDerivationIndexes.add(e.derivationIndex);\n }\n });\n\nexport type NodesYamlEntry = z.infer<typeof NodesYamlEntrySchema>;\nexport type NodesYaml = z.infer<typeof NodesYamlSchema>;\n\n/**\n * Read and validate `nodes.yaml` at the given path.\n *\n * Returns `{ entries: [] }` if the file does not exist (graceful first-run).\n * Throws a `ZodError` with a useful path if the file is present but invalid.\n */\nexport async function readNodesYaml(path: string): Promise<NodesYaml> {\n let raw: string;\n try {\n raw = await fs.readFile(path, 'utf-8');\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n return { entries: [] };\n }\n throw err;\n }\n const parsed: unknown = yamlParse(raw);\n // An empty yaml file (or one containing only `~`) parses to null — coerce\n // to the empty default so the schema validation succeeds.\n if (parsed === null || parsed === undefined) {\n return { entries: [] };\n }\n return NodesYamlSchema.parse(parsed);\n}\n\n/**\n * Write `nodes.yaml` atomically with file mode `0o600`.\n *\n * Atomic = write to `<path>.tmp` then `fs.rename`. On POSIX, rename is\n * atomic when source + destination live on the same filesystem (always true\n * for `~/.townhouse/nodes.yaml`). Prevents partial-write corruption if the\n * process is killed mid-write.\n */\nexport async function writeNodesYaml(\n path: string,\n data: NodesYaml\n): Promise<void> {\n // Validate before serializing — never persist a yaml that won't round-trip.\n const validated = NodesYamlSchema.parse(data);\n const yamlContent = yamlStringify(validated);\n const tmpPath = `${path}.tmp`;\n await fs.mkdir(dirname(path), { recursive: true, mode: 0o700 });\n await fs.writeFile(tmpPath, yamlContent, { encoding: 'utf-8', mode: 0o600 });\n await fs.rename(tmpPath, path);\n // fs.writeFile honors mode only on file creation. Re-chmod after rename\n // to handle the case where `<path>` already existed and inherited a\n // different mode from a previous run.\n await fs.chmod(path, 0o600);\n}\n\n// Re-export for downstream validators (Epic 47 aggregator) that need to\n// validate operator input without reaching into a deep import path.\nexport { NodesYamlEntrySchema };\n","/**\n * Boot reconciler (Story 46.1).\n *\n * Converges connector peer state to `~/.townhouse/nodes.yaml` (truth) on\n * every `townhouse hs up`. Reads yaml + connector peers, diffs them,\n * re-registers any yaml entries missing from the connector, and logs\n * connector peers without yaml entries as `'external'` (left alone).\n *\n * Container lifecycle is OUT of scope — that lives in Epic 46.2.\n */\n\nimport { promises as fs } from 'node:fs';\nimport { dirname } from 'node:path';\n\nimport type { ConnectorAdminClient } from './connector/admin-client.js';\nimport type { PeerStatus } from './connector/types.js';\nimport { CONTAINER_PREFIX, NODE_BTP_PORT } from './constants.js';\nimport {\n readNodesYaml,\n type NodesYaml,\n type NodesYamlEntry,\n} from './state/nodes-yaml.js';\n\n/** Action recorded for a single divergence. */\nexport type DivergenceAction =\n | 'reregistered'\n | 'reregister-failed'\n | 'external';\n\n/** A single divergence record for the reconciler log. */\nexport interface DivergenceLog {\n timestamp: string;\n peerId: string;\n action: DivergenceAction;\n detail?: string;\n}\n\n/** Per-divergence intent computed by `diff()` — converted to a `DivergenceLog` at action time. */\ninterface DivergencePlan {\n peerId: string;\n intent: 'reregister' | 'external';\n}\n\n/** Summary returned by `reconcile()` so callers can surface partial-failure counts. */\nexport interface ReconcileSummary {\n reregistered: number;\n failed: number;\n external: number;\n}\n\nexport class BootReconciler {\n private logDirEnsured = false;\n private logFileChmodEnsured = false;\n\n constructor(\n private readonly adminClient: Pick<\n ConnectorAdminClient,\n 'getPeers' | 'registerPeer'\n >,\n private readonly nodesYamlPath: string,\n private readonly reconcilerLogPath: string\n ) {}\n\n /**\n * Diff `nodes.yaml` (truth) against `GET /admin/peers` (derived state)\n * and converge.\n *\n * Ordering rule (Epic 46.2 dependency — load-bearing):\n * `nodes.yaml` write happens BEFORE connector registration\n * (`POST /admin/peers`).\n *\n * The drift window resolves in the safe direction:\n * - yaml entry without a connector peer = harmless. The reconciler\n * re-registers it on next `hs up` (this method does that).\n * - connector peer without a yaml entry = treated as `'external'` and\n * left alone (operators may legitimately route non-Townhouse peers\n * through the same connector).\n *\n * The unsafe direction (register first, then write yaml) creates a\n * window where the connector routes to a peer Townhouse cannot clean\n * up. Epic 46.2's provisioning pipeline MUST honor the yaml-first rule.\n *\n * Failures fetching `getPeers()` are surfaced (not swallowed) so the\n * caller in `handleHsUp` can decide whether to treat reconciler\n * divergence as fatal. (Today: non-fatal — see cli.ts wire point.)\n *\n * Per-divergence appendLog failures are caught so a single log-write\n * failure does not abort the rest of the reconciliation pass.\n */\n async reconcile(): Promise<ReconcileSummary> {\n const yaml = await readNodesYaml(this.nodesYamlPath);\n const peers = await this.adminClient.getPeers();\n const plans = this.diff(yaml, peers);\n\n const summary: ReconcileSummary = {\n reregistered: 0,\n failed: 0,\n external: 0,\n };\n\n for (const plan of plans) {\n if (plan.intent === 'reregister') {\n const entry = yaml.entries.find((e) => e.peerId === plan.peerId);\n if (!entry) continue;\n try {\n await this.adminClient.registerPeer({\n id: entry.peerId,\n url: deriveBtpUrl(entry),\n authToken: '',\n routes: [{ prefix: entry.ilpAddress, priority: 0 }],\n });\n summary.reregistered++;\n await this.tryAppendLog({\n timestamp: new Date().toISOString(),\n peerId: plan.peerId,\n action: 'reregistered',\n });\n } catch (err: unknown) {\n summary.failed++;\n const msg = err instanceof Error ? err.message : String(err);\n await this.tryAppendLog({\n timestamp: new Date().toISOString(),\n peerId: plan.peerId,\n action: 'reregister-failed',\n detail: msg,\n });\n }\n } else {\n summary.external++;\n await this.tryAppendLog({\n timestamp: new Date().toISOString(),\n peerId: plan.peerId,\n action: 'external',\n });\n }\n }\n\n return summary;\n }\n\n /**\n * Compute divergences without mutating the connector. Exposed for\n * testability — production callers use `reconcile()`.\n */\n private diff(yaml: NodesYaml, peers: PeerStatus[]): DivergencePlan[] {\n const peerIds = new Set(peers.map((p) => p.id));\n const yamlPeerIds = new Set(yaml.entries.map((e) => e.peerId));\n const out: DivergencePlan[] = [];\n\n for (const entry of yaml.entries) {\n if (!peerIds.has(entry.peerId)) {\n out.push({ peerId: entry.peerId, intent: 'reregister' });\n }\n }\n for (const peer of peers) {\n if (!yamlPeerIds.has(peer.id)) {\n out.push({ peerId: peer.id, intent: 'external' });\n }\n }\n return out;\n }\n\n /**\n * Append one divergence record without aborting the whole reconciliation\n * pass on a single log-write failure (disk full, EACCES, etc.). Failures\n * are themselves logged to stderr — not silently swallowed — so the\n * operator can see them in the same `hs up` session.\n */\n private async tryAppendLog(div: DivergenceLog): Promise<void> {\n try {\n await this.appendLog(div);\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(\n `[townhouse boot-reconciler] failed to append divergence log: ${msg}`\n );\n }\n }\n\n /**\n * Append one divergence record to the reconciler log as a single line of\n * JSON (jsonl-style — easy to grep, easy to parse).\n *\n * `mkdir` runs once per reconciler instance. `chmod 0o600` on the log file\n * also runs once — `fs.appendFile`'s `mode` option only applies on\n * creation, so without a post-create chmod a pre-existing log file with\n * permissive mode would never be tightened.\n */\n private async appendLog(div: DivergenceLog): Promise<void> {\n const line = JSON.stringify(div) + '\\n';\n if (!this.logDirEnsured) {\n await fs.mkdir(dirname(this.reconcilerLogPath), {\n recursive: true,\n mode: 0o700,\n });\n this.logDirEnsured = true;\n }\n await fs.appendFile(this.reconcilerLogPath, line, {\n encoding: 'utf-8',\n mode: 0o600,\n });\n if (!this.logFileChmodEnsured) {\n try {\n await fs.chmod(this.reconcilerLogPath, 0o600);\n } catch {\n /* best-effort: file may have been removed; next call retries */\n }\n this.logFileChmodEnsured = true;\n }\n }\n}\n\n/**\n * Derive the BTP WebSocket URL for a Townhouse internal peer.\n *\n * Convention: `ws://${CONTAINER_PREFIX}${type}:${NODE_BTP_PORT}` — matches\n * the URL used by `ConnectorConfigGenerator` at boot, minus the `btp+`\n * scheme prefix (the connector's POST /admin/peers requires `ws://` or\n * `wss://`, while the static yaml config uses `btp+ws://`).\n *\n * Epic 46.2 may persist the URL into `nodes.yaml` directly when the\n * provisioning pipeline supports operator-defined peer URLs; until then,\n * convention is sufficient because the only producer of yaml entries is\n * Townhouse itself.\n */\nfunction deriveBtpUrl(entry: NodesYamlEntry): string {\n return `ws://${CONTAINER_PREFIX}${entry.type}:${NODE_BTP_PORT}`;\n}\n","/**\n * Hourly earnings snapshot writer (Story 47.3).\n *\n * Persists `claimsReceivedTotal` per (peerId × assetCode) — plus apex\n * `connectorFees[]` rows under `peerId: '__apex__'` — to\n * `${dirname(configPath)}/earnings-snapshots.jsonl` once per hour. Consumed\n * by `snapshot-reader.ts`'s `DeltaComputer` factory. Failure mode: any\n * per-tick error is logged via `logger.warn` and swallowed (the writer NEVER\n * throws into the apex event loop) — the next tick retries cleanly. Pruning\n * runs after each successful append (entries older than 13 months are\n * rewritten atomically). File mode is `0o600` on every write.\n *\n * @module\n * @since 47.3\n */\n\nimport { promises as fs } from 'node:fs';\nimport { dirname } from 'node:path';\nimport type { ConnectorAdminClient } from '../connector/index.js';\n\n/**\n * One JSONL row in `earnings-snapshots.jsonl`.\n *\n * NOTE: apex routing-fee rows use `peerId: '__apex__'`. The field name\n * `claimsReceivedTotal` is technically a misnomer for apex rows — those\n * are connector routing fees, not received claims — but the uniform column\n * name keeps the JSONL schema simple and the reader doesn't need a special\n * case.\n */\nexport interface SnapshotEntry {\n /** ISO-8601 UTC timestamp of the tick boundary (e.g. '2026-05-12T15:00:00.000Z'). */\n ts: string;\n /** Connector peerId, OR the literal `'__apex__'` for apex routing-fee rows. */\n peerId: string;\n assetCode: string;\n /** Decimal-string cumulative (claims received for peers, routing-fee total for apex). */\n claimsReceivedTotal: string;\n}\n\nexport interface SnapshotWriterOptions {\n connectorAdmin: ConnectorAdminClient;\n /** Absolute path to `earnings-snapshots.jsonl`. */\n snapshotPath: string;\n /** Tick interval (ms). Default 3_600_000 (1 hour). */\n tickIntervalMs?: number;\n /** Injected clock for tests. Default `() => new Date()`. */\n now?: () => Date;\n /** Retention window in months. Default 13. */\n retentionMonths?: number;\n /** pino/Fastify-compatible logger; warn-only. */\n logger?: { warn(obj: object, msg?: string): void };\n /**\n * Fire one tick immediately on `start()` instead of waiting for the first\n * interval. Default `false` (production). Tests set this to `true` to\n * assert append behavior without advancing fake timers.\n */\n fireOnStart?: boolean;\n}\n\nexport class SnapshotWriter {\n private timer: ReturnType<typeof setInterval> | null = null;\n private tickPending = false;\n\n constructor(private readonly opts: SnapshotWriterOptions) {}\n\n start(): void {\n // Idempotent — calling start() twice without stop() must not leak the first timer.\n if (this.timer !== null) return;\n const intervalMs = this.opts.tickIntervalMs ?? 3_600_000;\n this.timer = setInterval(() => {\n void this.tick();\n }, intervalMs);\n if (this.opts.fireOnStart) {\n void this.tick();\n }\n }\n\n stop(): void {\n if (this.timer !== null) {\n clearInterval(this.timer);\n this.timer = null;\n }\n }\n\n /** Exposed for test ergonomics — runs one full append+prune cycle. */\n async tick(): Promise<void> {\n if (this.tickPending) {\n // Log the skip so operators can detect a wedged getEarnings (hours of silent no-ops otherwise).\n this.opts.logger?.warn(\n { snapshotPath: this.opts.snapshotPath },\n 'snapshot writer: tick skipped — previous tick still in flight'\n );\n return;\n }\n this.tickPending = true;\n try {\n await this.runTick();\n } finally {\n this.tickPending = false;\n }\n }\n\n private async runTick(): Promise<void> {\n const now = (this.opts.now ?? (() => new Date()))();\n const tsMs = Math.floor(now.getTime() / 3_600_000) * 3_600_000;\n const ts = new Date(tsMs).toISOString();\n\n let earnings: Awaited<ReturnType<ConnectorAdminClient['getEarnings']>>;\n try {\n earnings = await this.opts.connectorAdmin.getEarnings();\n } catch (err) {\n this.opts.logger?.warn(\n { err },\n 'snapshot writer: getEarnings failed — skipping this tick'\n );\n return;\n }\n\n // Wrap append + prune so any fs error (ENOSPC, EACCES, EROFS, EIO, EXDEV)\n // is contained — the writer NEVER throws into the apex event loop.\n try {\n const entries: SnapshotEntry[] = [];\n for (const peer of earnings.peers ?? []) {\n if (peer.peerId === '__apex__') {\n // Defensive: a connector-side peer literally named '__apex__' would\n // collide with the apex routing-fee sentinel. Skip and warn once.\n this.opts.logger?.warn(\n { peerId: peer.peerId },\n 'snapshot writer: peer with reserved id \"__apex__\" — row dropped'\n );\n continue;\n }\n for (const a of peer.byAsset ?? []) {\n entries.push({\n ts,\n peerId: peer.peerId,\n assetCode: a.assetCode,\n claimsReceivedTotal: a.claimsReceivedTotal,\n });\n }\n }\n for (const fee of earnings.connectorFees ?? []) {\n entries.push({\n ts,\n peerId: '__apex__',\n assetCode: fee.assetCode,\n claimsReceivedTotal: fee.total,\n });\n }\n\n if (entries.length === 0) {\n // Nothing to write — but still try pruning.\n await this.pruneIfNeeded(now);\n return;\n }\n\n await this.appendEntries(entries);\n await this.pruneIfNeeded(now);\n } catch (err) {\n this.opts.logger?.warn(\n { err },\n 'snapshot writer: append/prune failed — skipping this tick'\n );\n }\n }\n\n private async appendEntries(entries: SnapshotEntry[]): Promise<void> {\n // Always mkdir + chmod: directory or file may be deleted between ticks\n // (operator cleanup, container restart). Both ops are cheap-idempotent.\n await fs.mkdir(dirname(this.opts.snapshotPath), {\n recursive: true,\n mode: 0o700,\n });\n\n const body = entries.map((e) => JSON.stringify(e)).join('\\n') + '\\n';\n await fs.appendFile(this.opts.snapshotPath, body, {\n encoding: 'utf-8',\n mode: 0o600,\n });\n // `appendFile`'s `mode` is ignored if the file already exists; chmod ensures\n // 0o600 on both first-create and subsequent appends.\n await fs.chmod(this.opts.snapshotPath, 0o600);\n }\n\n private async pruneIfNeeded(now: Date): Promise<void> {\n const WATERMARK = 256 * 1024; // 256 KB\n let stat: Awaited<ReturnType<typeof fs.stat>> | null = null;\n try {\n stat = await fs.stat(this.opts.snapshotPath);\n } catch {\n return; // File doesn't exist yet — nothing to prune.\n }\n if (stat.size < WATERMARK) return;\n\n const retentionMonths = this.opts.retentionMonths ?? 13;\n const cutoff = new Date(now);\n cutoff.setUTCMonth(cutoff.getUTCMonth() - retentionMonths);\n const cutoffMs = cutoff.getTime();\n\n let raw: string;\n try {\n raw = await fs.readFile(this.opts.snapshotPath, 'utf-8');\n } catch {\n return;\n }\n\n const lines = raw.split('\\n').filter((l) => l.length > 0);\n let anyDropped = false;\n const kept: string[] = [];\n for (const line of lines) {\n let entry: unknown;\n try {\n entry = JSON.parse(line);\n } catch {\n anyDropped = true;\n continue; // drop malformed line (lazy compaction)\n }\n if (!isSnapshotEntry(entry)) {\n anyDropped = true;\n continue;\n }\n const entryMs = new Date(entry.ts).getTime();\n if (isNaN(entryMs) || entryMs < cutoffMs) {\n anyDropped = true;\n } else {\n kept.push(line);\n }\n }\n\n if (!anyDropped) return; // No rewrite needed.\n\n const tmpPath = `${this.opts.snapshotPath}.tmp`;\n const newContent = kept.length > 0 ? kept.join('\\n') + '\\n' : '';\n await fs.writeFile(tmpPath, newContent, { encoding: 'utf-8', mode: 0o600 });\n await fs.rename(tmpPath, this.opts.snapshotPath);\n await fs.chmod(this.opts.snapshotPath, 0o600);\n }\n}\n\nfunction isSnapshotEntry(v: unknown): v is SnapshotEntry {\n if (typeof v !== 'object' || v === null) return false;\n const e = v as Record<string, unknown>;\n return (\n typeof e['ts'] === 'string' &&\n typeof e['peerId'] === 'string' &&\n typeof e['assetCode'] === 'string' &&\n typeof e['claimsReceivedTotal'] === 'string'\n );\n}\n","/**\n * Wallet file I/O for Townhouse (Story 21.4, Task 2.2).\n *\n * Persists encrypted wallet to disk with 0o600 permissions (owner-only).\n * Warns if existing file has world-readable permissions.\n */\n\nimport { writeFile, readFile, mkdir, stat } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport type { EncryptedWallet } from './types.js';\n\n/**\n * Save encrypted wallet to disk with restrictive permissions.\n * Creates parent directory if missing.\n */\nexport async function saveWallet(\n path: string,\n encrypted: EncryptedWallet\n): Promise<void> {\n const dir = dirname(path);\n await mkdir(dir, { recursive: true, mode: 0o700 });\n\n const data = JSON.stringify(encrypted, null, 2);\n await writeFile(path, data, { encoding: 'utf-8', mode: 0o600 });\n}\n\n/**\n * Load encrypted wallet from disk.\n * Returns null if file does not exist.\n * Warns (via returned flag) if file permissions are too open.\n */\nexport async function loadWallet(\n path: string\n): Promise<{ wallet: EncryptedWallet; permissionsWarning?: string } | null> {\n let data: string;\n try {\n data = await readFile(path, 'utf-8');\n } catch (err: unknown) {\n if (\n err instanceof Error &&\n 'code' in err &&\n (err as NodeJS.ErrnoException).code === 'ENOENT'\n ) {\n return null;\n }\n throw err;\n }\n\n // Check permissions\n let permissionsWarning: string | undefined;\n try {\n const stats = await stat(path);\n const mode = stats.mode & 0o777;\n if (mode & 0o077) {\n permissionsWarning = `Warning: wallet file ${path} has permissions ${mode.toString(8)} (should be 600)`;\n }\n } catch {\n // stat failure is non-fatal — skip permissions check\n }\n\n const wallet = JSON.parse(data) as EncryptedWallet;\n return { wallet, permissionsWarning };\n}\n","/**\n * Wallet encryption/decryption for Townhouse (Story 21.4, Task 2).\n *\n * Uses Node.js crypto: scrypt for KDF, AES-256-GCM for authenticated encryption.\n * The mnemonic is the plaintext being encrypted.\n */\n\nimport {\n scryptSync,\n createCipheriv,\n createDecipheriv,\n randomBytes,\n} from 'node:crypto';\nimport type { ArweaveJwk, EncryptedWallet } from './types.js';\n\n/** scrypt parameters — N=2^17 (~0.5-1s on modern hardware), r=8, p=1 */\nconst SCRYPT_N = 2 ** 17;\nconst SCRYPT_R = 8;\nconst SCRYPT_P = 1;\nconst SCRYPT_KEY_LEN = 32;\n/** maxmem for scrypt: N * r * 128 * 2 (with headroom for Node.js overhead) */\nconst SCRYPT_MAXMEM = SCRYPT_N * SCRYPT_R * 256 + 32 * 1024 * 1024;\n\n/** Salt length in bytes */\nconst SALT_LEN = 32;\n\n/** AES-GCM IV length in bytes */\nconst IV_LEN = 12;\n\n/** AES-GCM authentication tag length in bytes (128-bit) */\nconst AUTH_TAG_LEN = 16;\n\n/**\n * Encrypt a mnemonic with a password using scrypt + AES-256-GCM.\n */\nexport function encryptWallet(\n mnemonic: string,\n password: string\n): EncryptedWallet {\n const salt = randomBytes(SALT_LEN);\n const iv = randomBytes(IV_LEN);\n\n const key = scryptSync(password, salt, SCRYPT_KEY_LEN, {\n N: SCRYPT_N,\n r: SCRYPT_R,\n p: SCRYPT_P,\n maxmem: SCRYPT_MAXMEM,\n });\n\n try {\n const cipher = createCipheriv('aes-256-gcm', key, iv, {\n authTagLength: AUTH_TAG_LEN,\n });\n const ciphertext = Buffer.concat([\n cipher.update(mnemonic, 'utf8'),\n cipher.final(),\n ]);\n const tag = cipher.getAuthTag();\n\n return {\n salt: salt.toString('base64'),\n iv: iv.toString('base64'),\n ciphertext: ciphertext.toString('base64'),\n tag: tag.toString('base64'),\n };\n } finally {\n key.fill(0);\n }\n}\n\n/**\n * Decrypt an encrypted wallet with a password.\n * Throws on wrong password (GCM auth tag verification failure).\n */\nexport function decryptWallet(\n encrypted: EncryptedWallet,\n password: string\n): string {\n const salt = Buffer.from(encrypted.salt, 'base64');\n const iv = Buffer.from(encrypted.iv, 'base64');\n const ciphertext = Buffer.from(encrypted.ciphertext, 'base64');\n const tag = Buffer.from(encrypted.tag, 'base64');\n\n const key = scryptSync(password, salt, SCRYPT_KEY_LEN, {\n N: SCRYPT_N,\n r: SCRYPT_R,\n p: SCRYPT_P,\n maxmem: SCRYPT_MAXMEM,\n });\n\n try {\n const decipher = createDecipheriv('aes-256-gcm', key, iv, {\n authTagLength: AUTH_TAG_LEN,\n });\n decipher.setAuthTag(tag);\n\n try {\n const plaintext = Buffer.concat([\n decipher.update(ciphertext),\n decipher.final(),\n ]);\n return plaintext.toString('utf8');\n } catch {\n throw new Error(\n 'Decryption failed: wrong password or corrupted wallet file'\n );\n }\n } finally {\n key.fill(0);\n }\n}\n\n/**\n * Generic string encryption — same envelope as encryptWallet, used by the\n * Arweave JWK cache (epic-49 Followup A) and any future caller that wants\n * to reuse the scrypt+AES-256-GCM primitive without committing to mnemonic\n * semantics.\n */\nexport function encryptString(\n plaintext: string,\n password: string\n): EncryptedWallet {\n const salt = randomBytes(SALT_LEN);\n const iv = randomBytes(IV_LEN);\n const key = scryptSync(password, salt, SCRYPT_KEY_LEN, {\n N: SCRYPT_N,\n r: SCRYPT_R,\n p: SCRYPT_P,\n maxmem: SCRYPT_MAXMEM,\n });\n try {\n const cipher = createCipheriv('aes-256-gcm', key, iv, {\n authTagLength: AUTH_TAG_LEN,\n });\n const ciphertext = Buffer.concat([\n cipher.update(plaintext, 'utf8'),\n cipher.final(),\n ]);\n const tag = cipher.getAuthTag();\n return {\n salt: salt.toString('base64'),\n iv: iv.toString('base64'),\n ciphertext: ciphertext.toString('base64'),\n tag: tag.toString('base64'),\n };\n } finally {\n key.fill(0);\n }\n}\n\n/** Inverse of encryptString. Throws on wrong password / corruption. */\nexport function decryptString(\n encrypted: EncryptedWallet,\n password: string\n): string {\n const salt = Buffer.from(encrypted.salt, 'base64');\n const iv = Buffer.from(encrypted.iv, 'base64');\n const ciphertext = Buffer.from(encrypted.ciphertext, 'base64');\n const tag = Buffer.from(encrypted.tag, 'base64');\n const key = scryptSync(password, salt, SCRYPT_KEY_LEN, {\n N: SCRYPT_N,\n r: SCRYPT_R,\n p: SCRYPT_P,\n maxmem: SCRYPT_MAXMEM,\n });\n try {\n const decipher = createDecipheriv('aes-256-gcm', key, iv, {\n authTagLength: AUTH_TAG_LEN,\n });\n decipher.setAuthTag(tag);\n try {\n const plaintext = Buffer.concat([\n decipher.update(ciphertext),\n decipher.final(),\n ]);\n return plaintext.toString('utf8');\n } catch {\n throw new Error(\n 'Decryption failed: wrong password or corrupted ciphertext'\n );\n }\n } finally {\n key.fill(0);\n }\n}\n\n/** Encrypt an Arweave RSA JWK under the operator password (epic-49 Followup A). */\nexport function encryptArweaveJwk(\n jwk: ArweaveJwk,\n password: string\n): EncryptedWallet {\n return encryptString(JSON.stringify(jwk), password);\n}\n\n/**\n * Decrypt an Arweave RSA JWK previously produced by `encryptArweaveJwk`.\n * Throws if the password is wrong, ciphertext is corrupt, or the plaintext\n * is not a well-formed JWK (missing `kty`/`n`/`e`).\n */\nexport function decryptArweaveJwk(\n encrypted: EncryptedWallet,\n password: string\n): ArweaveJwk {\n const plaintext = decryptString(encrypted, password);\n let parsed: unknown;\n try {\n parsed = JSON.parse(plaintext);\n } catch {\n throw new Error(\n 'Arweave JWK cache is corrupt: plaintext is not valid JSON'\n );\n }\n if (\n typeof parsed !== 'object' ||\n parsed === null ||\n (parsed as { kty?: unknown }).kty !== 'RSA' ||\n typeof (parsed as { n?: unknown }).n !== 'string' ||\n typeof (parsed as { e?: unknown }).e !== 'string'\n ) {\n throw new Error(\n 'Arweave JWK cache is corrupt: plaintext is not a well-formed RSA JWK'\n );\n }\n return parsed as ArweaveJwk;\n}\n","/**\n * Image manifest reader for Townhouse (Story 46.2).\n *\n * `~/.townhouse/image-manifest.json` is materialized by `compose-loader.ts`\n * during `townhouse hs up`. It maps node types to their digest-pinned image\n * refs, consumed by `POST /api/nodes` step 2 (pull image).\n */\n\nimport { promises as fs } from 'node:fs';\nimport { z } from 'zod';\n\nconst ImageEntrySchema = z\n .object({\n name: z.string().min(1),\n tag: z.string().min(1),\n digest: z.string().regex(/^sha256:[0-9a-f]{64}$/),\n })\n .strict();\n\nexport const ImageManifestSchema = z\n .object({\n schemaVersion: z.literal(1),\n townhouseVersion: z.string(),\n builtAt: z.string().datetime({ offset: true }),\n images: z\n .object({\n 'townhouse-api': ImageEntrySchema,\n town: ImageEntrySchema,\n mill: ImageEntrySchema,\n dvm: ImageEntrySchema,\n connector: ImageEntrySchema,\n })\n .strict(),\n })\n .strict();\n\nexport type ImageManifest = z.infer<typeof ImageManifestSchema>;\n\n/**\n * Sentinel digest used by `.github/workflows/connector-publish-smoke.yml`\n * to populate the four non-connector entries of a synthetic\n * `image-manifest.json` it writes when the operator supplies\n * `connector_digest` for a candidate connector tag.\n *\n * The smoke workflow only validates the CONNECTOR entry; the four other\n * entries exist purely to satisfy `ImageManifestSchema.strict()`. Any future\n * per-image alignment check MUST treat this value as \"not a real registry\n * digest\" and skip the comparison rather than fail. Recognize via either\n * literal equality or `isSyntheticDigest(d)`.\n */\nexport const SYNTHETIC_DIGEST_SENTINEL =\n 'sha256:dead000000000000000000000000000000000000000000000000000000000000';\n\n/** True iff the digest is the synthetic sentinel produced by the smoke workflow. */\nexport function isSyntheticDigest(digest: string): boolean {\n return digest === SYNTHETIC_DIGEST_SENTINEL;\n}\n\n/**\n * Read and validate `image-manifest.json` at the given path.\n *\n * Throws ENOENT if the file is missing — there is no graceful fallback for a\n * missing manifest; it means `townhouse hs up` was not run first.\n * Throws `ZodError` with a useful path if the file is present but invalid.\n */\nexport async function readImageManifest(path: string): Promise<ImageManifest> {\n const raw = await fs.readFile(path, 'utf-8');\n const parsed: unknown = JSON.parse(raw);\n return ImageManifestSchema.parse(parsed);\n}\n","/**\n * WalletManager — HD key derivation for Townhouse (Story 21.4, Task 1).\n *\n * Single BIP-39 mnemonic, deterministic HD derivation per node type.\n * Uses BIP-44 paths with distinct account indices per node type:\n * - Town: account 0\n * - Mill: account 1\n * - DVM: account 2\n *\n * Nostr keys use NIP-06 coin type 1237: m/44'/1237'/{account}'/0/0\n * EVM keys use standard coin type 60: m/44'/60'/{account}'/0/0\n * Solana keys (all node types) coin 501: m/44'/501'/{account}'/0'/0'\n * (SLIP-0010 all-hardened, via @toon-protocol/mill::deriveMillKeys)\n * Arweave keys (DVM only) coin 472: m/44'/472'/2'/0/0\n * (32-byte BIP-32 sub-seed feeds a deterministic RSA-4096 PRNG via\n * rsa-from-seed.ts (HMAC-DRBG + node-forge 1.3.3). Derivation takes 5–30s per DVM unlock; this runs\n * once at unlock time, not per operation.)\n */\n\nimport {\n generateMnemonic,\n validateMnemonic,\n mnemonicToSeedSync,\n} from '@scure/bip39';\nimport { wordlist } from '@scure/bip39/wordlists/english.js';\nimport { HDKey } from '@scure/bip32';\nimport { getPublicKey } from 'nostr-tools/pure';\nimport { bytesToHex } from '@noble/hashes/utils';\nimport { keccak_256 } from '@noble/hashes/sha3';\nimport { secp256k1 } from '@noble/curves/secp256k1';\nimport { createPrivateKey, createHash } from 'node:crypto';\n\nimport type { NodeType } from '../docker/types.js';\nimport {\n arweaveCachePath,\n deleteArweaveJwkFromCache,\n readArweaveJwkFromCache,\n writeArweaveJwkToCache,\n} from './ar-cache.js';\nimport {\n ACCOUNT_INDEX_TOWN,\n ACCOUNT_INDEX_MILL,\n ACCOUNT_INDEX_DVM,\n} from '../constants.js';\nimport type {\n WalletManagerConfig,\n WalletState,\n NodeKeys,\n DerivedNodeKeys,\n NodeKeyInfo,\n ArweaveJwk,\n} from './types.js';\nimport { deriveMillKeys } from '@toon-protocol/mill';\n\nconst BASE58_ALPHABET =\n '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';\nfunction base58Encode(bytes: Uint8Array): string {\n let zeros = 0;\n for (let i = 0; i < bytes.length && bytes[i] === 0; i++) zeros++;\n let value = 0n;\n for (const byte of bytes) value = value * 256n + BigInt(byte);\n let result = '';\n while (value > 0n) {\n result = BASE58_ALPHABET[Number(value % 58n)] + result;\n value = value / 58n;\n }\n for (let i = 0; i < zeros; i++) result = '1' + result;\n return result || '1';\n}\n\n/** Map node type to account index */\nconst NODE_ACCOUNT_INDEX: Record<NodeType, number> = {\n town: ACCOUNT_INDEX_TOWN,\n mill: ACCOUNT_INDEX_MILL,\n dvm: ACCOUNT_INDEX_DVM,\n};\n\n/**\n * WalletManager handles mnemonic generation, key derivation, and in-memory\n * key lifecycle for Townhouse node operations.\n */\nexport class WalletManager {\n private readonly config: WalletManagerConfig;\n private state: WalletState | null = null;\n\n constructor(config: WalletManagerConfig) {\n this.config = config;\n }\n\n /** Path to the encrypted wallet file */\n get encryptedPath(): string {\n return this.config.encryptedPath;\n }\n\n /**\n * Generate a new 12-word BIP-39 mnemonic and derive all node keys.\n * Returns the mnemonic (for one-time display) and the derived state.\n */\n async generate(): Promise<{ mnemonic: string; state: WalletState }> {\n const mnemonic = generateMnemonic(wordlist, 128); // 128 bits = 12 words\n const state = await this.deriveAllKeys(mnemonic);\n this.state = state;\n return { mnemonic, state };\n }\n\n /**\n * Import an existing mnemonic (12 or 24 words) and derive all node keys.\n * Throws if mnemonic is invalid (wrong checksum, wrong word count, etc).\n */\n async fromMnemonic(mnemonic: string): Promise<WalletState> {\n if (!validateMnemonic(mnemonic, wordlist)) {\n throw new Error(\n 'Invalid BIP-39 mnemonic: checksum or word list validation failed'\n );\n }\n const state = await this.deriveAllKeys(mnemonic);\n this.state = state;\n return state;\n }\n\n /**\n * Get derived keys for a specific node type.\n * Throws if wallet has not been initialized (call generate() or fromMnemonic() first).\n */\n getNodeKeys(nodeType: NodeType): NodeKeys {\n if (!this.state) {\n throw new Error(\n 'Wallet not initialized. Call generate() or fromMnemonic() first.'\n );\n }\n return this.state.keys[nodeType];\n }\n\n /**\n * Get display-safe info for all node types (no secrets).\n */\n getAllKeys(): NodeKeyInfo[] {\n if (!this.state) {\n throw new Error(\n 'Wallet not initialized. Call generate() or fromMnemonic() first.'\n );\n }\n\n const state = this.state;\n const types: NodeType[] = ['town', 'mill', 'dvm'];\n return types.map((nodeType) => {\n const keys = state.keys[nodeType];\n const info: NodeKeyInfo = {\n nodeType,\n nostrPubkey: keys.nostrPubkey,\n evmAddress: keys.evmAddress,\n nostrDerivationPath: keys.nostrDerivationPath,\n evmDerivationPath: keys.evmDerivationPath,\n };\n if (keys.solanaAddress) info.solanaAddress = keys.solanaAddress;\n if (keys.solanaDerivationPath)\n info.solanaDerivationPath = keys.solanaDerivationPath;\n if (nodeType === 'mill' && keys.minaAddress) {\n info.minaAddress = keys.minaAddress;\n }\n if (keys.arweaveAddress) info.arweaveAddress = keys.arweaveAddress;\n if (keys.arweaveDerivationPath)\n info.arweaveDerivationPath = keys.arweaveDerivationPath;\n return info;\n });\n }\n\n /**\n * List keys for all node types (alias for getAllKeys for API compatibility).\n */\n listKeys(): NodeKeyInfo[] {\n return this.getAllKeys();\n }\n\n /**\n * Zero all in-memory key material. After calling lock(),\n * getNodeKeys() and getAllKeys() will throw.\n */\n lock(): void {\n if (!this.state) return;\n\n const types: NodeType[] = ['town', 'mill', 'dvm'];\n for (const nodeType of types) {\n const keys = this.state.keys[nodeType];\n keys.nostrSecretKey.fill(0);\n keys.evmPrivateKey.fill(0);\n if (keys.solanaPrivateKey) keys.solanaPrivateKey.fill(0);\n if (keys.arweaveJwk) zeroArweaveJwk(keys.arweaveJwk);\n }\n this.state = null;\n }\n\n /**\n * Return the BIP-39 mnemonic from in-memory wallet state.\n * Returns null when the wallet is locked or not initialized.\n */\n getMnemonic(): string | null {\n return this.state?.mnemonic ?? null;\n }\n\n /**\n * Get derived keys for a specific node type at a given derivation index.\n *\n * Pure derivation — does NOT mutate `state`. Re-derives from the stored\n * mnemonic each time it is called. For every node type, also derives the\n * Solana key at the same account index. For 'dvm', also derives Arweave.\n * Throws if the wallet is locked.\n *\n * v1 callers MUST pass `derivationIndex = ACCOUNT_INDEX_{type}` for the\n * first (and only) instance per type. Multi-instance support is out of\n * scope for v1 — the route layer enforces single-instance-per-type.\n */\n async deriveNodeKey(\n type: NodeType,\n derivationIndex: number\n ): Promise<NodeKeys> {\n if (!this.state) {\n throw new Error(\n 'Wallet not initialized. Call generate() or fromMnemonic() first.'\n );\n }\n const mnemonic = this.state.mnemonic;\n let seed: Uint8Array | undefined;\n try {\n seed = mnemonicToSeedSync(mnemonic);\n const baseKeys = this.deriveNodeKeys(seed, type, derivationIndex);\n // Derive Solana for ALL node types (P21-008 — DVM needs SOL for\n // Turbo credit funding; town's SOL is exposed for symmetry).\n const chains: ('solana' | 'mina')[] =\n type === 'mill' ? ['solana', 'mina'] : ['solana'];\n let solanaAddress: string | undefined;\n let solanaPrivateKey: Uint8Array | undefined;\n let solanaDerivationPath: string | undefined;\n let minaAddress: string | undefined;\n try {\n const chainKeys = await deriveMillKeys({\n mnemonic,\n chains,\n accountIndex: derivationIndex,\n });\n if (chainKeys.solana) {\n solanaAddress = base58Encode(chainKeys.solana.publicKey);\n solanaPrivateKey = chainKeys.solana.privateKey;\n solanaDerivationPath = chainKeys.solana.path;\n }\n if (chainKeys.mina && type === 'mill') {\n minaAddress = chainKeys.mina.publicKey;\n }\n } catch (err: unknown) {\n // deriveMillKeys failure (e.g. unsupported platform, library load\n // error) — chain addresses are optional at derivation time, but a\n // non-platform failure should be visible (P8). Log via console.warn\n // because WalletManager has no logger injected.\n const errMsg = err instanceof Error ? err.message : String(err);\n console.warn(\n `[WalletManager] deriveMillKeys failed for type=${type} accountIndex=${derivationIndex}: ${errMsg} — Solana/Mina addresses omitted`\n );\n }\n // Note: AR is intentionally NOT derived here either — `deriveNodeKey`\n // is a pure re-derivation helper called by route/admin code paths\n // where blocking 5–30s on RSA-4096 generation would surprise callers.\n // Use `ensureArweaveKey(type)` from the credit-funding code path to\n // obtain the AR JWK on demand.\n return {\n ...baseKeys,\n solanaAddress,\n solanaPrivateKey,\n solanaDerivationPath,\n minaAddress,\n };\n } finally {\n if (seed) seed.fill(0);\n }\n }\n\n // ── Private-key accessors (epic-49 credit funding) ───────────────────────\n\n /**\n * Returns the EVM private key for a node as a 64-char lowercase hex string.\n * Throws when the wallet is locked. Callers MUST treat the returned string\n * as sensitive (no logging, no persisting). The underlying Uint8Array is\n * still owned by WalletManager and will be zeroed by `lock()`.\n */\n getEvmPrivateKeyHex(nodeType: NodeType): string {\n const keys = this.getNodeKeys(nodeType);\n return bytesToHex(keys.evmPrivateKey);\n }\n\n /**\n * Returns the Solana Ed25519 private key seed for a node as a 64-char\n * lowercase hex string (32 raw seed bytes). Throws when the wallet is\n * locked or when the Solana key was not derived for this node type.\n */\n getSolanaPrivateKeyHex(nodeType: NodeType): string {\n const keys = this.getNodeKeys(nodeType);\n if (!keys.solanaPrivateKey) {\n throw new Error(\n `Solana private key not available for node '${nodeType}'`\n );\n }\n return bytesToHex(keys.solanaPrivateKey);\n }\n\n /**\n * Returns the Arweave RSA JWK for a node. Throws when the wallet is locked\n * or when AR derivation has not yet been triggered for this node type.\n *\n * Callers MUST `await ensureArweaveKey(nodeType)` first the first time per\n * unlock — RSA-4096 derivation is 5–30s and is therefore not done eagerly\n * at `fromMnemonic`/`generate` time. After the first `ensureArweaveKey`\n * the JWK is cached on the in-memory state until `lock()`.\n */\n getArweaveJwk(nodeType: NodeType): ArweaveJwk {\n const keys = this.getNodeKeys(nodeType);\n if (!keys.arweaveJwk) {\n throw new Error(\n `Arweave JWK not yet derived for node '${nodeType}'. Call ensureArweaveKey('${nodeType}') first (note: derivation takes 5–30s).`\n );\n }\n return keys.arweaveJwk;\n }\n\n /**\n * Lazily derive the Arweave RSA-4096 JWK for a node type and cache it on\n * the in-memory wallet state. Subsequent calls (within the same unlocked\n * session) return the cached result without re-deriving.\n *\n * Only meaningful for node types that participate in the Arweave credit\n * flow — `dvm` (account 2) in the current Townhouse layout. Calling on\n * `town` or `mill` will derive a valid AR key at the corresponding\n * account index but those keys are not used by any current code path.\n *\n * Throws if the wallet is locked or RSA derivation fails (unsupported\n * platform, etc.). On success the result is also reflected in subsequent\n * `getAllKeys()` calls (arweaveAddress + arweaveDerivationPath fields).\n */\n async ensureArweaveKey(\n nodeType: NodeType,\n password?: string\n ): Promise<ArweaveJwk> {\n if (!this.state) {\n throw new Error(\n 'Wallet not initialized. Call generate() or fromMnemonic() first.'\n );\n }\n const existing = this.state.keys[nodeType].arweaveJwk;\n if (existing) return existing;\n\n const accountIndex = NODE_ACCOUNT_INDEX[nodeType];\n const path = `m/44'/472'/${accountIndex}'/0/0`;\n let seed: Uint8Array | undefined;\n let subSeed: Uint8Array | undefined;\n try {\n seed = mnemonicToSeedSync(this.state.mnemonic);\n\n // Derive the BIP-32 sub-seed (fast) up front so we can compute a public\n // fingerprint of the mnemonic-bound material. The fingerprint lets the\n // on-disk cache detect \"wallet restored from a different mnemonic\"\n // without paying the 5–30s RSA cost on every load.\n const hdKey = HDKey.fromMasterSeed(seed).derive(path);\n if (!hdKey.privateKey) {\n throw new Error(`Arweave sub-seed missing at ${path}`);\n }\n subSeed = new Uint8Array(hdKey.privateKey);\n const fingerprint = createHash('sha256')\n .update(subSeed)\n .digest('base64url');\n\n // ── Disk cache check (epic-49 Followup A) ─────────────────────────\n // Only attempted when a password is supplied — the cache file is\n // encrypted under the same operator password as wallet.enc. Callers\n // that don't have the password (e.g. test setups that called\n // fromMnemonic directly) skip the cache; they pay the full RSA cost.\n if (password) {\n const cachePath = arweaveCachePath(this.config.encryptedPath);\n const result = await readArweaveJwkFromCache(\n cachePath,\n nodeType,\n password,\n fingerprint\n );\n if (result.status === 'hit') {\n this.state.keys[nodeType].arweaveJwk = result.jwk;\n this.state.keys[nodeType].arweaveAddress =\n result.entry.arweaveAddress;\n this.state.keys[nodeType].arweaveDerivationPath = path;\n return result.jwk;\n }\n if (result.status === 'stale') {\n console.warn(\n `[WalletManager] Arweave JWK cache for ${nodeType} was written from a different mnemonic (cached address ${result.cachedAddress.slice(0, 12)}…). Discarding and re-deriving.`\n );\n await deleteArweaveJwkFromCache(cachePath, nodeType);\n // fall through to RSA derivation\n }\n // status === 'miss' → fall through to RSA derivation\n }\n\n // ── RSA-4096 derivation (5–30s) ──────────────────────────────────\n const ar = await deriveArweaveKey(seed, accountIndex);\n // Re-check state under the assumption a concurrent lock() may have\n // wiped it while we were generating RSA (5–30s window).\n if (!this.state) {\n zeroArweaveJwk(ar.jwk);\n throw new Error(\n 'Wallet was locked during Arweave key derivation. Discarding derived key.'\n );\n }\n this.state.keys[nodeType].arweaveJwk = ar.jwk;\n this.state.keys[nodeType].arweaveAddress = ar.address;\n this.state.keys[nodeType].arweaveDerivationPath = ar.path;\n\n // ── Persist to disk cache (best-effort, password-gated) ──────────\n if (password) {\n try {\n const cachePath = arweaveCachePath(this.config.encryptedPath);\n await writeArweaveJwkToCache(\n cachePath,\n nodeType,\n ar.jwk,\n password,\n fingerprint,\n ar.address\n );\n } catch (err: unknown) {\n // Cache write failure is non-fatal — RSA derivation succeeded.\n const msg = err instanceof Error ? err.message : String(err);\n console.warn(\n `[WalletManager] Failed to write Arweave JWK cache (non-fatal): ${msg}`\n );\n }\n }\n\n return ar.jwk;\n } finally {\n if (seed) seed.fill(0);\n if (subSeed) subSeed.fill(0);\n }\n }\n\n /**\n * Derive keys for all node types from a mnemonic.\n */\n private async deriveAllKeys(mnemonic: string): Promise<WalletState> {\n let seed: Uint8Array | undefined;\n try {\n seed = mnemonicToSeedSync(mnemonic);\n\n // Derive Solana addresses for ALL node types (D21-008 / epic-49).\n // Mill additionally gets a Mina address. Errors here are\n // non-fatal — chain addresses are optional, base BIP-44 keys are\n // still derived per node type below.\n interface ChainExtras {\n solanaAddress?: string;\n solanaPrivateKey?: Uint8Array;\n solanaDerivationPath?: string;\n minaAddress?: string;\n }\n const chainExtras: Record<NodeType, ChainExtras> = {\n town: {},\n mill: {},\n dvm: {},\n };\n const types: NodeType[] = ['town', 'mill', 'dvm'];\n for (const nodeType of types) {\n const accountIndex = NODE_ACCOUNT_INDEX[nodeType];\n const chains: ('solana' | 'mina')[] =\n nodeType === 'mill' ? ['solana', 'mina'] : ['solana'];\n try {\n const chainKeys = await deriveMillKeys({\n mnemonic,\n chains,\n accountIndex,\n });\n if (chainKeys.solana) {\n chainExtras[nodeType].solanaAddress = base58Encode(\n chainKeys.solana.publicKey\n );\n chainExtras[nodeType].solanaPrivateKey =\n chainKeys.solana.privateKey;\n chainExtras[nodeType].solanaDerivationPath = chainKeys.solana.path;\n }\n if (nodeType === 'mill' && chainKeys.mina) {\n chainExtras[nodeType].minaAddress = chainKeys.mina.publicKey;\n }\n } catch (err: unknown) {\n const errMsg = err instanceof Error ? err.message : String(err);\n console.warn(\n `[WalletManager] deriveMillKeys failed for ${nodeType} (accountIndex=${accountIndex}): ${errMsg} — chain addresses omitted`\n );\n }\n }\n\n // Note: Arweave (RSA-4096) derivation is NOT done eagerly here.\n // RSA-4096 generation from a deterministic PRNG takes 5–30 seconds\n // per call — too slow to block every wallet unlock. Callers that\n // need the AR key (CLI `credits buy`, orchestrator before starting\n // the DVM container, etc.) must call `ensureArweaveKey('dvm')`\n // explicitly. The result is cached on the in-memory state for the\n // rest of the unlocked session.\n\n const keys: DerivedNodeKeys = {\n town: { ...this.deriveNodeKeys(seed, 'town'), ...chainExtras.town },\n mill: { ...this.deriveNodeKeys(seed, 'mill'), ...chainExtras.mill },\n dvm: { ...this.deriveNodeKeys(seed, 'dvm'), ...chainExtras.dvm },\n };\n return { keys, mnemonic };\n } finally {\n if (seed) seed.fill(0);\n }\n }\n\n /**\n * Derive Nostr + EVM keys for a specific node type.\n * Accepts an optional `accountIndex` to override the default per-type index.\n * When omitted, uses `NODE_ACCOUNT_INDEX[nodeType]` (existing behavior).\n */\n private deriveNodeKeys(\n seed: Uint8Array,\n nodeType: NodeType,\n accountIndex?: number\n ): NodeKeys {\n const idx = accountIndex ?? NODE_ACCOUNT_INDEX[nodeType];\n\n // Nostr key: NIP-06 path m/44'/1237'/{account}'/0/0\n const nostrPath = `m/44'/1237'/${idx}'/0/0`;\n const nostrHdKey = HDKey.fromMasterSeed(seed).derive(nostrPath);\n if (!nostrHdKey.privateKey) {\n throw new Error(`Nostr private key missing at ${nostrPath}`);\n }\n const nostrSecretKey = new Uint8Array(nostrHdKey.privateKey);\n const nostrPubkey = getPublicKey(nostrSecretKey);\n\n // EVM key: standard path m/44'/60'/{account}'/0/0\n const evmPath = `m/44'/60'/${idx}'/0/0`;\n const evmHdKey = HDKey.fromMasterSeed(seed).derive(evmPath);\n if (!evmHdKey.privateKey) {\n throw new Error(`EVM private key missing at ${evmPath}`);\n }\n const evmPrivateKey = new Uint8Array(evmHdKey.privateKey);\n const evmAddress = computeEvmAddress(evmPrivateKey);\n\n return {\n nostrPubkey,\n nostrSecretKey,\n evmAddress,\n evmPrivateKey,\n nostrDerivationPath: nostrPath,\n evmDerivationPath: evmPath,\n };\n }\n}\n\n/**\n * Derive an Arweave RSA-4096 JWK from a BIP-39 seed at a given account index.\n *\n * Algorithm:\n * 1. BIP-32 derive a 32-byte sub-seed at m/44'/472'/{account}'/0/0.\n * 2. Feed that sub-seed into our HMAC-DRBG(SHA-256, seed) PRNG implemented\n * with @noble/hashes, which drives node-forge 1.3.3 (CVE-free) PRIMEINC\n * prime search → deterministic RSA keys identical to human-crypto-keys 0.1.4.\n * 3. Parse the PKCS#1 PEM via Node's built-in `crypto.createPrivateKey()`\n * and export as JWK. Strip non-arweave fields (alg, kid).\n * 4. Compute the Arweave address: base64url(sha256(base64url_decode(n))).\n *\n * Determinism verified empirically: identical seed → identical JWK across runs\n * (Node 20.x, rsa-from-seed.ts, node-forge 1.3.3).\n *\n * Performance: 5–30 seconds per call on a 2024 desktop. This is a one-time\n * cost per wallet unlock — the JWK is then held in memory until `lock()`.\n */\nasync function deriveArweaveKey(\n seed: Uint8Array,\n accountIndex: number\n): Promise<{ jwk: ArweaveJwk; address: string; path: string }> {\n // Coin type 472 = Arweave (https://github.com/satoshilabs/slips/blob/master/slip-0044.md)\n const path = `m/44'/472'/${accountIndex}'/0/0`;\n const hdKey = HDKey.fromMasterSeed(seed).derive(path);\n if (!hdKey.privateKey) {\n throw new Error(`Arweave sub-seed missing at ${path}`);\n }\n const subSeed = new Uint8Array(hdKey.privateKey);\n\n // rsa-from-seed uses @noble/hashes HMAC-DRBG + node-forge 1.3.3 (CVE-free).\n // Lazy-import keeps startup cheap for Town/Mill callers that never touch Arweave.\n const { rsaPrivateKeyPemFromSeed } = await import('./rsa-from-seed.js');\n\n let pemPrivateKey: string;\n try {\n pemPrivateKey = await rsaPrivateKeyPemFromSeed(subSeed);\n } finally {\n // Sub-seed leaves the function. Zero it ASAP — the RSA key derived from\n // it is what we keep, not the seed itself.\n subSeed.fill(0);\n }\n\n // PEM → JWK via Node's built-in crypto (no extra dependency).\n const keyObject = createPrivateKey({\n key: pemPrivateKey,\n format: 'pem',\n type: 'pkcs1',\n });\n const rawJwk = keyObject.export({ format: 'jwk' }) as {\n kty?: string;\n e?: string;\n n?: string;\n d?: string;\n p?: string;\n q?: string;\n dp?: string;\n dq?: string;\n qi?: string;\n };\n if (\n rawJwk.kty !== 'RSA' ||\n !rawJwk.n ||\n !rawJwk.e ||\n !rawJwk.d ||\n !rawJwk.p ||\n !rawJwk.q ||\n !rawJwk.dp ||\n !rawJwk.dq ||\n !rawJwk.qi\n ) {\n throw new Error(\n `Arweave JWK conversion produced unexpected shape (kty=${String(rawJwk.kty)}, has-private=${Boolean(rawJwk.d)})`\n );\n }\n const jwk: ArweaveJwk = {\n kty: 'RSA',\n e: rawJwk.e,\n n: rawJwk.n,\n d: rawJwk.d,\n p: rawJwk.p,\n q: rawJwk.q,\n dp: rawJwk.dp,\n dq: rawJwk.dq,\n qi: rawJwk.qi,\n };\n\n // Arweave address = base64url(sha256(modulus_bytes)).\n const modulusBytes = Buffer.from(jwk.n, 'base64url');\n const address = createHash('sha256').update(modulusBytes).digest('base64url');\n\n return { jwk, address, path };\n}\n\n/**\n * Zero out every base64url string field of an Arweave JWK in place.\n *\n * RSA private key material lives in `d`, `p`, `q`, `dp`, `dq`, `qi`. String\n * primitives are immutable in JavaScript, so we cannot truly zero them — what\n * we CAN do is overwrite the property with an empty string so a later read of\n * the JWK object after `lock()` cannot recover the key material.\n *\n * This matches the best-effort zeroing approach used elsewhere (Uint8Array\n * `.fill(0)` is similarly best-effort against off-heap copies).\n */\nfunction zeroArweaveJwk(jwk: ArweaveJwk): void {\n // Public components are not secrets; leave them. Wipe private exponents.\n jwk.d = '';\n jwk.p = '';\n jwk.q = '';\n jwk.dp = '';\n jwk.dq = '';\n jwk.qi = '';\n}\n\n/**\n * Compute EVM address from private key: uncompressed pubkey -> keccak256 -> last 20 bytes.\n */\nfunction computeEvmAddress(privateKey: Uint8Array): string {\n const uncompressed = secp256k1.getPublicKey(privateKey, false);\n const hash = keccak_256(uncompressed.slice(1));\n const addressHex = bytesToHex(hash.slice(-20));\n return toChecksumAddress(addressHex);\n}\n\n/**\n * EIP-55 checksum address encoding.\n */\nfunction toChecksumAddress(addressHex: string): string {\n const lower = addressHex.toLowerCase();\n const hashHex = bytesToHex(keccak_256(new TextEncoder().encode(lower)));\n let out = '0x';\n for (let i = 0; i < 40; i++) {\n const ch = lower.charAt(i);\n const hashNibble = parseInt(hashHex.charAt(i), 16);\n out += hashNibble >= 8 ? ch.toUpperCase() : ch;\n }\n return out;\n}\n","/**\n * On-disk cache for derived Arweave RSA-4096 JWKs (Followup A).\n *\n * The Arweave HD-derivation costs 5–30 s of RSA prime search per process\n * invocation. Persisting the derived JWK to `wallet.arweave.enc` (alongside\n * `wallet.enc`) pays the cost once per wallet, not once per CLI invocation.\n *\n * File format: JSON envelope, mode 0o600, JWK plaintext encrypted with the\n * same operator password as `wallet.enc`. Per-node-type map under `nodes`\n * so future per-node AR keys are forward-compatible (today only `dvm` is\n * populated). Each entry stores a public sub-seed fingerprint and AR address\n * so we can detect a stale cache (different mnemonic) without paying the\n * RSA cost.\n *\n * {\n * \"version\": 1,\n * \"nodes\": {\n * \"dvm\": {\n * \"subSeedFingerprint\": \"<base64url-sha256>\",\n * \"arweaveAddress\": \"<base64url-sha256>\",\n * \"encryptedJwk\": { salt, iv, ciphertext, tag }\n * }\n * }\n * }\n */\n\nimport { mkdir, readFile, stat, writeFile, unlink } from 'node:fs/promises';\nimport { dirname } from 'node:path';\n\nimport type { NodeType } from '../docker/types.js';\nimport type {\n ArweaveCacheEntry,\n ArweaveJwk,\n EncryptedArweaveCacheFile,\n} from './types.js';\nimport { decryptArweaveJwk, encryptArweaveJwk } from './crypto.js';\n\n/** Derive the cache file path from the wallet.enc path. Same directory. */\nexport function arweaveCachePath(encryptedWalletPath: string): string {\n return `${dirname(encryptedWalletPath)}/wallet.arweave.enc`;\n}\n\n/**\n * Load and parse the on-disk cache. Returns `null` if the file does not\n * exist. Throws on malformed JSON or schema mismatch.\n */\nexport async function loadArweaveCacheFile(\n path: string\n): Promise<EncryptedArweaveCacheFile | null> {\n let data: string;\n try {\n data = await readFile(path, 'utf-8');\n } catch (err: unknown) {\n if (\n err instanceof Error &&\n 'code' in err &&\n (err as NodeJS.ErrnoException).code === 'ENOENT'\n ) {\n return null;\n }\n throw err;\n }\n\n // Permissions check — surface a warning via stderr if too open (best-effort,\n // mirrors loadWallet behavior; consumers can choose to harden separately).\n try {\n const stats = await stat(path);\n const mode = stats.mode & 0o777;\n if (mode & 0o077) {\n console.error(\n `Warning: arweave cache ${path} has permissions ${mode.toString(8)} (should be 600)`\n );\n }\n } catch {\n /* best-effort */\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(data);\n } catch {\n throw new Error(\n `Arweave JWK cache at ${path} is corrupt: not valid JSON. Delete the file and re-run to re-derive.`\n );\n }\n if (\n typeof parsed !== 'object' ||\n parsed === null ||\n (parsed as { version?: unknown }).version !== 1 ||\n typeof (parsed as { nodes?: unknown }).nodes !== 'object' ||\n (parsed as { nodes: unknown }).nodes === null\n ) {\n throw new Error(\n `Arweave JWK cache at ${path} is corrupt: unexpected envelope shape. Delete the file and re-run to re-derive.`\n );\n }\n return parsed as EncryptedArweaveCacheFile;\n}\n\n/** Result of an attempted cache read — distinguishes miss from mismatch. */\nexport type ArweaveCacheReadResult =\n | { status: 'miss' }\n | {\n status: 'stale';\n cachedFingerprint: string;\n cachedAddress: string;\n }\n | { status: 'hit'; jwk: ArweaveJwk; entry: ArweaveCacheEntry };\n\n/**\n * Read + verify + decrypt the cached JWK for `nodeType`.\n *\n * - `miss` → file or entry absent; caller should derive + write.\n * - `stale` → entry exists but its sub-seed fingerprint does not match\n * `expectedFingerprint` (cache was written from a different\n * mnemonic). Caller should `deleteArweaveJwkFromCache` then\n * derive + write.\n * - `hit` → fingerprint matches; JWK successfully decrypted. **Throws**\n * if decryption fails (wrong password / corrupt ciphertext) —\n * the spec disallows silently re-deriving on password mismatch.\n */\nexport async function readArweaveJwkFromCache(\n path: string,\n nodeType: NodeType,\n password: string,\n expectedFingerprint: string\n): Promise<ArweaveCacheReadResult> {\n const file = await loadArweaveCacheFile(path);\n if (!file) return { status: 'miss' };\n const entry = file.nodes[nodeType];\n if (!entry) return { status: 'miss' };\n\n // Validate entry shape before doing anything else.\n if (\n typeof entry.subSeedFingerprint !== 'string' ||\n typeof entry.arweaveAddress !== 'string' ||\n typeof entry.encryptedJwk !== 'object' ||\n entry.encryptedJwk === null\n ) {\n throw new Error(\n `Arweave JWK cache entry for ${nodeType} at ${path} is corrupt: missing fields.`\n );\n }\n\n if (entry.subSeedFingerprint !== expectedFingerprint) {\n return {\n status: 'stale',\n cachedFingerprint: entry.subSeedFingerprint,\n cachedAddress: entry.arweaveAddress,\n };\n }\n\n // Fingerprint matched — now decrypt. If this throws, the password is wrong\n // (or the JWK plaintext is malformed); both surface to the caller.\n const jwk = decryptArweaveJwk(entry.encryptedJwk, password);\n return { status: 'hit', jwk, entry };\n}\n\n/**\n * Encrypt and write `jwk` for `nodeType` into the cache file. Preserves\n * entries for other node types. Idempotent re-write (overwrites the same\n * entry). File mode is 0o600; parent directory is created if missing.\n */\nexport async function writeArweaveJwkToCache(\n path: string,\n nodeType: NodeType,\n jwk: ArweaveJwk,\n password: string,\n subSeedFingerprint: string,\n arweaveAddress: string\n): Promise<void> {\n const existing = await loadArweaveCacheFile(path);\n const entry: ArweaveCacheEntry = {\n subSeedFingerprint,\n arweaveAddress,\n encryptedJwk: encryptArweaveJwk(jwk, password),\n };\n const file: EncryptedArweaveCacheFile = existing ?? {\n version: 1,\n nodes: {},\n };\n file.nodes[nodeType] = entry;\n\n const dir = dirname(path);\n await mkdir(dir, { recursive: true, mode: 0o700 });\n await writeFile(path, JSON.stringify(file, null, 2), {\n encoding: 'utf-8',\n mode: 0o600,\n });\n}\n\n/**\n * Delete the cache entry for a specific node type. Used when a stale-cache\n * mismatch is detected (e.g., operator restored from a different mnemonic).\n * No-op if the file or entry does not exist. Removes the file entirely\n * when the last entry is dropped, to keep the on-disk surface tidy.\n */\nexport async function deleteArweaveJwkFromCache(\n path: string,\n nodeType: NodeType\n): Promise<void> {\n const file = await loadArweaveCacheFile(path);\n if (!file) return;\n if (!(nodeType in file.nodes)) return;\n const { [nodeType]: _removed, ...remaining } = file.nodes;\n file.nodes = remaining;\n\n if (Object.keys(file.nodes).length === 0) {\n try {\n await unlink(path);\n } catch (err: unknown) {\n if (\n err instanceof Error &&\n 'code' in err &&\n (err as NodeJS.ErrnoException).code === 'ENOENT'\n ) {\n return;\n }\n throw err;\n }\n return;\n }\n\n await writeFile(path, JSON.stringify(file, null, 2), {\n encoding: 'utf-8',\n mode: 0o600,\n });\n}\n","/**\n * Earnings aggregator (Story 47.2).\n *\n * Aggregates connector-reported earnings into the canonical\n * `{ status, apex, peers }` shape consumed by the host-API\n * `/api/earnings` endpoint.\n *\n * Source of truth: `connectorAdmin.getEarnings()` (Story 47.1).\n * Peer-type attribution via `PeerTypeResolver` (Story 46.1); the resolver\n * buckets unmatched peerIds as `'external'` (enforcement lives in the\n * resolver, not here — we trust its contract).\n *\n * Failure mode: if `getEarnings()` throws (network, 503-when-disabled,\n * shape drift), returns the empty payload with\n * `status: 'connector_unavailable'`. The route returns 200 either way;\n * operators see zeros plus a UI banner rather than a 5xx. An injected\n * `logger.warn` (Fastify / pino-compatible) is called on failure so ops\n * can distinguish \"connector outage\" from \"no earnings yet.\"\n *\n * @module\n * @since 47.2\n */\n\nimport type { ConnectorAdminClient } from '../connector/index.js';\nimport type { RecentClaim } from '../connector/types.js';\nimport type { PeerTypeResolver } from '../registry/peer-type-resolver.js';\nimport type { NodeType } from '../docker/types.js';\n\nexport type { NodeType };\nexport type { RecentClaim };\n\n/**\n * Per-asset cumulative + delta breakdown. `lifetime` is the connector's\n * cumulative `claimsReceivedTotal` (decimal-string bigint at `assetScale`\n * decimals). `today` / `month` / `year` are deltas computed by Story 47.3's\n * snapshot-reader; until the `deltaComputer` dep is provided, they stub\n * to '0'. Asset-scale interpretation (USD: 6, ETH: 18, sats: 0) is the\n * dashboard's job — the aggregator never collapses to a unit.\n */\nexport interface PerAsset {\n lifetime: string;\n today: string;\n month: string;\n year: string;\n}\n\n/** Per-peer earnings entry in the aggregator output. */\nexport interface NodeEarnings {\n id: string; // == connector peerId\n type: NodeType | 'external'; // PeerTypeResolver attribution\n byAsset: Record<string, PerAsset>; // keyed by assetCode\n /** Max `lastClaimAt` across this peer's assets, or `null` if none. Added in 47.4. */\n lastClaimAt: string | null;\n}\n\n/**\n * Wire-level status for the aggregator response.\n *\n * `'ok'` — `getEarnings()` succeeded; payload reflects connector state.\n * `'connector_unavailable'` — `getEarnings()` threw (network, 503, shape\n * drift); apex + peers are empty. The dashboard renders a banner.\n */\nexport type AggregatedEarningsStatus = 'ok' | 'connector_unavailable';\n\n/** Top-level aggregator output. Extended in 47.4 with dashboard fields. */\nexport interface AggregatedEarnings {\n status: AggregatedEarningsStatus;\n apex: {\n routingFees: Record<string, PerAsset>; // keyed by assetCode\n };\n peers: NodeEarnings[];\n /** Pass-through from connector `recentClaims`. Empty array on connector outage. */\n recentClaims: RecentClaim[];\n /** Sum of `getMetrics().peers[].packetsForwarded` PLUS `packetsLocallyDelivered`\n * (connector v3.7.0+, toon-protocol/connector#73 — counts events that landed\n * via the self-delivery route, where the connector's in-process relay accepts\n * the event locally rather than forwarding to a remote peer). 0 on connector\n * outage or metrics failure. */\n eventsRelayed: number;\n /** From `getMetrics().uptimeSeconds`. 0 on connector outage or metrics failure. */\n uptimeSeconds: number;\n}\n\n/** Resolves TODAY / MONTH / YEAR deltas for a (scope, assetCode) tuple. */\nexport type DeltaComputer = (params: {\n /** Either a connector peerId or the literal `'__apex__'` for routing-fee rows. */\n scope: string;\n assetCode: string;\n /** Current cumulative (matches the lifetime value in the response). */\n currentLifetime: string;\n}) => Promise<{ today: string; month: string; year: string }>;\n\n/**\n * Minimal logger contract; Fastify `request.log` and pino satisfy it.\n * Kept narrow so tests can pass a `{ warn: vi.fn() }` stub.\n */\nexport interface AggregatorLogger {\n warn(obj: object, msg?: string): void;\n}\n\nexport interface AggregateEarningsInput {\n connectorAdmin: ConnectorAdminClient;\n peerTypeResolver: PeerTypeResolver;\n /**\n * Optional delta computer (Story 47.3). When omitted, all PerAsset\n * `today` / `month` / `year` fields stub to '0'. The route layer (47.4)\n * wires the snapshot-backed implementation. A rejection on a single\n * asset stubs that asset's deltas to '0' and emits `logger.warn`; one\n * bad asset never breaks the aggregate.\n */\n deltaComputer?: DeltaComputer;\n /**\n * Optional logger. When provided, `getEarnings()` failures and\n * `deltaComputer` rejections are surfaced via `logger.warn` so ops can\n * distinguish a connector outage from \"no earnings yet.\"\n */\n logger?: AggregatorLogger;\n}\n\nconst STUB_DELTAS = { today: '0', month: '0', year: '0' } as const;\n\nasync function maybeDeltas(\n deltaComputer: DeltaComputer | undefined,\n scope: string,\n assetCode: string,\n currentLifetime: string,\n logger: AggregatorLogger | undefined\n): Promise<{ today: string; month: string; year: string }> {\n if (!deltaComputer) return { ...STUB_DELTAS };\n try {\n return await deltaComputer({ scope, assetCode, currentLifetime });\n } catch (err) {\n logger?.warn(\n { err, scope, assetCode },\n 'aggregator: deltaComputer rejected — stubbing deltas to 0 for this asset'\n );\n return { ...STUB_DELTAS };\n }\n}\n\n/**\n * Aggregate connector-reported earnings into the canonical\n * `{ status, apex, peers }` shape.\n *\n * Failure mode: any throw from `getEarnings()` returns the empty payload\n * with `status: 'connector_unavailable'` — operators see zeros + a banner,\n * not a 5xx. `deltaComputer` opt-in: when provided, today/month/year are\n * computed per-asset via concurrent `Promise.all` fan-out within each peer;\n * when omitted (or when an individual asset's delta rejects), the fields\n * stub to '0'. Story 47.3 ships the computer; Story 47.4 wires it.\n */\nexport async function aggregateEarnings(\n input: AggregateEarningsInput\n): Promise<AggregatedEarnings> {\n let earnings: Awaited<ReturnType<typeof input.connectorAdmin.getEarnings>>;\n try {\n earnings = await input.connectorAdmin.getEarnings();\n } catch (err) {\n input.logger?.warn(\n { err },\n 'aggregator: connectorAdmin.getEarnings failed — returning status=connector_unavailable'\n );\n return {\n status: 'connector_unavailable',\n apex: { routingFees: {} },\n peers: [],\n recentClaims: [],\n eventsRelayed: 0,\n uptimeSeconds: 0,\n };\n }\n\n // Apex routing fees: connector-level fees keyed by assetCode.\n const buildRoutingFees = async (): Promise<Record<string, PerAsset>> => {\n const out: Record<string, PerAsset> = {};\n await Promise.all(\n earnings.connectorFees.map(async (fee) => {\n const deltas = await maybeDeltas(\n input.deltaComputer,\n '__apex__',\n fee.assetCode,\n fee.total,\n input.logger\n );\n out[fee.assetCode] = { lifetime: fee.total, ...deltas };\n })\n );\n return out;\n };\n\n // Per-peer earnings: type attributed via PeerTypeResolver; the resolver\n // returns `'external'` for any peerId not in nodes.yaml, so we never\n // drop peers here.\n const buildPeers = async (): Promise<NodeEarnings[]> =>\n Promise.all(\n earnings.peers.map(async (peer) => {\n const type = input.peerTypeResolver.resolvePeerType(peer.peerId);\n\n // Shape conversion: connector ships `byAsset` as an array\n // (`AssetEarnings[]`); the aggregator output is a\n // `Record<assetCode, PerAsset>`. Delta calls fan out concurrently\n // across this peer's assets.\n const byAsset: Record<string, PerAsset> = {};\n await Promise.all(\n peer.byAsset.map(async (a) => {\n const deltas = await maybeDeltas(\n input.deltaComputer,\n peer.peerId,\n a.assetCode,\n a.claimsReceivedTotal,\n input.logger\n );\n byAsset[a.assetCode] = {\n lifetime: a.claimsReceivedTotal,\n ...deltas,\n };\n })\n );\n\n // lastClaimAt: temporally-latest non-null timestamp across this peer's\n // assets. Uses `Date.parse` rather than raw string compare so the result\n // is stable under ISO-8601 format drift (millisecond-precision variance,\n // timezone-offset suffix). Unparseable strings are skipped — never crash\n // the response on a connector format regression.\n const lastClaimAt = peer.byAsset.reduce<string | null>((acc, a) => {\n const v = a.lastClaimAt;\n if (!v) return acc;\n const vMs = Date.parse(v);\n if (!Number.isFinite(vMs)) return acc;\n if (acc === null) return v;\n const accMs = Date.parse(acc);\n if (!Number.isFinite(accMs)) return v;\n return vMs > accMs ? v : acc;\n }, null);\n\n return { id: peer.peerId, type, byAsset, lastClaimAt };\n })\n );\n\n // Fan out: routing fees + peers + metrics concurrently.\n // Reachable only after getEarnings() succeeded — early return above guards the failure path.\n const metricsPromise = input.connectorAdmin.getMetrics().catch((err) => {\n input.logger?.warn(\n { err },\n 'aggregator: getMetrics failed — eventsRelayed/uptimeSeconds defaulting to 0'\n );\n return null;\n });\n\n const [routingFees, peers, metricsResult] = await Promise.all([\n buildRoutingFees(),\n buildPeers(),\n metricsPromise,\n ]);\n\n // eventsRelayed:\n // - primary: sum of peers[].packetsForwarded + peers[].packetsLocallyDelivered\n // - fallback: aggregate.packetsForwarded + aggregate.packetsLocallyDelivered\n // when peers[] is empty (early-boot case per Task 1.8 — connector returns\n // 200 with peers: [] before any peer has registered, but aggregate counts\n // may already be > 0)\n //\n // packetsLocallyDelivered is connector v3.7.0+ (toon-protocol/connector#73).\n // Older connectors omit the field; `?? 0` coalesces undefined so this still\n // works against pre-3.7.0 deployments.\n //\n // All sources are clamped to non-negative finite integers — schema declares\n // `{ type: 'integer', minimum: 0 }` but Fastify response is a serializer,\n // not a validator, so we clamp at the source.\n const clampInt = (n: number): number =>\n Number.isFinite(n) && n >= 0 ? Math.floor(n) : 0;\n let eventsRelayed = 0;\n if (metricsResult) {\n if (metricsResult.peers.length === 0) {\n eventsRelayed =\n clampInt(metricsResult.aggregate.packetsForwarded) +\n clampInt(metricsResult.aggregate.packetsLocallyDelivered ?? 0);\n } else {\n eventsRelayed = metricsResult.peers.reduce(\n (sum, p) =>\n sum +\n clampInt(p.packetsForwarded) +\n clampInt(p.packetsLocallyDelivered ?? 0),\n 0\n );\n }\n }\n const uptimeSeconds = clampInt(metricsResult?.uptimeSeconds ?? 0);\n\n return {\n status: 'ok',\n apex: { routingFees },\n peers,\n recentClaims: earnings.recentClaims,\n eventsRelayed,\n uptimeSeconds,\n };\n}\n","/**\n * Snapshot reader + `DeltaComputer` factory (Story 47.3).\n *\n * Reads `earnings-snapshots.jsonl` and computes TODAY/MONTH/YEAR deltas vs.\n * UTC boundaries (midnight, 1st-of-month, 1st-of-year). Tolerates malformed\n * lines (skip) and clock-skewed snapshots (filter `ts > now`). Returns `'0'`\n * when no boundary snapshot exists yet.\n *\n * @module\n * @since 47.3\n */\n\nimport { createReadStream } from 'node:fs';\nimport { createInterface } from 'node:readline';\nimport type { DeltaComputer } from './aggregator.js';\nimport type { SnapshotEntry } from './snapshot-writer.js';\n\nexport type { SnapshotEntry };\n\n/** ISO of the most recent UTC midnight <= ref. */\nexport function utcDayBoundary(ref: Date): string {\n const d = new Date(ref);\n d.setUTCHours(0, 0, 0, 0);\n return d.toISOString();\n}\n\n/** ISO of the first instant of the current calendar month in UTC. */\nexport function utcMonthBoundary(ref: Date): string {\n const d = new Date(ref);\n d.setUTCDate(1);\n d.setUTCHours(0, 0, 0, 0);\n return d.toISOString();\n}\n\n/** ISO of the first instant of the current calendar year in UTC. */\nexport function utcYearBoundary(ref: Date): string {\n const d = new Date(ref);\n d.setUTCMonth(0, 1);\n d.setUTCHours(0, 0, 0, 0);\n return d.toISOString();\n}\n\n/**\n * Read all snapshot entries for any (scope, assetCode) pair from the JSONL\n * file, filtered to those with `ts <= nowMs` (exclude future snapshots).\n * Returns a map keyed by `(peerId, assetCode)`; the caller picks the best\n * match per boundary from the array.\n *\n * Reads the file once via readline (streaming), so all three boundaries\n * share a single file scan. On stream I/O error, returns an empty map\n * (caller treats as \"no snapshots\" rather than acting on a partial read).\n */\nasync function readSnapshotMap(\n snapshotPath: string,\n nowMs: number\n): Promise<Map<string, SnapshotEntry[]>> {\n const map = new Map<string, SnapshotEntry[]>();\n\n let stream: ReturnType<typeof createReadStream>;\n try {\n stream = createReadStream(snapshotPath, { encoding: 'utf-8' });\n } catch {\n return map;\n }\n\n let streamFailed = false;\n await new Promise<void>((resolve) => {\n const rl = createInterface({ input: stream, crlfDelay: Infinity });\n\n rl.on('line', (line) => {\n if (!line.trim()) return;\n let entry: unknown;\n try {\n entry = JSON.parse(line);\n } catch {\n return; // malformed — skip\n }\n if (!isSnapshotEntry(entry)) return;\n const tsMs = Date.parse(entry.ts);\n if (!Number.isFinite(tsMs)) return; // unparseable ts — skip\n if (tsMs > nowMs) return; // clock-skewed future snapshot — exclude\n\n const key = `${entry.peerId}\\0${entry.assetCode}`;\n let arr = map.get(key);\n if (!arr) {\n arr = [];\n map.set(key, arr);\n }\n arr.push(entry);\n });\n\n rl.on('close', resolve);\n rl.on('error', () => {\n streamFailed = true;\n resolve();\n });\n stream.on('error', () => {\n streamFailed = true;\n rl.close();\n resolve();\n });\n });\n\n // Surface I/O errors as empty rather than returning a partial map the caller\n // would treat as authoritative.\n if (streamFailed) return new Map();\n return map;\n}\n\n/**\n * Find the entry with the greatest `tsMs <= boundaryMs` from the entries\n * for a (scope, assetCode) pair. Input is not assumed sorted — does a\n * linear scan tracking the running max.\n */\nfunction findBestMatch(\n entries: SnapshotEntry[] | undefined,\n boundaryMs: number\n): SnapshotEntry | null {\n if (!entries || entries.length === 0) return null;\n let best: SnapshotEntry | null = null;\n let bestMs = -Infinity;\n for (const e of entries) {\n const eMs = Date.parse(e.ts);\n if (!Number.isFinite(eMs)) continue;\n if (eMs <= boundaryMs && eMs > bestMs) {\n best = e;\n bestMs = eMs;\n }\n }\n return best;\n}\n\n/**\n * Construct a `DeltaComputer` (Story 47.2's type) backed by the snapshot\n * file at `snapshotPath`. The returned function is the one wired into\n * `aggregateEarnings({ ..., deltaComputer })` by Story 47.4's route.\n *\n * Reads the snapshot file once per DeltaComputer call (single-pass), then\n * resolves all three boundaries (today/month/year) in-memory from the parsed\n * map. No cross-call cache in v1 — see Open Question 6 in story notes.\n */\nexport function createDeltaComputer(opts: {\n snapshotPath: string;\n /** Optional clock injection for tests. Default `() => new Date()`. */\n now?: () => Date;\n}): DeltaComputer {\n return async ({ scope, assetCode, currentLifetime }) => {\n const ref = (opts.now ?? (() => new Date()))();\n const nowMs = ref.getTime();\n if (!Number.isFinite(nowMs)) {\n // Defensive: bad clock injection (NaN time) would throw from toISOString.\n return { today: '0', month: '0', year: '0' };\n }\n\n const dayMs = Date.parse(utcDayBoundary(ref));\n const monthMs = Date.parse(utcMonthBoundary(ref));\n const yearMs = Date.parse(utcYearBoundary(ref));\n\n // Single file scan for all three boundaries.\n const map = await readSnapshotMap(opts.snapshotPath, nowMs);\n const key = `${scope}\\0${assetCode}`;\n const entries = map.get(key);\n\n const daySnap = findBestMatch(entries, dayMs);\n const monthSnap = findBestMatch(entries, monthMs);\n const yearSnap = findBestMatch(entries, yearMs);\n\n let cur: bigint;\n try {\n cur = BigInt(currentLifetime);\n } catch {\n return { today: '0', month: '0', year: '0' };\n }\n\n const subOrZero = (snap: SnapshotEntry | null): string => {\n if (!snap) return '0';\n try {\n const base = BigInt(snap.claimsReceivedTotal);\n // Clamp negative BASELINE (not just negative diff) — a corrupt row\n // with claimsReceivedTotal = '-N' would otherwise INFLATE the delta\n // because `cur - (-Nn) = cur + Nn`.\n if (base < 0n) return '0';\n const diff = cur - base;\n return diff < 0n ? '0' : diff.toString();\n } catch {\n return '0';\n }\n };\n\n return {\n today: subOrZero(daySnap),\n month: subOrZero(monthSnap),\n year: subOrZero(yearSnap),\n };\n };\n}\n\nfunction isSnapshotEntry(v: unknown): v is SnapshotEntry {\n if (typeof v !== 'object' || v === null) return false;\n const e = v as Record<string, unknown>;\n return (\n typeof e['ts'] === 'string' &&\n typeof e['peerId'] === 'string' &&\n typeof e['assetCode'] === 'string' &&\n typeof e['claimsReceivedTotal'] === 'string'\n );\n}\n","/**\n * `PeerTypeResolver` (Story 46.1).\n *\n * The connector is a generic ILP router — it has no concept of\n * `'town' | 'mill' | 'dvm'`. Townhouse owns the type concept entirely\n * via this resolver, which is the single translation layer between\n * connector `peerId` values and operator-meaningful node types.\n *\n * Architectural rule (Epic 46 planning §Architectural Layering):\n * downstream consumers (Epic 47 aggregator, Epic 48 TUI, Epic 49 telemetry)\n * MUST call through this resolver — they never hardcode peer-to-type\n * mappings.\n *\n * The resolver is rebuilt from a `NodesYaml` snapshot — prefer immutable\n * rebuild (re-instantiate) over mutable update for testability.\n */\n\nimport type { NodesYaml } from '../state/nodes-yaml.js';\nimport type { NodeType } from '../docker/types.js';\n\nexport class PeerTypeResolver {\n private readonly map: Map<string, NodeType>;\n\n constructor(yaml: NodesYaml) {\n this.map = new Map(yaml.entries.map((e) => [e.peerId, e.type]));\n }\n\n /**\n * Resolve a connector `peerId` to its operator-declared node type.\n * Returns `'external'` for unknown peerIds (legitimate non-Townhouse\n * peers running through the same connector).\n */\n resolvePeerType(peerId: string): NodeType | 'external' {\n return this.map.get(peerId) ?? 'external';\n }\n}\n","/**\n * API Server Factory.\n *\n * SECURITY: Only binds to loopback address by default (localhost-only for v1).\n * Set TOWNHOUSE_API_ALLOW_REMOTE=1 to override this security boundary.\n */\n\nimport { join, dirname } from 'node:path';\nimport { WebSocket } from 'ws';\nimport { buildFastifyApp } from './build-app.js';\nimport type { ApiServer, ApiDeps } from './types.js';\nimport { SnapshotWriter } from '../earnings/snapshot-writer.js';\nimport { registerNodeRoutes } from './routes/nodes.js';\nimport { registerWalletRoutes } from './routes/wallet.js';\nimport { registerWalletBalancesRoutes } from './routes/wallet-balances.js';\nimport { registerWalletRevealRoutes } from './routes/wallet-reveal.js';\nimport { registerWalletWithdrawRoutes } from './routes/wallet-withdraw.js';\nimport { registerConfigPatchRoutes } from './routes/nodes-patch.js';\nimport { registerNodeLifecycleRoutes } from './routes/nodes-lifecycle.js';\nimport {\n registerMetricsWsRoutes,\n getOpenWebSockets,\n} from './routes/metrics-ws.js';\nimport { registerWizardRoutes } from './routes/wizard.js';\nimport { registerTransportRoutes } from './routes/transport.js';\nimport { registerEarningsRoutes } from './routes/earnings.js';\nimport { registerLogsRoutes } from './routes/logs.js';\n\n/**\n * Create the Fastify API server. Caller MUST supply a `transportProbe` in\n * `deps` (constructed from the config and started if mode === 'ator').\n */\nexport async function createApiServer(deps: ApiDeps): Promise<ApiServer> {\n const { config, logger } = deps;\n\n const snapshotPath = join(\n dirname(deps.configPath),\n 'earnings-snapshots.jsonl'\n );\n const snapshotWriter = new SnapshotWriter({\n connectorAdmin: deps.connectorAdmin,\n snapshotPath,\n logger: logger as { warn(obj: object, msg?: string): void } | undefined,\n });\n\n const app = await buildFastifyApp({\n logger: logger ?? true,\n bindHost: config.api.host ?? '127.0.0.1',\n });\n\n // Register wizard state route in normal mode so the SPA can check state\n registerWizardRoutes(\n app,\n {\n configPath: deps.configPath,\n walletPath: config.wallet.encrypted_path,\n },\n { mode: 'normal' }\n );\n\n // Register transport routes (before nodes routes)\n registerTransportRoutes(app, deps);\n\n // Register all normal routes\n registerNodeRoutes(app, deps);\n registerWalletRoutes(app, deps);\n registerWalletBalancesRoutes(app, deps);\n registerWalletRevealRoutes(app, deps);\n registerWalletWithdrawRoutes(app, deps);\n registerConfigPatchRoutes(app, deps);\n registerNodeLifecycleRoutes(app, deps);\n registerEarningsRoutes(app, deps);\n registerLogsRoutes(app, deps);\n registerMetricsWsRoutes(app, deps);\n\n snapshotWriter.start();\n\n const CLOSE_TIMEOUT_MS = 5000;\n async function close(): Promise<void> {\n try {\n snapshotWriter.stop();\n } catch {\n /* best-effort */\n }\n try {\n deps.transportProbe.stop();\n } catch {\n /* best-effort — must not block shutdown */\n }\n\n const openSockets = getOpenWebSockets();\n for (const socket of openSockets) {\n try {\n if (socket.readyState === WebSocket.OPEN) {\n socket.close(1001, 'server_shutdown');\n }\n } catch {\n // Best-effort\n }\n }\n openSockets.clear();\n\n await Promise.race([\n app.close(),\n new Promise<void>((resolve) => setTimeout(resolve, CLOSE_TIMEOUT_MS)),\n ]);\n }\n\n return { app, close };\n}\n","import createWebSocketStream from './lib/stream.js';\nimport Receiver from './lib/receiver.js';\nimport Sender from './lib/sender.js';\nimport WebSocket from './lib/websocket.js';\nimport WebSocketServer from './lib/websocket-server.js';\n\nexport { createWebSocketStream, Receiver, Sender, WebSocket, WebSocketServer };\nexport default WebSocket;\n","/**\n * Shared Fastify instance builder — loopback validation + CORS + WebSocket + error handler.\n * Consumed by both createApiServer and createWizardApiServer.\n */\n\nimport Fastify, {\n type FastifyInstance,\n type FastifyServerOptions,\n type FastifyBaseLogger,\n} from 'fastify';\nimport cors from '@fastify/cors';\nimport websocket from '@fastify/websocket';\n// Local alias so the bundled output doesn't collide with the tsup banner's own\n// `import { createRequire } from 'module'` (Node ESM rejects duplicate identifier\n// imports from the same module). Retroactively authorized 2026-05-18 code review of\n// Story 49.1 — see Hard Rule #2 exception (d) in the story file. Smaller-radius fix\n// than dropping the tsup banner entirely.\nimport { createRequire as nodeCreateRequire } from 'node:module';\nimport { buildCorsOptions } from './cors.js';\n\nconst STARTED_AT = new Date().toISOString();\n// Resolve `package.json` defensively. At runtime `import.meta.url` points at the\n// bundled output (e.g. `dist/cli.js`), where `../package.json` resolves to\n// `packages/townhouse/package.json`. If tsup ever chunks this module deeper\n// (e.g. `dist/api/build-app.js`), `../../package.json` is needed. Try the\n// expected path first, then the deeper-chunk fallback. Pass 2 code review\n// 2026-05-18 hardening per P37 — keeps the file working across bundle layouts\n// instead of silently MODULE_NOT_FOUND-ing at boot under a future tsup config change.\nconst _localRequire = nodeCreateRequire(import.meta.url);\nfunction _loadPackageJson(): { version: string } {\n for (const rel of ['../package.json', '../../package.json']) {\n try {\n return _localRequire(rel) as { version: string };\n } catch {\n // try next candidate\n }\n }\n throw new Error(\n \"build-app.ts: could not resolve package.json from '../package.json' or '../../package.json'. \" +\n 'Bundle layout may have changed — update the resolution ladder.'\n );\n}\nconst _pkgVersion: string = _loadPackageJson()['version'];\n\n/** Allowed loopback hosts */\nexport const LOOPBACK_HOSTS = ['127.0.0.1', '::1', 'localhost'];\n\nexport interface FastifyBuildOptions {\n logger?: FastifyBaseLogger | boolean;\n /** Validated bind host — must be a loopback address in wizard mode */\n bindHost?: string;\n /** When true, throws if bindHost is non-loopback regardless of env var */\n requireLoopback?: boolean;\n}\n\n/**\n * Build a Fastify instance with shared middleware: CORS, WebSocket, and error handler.\n * Does NOT register any routes.\n *\n * SECURITY: Pino logger is configured with `redact` paths covering wizard-mode\n * secrets (mnemonic, password, password_confirm) so request-shape logging cannot\n * leak credentials even if the log level is bumped.\n */\nexport async function buildFastifyApp(\n opts: FastifyBuildOptions = {}\n): Promise<FastifyInstance> {\n const bindHost = opts.bindHost ?? '127.0.0.1';\n\n if (!LOOPBACK_HOSTS.includes(bindHost)) {\n if (opts.requireLoopback) {\n throw new Error(\n 'The wizard refuses remote bind for security. Edit ~/.townhouse/config.yaml after setup if you need remote API access.'\n );\n }\n if (process.env['TOWNHOUSE_API_ALLOW_REMOTE'] !== '1') {\n throw new Error(\n 'Townhouse API refuses to bind to non-loopback host without TOWNHOUSE_API_ALLOW_REMOTE=1'\n );\n }\n }\n\n // Build logger options. When the caller passes a boolean (the common path) we\n // attach Pino redact paths. A custom FastifyBaseLogger instance is opaque so\n // we trust the caller has already configured redaction.\n const loggerOpt = opts.logger ?? true;\n const logger: FastifyServerOptions['logger'] =\n typeof loggerOpt === 'boolean' && loggerOpt\n ? {\n redact: {\n paths: [\n 'req.body.mnemonic',\n 'req.body.password',\n 'req.body.password_confirm',\n 'res.body.mnemonic',\n 'mnemonic',\n 'password',\n 'password_confirm',\n // Story 46.2: secret-bearing fields introduced by node lifecycle\n // routes. These never appear in request/response bodies (they go\n // to subprocess env), but defense-in-depth covers them at every\n // path Pino might log a stray object (error objects, debug dumps).\n 'nostrSecretKey',\n 'evmPrivateKey',\n 'TOWN_SECRET_KEY',\n 'MILL_SECRET_KEY',\n 'DVM_SECRET_KEY',\n 'TOWN_SETTLEMENT_PRIVATE_KEY',\n 'MILL_SETTLEMENT_PRIVATE_KEY',\n 'DVM_SETTLEMENT_PRIVATE_KEY',\n 'MILL_MNEMONIC',\n 'TOWNHOUSE_WALLET_PASSWORD',\n ],\n censor: '[REDACTED]',\n },\n }\n : (loggerOpt as FastifyServerOptions['logger']);\n\n const app = Fastify({\n logger,\n bodyLimit: 16 * 1024,\n // SECURITY/CONTRACT: schemas with `additionalProperties: false` must REJECT\n // unknown keys with a 400, not silently strip them. Operators shipping a typo\n // should see a loud failure, not a no-op success.\n ajv: {\n customOptions: {\n removeAdditional: false,\n },\n },\n } as FastifyServerOptions);\n\n app.setErrorHandler((error, _request, reply) => {\n app.log.error(error);\n const err = error as {\n statusCode?: number;\n code?: string;\n message?: string;\n validation?: unknown;\n };\n const isCorsRejection = err.message === 'Origin not allowed';\n const statusCode = isCorsRejection ? 403 : (err.statusCode ?? 500);\n\n // SECURITY: 5xx responses always sanitized — err.message can carry payload\n // bits (validation context, file paths, partial inputs) and must not reach\n // the client. Only known-safe error classes (CORS rejection, Fastify\n // validation 4xx) get the original message.\n let message: string;\n if (isCorsRejection) {\n message = err.message ?? 'Origin not allowed';\n } else if (statusCode >= 400 && statusCode < 500 && err.validation) {\n message = err.message ?? 'Bad request';\n } else if (statusCode >= 400 && statusCode < 500) {\n message = err.message ?? 'Bad request';\n } else {\n message = 'Internal server error';\n }\n\n reply.status(statusCode).send({\n error: isCorsRejection\n ? 'origin_not_allowed'\n : (err.code ?? 'internal_error'),\n message,\n });\n });\n\n await app.register(cors, buildCorsOptions());\n await app.register(websocket);\n\n app.get('/health', async () => ({\n status: 'healthy' as const,\n uptime: Math.floor(process.uptime()),\n startedAt: STARTED_AT,\n version: _pkgVersion,\n }));\n\n return app;\n}\n","/**\n * CORS configuration for Townhouse API.\n *\n * SECURITY: Only allows localhost origins (loopback only for v1).\n */\n\nimport type { FastifyCorsOptions } from '@fastify/cors';\n\n/**\n * Allowed localhost origins for CORS.\n */\nconst ALLOWED_ORIGINS = ['localhost', '127.0.0.1', '[::1]', '::1'];\n\n/**\n * Check if an origin host is allowed.\n */\nfunction isOriginAllowed(origin: string | undefined): boolean {\n // No origin header = curl, native fetch from file:// page = allowed\n if (!origin) {\n return true;\n }\n\n try {\n const url = new URL(origin);\n return ALLOWED_ORIGINS.includes(url.hostname);\n } catch {\n // Invalid origin = reject\n return false;\n }\n}\n\n/**\n * Build CORS options for Fastify.\n */\nexport function buildCorsOptions(): FastifyCorsOptions {\n return {\n origin: (origin, callback) => {\n // origin is the value of the Origin header, or undefined if not present\n\n if (isOriginAllowed(origin)) {\n callback(null, true);\n } else {\n // Reject with 403; Fastify will handle the response\n callback(new Error('Origin not allowed'), false);\n }\n },\n methods: ['GET', 'PATCH', 'OPTIONS', 'HEAD'],\n credentials: false,\n };\n}\n","/**\n * Node routes: GET /nodes, GET /nodes/:type, GET /nodes/:type/packets/timeseries,\n * GET /nodes/:type/bandwidth,\n * GET /nodes/:nodeId/health, GET /nodes/:nodeId/swaps/recent,\n * GET /nodes/:nodeId/deposit-addresses.\n *\n * `:type` routes are scoped per node kind ('town' | 'mill' | 'dvm').\n * `:nodeId` routes are scoped per running instance and accept either a\n * container name (e.g. 'dev-mill-01') or a type-level placeholder\n * ('mill' when no instance has started yet).\n */\n\nimport type { FastifyInstance } from 'fastify';\nimport type {\n ApiDeps,\n NodeInfo,\n MetricsPayload,\n BandwidthPayload,\n PacketTimeseriesPayload,\n TimeseriesBucket,\n NodeHealthPayload,\n MillSwapsRecentPayload,\n JobsRecentPayload,\n DepositAddressesPayload,\n DvmHealthResponse,\n} from '../types.js';\nimport type { NodeType, NodeState } from '../types.js';\nimport { CONTAINER_PREFIX } from '../../constants.js';\n\n/** Cache entry for health proxy responses */\ninterface HealthCacheEntry {\n payload: NodeHealthPayload;\n cachedAt: number;\n}\n\nconst HEALTH_CACHE_TTL_MS = 2_000;\n\n/**\n * Feature-detect the event `kind` from a connector packet log entry. The\n * connector contract's `PacketLogEntry` does not yet expose a `kind` field\n * (see `@toon-protocol/sdk` CONNECTOR_MIGRATION.md \"Townhouse-Side\n * Contract\"); when absent, packets group under bucket 0 (\"unattributed\")\n * so operators can see the shortfall instead of silent data loss.\n */\nexport function extractKindFromPacketEntry(entry: unknown): number {\n const kind = (entry as { kind?: unknown } | null)?.kind;\n return typeof kind === 'number' && Number.isFinite(kind) ? kind : 0;\n}\n\n/** Map raw Docker container state to the API's NodeState enum. */\nfunction mapDockerState(raw: string | undefined): NodeState {\n switch (raw) {\n case 'running':\n return 'running';\n case 'exited':\n case 'stopped':\n case 'created':\n case 'paused':\n return 'stopped';\n case undefined:\n return 'not-created';\n default:\n // 'restarting', 'removing', 'dead' → error\n return 'error';\n }\n}\n\n/** Build the mutable fee-field subset of a node's config, typed per node kind. */\nfunction pickMutableFees(\n type: NodeType,\n nodeConfig: {\n enabled: boolean;\n feePerEvent?: number;\n feeBasisPoints?: number;\n feePerJob?: number;\n }\n): {\n enabled: boolean;\n feePerEvent?: number;\n feeBasisPoints?: number;\n feePerJob?: number;\n} {\n switch (type) {\n case 'town':\n return {\n enabled: nodeConfig.enabled,\n feePerEvent: nodeConfig.feePerEvent,\n };\n case 'mill':\n return {\n enabled: nodeConfig.enabled,\n feeBasisPoints: nodeConfig.feeBasisPoints,\n };\n case 'dvm':\n return { enabled: nodeConfig.enabled, feePerJob: nodeConfig.feePerJob };\n }\n}\n\n/** Guard an ISO-8601 StartedAt and return uptime in seconds, or null if implausible. */\nfunction computeUptimeSeconds(\n startedAt: string | undefined,\n state: NodeState\n): number | null {\n if (state !== 'running' || !startedAt) return null;\n const started = new Date(startedAt).getTime();\n if (isNaN(started) || started <= 0) return null;\n const now = Date.now();\n const ONE_YEAR_MS = 365 * 24 * 60 * 60 * 1000;\n if (started > now || now - started >= ONE_YEAR_MS) return null;\n return Math.floor((now - started) / 1000);\n}\n\n/**\n * Register node routes.\n */\nexport function registerNodeRoutes(app: FastifyInstance, deps: ApiDeps): void {\n const healthCache = new Map<string, HealthCacheEntry>();\n // GET /nodes - list all node types (multi-instance aware)\n app.get('/nodes', async (_request, _reply) => {\n const status = await deps.orchestrator.status();\n const nodes: NodeInfo[] = [];\n\n for (const type of ['town', 'mill', 'dvm'] as const) {\n const nodeConfig = deps.config.nodes[type];\n if (!nodeConfig) continue;\n\n const instances = status.filter((s) => s.type === type);\n\n if (instances.length === 0) {\n nodes.push({\n id: type,\n type,\n enabled: nodeConfig.enabled,\n state: 'not-created',\n uptimeSeconds: null,\n image: nodeConfig.image ?? `toon:${type}`,\n });\n continue;\n }\n\n for (const entry of instances) {\n const state = mapDockerState(entry.state);\n const uptimeSeconds = computeUptimeSeconds(entry.startedAt, state);\n nodes.push({\n id: entry.name, // \"town\" for single-instance, \"dev-town-01\" for multi\n type,\n enabled: nodeConfig.enabled,\n state,\n uptimeSeconds,\n image: nodeConfig.image ?? `toon:${type}`,\n });\n }\n }\n\n return nodes;\n });\n\n // GET /nodes/:type - get single node detail\n app.get<{ Params: { type: string } }>(\n '/nodes/:type',\n async (request, reply) => {\n const { type } = request.params;\n\n // Validate type - return 404 for unknown types (for AC #9)\n if (type !== 'town' && type !== 'mill' && type !== 'dvm') {\n return reply.status(404).send({\n error: 'unknown_node_type',\n type,\n });\n }\n\n const nodeConfig = deps.config.nodes[type as NodeType];\n if (!nodeConfig) {\n return reply.status(404).send({\n error: 'unknown_node_type',\n type,\n });\n }\n\n // Get status from orchestrator (use first matching instance for single-type endpoint)\n const status = await deps.orchestrator.status();\n const statusEntry = status.find((s) => s.type === type);\n const state: NodeState = statusEntry\n ? mapDockerState(statusEntry.state)\n : 'not-created';\n const uptimeSeconds = computeUptimeSeconds(statusEntry?.startedAt, state);\n\n // Get metrics from connector admin (degraded state on failure).\n // ConnectorAdminClient.getMetrics() returns the connector's\n // /admin/metrics.json shape verbatim — aggregate counters live under\n // `aggregate`, per-peer entries under `peers`. Narrowed MetricsPayload\n // here surfaces the aggregate rollup; per-peer breakdowns are available\n // to consumers that need them via metricsRes.peers.\n let metrics: MetricsPayload | null = null;\n try {\n const metricsRes = await deps.connectorAdmin.getMetrics();\n if (metricsRes) {\n metrics = {\n packetsForwarded: metricsRes.aggregate.packetsForwarded,\n packetsRejected: metricsRes.aggregate.packetsRejected,\n bytesSent: metricsRes.aggregate.bytesSent,\n attribution: 'aggregate',\n available: true,\n };\n }\n } catch {\n // Connector down - return degraded state\n metrics = {\n packetsForwarded: 0,\n packetsRejected: 0,\n bytesSent: 0,\n attribution: 'aggregate',\n available: false,\n };\n }\n\n // Build config subset (mutable fields) — per-type, typed safely\n const config = pickMutableFees(type as NodeType, nodeConfig);\n\n return {\n id: type,\n type,\n enabled: nodeConfig.enabled,\n state,\n uptimeSeconds,\n image: nodeConfig.image ?? `toon:${type}`,\n config,\n metrics,\n };\n }\n );\n\n // ── GET /nodes/:type/packets/timeseries ────────────────────────────────────\n\n app.get<{\n Params: { type: string };\n Querystring: { bucket?: string; since?: string };\n }>('/nodes/:type/packets/timeseries', async (request, reply) => {\n const { type } = request.params;\n const { bucket = 'hour', since } = request.query;\n\n if (type !== 'town' && type !== 'mill' && type !== 'dvm') {\n return reply.status(404).send({ error: 'unknown_node_type', type });\n }\n\n const supportedBuckets = ['hour', 'day', 'minute'];\n if (!supportedBuckets.includes(bucket)) {\n return reply.status(400).send({\n error: 'unsupported_bucket',\n message: `bucket must be one of: ${supportedBuckets.join(', ')}`,\n });\n }\n\n const sinceMs = since\n ? new Date(since).getTime()\n : Date.now() - 24 * 60 * 60 * 1000;\n if (isNaN(sinceMs)) {\n return reply\n .status(400)\n .send({ error: 'invalid_since', message: 'since must be ISO 8601' });\n }\n\n try {\n // Resolve the node's ILP address from the connector's peer roster so the\n // packet log is scoped to this node type only (AC-3 / Task 2.3).\n // Falls through without filter if peers are unavailable.\n let ilpAddress: string | undefined;\n try {\n const peers = await deps.connectorAdmin.getPeers();\n const peer = peers.find((p) => p.id === type);\n ilpAddress = peer?.ilpAddresses[0];\n } catch {\n // Connector peer list unavailable — return unfiltered data as fallback\n }\n\n const packets = await deps.connectorAdmin.getPacketLog({\n ilpAddress,\n since: sinceMs,\n limit: 10_000,\n });\n\n // Bucket the packet log by time\n const bucketMs =\n bucket === 'minute'\n ? 60_000\n : bucket === 'day'\n ? 24 * 60 * 60_000\n : /* hour */ 60 * 60_000;\n\n const countsMap = new Map<number, number>();\n for (const entry of packets) {\n const bucketTs = Math.floor(entry.ts / bucketMs) * bucketMs;\n countsMap.set(bucketTs, (countsMap.get(bucketTs) ?? 0) + 1);\n }\n\n const buckets: TimeseriesBucket[] = Array.from(countsMap.entries())\n .sort(([a], [b]) => a - b)\n .map(([ts, count]) => ({ ts, count }));\n\n const payload: PacketTimeseriesPayload = { buckets };\n return payload;\n } catch (error: unknown) {\n const err = error as NodeJS.ErrnoException;\n if (err.code === 'ConnectorEndpointNotFound') {\n return reply.status(503).send({\n error: 'connector_endpoint_not_found',\n message:\n 'Connector image does not expose GET /packets. See CONNECTOR_MIGRATION.md §getPacketLog.',\n });\n }\n // Connector down or other error\n return reply.status(503).send({\n error: 'connector_unavailable',\n message: 'Could not reach connector admin API',\n });\n }\n });\n\n // ── GET /nodes/:type/bandwidth ─────────────────────────────────────────────\n\n app.get<{ Params: { type: string } }>(\n '/nodes/:type/bandwidth',\n async (request, reply) => {\n const { type } = request.params;\n\n if (type !== 'town' && type !== 'mill' && type !== 'dvm') {\n return reply.status(404).send({ error: 'unknown_node_type', type });\n }\n\n const containerName = `${CONTAINER_PREFIX}${type}`;\n const stats = await deps.orchestrator.getContainerStats(containerName);\n\n if (stats === null) {\n return null;\n }\n\n const payload: BandwidthPayload = {\n bytesIn: stats.bytesIn,\n bytesOut: stats.bytesOut,\n sampleAt: stats.sampleAt,\n };\n return payload;\n }\n );\n\n // ── per-instance node resolution helper ───────────────────────────────────\n\n async function resolveNodeId(\n nodeId: string\n ): Promise<{ type: NodeType; instanceName: string } | null> {\n const status = await deps.orchestrator.status();\n const instance = status.find((s) => s.name === nodeId);\n if (instance) {\n return { type: instance.type as NodeType, instanceName: instance.name };\n }\n if (nodeId === 'town' || nodeId === 'mill' || nodeId === 'dvm') {\n return { type: nodeId, instanceName: nodeId };\n }\n return null;\n }\n\n // ── GET /nodes/:nodeId/health ──────────────────────────────────────────────\n\n app.get<{ Params: { nodeId: string } }>(\n '/nodes/:nodeId/health',\n async (request, reply) => {\n const { nodeId } = request.params;\n\n // Skip the type-level routes that share the prefix.\n if (nodeId === 'packets' || nodeId === 'bandwidth') {\n return reply.status(404).send({ error: 'unknown_node', nodeId });\n }\n\n const resolved = await resolveNodeId(nodeId);\n if (!resolved) {\n return reply.status(404).send({ error: 'unknown_node', nodeId });\n }\n\n const cacheKey = resolved.instanceName;\n const cached = healthCache.get(cacheKey);\n if (cached && Date.now() - cached.cachedAt < HEALTH_CACHE_TTL_MS) {\n return cached.payload;\n }\n\n try {\n const endpoint = await deps.orchestrator.getNodeHealthEndpoint(\n resolved.instanceName,\n resolved.type\n );\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), 3_000);\n let res: Response;\n try {\n // nosemgrep: javascript.lang.security.detect-insecure-http -- Docker-internal, TLS unnecessary\n res = await fetch(`${endpoint}/health`, {\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timeout);\n }\n if (!res.ok) {\n return reply.status(503).send({ error: 'node_unreachable' });\n }\n const payload = (await res.json()) as NodeHealthPayload;\n healthCache.set(cacheKey, { payload, cachedAt: Date.now() });\n return payload;\n } catch {\n return reply.status(503).send({ error: 'node_unreachable' });\n }\n }\n );\n\n // ── GET /nodes/:nodeId/swaps/recent ───────────────────────────────────────\n\n app.get<{\n Params: { nodeId: string };\n Querystring: { windowSec?: string };\n }>('/nodes/:nodeId/swaps/recent', async (request, reply) => {\n const { nodeId } = request.params;\n\n const resolved = await resolveNodeId(nodeId);\n if (!resolved) {\n return reply.status(404).send({ error: 'unknown_node', nodeId });\n }\n if (resolved.type !== 'mill') {\n return reply.status(404).send({\n error: 'swaps_only_for_mill',\n message: 'swaps/recent is only available for mill instances',\n });\n }\n\n const rawWindowSec = request.query.windowSec;\n // Reject scientific notation and non-decimal strings before parseInt\n // silently truncates them ('1e10' → 1).\n if (rawWindowSec !== undefined && !/^\\d+$/.test(rawWindowSec)) {\n return reply.status(400).send({\n error: 'invalid_window_sec',\n message: 'windowSec must be a non-negative integer (1–3600)',\n });\n }\n const windowSec =\n rawWindowSec !== undefined ? parseInt(rawWindowSec, 10) : 300;\n if (isNaN(windowSec) || windowSec < 1 || windowSec > 3600) {\n return reply.status(400).send({\n error: 'invalid_window_sec',\n message: 'windowSec must be 1–3600',\n });\n }\n\n // Resolve this instance's ILP address from connector peers.\n // If the peer is not registered yet, return an empty result rather than\n // an unfiltered packet log (which would include packets from every peer).\n let ilpAddress: string | undefined;\n try {\n const peers = await deps.connectorAdmin.getPeers();\n const peer = peers.find((p) => p.id === resolved.instanceName);\n ilpAddress = peer?.ilpAddresses[0];\n } catch {\n // Connector unavailable — handled in the next try block.\n }\n\n if (!ilpAddress) {\n const empty: MillSwapsRecentPayload = {\n count: 0,\n volume: '0',\n byPair: [],\n };\n return empty;\n }\n\n try {\n const packets = await deps.connectorAdmin.getPacketLog({\n ilpAddress,\n since: Date.now() - windowSec * 1_000,\n limit: 10_000,\n });\n\n const byPairMap = new Map<string, { count: number; volume: bigint }>();\n let totalVolume = 0n;\n\n for (const entry of packets) {\n totalVolume += BigInt(entry.amount ?? 0);\n const pairKey = `${entry.ilpAddressFrom ?? '?'}→${entry.ilpAddressTo ?? '?'}`;\n const existing = byPairMap.get(pairKey) ?? { count: 0, volume: 0n };\n byPairMap.set(pairKey, {\n count: existing.count + 1,\n volume: existing.volume + BigInt(entry.amount ?? 0),\n });\n }\n\n const byPair = Array.from(byPairMap.entries()).map(([pair, data]) => ({\n pair,\n count: data.count,\n volume: data.volume.toString(),\n }));\n\n const payload: MillSwapsRecentPayload = {\n count: packets.length,\n volume: totalVolume.toString(),\n byPair,\n };\n return payload;\n } catch (error: unknown) {\n const err = error as NodeJS.ErrnoException;\n if (err.code === 'ConnectorEndpointNotFound') {\n return reply.status(503).send({\n error: 'connector_endpoint_not_found',\n message:\n 'Connector image does not expose GET /packets. See CONNECTOR_MIGRATION.md.',\n });\n }\n return reply.status(503).send({ error: 'connector_unavailable' });\n }\n });\n\n // ── GET /nodes/:nodeId/jobs/recent ────────────────────────────────────────\n // Returns DVM job throughput for the requested window (default 300 s).\n // byKind is sourced from the DVM container's health endpoint (counter-shim,\n // the canonical source since the connector PacketLogEntry has no kind field).\n // volume is sourced from the connector packet log (amount sum).\n\n app.get<{\n Params: { nodeId: string };\n Querystring: { windowSec?: string };\n }>('/nodes/:nodeId/jobs/recent', async (request, reply) => {\n const { nodeId } = request.params;\n\n const resolved = await resolveNodeId(nodeId);\n if (!resolved) {\n return reply.status(404).send({ error: 'unknown_node', nodeId });\n }\n if (resolved.type !== 'dvm') {\n return reply.status(404).send({\n error: 'jobs_only_for_dvm',\n message: 'jobs/recent is only available for dvm instances',\n });\n }\n\n const rawWindowSec = request.query.windowSec;\n if (rawWindowSec !== undefined && !/^\\d+$/.test(rawWindowSec)) {\n return reply.status(400).send({\n error: 'invalid_window_sec',\n message: 'windowSec must be a non-negative integer (1–300)',\n });\n }\n const requestedWindowSec =\n rawWindowSec !== undefined ? parseInt(rawWindowSec, 10) : 300;\n // The DVM in-memory counter is fixed at a 5-minute (300 s) window,\n // so the byKind/byStatus/total fields can only honestly report the\n // last 300 s. Reject windows outside [1, 300] rather than silently\n // mixing windows across response fields.\n if (requestedWindowSec < 1 || requestedWindowSec > 300) {\n return reply.status(400).send({\n error: 'invalid_window_sec',\n message:\n 'windowSec must be 1–300 (DVM counter window is fixed at 5 min)',\n });\n }\n const windowSec = requestedWindowSec;\n\n // Resolve ILP address from connector peers\n let ilpAddress: string | undefined;\n let connectorDown = false;\n try {\n const peers = await deps.connectorAdmin.getPeers();\n const peer = peers.find((p) => p.id === resolved.instanceName);\n ilpAddress = peer?.ilpAddresses[0];\n } catch {\n connectorDown = true;\n }\n\n if (connectorDown) {\n return reply.status(503).send({ error: 'connector_unavailable' });\n }\n\n // Fetch DVM health for byKind and byStatus (the canonical counter source)\n let dvmHealth: DvmHealthResponse | null = null;\n try {\n const cached = healthCache.get(resolved.instanceName);\n if (cached && Date.now() - cached.cachedAt < HEALTH_CACHE_TTL_MS) {\n dvmHealth = cached.payload as DvmHealthResponse;\n } else {\n const endpoint = await deps.orchestrator.getNodeHealthEndpoint(\n resolved.instanceName,\n 'dvm'\n );\n // 3 s timeout mirrors the parallel /nodes/:nodeId/health route.\n // A hung DVM container otherwise blocks Fastify indefinitely.\n const healthController = new AbortController();\n const healthTimeout = setTimeout(() => healthController.abort(), 3_000);\n let healthRes: Response;\n try {\n // nosemgrep: javascript.lang.security.detect-insecure-http -- Docker-internal, TLS unnecessary\n healthRes = await fetch(`${endpoint}/health`, {\n signal: healthController.signal,\n });\n } finally {\n clearTimeout(healthTimeout);\n }\n if (healthRes.ok) {\n dvmHealth = (await healthRes.json()) as DvmHealthResponse;\n healthCache.set(resolved.instanceName, {\n payload: dvmHealth,\n cachedAt: Date.now(),\n });\n }\n }\n } catch {\n // Health fetch failed — degrade gracefully\n }\n\n const byStatus = dvmHealth?.jobsRecent?.byStatus ?? {\n processing: 0,\n success: 0,\n error: 0,\n partial: 0,\n };\n\n // Build byKind from DVM health counter (canonical) — group into JobsByKindEntry\n const byKindFromHealth = dvmHealth?.jobsRecent?.byKind ?? [];\n const byKindMap = new Map<number, { count: number; volume: bigint }>();\n for (const entry of byKindFromHealth) {\n byKindMap.set(entry.kind, { count: entry.count, volume: 0n });\n }\n\n if (!ilpAddress) {\n // ILP address unknown — return zero-volume result with health-sourced counters\n const payload: JobsRecentPayload = {\n count: dvmHealth?.jobsRecent?.total ?? 0,\n volume: '0',\n byKind: Array.from(byKindMap.entries()).map(([kind, d]) => ({\n kind,\n count: d.count,\n volume: '0',\n })),\n byStatus,\n };\n return payload;\n }\n\n try {\n const packets = await deps.connectorAdmin.getPacketLog({\n ilpAddress,\n since: Date.now() - windowSec * 1_000,\n limit: 10_000,\n });\n\n let totalVolume = 0n;\n for (const entry of packets) {\n totalVolume += BigInt(entry.amount ?? 0);\n // kind field: feature-detect — if absent, group under bucket 0 (unattributed)\n const kind = extractKindFromPacketEntry(entry);\n const existing = byKindMap.get(kind) ?? { count: 0, volume: 0n };\n // Only increment count from packet log if no DVM health data available;\n // prefer DVM counter for count, use packet log for volume only.\n byKindMap.set(kind, {\n count:\n byKindFromHealth.length > 0 ? existing.count : existing.count + 1,\n volume: existing.volume + BigInt(entry.amount ?? 0),\n });\n }\n\n const byKind = Array.from(byKindMap.entries()).map(([kind, d]) => ({\n kind,\n count: d.count,\n volume: d.volume.toString(),\n }));\n\n const payload: JobsRecentPayload = {\n count: dvmHealth?.jobsRecent?.total ?? packets.length,\n volume: totalVolume.toString(),\n byKind,\n byStatus,\n };\n return payload;\n } catch (error: unknown) {\n const err = error as NodeJS.ErrnoException;\n if (err.code === 'ConnectorEndpointNotFound') {\n return reply.status(503).send({\n error: 'connector_endpoint_not_found',\n message:\n 'Connector image does not expose GET /packets. See CONNECTOR_MIGRATION.md.',\n });\n }\n return reply.status(503).send({ error: 'connector_unavailable' });\n }\n });\n\n // ── GET /nodes/:nodeId/deposit-addresses ──────────────────────────────────\n\n app.get<{ Params: { nodeId: string } }>(\n '/nodes/:nodeId/deposit-addresses',\n async (request, reply) => {\n const { nodeId } = request.params;\n\n const resolved = await resolveNodeId(nodeId);\n if (!resolved) {\n return reply.status(404).send({ error: 'unknown_node', nodeId });\n }\n\n let keys;\n try {\n keys = deps.wallet.getNodeKeys(resolved.type);\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n if (/not initialized/i.test(msg)) {\n return reply.status(503).send({ error: 'wallet_not_initialized' });\n }\n return reply.status(500).send({ error: 'wallet_error', message: msg });\n }\n\n const chains: DepositAddressesPayload['chains'] = [\n { family: 'evm', address: keys.evmAddress },\n ];\n\n if (resolved.type === 'mill') {\n if (keys.solanaAddress) {\n chains.push({ family: 'solana', address: keys.solanaAddress });\n }\n if (keys.minaAddress) {\n chains.push({ family: 'mina', address: keys.minaAddress });\n }\n }\n\n const payload: DepositAddressesPayload = { chains };\n return payload;\n }\n );\n}\n","/**\n * Wallet routes: GET /wallet\n */\n\nimport type { FastifyInstance } from 'fastify';\nimport type { ApiDeps } from '../types.js';\n\n/**\n * Register wallet routes.\n */\nexport function registerWalletRoutes(\n app: FastifyInstance,\n deps: ApiDeps\n): void {\n // GET /wallet - list all wallet keys (address-only, no secrets)\n app.get('/wallet', async (_request, _reply) => {\n // Returns NodeKeyInfo[] which has NO secrets\n const keys = deps.wallet.listKeys();\n return { keys };\n });\n}\n","/**\n * EVM JSON-RPC helpers for balance queries.\n * Uses native fetch + AbortController (no external deps).\n */\n\nconst TIMEOUT_MS = 3_000;\nconst HEX_ADDR_RE = /^0x[0-9a-fA-F]{40}$/;\n\n/** JSON-RPC request helper. The local 3s timeout always fires regardless of\n * whether the caller passes their own signal. */\nasync function rpcCall(\n rpcUrl: string,\n method: string,\n params: unknown[],\n signal?: AbortSignal\n): Promise<unknown> {\n const controller = new AbortController();\n const timeout = setTimeout(\n () => controller.abort(new Error('rpc_timeout')),\n TIMEOUT_MS\n );\n // Forward caller cancellation into our controller so the local timeout-driven\n // abort and the caller-driven abort both reach the underlying fetch.\n const onCallerAbort = signal\n ? () =>\n controller.abort(\n (signal.reason as Error | undefined) ?? new Error('aborted')\n )\n : null;\n if (signal && onCallerAbort) {\n if (signal.aborted) onCallerAbort();\n else signal.addEventListener('abort', onCallerAbort, { once: true });\n }\n\n try {\n const res = await fetch(rpcUrl, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ jsonrpc: '2.0', id: 1, method, params }),\n signal: controller.signal,\n });\n if (!res.ok)\n throw new Error(`EVM RPC ${method} failed: HTTP ${res.status}`);\n const data = (await res.json()) as {\n result?: string;\n error?: { message: string };\n };\n if (data.error)\n throw new Error(`EVM RPC ${method} error: ${data.error.message}`);\n return data.result;\n } finally {\n clearTimeout(timeout);\n if (signal && onCallerAbort) {\n signal.removeEventListener('abort', onCallerAbort);\n }\n }\n}\n\n/**\n * Get native ETH balance (wei) for an address.\n * Returns balance as decimal string.\n */\nexport async function getEvmBalance(\n rpcUrl: string,\n address: string\n): Promise<string> {\n if (!HEX_ADDR_RE.test(address)) {\n throw new Error(`getEvmBalance: invalid address shape (${address})`);\n }\n const hex = (await rpcCall(rpcUrl, 'eth_getBalance', [\n address,\n 'latest',\n ])) as string;\n return BigInt(hex).toString();\n}\n\n/**\n * Get ERC-20 token balance using balanceOf(address) selector.\n * Returns balance as decimal string.\n */\nexport async function getErc20Balance(\n rpcUrl: string,\n contractAddress: string,\n holderAddress: string\n): Promise<string> {\n if (!HEX_ADDR_RE.test(contractAddress)) {\n throw new Error(\n `getErc20Balance: invalid contract address (${contractAddress})`\n );\n }\n if (!HEX_ADDR_RE.test(holderAddress)) {\n throw new Error(\n `getErc20Balance: invalid holder address (${holderAddress})`\n );\n }\n // ABI-encode balanceOf(address): selector 0x70a08231 + padded address\n const paddedAddr = holderAddress\n .toLowerCase()\n .replace(/^0x/, '')\n .padStart(64, '0');\n const data = `0x70a08231${paddedAddr}`;\n\n const hex = (await rpcCall(rpcUrl, 'eth_call', [\n { to: contractAddress, data },\n 'latest',\n ])) as string;\n\n if (!hex || hex === '0x') return '0';\n return BigInt(hex).toString();\n}\n","/**\n * Solana JSON-RPC helpers for balance queries.\n * Uses native fetch + AbortController (no external deps).\n */\n\nconst TIMEOUT_MS = 3_000;\n\n/**\n * Get native SOL balance (lamports) for a base58 address.\n * Returns balance as decimal string.\n */\nexport async function getSolanaBalance(\n rpcUrl: string,\n address: string\n): Promise<string> {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), TIMEOUT_MS);\n try {\n const res = await fetch(rpcUrl, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n jsonrpc: '2.0',\n id: 1,\n method: 'getBalance',\n params: [address],\n }),\n signal: controller.signal,\n });\n if (!res.ok)\n throw new Error(`Solana RPC getBalance failed: HTTP ${res.status}`);\n const data = (await res.json()) as {\n result?: { value?: number };\n error?: { message: string };\n };\n if (data.error) throw new Error(`Solana RPC error: ${data.error.message}`);\n const lamports = data.result?.value ?? 0;\n return BigInt(lamports).toString();\n } finally {\n clearTimeout(timeout);\n }\n}\n","/**\n * Mina GraphQL helpers for balance queries.\n * Uses native fetch + AbortController (no external deps).\n */\n\nconst TIMEOUT_MS = 3_000;\n\n/**\n * Get MINA balance (nanomina) for a base58check address.\n * Returns balance as decimal string in nanomina (scale 9).\n */\nexport async function getMinaBalance(\n graphqlUrl: string,\n address: string\n): Promise<string> {\n const query = `query GetBalance($pk: PublicKey!) {\n account(publicKey: $pk) {\n balance {\n total\n }\n }\n }`;\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), TIMEOUT_MS);\n try {\n const res = await fetch(graphqlUrl, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ query, variables: { pk: address } }),\n signal: controller.signal,\n });\n if (!res.ok) throw new Error(`Mina GraphQL failed: HTTP ${res.status}`);\n const data = (await res.json()) as {\n data?: { account?: { balance?: { total?: string } } };\n errors?: { message: string }[];\n };\n if (data.errors?.length)\n throw new Error(`Mina GraphQL error: ${data.errors[0]?.message}`);\n const total = data.data?.account?.balance?.total;\n if (!total) return '0';\n // total is returned as a decimal MINA string (e.g. \"1000.000000000\").\n // Reject malformed inputs (scientific notation, empty wholes, non-digits)\n // explicitly — a BigInt throw inside this function would surface as a\n // generic 503 instead of a clean `available:false / reason:bad_format`.\n if (!/^\\d+(\\.\\d+)?$/.test(total)) {\n throw new Error(`Mina balance: unsupported numeric format (${total})`);\n }\n const [whole = '0', frac = ''] = total.split('.');\n const fracPadded = frac.padEnd(9, '0').slice(0, 9) || '0';\n const nanomina = BigInt(whole) * 1_000_000_000n + BigInt(fracPadded);\n return nanomina.toString();\n } finally {\n clearTimeout(timeout);\n }\n}\n","/**\n * GET /api/wallet/balances\n *\n * Returns per-(nodeType × family × token) balance entries.\n * Per-RPC failure is partial — one bad chain never kills the whole response.\n * Server-side cache: 5 s TTL per (nodeType × address × token) to avoid\n * hammering RPCs and to keep `payload.ts` stable across cache-hit reads.\n * In-flight requests for the same key are deduped via a Promise map so two\n * parallel /balances calls don't double-call the upstream RPC.\n */\n\nimport type { FastifyInstance } from 'fastify';\nimport type { ApiDeps } from '../types.js';\nimport type { WalletBalanceEntry, WalletBalancesPayload } from '../types.js';\nimport { getEvmBalance, getErc20Balance } from '../../chain/evm-rpc.js';\nimport { getSolanaBalance } from '../../chain/solana-rpc.js';\nimport { getMinaBalance } from '../../chain/mina-graphql.js';\nimport type { NodeKeyInfo } from '../../wallet/types.js';\n\n// ── Cache ─────────────────────────────────────────────────────────────────────\n\ninterface CacheEntry {\n entry: WalletBalanceEntry;\n ts: number;\n}\n\n// In-memory best-effort cache (restart-volatile; single-process).\nconst CACHE = new Map<string, CacheEntry>();\nconst INFLIGHT = new Map<string, Promise<WalletBalanceEntry>>();\nconst CACHE_TTL_MS = 5_000;\n\n/** Test hook: reset module-scope cache between vitest tests. */\nexport function resetWalletBalancesCache(): void {\n CACHE.clear();\n INFLIGHT.clear();\n}\n\nfunction getCachedEntry(key: string): CacheEntry | undefined {\n const hit = CACHE.get(key);\n if (!hit) return undefined;\n if (Date.now() - hit.ts > CACHE_TTL_MS) {\n CACHE.delete(key);\n return undefined;\n }\n return hit;\n}\n\nfunction setCached(key: string, entry: WalletBalanceEntry): CacheEntry {\n const cached = { entry, ts: Date.now() };\n CACHE.set(key, cached);\n return cached;\n}\n\n/** Wrap a fetcher with cache + in-flight dedup so two callers for the same key\n * share one upstream RPC call. */\nasync function memoize(\n key: string,\n fetcher: () => Promise<WalletBalanceEntry>\n): Promise<{ entry: WalletBalanceEntry; ts: number }> {\n const cached = getCachedEntry(key);\n if (cached) return cached;\n const inflight = INFLIGHT.get(key);\n if (inflight) {\n const entry = await inflight;\n const after = getCachedEntry(key);\n return after ?? { entry, ts: Date.now() };\n }\n const promise = (async () => {\n try {\n const entry = await fetcher();\n setCached(key, entry);\n return entry;\n } finally {\n INFLIGHT.delete(key);\n }\n })();\n INFLIGHT.set(key, promise);\n const entry = await promise;\n const after = getCachedEntry(key);\n return after ?? { entry, ts: Date.now() };\n}\n\n// ── Fetch helpers ─────────────────────────────────────────────────────────────\n\nfunction unavailable(\n nodeType: 'town' | 'mill' | 'dvm',\n family: WalletBalanceEntry['family'],\n token: WalletBalanceEntry['token'],\n address: string,\n scale: number,\n reason: string\n): WalletBalanceEntry {\n return {\n nodeType,\n family,\n token,\n address,\n balance: '0',\n scale,\n available: false,\n reason,\n };\n}\n\nasync function fetchEvmEth(\n rpcUrl: string,\n nodeType: 'town' | 'mill' | 'dvm',\n address: string\n): Promise<{ entry: WalletBalanceEntry; ts: number }> {\n const cacheKey = `evm:${nodeType}:${address}:ETH`;\n return memoize(cacheKey, async () => {\n try {\n const balance = await getEvmBalance(rpcUrl, address);\n return {\n nodeType,\n family: 'evm',\n token: 'ETH',\n address,\n balance,\n scale: 18,\n available: true,\n };\n } catch (e) {\n return unavailable(\n nodeType,\n 'evm',\n 'ETH',\n address,\n 18,\n e instanceof Error ? e.message : 'evm_rpc_error'\n );\n }\n });\n}\n\nasync function fetchEvmUsdc(\n rpcUrl: string,\n usdcAddress: string | undefined,\n nodeType: 'town' | 'mill' | 'dvm',\n address: string\n): Promise<{ entry: WalletBalanceEntry; ts: number }> {\n if (!usdcAddress || !/^0x[0-9a-fA-F]{40}$/.test(usdcAddress)) {\n // Don't cache config-drift — it should clear immediately when the env is fixed.\n return {\n entry: unavailable(\n nodeType,\n 'evm',\n 'USDC',\n address,\n 6,\n 'usdc_address_not_configured'\n ),\n ts: Date.now(),\n };\n }\n const cacheKey = `evm:${nodeType}:${address}:USDC`;\n return memoize(cacheKey, async () => {\n try {\n const balance = await getErc20Balance(rpcUrl, usdcAddress, address);\n return {\n nodeType,\n family: 'evm',\n token: 'USDC',\n address,\n balance,\n scale: 6,\n available: true,\n };\n } catch (e) {\n return unavailable(\n nodeType,\n 'evm',\n 'USDC',\n address,\n 6,\n e instanceof Error ? e.message : 'evm_rpc_error'\n );\n }\n });\n}\n\nasync function fetchSolana(\n rpcUrl: string,\n nodeType: 'town' | 'mill' | 'dvm',\n address: string\n): Promise<{ entry: WalletBalanceEntry; ts: number }> {\n const cacheKey = `solana:${nodeType}:${address}:SOL`;\n return memoize(cacheKey, async () => {\n try {\n const balance = await getSolanaBalance(rpcUrl, address);\n return {\n nodeType,\n family: 'solana',\n token: 'SOL',\n address,\n balance,\n scale: 9,\n available: true,\n };\n } catch (e) {\n return unavailable(\n nodeType,\n 'solana',\n 'SOL',\n address,\n 9,\n e instanceof Error ? e.message : 'solana_rpc_error'\n );\n }\n });\n}\n\nasync function fetchMina(\n graphqlUrl: string,\n nodeType: 'town' | 'mill' | 'dvm',\n address: string\n): Promise<{ entry: WalletBalanceEntry; ts: number }> {\n const cacheKey = `mina:${nodeType}:${address}:MINA`;\n return memoize(cacheKey, async () => {\n try {\n const balance = await getMinaBalance(graphqlUrl, address);\n return {\n nodeType,\n family: 'mina',\n token: 'MINA',\n address,\n balance,\n scale: 9,\n available: true,\n };\n } catch (e) {\n return unavailable(\n nodeType,\n 'mina',\n 'MINA',\n address,\n 9,\n e instanceof Error ? e.message : 'mina_graphql_error'\n );\n }\n });\n}\n\n// ── Route registration ────────────────────────────────────────────────────────\n\ninterface FetchTask {\n /** Identity of the (nodeType, family, token, address) so a thrown task can\n * still be reported with its original tuple instead of a fake town/ETH stub. */\n identity: {\n nodeType: 'town' | 'mill' | 'dvm';\n family: WalletBalanceEntry['family'];\n token: WalletBalanceEntry['token'];\n address: string;\n scale: number;\n };\n run: () => Promise<{ entry: WalletBalanceEntry; ts: number }>;\n}\n\nexport function registerWalletBalancesRoutes(\n app: FastifyInstance,\n deps: ApiDeps\n): void {\n app.get('/wallet/balances', async (_request, reply) => {\n let keys: NodeKeyInfo[];\n try {\n keys = deps.wallet.getAllKeys();\n } catch {\n return reply.status(503).send({ error: 'wallet_not_initialized' });\n }\n\n const anvil =\n process.env['TOWNHOUSE_DEV_ANVIL_RPC'] ?? 'http://127.0.0.1:28545';\n const solanaRpc =\n process.env['TOWNHOUSE_DEV_SOLANA_RPC'] ?? 'http://127.0.0.1:28899';\n const minaGraphql =\n process.env['TOWNHOUSE_DEV_MINA_GRAPHQL'] ??\n 'http://127.0.0.1:28085/graphql';\n const usdcAddress = process.env['TOON_USDC_ADDRESS'] || undefined;\n\n const tasks: FetchTask[] = [];\n for (const keyInfo of keys) {\n const nodeType = keyInfo.nodeType as 'town' | 'mill' | 'dvm';\n tasks.push({\n identity: {\n nodeType,\n family: 'evm',\n token: 'ETH',\n address: keyInfo.evmAddress,\n scale: 18,\n },\n run: () => fetchEvmEth(anvil, nodeType, keyInfo.evmAddress),\n });\n tasks.push({\n identity: {\n nodeType,\n family: 'evm',\n token: 'USDC',\n address: keyInfo.evmAddress,\n scale: 6,\n },\n run: () =>\n fetchEvmUsdc(anvil, usdcAddress, nodeType, keyInfo.evmAddress),\n });\n if (nodeType === 'mill' && keyInfo.solanaAddress) {\n const solAddr = keyInfo.solanaAddress;\n tasks.push({\n identity: {\n nodeType,\n family: 'solana',\n token: 'SOL',\n address: solAddr,\n scale: 9,\n },\n run: () => fetchSolana(solanaRpc, nodeType, solAddr),\n });\n }\n if (nodeType === 'mill' && keyInfo.minaAddress) {\n const minaAddr = keyInfo.minaAddress;\n tasks.push({\n identity: {\n nodeType,\n family: 'mina',\n token: 'MINA',\n address: minaAddr,\n scale: 9,\n },\n run: () => fetchMina(minaGraphql, nodeType, minaAddr),\n });\n }\n }\n\n const results = await Promise.allSettled(tasks.map((t) => t.run()));\n\n let oldestTs = Number.POSITIVE_INFINITY;\n const entries: WalletBalanceEntry[] = results.map((r, idx) => {\n if (r.status === 'fulfilled') {\n if (r.value.ts < oldestTs) oldestTs = r.value.ts;\n return r.value.entry;\n }\n // Rejection branch: capture the original task identity rather than a fake stub.\n const task = tasks[idx];\n if (!task) {\n throw new Error(`task missing for index ${idx}`);\n }\n const { nodeType, family, token, address, scale } = task.identity;\n const reason =\n r.reason instanceof Error ? r.reason.message : 'internal_error';\n return unavailable(nodeType, family, token, address, scale, reason);\n });\n\n // AC-2: cache-hit returns same `ts`. Use the oldest cached entry's ts so\n // back-to-back cached requests yield identical timestamps.\n const ts = oldestTs === Number.POSITIVE_INFINITY ? Date.now() : oldestTs;\n const payload: WalletBalancesPayload = { entries, ts };\n return payload;\n });\n}\n","/**\n * POST /api/wallet/reveal\n *\n * Password-gated seed-phrase reveal.\n * SECURITY: Mnemonic is never logged, never cached beyond the request lifecycle.\n */\n\nimport type { FastifyInstance } from 'fastify';\nimport type { ApiDeps } from '../types.js';\nimport { loadWallet } from '../../wallet/storage.js';\nimport { decryptWallet } from '../../wallet/crypto.js';\n\nexport function registerWalletRevealRoutes(\n app: FastifyInstance,\n deps: ApiDeps\n): void {\n app.post('/wallet/reveal', async (request, reply) => {\n const body = request.body as { password?: unknown };\n\n if (\n !body ||\n typeof body.password !== 'string' ||\n body.password.length === 0 ||\n body.password.length > 256\n ) {\n return reply.status(400).send({\n error: 'invalid_request',\n message: 'password must be a non-empty string ≤ 256 characters',\n });\n }\n\n const walletPath = deps.config.wallet.encrypted_path;\n let loaded: Awaited<ReturnType<typeof loadWallet>>;\n try {\n loaded = await loadWallet(walletPath);\n } catch (e) {\n // ENOENT (or any \"file not found\" surface) is \"wallet not initialized\",\n // not \"corrupted\" — the user just hasn't run `townhouse init` yet.\n const code = (e as { code?: string } | null)?.code;\n if (code === 'ENOENT' || code === 'ENOTDIR') {\n return reply.status(503).send({ error: 'wallet_not_initialized' });\n }\n return reply.status(500).send({\n error: 'wallet_corrupted',\n message: 'Failed to read wallet file',\n });\n }\n\n if (!loaded) {\n return reply.status(503).send({ error: 'wallet_not_initialized' });\n }\n\n let wallet: typeof loaded.wallet;\n try {\n wallet = loaded.wallet;\n // Validate JSON structure: all required fields present\n if (!wallet.salt || !wallet.iv || !wallet.ciphertext || !wallet.tag) {\n return reply.status(500).send({\n error: 'wallet_corrupted',\n message: 'Wallet file is missing required fields',\n });\n }\n } catch {\n return reply.status(500).send({\n error: 'wallet_corrupted',\n message: 'Wallet file JSON is invalid',\n });\n }\n\n let mnemonic: string;\n try {\n mnemonic = decryptWallet(wallet, body.password);\n } catch {\n return reply.status(401).send({ error: 'invalid_password' });\n }\n\n // Return mnemonic directly — not stored in any module-scoped variable\n return reply.status(200).send({ mnemonic });\n });\n}\n","/**\n * POST /api/wallet/withdraw — EVM-only signed withdrawal (v1)\n * GET /api/wallet/transaction/:txHash — receipt polling\n *\n * SECURITY: Private keys and signed-tx hex are never logged.\n * Localhost-only boundary enforced by the API server bind address.\n */\n\nimport type { FastifyInstance } from 'fastify';\nimport { isAddress } from 'viem';\nimport type {\n ApiDeps,\n WithdrawRequest,\n WithdrawSuccessResponse,\n WithdrawDryRunResponse,\n TransactionReceiptPayload,\n} from '../types.js';\nimport {\n signAndBroadcastEthTransfer,\n signAndBroadcastUsdcTransfer,\n getReceipt,\n estimateNativeTransferGas,\n estimateUsdcTransferGas,\n} from '../../chain/evm-tx.js';\nimport { getEvmBalance, getErc20Balance } from '../../chain/evm-rpc.js';\nimport type { NodeType } from '../../docker/types.js';\n\nconst NODE_TYPES = new Set<string>(['town', 'mill', 'dvm']);\n\n/** Map a thrown viem/fetch error to an \"RPC unreachable\" verdict.\n * Replaces brittle `e.message.includes('fetch')` substring matching, which\n * over-catches viem strings like \"could not fetch nonce\" (a real broadcast\n * failure, not a transport problem). */\nfunction isRpcUnreachable(e: unknown): boolean {\n if (!(e instanceof Error)) return false;\n // Node fetch / undici surface transport failures as TypeError with a `cause`\n // whose `code` is set to one of these. Anything else is a real broadcast\n // failure that should propagate as 500 broadcast_failed.\n const cause = (e as { cause?: { code?: string } }).cause;\n if (cause?.code) {\n return [\n 'ECONNREFUSED',\n 'ENOTFOUND',\n 'EAI_AGAIN',\n 'ETIMEDOUT',\n 'UND_ERR_CONNECT_TIMEOUT',\n ].includes(cause.code);\n }\n // Bare TypeError \"fetch failed\" (no cause attached, e.g. mocked failures in\n // tests) — match exact viem fetch-failure strings, not the looser substring.\n return (\n /^fetch failed$/i.test(e.message) || /HTTP request failed/i.test(e.message)\n );\n}\n\nexport function registerWalletWithdrawRoutes(\n app: FastifyInstance,\n deps: ApiDeps\n): void {\n // POST /wallet/withdraw\n app.post('/wallet/withdraw', async (request, reply) => {\n const body = request.body as Partial<WithdrawRequest>;\n\n // Validate nodeType\n if (!body.nodeType || !NODE_TYPES.has(body.nodeType)) {\n return reply.status(400).send({\n error: 'invalid_node_type',\n message: 'nodeType must be town, mill, or dvm',\n });\n }\n\n // v1: EVM-only; Solana/Mina → 501\n if (body.chainFamily === 'solana' || body.chainFamily === 'mina') {\n return reply.status(501).send({\n error: 'chain_not_supported_for_withdrawal',\n message: `${body.chainFamily} withdrawal coming soon — copy the address and use an external wallet for now`,\n supportedFamilies: ['evm'],\n });\n }\n if (body.chainFamily !== 'evm') {\n return reply.status(400).send({\n error: 'invalid_chain_family',\n message: 'chainFamily must be evm, solana, or mina',\n });\n }\n\n // Validate token\n if (body.token !== 'native' && body.token !== 'USDC') {\n return reply.status(400).send({\n error: 'invalid_token',\n message: 'token must be native or USDC',\n });\n }\n\n // Validate recipient — regex + viem isAddress for EIP-55\n const recipient = body.recipient ?? '';\n if (!/^0x[0-9a-fA-F]{40}$/.test(recipient)) {\n return reply\n .status(400)\n .send({ error: 'invalid_recipient', code: 'invalid_recipient_format' });\n }\n if (!isAddress(recipient)) {\n return reply.status(400).send({\n error: 'invalid_recipient',\n code: 'invalid_recipient_checksum',\n });\n }\n\n // Validate amount\n let amountBig: bigint;\n try {\n amountBig = BigInt(body.amount ?? '');\n if (amountBig <= 0n) throw new Error('non-positive');\n } catch {\n return reply.status(400).send({\n error: 'invalid_amount',\n message: 'amount must be a positive integer string (raw units)',\n });\n }\n\n const anvil =\n process.env['TOWNHOUSE_DEV_ANVIL_RPC'] ?? 'http://127.0.0.1:28545';\n const usdcAddress = (process.env['TOON_USDC_ADDRESS'] || undefined) as\n | `0x${string}`\n | undefined;\n\n // Get keys\n let nodeKeys: ReturnType<typeof deps.wallet.getNodeKeys>;\n try {\n nodeKeys = deps.wallet.getNodeKeys(body.nodeType as NodeType);\n } catch {\n return reply.status(503).send({ error: 'wallet_not_initialized' });\n }\n\n const evmAddress = nodeKeys.evmAddress as `0x${string}`;\n\n // USDC config is a server-side requirement, not user input — 503 not 400.\n if (body.token === 'USDC' && !usdcAddress) {\n return reply.status(503).send({\n error: 'usdc_address_not_configured',\n message: 'TOON_USDC_ADDRESS not set on server',\n });\n }\n // After the guard above, USDC requests have a valid usdcAddress.\n function requireUsdc(): `0x${string}` {\n if (!usdcAddress) {\n throw new Error('usdcAddress missing after guard');\n }\n return usdcAddress;\n }\n\n // Balance check + gas-aware native cap.\n let onChainBalance: bigint;\n let estimateForGuard: { gas: string; fee: string } | null = null;\n try {\n if (body.token === 'native') {\n onChainBalance = BigInt(await getEvmBalance(anvil, evmAddress));\n } else {\n // USDC — gas is paid in native ETH, not the USDC balance.\n onChainBalance = BigInt(\n await getErc20Balance(anvil, requireUsdc(), evmAddress)\n );\n }\n if (amountBig > onChainBalance) {\n return reply.status(400).send({\n error: 'insufficient_balance',\n code: 'insufficient_balance',\n });\n }\n // Native-only gas headroom check: a \"Max\" withdraw passes the raw\n // balance check then fails at broadcast with \"insufficient funds for\n // gas * price + value\". Reject early with a distinct code so the UI\n // can surface \"leave headroom for fees\".\n if (body.token === 'native') {\n try {\n estimateForGuard = await estimateNativeTransferGas(\n anvil,\n evmAddress,\n recipient as `0x${string}`,\n amountBig\n );\n const fee = BigInt(estimateForGuard.fee);\n if (amountBig + fee > onChainBalance) {\n return reply.status(400).send({\n error: 'insufficient_balance',\n code: 'insufficient_balance_for_gas',\n message:\n 'amount + estimated gas exceeds balance — leave headroom for fees',\n });\n }\n } catch {\n // Estimate unavailable — broadcast will surface the real reason.\n }\n }\n } catch (e) {\n if (isRpcUnreachable(e)) {\n return reply.status(503).send({ error: 'rpc_unreachable' });\n }\n throw e;\n }\n\n // dryRun: return gas estimate without broadcasting. Estimator must match\n // the broadcast path or the displayed fee under-reports for ERC-20.\n if (body.dryRun === true) {\n try {\n const est =\n body.token === 'native'\n ? (estimateForGuard ??\n (await estimateNativeTransferGas(\n anvil,\n evmAddress,\n recipient as `0x${string}`,\n amountBig\n )))\n : await estimateUsdcTransferGas(\n anvil,\n evmAddress,\n requireUsdc(),\n recipient as `0x${string}`,\n amountBig\n );\n const response: WithdrawDryRunResponse = {\n estimatedGas: est.gas,\n estimatedFee: est.fee,\n };\n return reply.status(200).send(response);\n } catch (e) {\n if (isRpcUnreachable(e)) {\n return reply.status(503).send({ error: 'rpc_unreachable' });\n }\n throw e;\n }\n }\n\n // Broadcast\n try {\n let txHash: `0x${string}`;\n let chainId: number;\n\n if (body.token === 'native') {\n const result = await signAndBroadcastEthTransfer(\n anvil,\n nodeKeys.evmPrivateKey,\n recipient as `0x${string}`,\n amountBig\n );\n txHash = result.txHash;\n chainId = result.chainId;\n } else {\n const result = await signAndBroadcastUsdcTransfer(\n anvil,\n requireUsdc(),\n nodeKeys.evmPrivateKey,\n recipient as `0x${string}`,\n amountBig\n );\n txHash = result.txHash;\n chainId = result.chainId;\n }\n\n const response: WithdrawSuccessResponse = { txHash, chainId };\n return reply.status(200).send(response);\n } catch (e) {\n if (isRpcUnreachable(e)) {\n return reply.status(503).send({ error: 'rpc_unreachable' });\n }\n const msg = e instanceof Error ? e.message : String(e);\n return reply\n .status(500)\n .send({ error: 'broadcast_failed', message: msg });\n }\n });\n\n // GET /wallet/transaction/:txHash\n app.get<{ Params: { txHash: string } }>(\n '/wallet/transaction/:txHash',\n async (request, reply) => {\n const { txHash } = request.params;\n\n if (!/^0x[0-9a-fA-F]{64}$/.test(txHash)) {\n return reply.status(400).send({\n error: 'invalid_tx_hash',\n message: 'txHash must be a 0x-prefixed 32-byte hex string',\n });\n }\n\n const anvil =\n process.env['TOWNHOUSE_DEV_ANVIL_RPC'] ?? 'http://127.0.0.1:28545';\n try {\n const receipt: TransactionReceiptPayload = await getReceipt(\n anvil,\n txHash as `0x${string}`\n );\n return reply.status(200).send(receipt);\n } catch (e) {\n if (isRpcUnreachable(e)) {\n return reply.status(503).send({ error: 'rpc_unreachable' });\n }\n throw e;\n }\n }\n );\n}\n","/**\n * EVM transaction construction and broadcast helpers.\n * Uses viem for EIP-1559 tx construction, signing, and receipt polling.\n * SECURITY: private keys are never logged.\n */\n\nimport {\n createWalletClient,\n createPublicClient,\n defineChain,\n encodeFunctionData,\n http,\n parseAbi,\n type Hash,\n} from 'viem';\nimport { privateKeyToAccount } from 'viem/accounts';\nimport type { TransactionReceiptPayload } from '../api/types.js';\n\nfunction toViemPrivkey(raw: Uint8Array): `0x${string}` {\n const hex = Buffer.from(raw).toString('hex');\n return `0x${hex}` as `0x${string}`;\n}\n\n/** Build a viem chain object pinned to the runtime-fetched chainId so EIP-155\n * replay protection is enforced. Refusing `chain: null` is the whole point. */\nfunction buildChain(rpcUrl: string, chainId: number) {\n return defineChain({\n id: chainId,\n name: `evm-${chainId}`,\n nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },\n rpcUrls: { default: { http: [rpcUrl] } },\n });\n}\n\nconst ERC20_ABI = parseAbi([\n 'function transfer(address to, uint256 amount) returns (bool)',\n]);\n\nexport async function signAndBroadcastEthTransfer(\n rpcUrl: string,\n privateKey: Uint8Array,\n recipient: `0x${string}`,\n amount: bigint\n): Promise<{ txHash: Hash; chainId: number }> {\n const account = privateKeyToAccount(toViemPrivkey(privateKey));\n const transport = http(rpcUrl);\n\n const publicClient = createPublicClient({ transport });\n const chainId = await publicClient.getChainId();\n const chain = buildChain(rpcUrl, chainId);\n\n const walletClient = createWalletClient({ account, transport, chain });\n\n const txHash = await walletClient.sendTransaction({\n to: recipient,\n value: amount,\n chain,\n });\n\n return { txHash, chainId };\n}\n\nexport async function signAndBroadcastUsdcTransfer(\n rpcUrl: string,\n usdcAddress: `0x${string}`,\n privateKey: Uint8Array,\n recipient: `0x${string}`,\n amount: bigint\n): Promise<{ txHash: Hash; chainId: number }> {\n const account = privateKeyToAccount(toViemPrivkey(privateKey));\n const transport = http(rpcUrl);\n\n const publicClient = createPublicClient({ transport });\n const chainId = await publicClient.getChainId();\n const chain = buildChain(rpcUrl, chainId);\n\n const walletClient = createWalletClient({ account, transport, chain });\n\n const txHash = await walletClient.writeContract({\n address: usdcAddress,\n abi: ERC20_ABI,\n functionName: 'transfer',\n args: [recipient, amount],\n chain,\n });\n\n return { txHash, chainId };\n}\n\nexport async function getReceipt(\n rpcUrl: string,\n txHash: Hash\n): Promise<TransactionReceiptPayload> {\n const publicClient = createPublicClient({ transport: http(rpcUrl) });\n try {\n const receipt = await publicClient.getTransactionReceipt({ hash: txHash });\n if (!receipt) return { status: 'pending', txHash };\n return {\n status: receipt.status === 'success' ? 'success' : 'reverted',\n blockNumber: Number(receipt.blockNumber),\n txHash,\n };\n } catch (e) {\n // Receipt not found yet\n if (e instanceof Error && e.message.includes('not found')) {\n return { status: 'pending', txHash };\n }\n throw e;\n }\n}\n\nexport async function estimateNativeTransferGas(\n rpcUrl: string,\n fromAddress: `0x${string}`,\n recipient: `0x${string}`,\n amount: bigint\n): Promise<{ gas: string; fee: string }> {\n const publicClient = createPublicClient({ transport: http(rpcUrl) });\n const [gas, gasPrice] = await Promise.all([\n publicClient.estimateGas({\n account: fromAddress,\n to: recipient,\n value: amount,\n }),\n publicClient.getGasPrice(),\n ]);\n const fee = gas * gasPrice;\n return { gas: gas.toString(), fee: fee.toString() };\n}\n\n/** Estimate gas for an ERC-20 `transfer(to, amount)` call.\n * Native transfer is ~21k; ERC-20 transfer is ~50-65k — using the native\n * estimator for USDC under-reports the fee by ~3×. */\nexport async function estimateUsdcTransferGas(\n rpcUrl: string,\n fromAddress: `0x${string}`,\n usdcAddress: `0x${string}`,\n recipient: `0x${string}`,\n amount: bigint\n): Promise<{ gas: string; fee: string }> {\n const publicClient = createPublicClient({ transport: http(rpcUrl) });\n const data = encodeFunctionData({\n abi: ERC20_ABI,\n functionName: 'transfer',\n args: [recipient, amount],\n });\n const [gas, gasPrice] = await Promise.all([\n publicClient.estimateGas({ account: fromAddress, to: usdcAddress, data }),\n publicClient.getGasPrice(),\n ]);\n const fee = gas * gasPrice;\n return { gas: gas.toString(), fee: fee.toString() };\n}\n","/**\n * Shared config-mutation mutex.\n *\n * Serializes concurrent PATCH /api/nodes/:type/config and PATCH /api/transport\n * mutations so they cannot race on the same config.yaml file.\n */\n\nlet isMutating = false;\n\n/**\n * Synchronous test-and-set acquire of the mutex. Returns true on success,\n * false if already held.\n *\n * Safety: relies on the JavaScript event-loop running the if-check and the\n * assignment in the same synchronous tick. Do NOT introduce an `await`\n * between the read and the write or this guard becomes racy.\n */\nexport function acquireConfigMutex(): boolean {\n if (isMutating) return false;\n isMutating = true;\n return true;\n}\n\nexport function releaseConfigMutex(): void {\n isMutating = false;\n}\n\n/** Reset for testing. */\nexport function resetConfigMutex(): void {\n isMutating = false;\n}\n\n// ── Node lifecycle mutex ────────────────────────────────────────────────────\n// Serializes concurrent POST/DELETE /api/nodes requests so the 6-step\n// provision pipeline cannot race with a concurrent teardown.\n// Kept separate from configMutex so config fee-patches and node-add can run\n// in parallel (they touch disjoint state).\n\nlet isNodeLifecycleRunning = false;\n\n/** Acquire the node-lifecycle mutex. Returns true on success, false if busy. */\nexport function acquireNodeLifecycleMutex(): boolean {\n if (isNodeLifecycleRunning) return false;\n isNodeLifecycleRunning = true;\n return true;\n}\n\nexport function releaseNodeLifecycleMutex(): void {\n isNodeLifecycleRunning = false;\n}\n\n/** Reset for testing. */\nexport function resetNodeLifecycleMutex(): void {\n isNodeLifecycleRunning = false;\n}\n","/**\n * Node configuration mutation routes: PATCH /nodes/:type/config\n */\n\nimport type { FastifyInstance, FastifySchema } from 'fastify';\nimport type { ApiDeps, NodeType } from '../types.js';\nimport { validateConfig } from '../../config/validator.js';\nimport { saveConfig } from '../../config/loader.js';\nimport {\n acquireConfigMutex,\n releaseConfigMutex,\n resetConfigMutex as _resetConfigMutex,\n} from '../config-mutex.js';\n\n/** Request body for PATCH /nodes/:type/config */\ninterface ConfigPatchBody {\n feePerEvent?: number;\n feeBasisPoints?: number;\n feePerJob?: number;\n kindPricing?: Record<string, number>;\n enabled?: boolean;\n}\n\n/**\n * JSON Schema for PATCH body — rejects unknown keys (typos) and enforces\n * numeric fee ceiling at JS Number.MAX_SAFE_INTEGER (2^53-1). Per Task 3.1 +\n * Dev Note \"Standard Guards\".\n */\nconst patchBodySchema: FastifySchema = {\n body: {\n type: 'object',\n additionalProperties: false,\n properties: {\n feePerEvent: { type: 'number', minimum: 0, maximum: 9007199254740991 },\n feeBasisPoints: { type: 'number', minimum: 0, maximum: 9007199254740991 },\n feePerJob: { type: 'number', minimum: 0, maximum: 9007199254740991 },\n kindPricing: {\n type: 'object',\n // Restrict keys to numeric strings (positive integers) to prevent\n // prototype pollution and env-var-injection attacks via arbitrary\n // keys (e.g. '__proto__', '5094\\nFOO=bar') that flow into\n // KIND_PRICING_<key> orchestrator env vars.\n propertyNames: { pattern: '^[0-9]+$' },\n additionalProperties: {\n type: 'integer',\n minimum: 0,\n maximum: 9007199254740991,\n },\n },\n enabled: { type: 'boolean' },\n },\n },\n};\n\n/**\n * Register config mutation routes.\n */\nexport function registerConfigPatchRoutes(\n app: FastifyInstance,\n deps: ApiDeps\n): void {\n app.patch<{ Params: { type: string }; Body: ConfigPatchBody }>(\n '/nodes/:type/config',\n { schema: patchBodySchema },\n async (request, reply) => {\n const { type } = request.params;\n const body = request.body ?? {};\n\n // Validate type\n if (type !== 'town' && type !== 'mill' && type !== 'dvm') {\n return reply.status(404).send({\n error: 'unknown_node_type',\n type,\n });\n }\n\n // kindPricing is only valid for dvm\n if (body.kindPricing !== undefined && type !== 'dvm') {\n return reply.status(400).send({\n error: 'invalid_field',\n message: `kindPricing not supported for type=${type}`,\n });\n }\n\n // Check mutex\n if (!acquireConfigMutex()) {\n return reply.status(409).send({\n error: 'config_mutation_in_flight',\n });\n }\n\n try {\n // Use the live reference — mutations applied to `deps.config` persist across requests\n const currentConfig = deps.config;\n const nodeConfig = currentConfig.nodes[type as NodeType];\n if (!nodeConfig) {\n return reply.status(404).send({\n error: 'unknown_node_type',\n type,\n });\n }\n\n // Deep merge the body into current config. kindPricing merges\n // per-key so a partial PATCH (e.g. `{ kindPricing: { '5094': 7 } }`)\n // does not clobber other already-set kinds in the persisted config.\n const existingKindPricing =\n (nodeConfig as { kindPricing?: Record<string, number> })\n .kindPricing ?? undefined;\n const mergedKindPricing =\n body.kindPricing !== undefined\n ? { ...(existingKindPricing ?? {}), ...body.kindPricing }\n : existingKindPricing;\n\n const mergedConfig = {\n ...currentConfig,\n nodes: {\n ...currentConfig.nodes,\n [type]: {\n ...nodeConfig,\n ...body,\n ...(mergedKindPricing !== undefined\n ? { kindPricing: mergedKindPricing }\n : {}),\n },\n },\n };\n\n // Validate merged config\n try {\n validateConfig(mergedConfig);\n } catch (validationError) {\n return reply.status(400).send({\n error: 'config_validation_error',\n message:\n validationError instanceof Error\n ? validationError.message\n : 'Invalid configuration',\n });\n }\n\n // Persist to disk\n await saveConfig(deps.configPath, mergedConfig);\n\n // Update in-memory config so subsequent requests see the new state.\n // Mutate in place on the same object reference the rest of the app holds.\n deps.config.nodes = mergedConfig.nodes;\n\n // Determine orchestrator action\n const nodeType = type as NodeType;\n const oldEnabled = nodeConfig.enabled;\n const newEnabled =\n body.enabled !== undefined ? body.enabled : oldEnabled;\n\n // D2 resolution (2026-04-21): run BOTH if enabled flips AND fee fields change\n if (oldEnabled !== newEnabled) {\n // Enabled was flipped\n if (newEnabled) {\n await deps.orchestrator.addNode(nodeType);\n } else {\n await deps.orchestrator.removeNode(nodeType);\n }\n }\n\n // Also regenerate if fee fields changed (even if enabled also changed)\n if (\n body.feePerEvent !== undefined ||\n body.feeBasisPoints !== undefined ||\n body.feePerJob !== undefined ||\n body.kindPricing !== undefined\n ) {\n // Fee fields changed - regenerate connector config\n const activeTypes = Object.entries(mergedConfig.nodes)\n .filter(([, config]) => config.enabled)\n .map(([type]) => type as NodeType);\n\n await deps.orchestrator.regenerateConnectorConfig(activeTypes);\n }\n\n // Return updated config subset (per-type, typed safely)\n const u = mergedConfig.nodes[type as NodeType] as {\n enabled: boolean;\n feePerEvent?: number;\n feeBasisPoints?: number;\n feePerJob?: number;\n kindPricing?: Record<string, number>;\n };\n if (nodeType === 'town') {\n return { enabled: u.enabled, feePerEvent: u.feePerEvent };\n } else if (nodeType === 'mill') {\n return { enabled: u.enabled, feeBasisPoints: u.feeBasisPoints };\n } else {\n return {\n enabled: u.enabled,\n feePerJob: u.feePerJob,\n kindPricing: u.kindPricing,\n };\n }\n } finally {\n releaseConfigMutex();\n }\n }\n );\n}\n\n/**\n * Reset the mutation mutex (for testing).\n * Re-exported from config-mutex.ts for backward compatibility with existing tests.\n */\nexport { _resetConfigMutex as resetConfigMutex };\n","/**\n * Node lifecycle routes: POST /api/nodes and DELETE /api/nodes/:id.\n *\n * Implements the 6-step atomic provisioning pipeline (Story 46.2):\n * 1. derive-key — WalletManager.deriveNodeKey (no state change)\n * 2. pull-image — DockerOrchestrator.pullImage (no state change)\n * 3. write-yaml — writeNodesYaml (first state mutation)\n * 4. start-container — DockerOrchestrator.startNodeViaCompose\n * 5. healthcheck — waitForHealthy (HTTP poll until 200 or 60 s timeout)\n * 6. register-peer — ConnectorAdminClient.registerPeer\n *\n * YAML-FIRST ordering invariant: step 3 (nodes.yaml write) MUST happen\n * BEFORE step 6 (connector /admin/peers registration). The drift window\n * resolves in the safe direction — a yaml entry without a connector peer\n * is re-registered on next `hs up`; the reverse creates a peer the\n * reconciler cannot clean up. Never invert this order.\n */\n\nimport { promises as fs } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { bytesToHex } from '@noble/hashes/utils';\nimport type { FastifyInstance, FastifyRequest } from 'fastify';\nimport type { ApiDeps } from '../types.js';\nimport type { NodeType } from '../types.js';\nimport type { TownhouseConfig } from '../../config/schema.js';\nimport { readNodesYaml, writeNodesYaml } from '../../state/nodes-yaml.js';\nimport {\n readImageManifest,\n isSyntheticDigest,\n} from '../../state/image-manifest.js';\nimport {\n CONTAINER_PREFIX,\n NODE_BTP_PORT,\n TOWN_HEALTH_PORT,\n MILL_HEALTH_PORT,\n DVM_HEALTH_PORT,\n ACCOUNT_INDEX_TOWN,\n ACCOUNT_INDEX_MILL,\n ACCOUNT_INDEX_DVM,\n} from '../../constants.js';\nimport {\n acquireNodeLifecycleMutex,\n releaseNodeLifecycleMutex,\n} from '../config-mutex.js';\n\nconst HEALTH_PORT: Record<NodeType, number> = {\n town: TOWN_HEALTH_PORT,\n mill: MILL_HEALTH_PORT,\n dvm: DVM_HEALTH_PORT,\n};\n\nconst ACCOUNT_INDEX: Record<NodeType, number> = {\n town: ACCOUNT_INDEX_TOWN,\n mill: ACCOUNT_INDEX_MILL,\n dvm: ACCOUNT_INDEX_DVM,\n};\n\n/** Default ILP prefix for the apex connector (constant from config-generator). */\nconst APEX_ILP_ADDRESS = 'g.townhouse';\n\n/**\n * Build the initial mill.config.json object for a freshly provisioned Mill.\n *\n * Mill's parseRawConfig() converts channels[*].cumulativeAmount / nonce and\n * inventory[*] from string → bigint. JSON cannot serialize bigint natively, so\n * these fields MUST be written as strings.\n *\n * The bootstrap channel and zero inventory allow validateConfig() to pass\n * without pre-funding the operator's Mill inventory.\n *\n * NOTE: validateConfig() also requires a non-empty `relayUrls` array, which\n * is NOT included here — it is injected at runtime via the `MILL_RELAYS`\n * environment variable (set in townhouse-hs.yml compose env). The POST handler\n * checks MILL_RELAYS before calling this function, so a missing env var is\n * caught before any file is written.\n */\nfunction buildMillSwapPairConfig(config: TownhouseConfig): object {\n // Both absent (undefined) and explicitly empty ([]) fall through to the\n // dev-Anvil sentinel — if a live EVM chain is configured, ensure\n // chainProviders has at least one entry with the correct chainId.\n const fromChain = config.chainProviders?.[0]?.chainId ?? 'evm:base:31337';\n // toChain is fixed at 'solana:devnet' for the v0.1 pilot — TownhouseConfig\n // has no Solana chain-ID field yet. Add one and read it here when mainnet\n // support is needed.\n const toChain = 'solana:devnet';\n\n return {\n swapPairs: [\n {\n from: { assetCode: 'USDC', assetScale: 6, chain: fromChain },\n to: { assetCode: 'USDC', assetScale: 6, chain: toChain },\n rate: '1.0',\n minAmount: '1000',\n maxAmount: '1000000000',\n },\n ],\n chains: ['evm', 'solana'],\n // Bootstrap: validateConfig() requires a non-empty channels array for\n // each distinct pair.to.chain. The zero channelId is a valid-format\n // sentinel that will never match a real on-chain channel.\n channels: {\n [toChain]: [\n {\n channelId: '0x' + '0'.repeat(64),\n cumulativeAmount: '0',\n nonce: '0',\n },\n ],\n },\n // Zero initial SOL inventory; parsed to 0n by the Mill CLI.\n inventory: {\n [toChain]: '0',\n },\n };\n}\n\n/**\n * Poll `url` with a per-request timeout until the server returns HTTP 200.\n * Throws if `timeoutMs` elapses without a successful response.\n *\n * Body content is intentionally ignored — only HTTP status matters, so this\n * helper stays decoupled from the three distinct node health payload shapes.\n */\nasync function waitForHealthy(url: string, timeoutMs: number): Promise<void> {\n const deadline = Date.now() + timeoutMs;\n const POLL_INTERVAL_MS = 1_000;\n const REQUEST_TIMEOUT_MS = 3_000;\n\n while (Date.now() < deadline) {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);\n try {\n const res = await fetch(url, { signal: controller.signal });\n if (res.ok) return;\n } catch {\n // connection refused or abort — keep polling\n } finally {\n clearTimeout(timer);\n }\n await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));\n }\n throw new Error(\n `Health check timeout: ${url} did not return 200 within ${timeoutMs}ms`\n );\n}\n\n/**\n * Build the per-node env vars that compose interpolates at `up -d` time.\n * Callers must start from `process.env` and layer these on top (done inside\n * `startNodeViaCompose`). The returned object is the SECRET OVERLAY ONLY.\n *\n * NEVER log the return value of this function — it contains secret keys.\n */\nfunction buildNodeEnv(\n type: NodeType,\n nostrSecretKeyHex: string,\n evmPrivateKeyHex: string,\n mnemonic: string | null,\n apexEvmAddress: string\n): Record<string, string> {\n // Town's TOON_SETTLEMENT_PRIVATE_KEY (and mill's settlement key, if it\n // were ever read) requires a 0x-prefixed 32-byte hex string. bytesToHex\n // from @noble/hashes returns unprefixed hex — without the 0x, town\n // crashes at boot with `TOON_SETTLEMENT_PRIVATE_KEY must be a 0x-prefixed\n // 32-byte hex string`. Story 46.4 live gate run (Finding O, 2026-05-12).\n const evmPrivateKeyHex0x = `0x${evmPrivateKeyHex}`;\n switch (type) {\n case 'town':\n return {\n TOWN_SECRET_KEY: nostrSecretKeyHex,\n TOWN_SETTLEMENT_PRIVATE_KEY: evmPrivateKeyHex0x,\n APEX_EVM_ADDRESS: apexEvmAddress,\n };\n case 'mill':\n return {\n MILL_SECRET_KEY: nostrSecretKeyHex,\n MILL_SETTLEMENT_PRIVATE_KEY: evmPrivateKeyHex0x,\n MILL_MNEMONIC: mnemonic ?? '',\n APEX_EVM_ADDRESS: apexEvmAddress,\n };\n case 'dvm':\n return {\n DVM_SECRET_KEY: nostrSecretKeyHex,\n };\n }\n}\n\nexport function registerNodeLifecycleRoutes(\n app: FastifyInstance,\n deps: ApiDeps\n): void {\n // ── GET /api/nodes ─────────────────────────────────────────────────────────\n // Yaml-driven node list: joins nodes.yaml entries with connector peer state.\n // Always returns 200; connector-down degrades to status:'unknown' (not 500).\n // Note: the legacy GET /nodes route (no /api prefix, docker-status-driven,\n // in nodes.ts) is intentionally separate and powers the SPA docker-state view.\n\n app.get('/api/nodes', async (request, reply) => {\n const homeDir = dirname(deps.configPath);\n const nodesYamlPath = join(homeDir, 'nodes.yaml');\n\n let yaml: Awaited<ReturnType<typeof readNodesYaml>>;\n try {\n yaml = await readNodesYaml(nodesYamlPath);\n } catch (err: unknown) {\n const errMsg = err instanceof Error ? err.message : String(err);\n request.log.error(\n { event: 'get_nodes_yaml_error', err: errMsg },\n 'Failed to read nodes.yaml'\n );\n return reply.status(500).send({ error: 'yaml_read_failed', err: errMsg });\n }\n\n type PeerStatus = Awaited<\n ReturnType<typeof deps.connectorAdmin.getPeers>\n >[number];\n let peers: PeerStatus[] = [];\n let connectorUnreachable = false;\n try {\n peers = await deps.connectorAdmin.getPeers();\n } catch (err: unknown) {\n connectorUnreachable = true;\n request.log.warn(\n { event: 'get_nodes_connector_warn', err: String(err) },\n 'connector unreachable during GET /api/nodes — returning status:unknown'\n );\n }\n\n const nodes = yaml.entries.map((entry) => {\n let status: 'connected' | 'disconnected' | 'unknown';\n if (connectorUnreachable) {\n status = 'unknown';\n } else {\n const peer = peers.find((p) => p.id === entry.peerId);\n status = peer?.connected ? 'connected' : 'disconnected';\n }\n return {\n id: entry.id,\n type: entry.type,\n peerId: entry.peerId,\n ilpAddress: entry.ilpAddress,\n status,\n enabledAt: entry.enabledAt,\n lastSeenAt: entry.lastSeenAt,\n };\n });\n\n return reply.status(200).send({ nodes });\n });\n\n // ── POST /api/nodes ────────────────────────────────────────────────────────\n\n app.post<{ Body: { type: NodeType } }>(\n '/api/nodes',\n {\n schema: {\n body: {\n type: 'object',\n additionalProperties: false,\n required: ['type'],\n properties: {\n type: { type: 'string', enum: ['town', 'mill', 'dvm'] },\n },\n },\n },\n },\n async (request, reply) => {\n if (!acquireNodeLifecycleMutex()) {\n return reply.status(409).send({ error: 'node_lifecycle_in_flight' });\n }\n\n try {\n const { type } = request.body;\n const homeDir = dirname(deps.configPath);\n const nodesYamlPath = join(homeDir, 'nodes.yaml');\n const imageManifestPath = join(homeDir, 'image-manifest.json');\n const millConfigPath = join(homeDir, 'mill.config.json');\n\n // Pre-check: single instance per type (v1 constraint)\n const yaml = await readNodesYaml(nodesYamlPath);\n const existing = yaml.entries.find((e) => e.type === type);\n if (existing) {\n return reply.status(409).send({\n error: 'node_type_in_use',\n type,\n existingId: existing.id,\n });\n }\n\n // Pre-check: MILL_RELAYS must be set before any state is written.\n // Mill's validateConfig() throws INVALID_CONFIG on an empty relayUrls\n // array, producing a silent 60-second healthcheck timeout. Catching the\n // missing var here — before writeNodesYaml (step 3) — means no rollback\n // is needed and the caller gets an actionable 400 with zero side-effects.\n if (type === 'mill' && !process.env['MILL_RELAYS']?.trim()) {\n return reply.status(400).send({\n step: 'preflight',\n err: 'MILL_RELAYS is not set or is blank. Export a comma-separated list of relay URLs before provisioning Mill (e.g. export MILL_RELAYS=wss://relay.example.com). See packages/townhouse/README.md.',\n });\n }\n\n const derivationIndex = ACCOUNT_INDEX[type];\n const id = type; // v1: id === type\n const peerId = type; // v1: peerId === id\n const ilpAddress = `${APEX_ILP_ADDRESS}.${type}`;\n const containerName = `${CONTAINER_PREFIX}hs-${type}`;\n const healthPort = HEALTH_PORT[type];\n const healthCheckUrl = `http://${containerName}:${healthPort}/health`;\n const btpUrl = `ws://${CONTAINER_PREFIX}hs-${type}:${NODE_BTP_PORT}`;\n\n // ── Step 1: derive-key ─────────────────────────────────────────────\n request.log.info(\n { event: 'node_lifecycle_step', step: 'derive-key', type, peerId },\n 'Step 1: deriving node key'\n );\n let keys: Awaited<ReturnType<typeof deps.wallet.deriveNodeKey>>;\n try {\n keys = await deps.wallet.deriveNodeKey(type, derivationIndex);\n } catch (err: unknown) {\n const errMsg = sanitizeErrorMessage(\n err instanceof Error ? err.message : String(err)\n );\n request.log.error(\n {\n event: 'node_lifecycle_failure',\n step: 'derive-key',\n err: errMsg,\n },\n 'Step 1 failed: derive-key'\n );\n return reply.status(500).send({ step: 'derive-key', err: errMsg });\n }\n\n const nostrSecretKeyHex = bytesToHex(keys.nostrSecretKey);\n const evmPrivateKeyHex = bytesToHex(keys.evmPrivateKey);\n\n // Capture the mnemonic once at end of step 1. Step 1 succeeded, so the\n // wallet is unlocked; if `getMnemonic()` returns null now, the wallet\n // was locked between step 1's return and here (concurrent lock-route).\n // Fail-fast as a derive-key error rather than silently degrading later\n // env construction with empty-string secrets (P4).\n const mnemonicSnapshot = deps.wallet.getMnemonic();\n if (mnemonicSnapshot === null) {\n const errMsg =\n 'Wallet locked between step 1 and step 4 — refusing to start container without mnemonic';\n request.log.error(\n {\n event: 'node_lifecycle_failure',\n step: 'derive-key',\n err: errMsg,\n },\n 'Step 1 post-condition failed: mnemonic gone after derive'\n );\n return reply.status(500).send({ step: 'derive-key', err: errMsg });\n }\n // apex EVM address from the town node key (account 0 = primary wallet).\n const apexEvmAddress = deps.wallet.getNodeKeys('town').evmAddress;\n\n // ── Step 2: pull-image ─────────────────────────────────────────────\n request.log.info(\n { event: 'node_lifecycle_step', step: 'pull-image', type, peerId },\n 'Step 2: pulling image'\n );\n try {\n const manifest = await readImageManifest(imageManifestPath);\n const entry = manifest.images[type];\n if (isSyntheticDigest(entry.digest)) {\n return reply.status(400).send({\n step: 'pull-image',\n err: `Synthetic-digest manifest: image-manifest.json was produced by the connector-publish-smoke workflow for smoke testing only. Fetch a real manifest via 'gh run download' or rerun without --skip-fetch before provisioning nodes.`,\n });\n }\n const imageRef = `${entry.name}@${entry.digest}`;\n await deps.orchestrator.pullImage(imageRef);\n } catch (err: unknown) {\n const errMsg = sanitizeErrorMessage(\n err instanceof Error ? err.message : String(err)\n );\n request.log.error(\n {\n event: 'node_lifecycle_failure',\n step: 'pull-image',\n err: errMsg,\n },\n 'Step 2 failed: pull-image'\n );\n return reply.status(502).send({ step: 'pull-image', err: errMsg });\n }\n\n // ── Step 3: write-yaml (BEFORE connector registration — invariant) ──\n request.log.info(\n { event: 'node_lifecycle_step', step: 'write-yaml', type, peerId },\n 'Step 3: writing nodes.yaml entry'\n );\n const enabledAt = new Date().toISOString();\n const newEntry = {\n id,\n type,\n peerId,\n ilpAddress,\n derivationIndex,\n enabledAt,\n lastSeenAt: null,\n };\n try {\n await writeNodesYaml(nodesYamlPath, {\n entries: [...yaml.entries, newEntry],\n });\n } catch (err: unknown) {\n const errMsg = sanitizeErrorMessage(\n err instanceof Error ? err.message : String(err)\n );\n request.log.error(\n {\n event: 'node_lifecycle_failure',\n step: 'write-yaml',\n err: errMsg,\n },\n 'Step 3 failed: write-yaml'\n );\n return reply.status(500).send({ step: 'write-yaml', err: errMsg });\n }\n\n // ── Step 3b: mill — write mill.config.json (same rollback bucket as step 3) ──\n let millConfigWritten = false;\n if (type === 'mill') {\n // MILL_RELAYS pre-check already ran in pre-checks above — guaranteed set here.\n try {\n const defaultMillConfig = JSON.stringify(\n buildMillSwapPairConfig(deps.config),\n null,\n 2\n );\n await fs.mkdir(dirname(millConfigPath), {\n recursive: true,\n mode: 0o700,\n });\n // mkdir mode is a no-op on existing dirs — chmod explicitly so the\n // parent is 0o700 even if it pre-existed (P3).\n await fs.chmod(dirname(millConfigPath), 0o700);\n await fs.writeFile(millConfigPath, defaultMillConfig, {\n encoding: 'utf-8',\n mode: 0o600,\n });\n millConfigWritten = true;\n } catch (err: unknown) {\n const errMsg = sanitizeErrorMessage(\n err instanceof Error ? err.message : String(err)\n );\n request.log.error(\n {\n event: 'node_lifecycle_failure',\n step: 'write-mill-config',\n err: errMsg,\n },\n 'Step 3b failed: write mill.config.json'\n );\n // Rollback: remove any partial mill.config.json first, then the yaml entry.\n const rollbackMillError = await safeRollbackMillConfig(\n millConfigPath,\n request\n );\n const rollbackYamlError = await safeRollbackYaml(\n nodesYamlPath,\n peerId,\n request\n );\n const rollbackError = combineRollbackErrors(\n rollbackMillError,\n rollbackYamlError\n );\n return reply\n .status(500)\n .send({ step: 'write-mill-config', err: errMsg, rollbackError });\n }\n }\n\n // ── Step 4: start-container ────────────────────────────────────────\n request.log.info(\n {\n event: 'node_lifecycle_step',\n step: 'start-container',\n type,\n peerId,\n },\n 'Step 4: starting container via compose'\n );\n const nodeEnv = buildNodeEnv(\n type,\n nostrSecretKeyHex,\n evmPrivateKeyHex,\n mnemonicSnapshot,\n apexEvmAddress\n );\n try {\n await deps.orchestrator.startNodeViaCompose(type, nodeEnv);\n } catch (err: unknown) {\n const errMsg = sanitizeErrorMessage(\n err instanceof Error ? err.message : String(err)\n );\n request.log.error(\n {\n event: 'node_lifecycle_failure',\n step: 'start-container',\n err: errMsg,\n },\n 'Step 4 failed: start-container'\n );\n // Rollback: remove yaml entry (+ mill config)\n const rollbackError = await safeRollbackYaml(\n nodesYamlPath,\n peerId,\n request\n );\n let rollbackMillError: string | undefined;\n if (millConfigWritten) {\n rollbackMillError = await safeRollbackMillConfig(\n millConfigPath,\n request\n );\n }\n const combinedRollbackError = combineRollbackErrors(\n rollbackError,\n rollbackMillError\n );\n return reply.status(502).send({\n step: 'start-container',\n err: errMsg,\n rollbackError: combinedRollbackError,\n });\n }\n\n // ── Step 5: healthcheck ────────────────────────────────────────────\n request.log.info(\n {\n event: 'node_lifecycle_step',\n step: 'healthcheck',\n type,\n peerId,\n healthCheckUrl,\n },\n 'Step 5: waiting for container to become healthy'\n );\n try {\n await waitForHealthy(healthCheckUrl, 60_000);\n } catch (err: unknown) {\n const errMsg = sanitizeErrorMessage(\n err instanceof Error ? err.message : String(err)\n );\n request.log.error(\n {\n event: 'node_lifecycle_failure',\n step: 'healthcheck',\n err: errMsg,\n },\n 'Step 5 failed: healthcheck'\n );\n // Rollback: remove yaml entry + stop container + mill config\n const rollbackYamlError = await safeRollbackYaml(\n nodesYamlPath,\n peerId,\n request\n );\n const rollbackStopError = await safeRollbackStop(\n type,\n deps.orchestrator,\n request\n );\n let rollbackMillError: string | undefined;\n if (millConfigWritten) {\n rollbackMillError = await safeRollbackMillConfig(\n millConfigPath,\n request\n );\n }\n const combinedRollbackError = combineRollbackErrors(\n rollbackYamlError,\n rollbackStopError,\n rollbackMillError\n );\n return reply.status(502).send({\n step: 'healthcheck',\n err: errMsg,\n rollbackError: combinedRollbackError,\n });\n }\n\n // ── Step 6: register-peer ──────────────────────────────────────────\n request.log.info(\n {\n event: 'node_lifecycle_step',\n step: 'register-peer',\n type,\n peerId,\n ilpAddress,\n },\n 'Step 6: registering peer with connector'\n );\n try {\n await deps.connectorAdmin.registerPeer({\n id: peerId,\n url: btpUrl,\n authToken: '',\n routes: [{ prefix: ilpAddress, priority: 0 }],\n // Force direct (non-SOCKS5) BTP dial for this Docker-sibling\n // peer. The apex connector runs with `transport.type: socks5`\n // so the .anyone HS can publish; without this override, every\n // peer dial gets routed through the anon proxy and fails with\n // `HostUnreachable` on Docker-internal hostnames. Requires\n // connector >= 3.6.2 (toon-protocol/connector#70). Discovered\n // by Story 46.4 live gate run (Finding Q, 2026-05-12).\n transport: 'direct',\n });\n } catch (err: unknown) {\n const errMsg = sanitizeErrorMessage(\n err instanceof Error ? err.message : String(err)\n );\n request.log.error(\n {\n event: 'node_lifecycle_failure',\n step: 'register-peer',\n err: errMsg,\n },\n 'Step 6 failed: register-peer'\n );\n // Rollback: remove yaml entry + stop container + mill config\n const rollbackYamlError = await safeRollbackYaml(\n nodesYamlPath,\n peerId,\n request\n );\n const rollbackStopError = await safeRollbackStop(\n type,\n deps.orchestrator,\n request\n );\n let rollbackMillError: string | undefined;\n if (millConfigWritten) {\n rollbackMillError = await safeRollbackMillConfig(\n millConfigPath,\n request\n );\n }\n const combinedRollbackError = combineRollbackErrors(\n rollbackYamlError,\n rollbackStopError,\n rollbackMillError\n );\n return reply.status(502).send({\n step: 'register-peer',\n err: errMsg,\n rollbackError: combinedRollbackError,\n });\n }\n\n request.log.info(\n { event: 'node_lifecycle_success', type, peerId, ilpAddress },\n 'Node provisioned successfully'\n );\n\n return reply.status(201).send({\n id,\n type,\n peerId,\n ilpAddress,\n hsRoute: ilpAddress,\n healthCheckUrl,\n });\n } finally {\n releaseNodeLifecycleMutex();\n }\n }\n );\n\n // ── DELETE /api/nodes/:id ──────────────────────────────────────────────────\n\n app.delete<{ Params: { id: string } }>(\n '/api/nodes/:id',\n {\n schema: {\n params: {\n type: 'object',\n required: ['id'],\n properties: {\n id: {\n type: 'string',\n minLength: 1,\n maxLength: 64,\n pattern: '^[a-z][a-z0-9-]*$',\n },\n },\n },\n },\n },\n async (request, reply) => {\n if (!acquireNodeLifecycleMutex()) {\n return reply.status(409).send({ error: 'node_lifecycle_in_flight' });\n }\n\n try {\n const { id } = request.params;\n const homeDir = dirname(deps.configPath);\n const nodesYamlPath = join(homeDir, 'nodes.yaml');\n const millConfigPath = join(homeDir, 'mill.config.json');\n\n const yaml = await readNodesYaml(nodesYamlPath);\n const entry = yaml.entries.find((e) => e.id === id);\n if (!entry) {\n return reply.status(404).send({ error: 'unknown_node', id });\n }\n\n // Reverse pipeline — each step is idempotent\n\n // Step 1: deregister from connector FIRST (stop routing before stopping container)\n request.log.info(\n {\n event: 'node_lifecycle_step',\n step: 'deregister-peer',\n type: entry.type,\n peerId: entry.peerId,\n },\n 'DELETE step 1: deregistering peer from connector'\n );\n try {\n await deps.connectorAdmin.removePeer(entry.peerId);\n } catch (err: unknown) {\n const errMsg = sanitizeErrorMessage(\n err instanceof Error ? err.message : String(err)\n );\n request.log.error(\n {\n event: 'node_lifecycle_failure',\n step: 'deregister-peer',\n err: errMsg,\n },\n 'DELETE step 1 failed: deregister-peer'\n );\n return reply\n .status(502)\n .send({ step: 'deregister-peer', err: errMsg });\n }\n\n // Step 2: stop + remove container (idempotent — not running = no-op)\n request.log.info(\n {\n event: 'node_lifecycle_step',\n step: 'stop-container',\n type: entry.type,\n },\n 'DELETE step 2: stopping container'\n );\n try {\n await deps.orchestrator.stopNodeViaCompose(entry.type);\n } catch (err: unknown) {\n const errMsg = sanitizeErrorMessage(\n err instanceof Error ? err.message : String(err)\n );\n request.log.error(\n {\n event: 'node_lifecycle_failure',\n step: 'stop-container',\n err: errMsg,\n },\n 'DELETE step 2 failed: stop-container'\n );\n return reply\n .status(502)\n .send({ step: 'stop-container', err: errMsg });\n }\n\n // Step 3: remove yaml entry (idempotent — if entry not present, writes same array)\n request.log.info(\n {\n event: 'node_lifecycle_step',\n step: 'remove-yaml',\n type: entry.type,\n },\n 'DELETE step 3: removing nodes.yaml entry'\n );\n try {\n await writeNodesYaml(nodesYamlPath, {\n entries: yaml.entries.filter((e) => e.id !== id),\n });\n } catch (err: unknown) {\n const errMsg = sanitizeErrorMessage(\n err instanceof Error ? err.message : String(err)\n );\n request.log.error(\n {\n event: 'node_lifecycle_failure',\n step: 'remove-yaml',\n err: errMsg,\n },\n 'DELETE step 3 failed: remove-yaml'\n );\n // P1: yaml write failure is disk-class (500), not docker/connector (502).\n return reply.status(500).send({ step: 'remove-yaml', err: errMsg });\n }\n\n // Step 3b: mill only — remove mill.config.json (force: true = idempotent)\n if (entry.type === 'mill') {\n await fs.rm(millConfigPath, { force: true });\n }\n\n request.log.info(\n { event: 'node_lifecycle_deleted', id, type: entry.type },\n 'Node deprovisioned successfully'\n );\n\n return reply.status(200).send({ id, type: entry.type });\n } finally {\n releaseNodeLifecycleMutex();\n }\n }\n );\n}\n\n// ── Rollback helpers ───────────────────────────────────────────────────────\n// Each helper is \"safe\" — rollback failures are logged AND returned as a\n// string so the route can surface them in the response body's `rollbackError`\n// field (P6). The original step's HTTP response is still returned regardless —\n// the rollback error is auxiliary, not a second error class. Returns undefined\n// on rollback success.\n\ntype RollbackRequest = Pick<FastifyRequest, 'log'>;\n\n/**\n * Remove the entry we just added from nodes.yaml.\n *\n * P13: re-reads the file inside the helper and filters out only `addedPeerId`\n * (instead of restoring a pre-pipeline snapshot). This is robust to external\n * edits during the up-to-60s healthcheck window — anything an operator or\n * another writer added stays intact, while our entry is removed.\n *\n * Idempotent: entry already absent is a no-op write of the same array.\n */\nasync function safeRollbackYaml(\n nodesYamlPath: string,\n addedPeerId: string,\n request: RollbackRequest\n): Promise<string | undefined> {\n try {\n const current = await readNodesYaml(nodesYamlPath);\n const filtered = current.entries.filter((e) => e.peerId !== addedPeerId);\n await writeNodesYaml(nodesYamlPath, { entries: filtered });\n return undefined;\n } catch (err: unknown) {\n const errMsg = sanitizeErrorMessage(\n err instanceof Error ? err.message : String(err)\n );\n request.log.error(\n {\n event: 'node_lifecycle_rollback_failure',\n step: 'write-yaml',\n err: errMsg,\n },\n 'Rollback: failed to remove yaml entry — operator may need to hand-edit nodes.yaml'\n );\n return `write-yaml: ${errMsg}`;\n }\n}\n\nasync function safeRollbackMillConfig(\n millConfigPath: string,\n request: RollbackRequest\n): Promise<string | undefined> {\n try {\n await fs.rm(millConfigPath, { force: true });\n return undefined;\n } catch (err: unknown) {\n const errMsg = sanitizeErrorMessage(\n err instanceof Error ? err.message : String(err)\n );\n request.log.error(\n {\n event: 'node_lifecycle_rollback_failure',\n step: 'remove-mill-config',\n err: errMsg,\n },\n 'Rollback: failed to remove mill.config.json'\n );\n return `remove-mill-config: ${errMsg}`;\n }\n}\n\nasync function safeRollbackStop(\n type: NodeType,\n orchestrator: ApiDeps['orchestrator'],\n request: RollbackRequest\n): Promise<string | undefined> {\n try {\n await orchestrator.stopNodeViaCompose(type);\n return undefined;\n } catch (err: unknown) {\n const errMsg = sanitizeErrorMessage(\n err instanceof Error ? err.message : String(err)\n );\n request.log.error(\n {\n event: 'node_lifecycle_rollback_failure',\n step: 'stop-container',\n err: errMsg,\n },\n 'Rollback: failed to stop container — operator may need to docker rm by hand'\n );\n return `stop-container: ${errMsg}`;\n }\n}\n\n/**\n * Collapse one-or-more rollback-error strings into a single response-body\n * field. Returns undefined when every rollback step succeeded so the field\n * stays absent in the JSON.\n */\nfunction combineRollbackErrors(\n ...errors: (string | undefined)[]\n): string | undefined {\n const present = errors.filter((e): e is string => e !== undefined);\n if (present.length === 0) return undefined;\n return present.join('; ');\n}\n\n// Matches `KEY=value` where KEY is a known-secret name; redacts the value\n// up to the next whitespace, quote, or newline. Compiled once at module scope\n// so repeated error-path calls don't re-join the array and re-compile the RegExp.\nconst SECRET_KEYS = [\n 'TOWN_SECRET_KEY',\n 'MILL_SECRET_KEY',\n 'DVM_SECRET_KEY',\n 'TOWN_SETTLEMENT_PRIVATE_KEY',\n 'MILL_SETTLEMENT_PRIVATE_KEY',\n 'DVM_SETTLEMENT_PRIVATE_KEY',\n 'MILL_MNEMONIC',\n 'TOWNHOUSE_WALLET_PASSWORD',\n];\nconst REDACT_RE = new RegExp(`(${SECRET_KEYS.join('|')})=[^\\\\s\"'\\\\n\\\\r]+`, 'g');\n\n/**\n * Strip secret-name env assignments from an error message before it reaches\n * the HTTP response body (P5). Compose stderr can echo env interpolation in\n * error output; secrets injected via `startNodeViaCompose` would otherwise\n * surface to the API client. Conservative: replace the VALUE not the key, so\n * operators still see WHICH secret was involved.\n */\nfunction sanitizeErrorMessage(msg: string): string {\n return msg.replace(REDACT_RE, '$1=[REDACTED]');\n}\n","/**\n * WebSocket metrics route: WS /metrics\n *\n * Supports an optional `?subscribe=...` query parameter for relay-event subscriptions.\n * Format: `?subscribe=relayEvents:<nodeId>,relayEvents:<nodeId2>,...`\n * For each `relayEvents:<nodeId>` subscription, the server opens an upstream WebSocket\n * to the Town container's relay and forwards Nostr events to the client.\n */\n\nimport type { FastifyInstance } from 'fastify';\nimport { WebSocket } from 'ws';\nimport { decode as decodeToon } from '@toon-format/toon';\nimport type { ApiDeps, WsMessage, NostrEventPayload } from '../types.js';\n\n/** Maximum buffer size before dropping old messages */\nconst MAX_BUFFER_SIZE = 100;\n\n/** Track open WebSocket connections for graceful shutdown (AC #10) */\nconst openWebSockets = new Set<WebSocket>();\n\n/** Get the set of open WebSocket connections (for server.ts close()) */\nexport function getOpenWebSockets(): Set<WebSocket> {\n return openWebSockets;\n}\n\n/** Flush interval for message batching (ms) */\nconst FLUSH_INTERVAL_MS = 100;\n\n/** Metrics poll interval (ms) */\nconst METRICS_POLL_INTERVAL_MS = 1000;\n\n/** Heartbeat interval (ms) */\nconst HEARTBEAT_INTERVAL_MS = 15000;\n\n/**\n * Parse `relayEvents:<nodeId>` subscription tokens from the `?subscribe=` query param.\n * Returns an array of nodeIds to subscribe to.\n */\nfunction parseRelayEventSubscriptions(queryString: string): string[] {\n if (!queryString) return [];\n const params = new URLSearchParams(queryString);\n const subscribeParam = params.get('subscribe');\n if (!subscribeParam) return [];\n\n const nodeIds: string[] = [];\n for (const token of subscribeParam.split(',')) {\n const trimmed = token.trim();\n if (trimmed.startsWith('relayEvents:')) {\n const nodeId = trimmed.slice('relayEvents:'.length);\n if (nodeId) nodeIds.push(nodeId);\n }\n }\n return nodeIds;\n}\n\n/**\n * Register WebSocket metrics route.\n */\nexport function registerMetricsWsRoutes(\n app: FastifyInstance,\n deps: ApiDeps\n): void {\n // Register websocket plugin handler\n app.get('/metrics', { websocket: true }, (socket, request) => {\n const logger = deps.logger ?? app.log;\n logger.info({ client: request.ip }, 'WebSocket client connected');\n\n // Track this connection for graceful shutdown\n openWebSockets.add(socket);\n\n // Per-socket message buffer\n const messageBuffer: WsMessage[] = [];\n\n // Track timers for cleanup\n const timers: NodeJS.Timeout[] = [];\n\n // Per-client upstream relay WebSocket connections: nodeId → WebSocket\n const upstreamSockets = new Map<string, WebSocket>();\n\n // ── relayEvents subscriptions ─────────────────────────────────────────────\n const rawQuery = request.url.includes('?')\n ? request.url.slice(request.url.indexOf('?') + 1)\n : '';\n const relayNodeIds = parseRelayEventSubscriptions(rawQuery);\n\n // Open upstream WS for each subscribed nodeId (async, non-blocking)\n if (relayNodeIds.length > 0) {\n void (async () => {\n for (const nodeId of relayNodeIds) {\n if (socket.readyState !== WebSocket.OPEN) break;\n try {\n const relayUrl =\n await deps.orchestrator.getNodeRelayEndpoint(nodeId);\n const upstream = new WebSocket(relayUrl);\n upstreamSockets.set(nodeId, upstream);\n\n // Track the latest event timestamp for incremental polling.\n // Start 60 s back to show recent events on connect.\n let sinceTs = Math.floor(Date.now() / 1000) - 60;\n // Dedup events already forwarded in this session. Capped to prevent\n // unbounded memory growth on long-lived connections to busy relays.\n const MAX_SEEN_IDS = 10_000;\n const seenEventIds = new Set<string>();\n const subId = `live-${nodeId}`;\n\n function sendReq() {\n if (upstream.readyState === WebSocket.OPEN) {\n upstream.send(\n JSON.stringify([\n 'REQ',\n subId,\n {\n kinds: [0, 1, 6, 7, 9735],\n since: sinceTs,\n },\n ])\n );\n }\n }\n\n // Subscribe for live events once the upstream relay accepts the connection.\n // Also poll every 5 s via re-REQ so events stored via /handle-packet (which\n // currently do not trigger broadcastEvent in older relay builds) are surfaced.\n let pollTimer: NodeJS.Timeout | null = null;\n upstream.on('open', () => {\n sendReq();\n pollTimer = setInterval(sendReq, 5_000);\n });\n\n upstream.on('message', (data: Buffer | string) => {\n if (socket.readyState !== WebSocket.OPEN) return;\n try {\n const msg = JSON.parse(data.toString()) as unknown;\n let payload: NostrEventPayload | undefined;\n\n if (Array.isArray(msg) && msg[0] === 'EVENT' && msg[2]) {\n // Real relay: NIP-01 [\"EVENT\", sub_id, toon_string]\n payload = decodeToon(\n msg[2] as string\n ) as unknown as NostrEventPayload;\n } else if (\n msg !== null &&\n typeof msg === 'object' &&\n !Array.isArray(msg)\n ) {\n // Test stub: raw JSON event object\n payload = msg as NostrEventPayload;\n }\n\n if (payload) {\n const eventId = (payload as { id?: string }).id ?? '';\n if (eventId && seenEventIds.has(eventId)) return; // dedup\n if (eventId) {\n seenEventIds.add(eventId);\n if (seenEventIds.size > MAX_SEEN_IDS) {\n const oldest = seenEventIds.values().next().value;\n if (oldest !== undefined) seenEventIds.delete(oldest);\n }\n }\n // Advance sinceTs so the next poll only fetches newer events.\n const createdAt = (payload as { created_at?: number })\n .created_at;\n if (typeof createdAt === 'number' && createdAt >= sinceTs) {\n sinceTs = createdAt;\n }\n addToBuffer({\n type: 'relayEvents',\n nodeId,\n payload,\n ts: Date.now(),\n });\n }\n } catch {\n // Malformed or non-event message from upstream relay — skip\n }\n });\n\n upstream.on('error', (err) => {\n logger.warn({ err, nodeId }, 'Upstream relay WS error');\n });\n\n upstream.on('close', () => {\n if (pollTimer) {\n clearInterval(pollTimer);\n pollTimer = null;\n }\n upstreamSockets.delete(nodeId);\n // Notify the dashboard client so it can surface the AC-7 error UI\n // instead of silently showing an empty \"No events yet\" feed.\n addToBuffer({\n type: 'relayEventsStatus',\n nodeId,\n connected: false,\n ts: Date.now(),\n });\n });\n } catch (err) {\n logger.warn({ err, nodeId }, 'Failed to open upstream relay WS');\n }\n }\n })();\n }\n\n // ── Metrics poll ──────────────────────────────────────────────────────────\n let metricsPollPending = false;\n const metricsTimer = setInterval(async () => {\n if (metricsPollPending) {\n return; // Skip this poll, previous one still in flight\n }\n metricsPollPending = true;\n try {\n const metricsRes = await deps.connectorAdmin.getMetrics();\n if (metricsRes) {\n // ConnectorAdminClient.getMetrics() returns the connector's\n // /admin/metrics.json shape; aggregate counters live under `aggregate`.\n const payload = {\n packetsForwarded: metricsRes.aggregate.packetsForwarded,\n packetsRejected: metricsRes.aggregate.packetsRejected,\n bytesSent: metricsRes.aggregate.bytesSent,\n attribution: 'aggregate' as const,\n available: true,\n };\n addToBuffer({\n type: 'metrics',\n payload,\n ts: Date.now(),\n });\n }\n } catch {\n // Connector down or /metrics not available — add unavailable marker\n addToBuffer({\n type: 'metrics',\n payload: {\n packetsForwarded: 0,\n packetsRejected: 0,\n bytesSent: 0,\n attribution: 'aggregate',\n available: false,\n },\n ts: Date.now(),\n });\n } finally {\n metricsPollPending = false;\n }\n }, METRICS_POLL_INTERVAL_MS);\n timers.push(metricsTimer);\n\n // ── Container state event listeners ───────────────────────────────────────\n\n const onContainerState = (data: { name: string; state: string }) => {\n addToBuffer({\n type: 'nodeState',\n payload: data,\n ts: Date.now(),\n });\n };\n deps.orchestrator.on('containerState', onContainerState);\n\n // Pull progress events\n const onPullProgress = (data: {\n image: string;\n status: string;\n progress?: string;\n }) => {\n addToBuffer({\n type: 'nodeState',\n payload: { name: `pull:${data.image}`, state: data.status },\n ts: Date.now(),\n });\n };\n deps.orchestrator.on('pullProgress', onPullProgress);\n\n // Connector restart events (AC-12 in story 21.10)\n const onConnectorRestarting = () => {\n addToBuffer({\n type: 'connectorRestarting',\n ts: Date.now(),\n });\n };\n deps.orchestrator.on('connectorRestarting', onConnectorRestarting);\n\n const onConnectorRestarted = () => {\n addToBuffer({\n type: 'connectorRestarted',\n ts: Date.now(),\n });\n // Also emit legacy nodeState for backward compat with older clients\n addToBuffer({\n type: 'nodeState',\n payload: { name: 'connector', state: 'restarted' },\n ts: Date.now(),\n });\n };\n deps.orchestrator.on('connectorRestarted', onConnectorRestarted);\n\n // ── Heartbeat ─────────────────────────────────────────────────────────────\n\n const heartbeatTimer = setInterval(() => {\n addToBuffer({\n type: 'heartbeat',\n ts: Date.now(),\n });\n }, HEARTBEAT_INTERVAL_MS);\n timers.push(heartbeatTimer);\n\n // ── Flush ─────────────────────────────────────────────────────────────────\n\n const flushTimer = setInterval(() => {\n flushBuffer(socket);\n }, FLUSH_INTERVAL_MS);\n timers.push(flushTimer);\n\n // Add message to buffer with backpressure handling\n function addToBuffer(message: WsMessage): void {\n messageBuffer.push(message);\n\n // If buffer exceeds max, drop oldest metrics (keep latest)\n if (messageBuffer.length > MAX_BUFFER_SIZE) {\n const idx = messageBuffer.findIndex((m) => m.type === 'metrics');\n if (idx >= 0) {\n messageBuffer.splice(idx, 1);\n } else {\n messageBuffer.shift();\n }\n }\n }\n\n // Flush buffer with batching logic\n function flushBuffer(_ws: WebSocket): void {\n if (messageBuffer.length === 0) {\n return;\n }\n\n if (socket.readyState !== WebSocket.OPEN) {\n return;\n }\n\n if (messageBuffer.length === 1) {\n socket.send(JSON.stringify(messageBuffer[0]));\n } else {\n // Batch multiple messages\n const batch: WsMessage = {\n type: 'batch',\n messages: [...messageBuffer],\n ts: Date.now(),\n };\n socket.send(JSON.stringify(batch));\n }\n\n messageBuffer.length = 0;\n }\n\n // ── Incoming messages (unsubscribe) ──────────────────────────────────────\n\n socket.on('message', (data: Buffer | string) => {\n try {\n const msg = JSON.parse(data.toString()) as {\n type?: string;\n nodeId?: string;\n };\n if (msg.type === 'unsubscribe' && typeof msg.nodeId === 'string') {\n const upstream = upstreamSockets.get(msg.nodeId);\n if (upstream) {\n try {\n upstream.close();\n } catch {\n /* best-effort */\n }\n upstreamSockets.delete(msg.nodeId);\n }\n }\n } catch {\n // Malformed control message — ignore\n }\n });\n\n // ── Cleanup on disconnect ─────────────────────────────────────────────────\n\n socket.on('close', () => {\n logger.info({ client: request.ip }, 'WebSocket client disconnected');\n\n // Remove from tracking set\n openWebSockets.delete(socket);\n\n // Clear all timers\n for (const timer of timers) {\n clearInterval(timer);\n }\n\n // Remove orchestrator event listeners\n deps.orchestrator.off('containerState', onContainerState);\n deps.orchestrator.off('pullProgress', onPullProgress);\n deps.orchestrator.off('connectorRestarting', onConnectorRestarting);\n deps.orchestrator.off('connectorRestarted', onConnectorRestarted);\n\n // Close all upstream relay WebSocket connections\n for (const [, ws] of upstreamSockets) {\n try {\n ws.close();\n } catch {\n // best-effort\n }\n }\n upstreamSockets.clear();\n });\n\n // Handle socket errors\n socket.on('error', (error) => {\n logger.error({ err: error, client: request.ip }, 'WebSocket error');\n });\n });\n}\n","/**\n * Wizard API routes: GET /wizard/state, POST /wizard/mnemonic-preview,\n * POST /wizard/init, WS /wizard/progress.\n *\n * SECURITY: Mnemonic and password are never logged, never stored in module scope.\n */\n\nimport { existsSync, unlinkSync, chmodSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport type { FastifyInstance } from 'fastify';\nimport type { IncomingMessage } from 'node:http';\nimport { WebSocket } from 'ws';\nimport { generateMnemonic, validateMnemonic } from '@scure/bip39';\nimport { wordlist } from '@scure/bip39/wordlists/english.js';\nimport type { WizardInitRequest, WizardProgressMessage } from '../types.js';\nimport {\n WalletManager,\n encryptWallet,\n saveWallet,\n} from '../../wallet/index.js';\nimport { saveConfig } from '../../config/loader.js';\nimport { getDefaultConfig } from '../../config/defaults.js';\nimport type { TownhouseConfig } from '../../config/schema.js';\nimport type { NodeType } from '../../docker/types.js';\n\nexport interface WizardDeps {\n configPath: string;\n walletPath: string;\n}\n\nexport interface WizardTransitionState {\n mode: 'wizard' | 'normal';\n /** Buffered progress messages for late-connecting WS clients (wizard mode only) */\n progressBuffer?: WizardProgressMessage[];\n /** Active WS connections to forward events to (wizard mode only) */\n progressSockets?: Set<WebSocket>;\n /** Single in-flight init guard — prevents concurrent POST /wizard/init from racing past existsSync */\n initInFlight?: boolean;\n}\n\nexport type OnInitCallback = (\n config: TownhouseConfig,\n wallet: WalletManager,\n profiles: NodeType[]\n) => Promise<void>;\n\n/** Cap progress buffer to bound memory during long Docker pulls */\nexport const PROGRESS_BUFFER_MAX = 200;\n\n/** Allowed Origin hostnames for cross-origin WS upgrades on the wizard */\nconst ALLOWED_WS_ORIGIN_HOSTS = new Set([\n 'localhost',\n '127.0.0.1',\n '[::1]',\n '::1',\n]);\n\nfunction isAllowedWsOrigin(origin: string | undefined): boolean {\n if (!origin) return true; // same-origin WS request has no Origin header in some clients; the OS-level loopback bind already restricts\n try {\n const url = new URL(origin);\n return ALLOWED_WS_ORIGIN_HOSTS.has(url.hostname);\n } catch {\n return false;\n }\n}\n\n/**\n * Register wizard-scoped routes.\n * In 'wizard' mode: all wizard routes are active.\n * In 'normal' mode: only GET /wizard/state returns normal-mode payload; other routes refuse.\n */\nexport function registerWizardRoutes(\n app: FastifyInstance,\n deps: WizardDeps,\n state: WizardTransitionState,\n onInit?: OnInitCallback\n): void {\n // GET /wizard/state — registered in BOTH wizard and normal mode\n app.get('/wizard/state', async (_request, reply) => {\n const configExists = existsSync(deps.configPath);\n const walletExists = existsSync(deps.walletPath);\n // `containers_running` reflects whether the wizard server has transitioned\n // to normal mode (i.e., orchestrator.up() resolved at least once). It does\n // NOT track live container health — a container that dies post-transition\n // will not flip this back to false. The SPA uses this to know it can navigate\n // off /wizard, not as a liveness indicator.\n const containersRunning = state.mode === 'normal';\n\n return reply.status(200).send({\n config_exists: configExists,\n wallet_exists: walletExists,\n containers_running: containersRunning,\n mode: state.mode,\n ts: Date.now(),\n });\n });\n\n // POST /wizard/mnemonic-preview — stateless preview; disabled after transition\n app.post('/wizard/mnemonic-preview', async (_request, reply) => {\n if (state.mode === 'normal') {\n return reply.status(503).send({ error: 'wizard_already_completed' });\n }\n // SECURITY: mnemonic generated fresh per request, never logged, never stored\n const mnemonic = generateMnemonic(wordlist, 128);\n return reply.status(200).send({ mnemonic });\n });\n\n // POST /wizard/init — validate, write wallet + config, fire async launch\n app.post('/wizard/init', async (request, reply) => {\n if (state.mode === 'normal') {\n return reply.status(409).send({\n code: 'wizard_already_completed',\n message: 'Setup is already complete.',\n });\n }\n\n // Concurrent-init guard: serialize against double-clicks and parallel POSTs.\n // The `wallet.enc` write below also uses O_EXCL ('wx') as a TOCTOU backstop.\n if (state.initInFlight) {\n return reply.status(409).send({\n code: 'init_in_flight',\n message: 'A setup is already in progress.',\n });\n }\n state.initInFlight = true;\n\n try {\n const body = request.body as Partial<WizardInitRequest> | null;\n if (!body || typeof body !== 'object') {\n return reply\n .status(400)\n .send({ code: 'invalid_request', message: 'Request body required.' });\n }\n\n // Validate password — boundary-trim trailing whitespace check; passwords with leading/trailing\n // whitespace are silently corrupted by browser autofill and cannot be decrypted later.\n const password = body.password;\n if (\n typeof password !== 'string' ||\n password.length === 0 ||\n password.length > 256\n ) {\n return reply.status(400).send({\n code: 'password_invalid',\n message: 'password must be 1–256 characters.',\n });\n }\n if (password !== password.trim()) {\n return reply.status(400).send({\n code: 'password_invalid',\n message: 'password cannot have leading or trailing whitespace.',\n });\n }\n if (password !== body.password_confirm) {\n return reply.status(400).send({\n code: 'password_mismatch',\n message: 'Passwords do not match.',\n });\n }\n\n // Validate mnemonic_mode\n const mnemonicMode = body.mnemonic_mode;\n if (mnemonicMode !== 'generate' && mnemonicMode !== 'import') {\n return reply.status(400).send({\n code: 'mnemonic_mode_invalid',\n message: 'mnemonic_mode must be \"generate\" or \"import\".',\n });\n }\n\n // Validate mnemonic (both modes require a valid phrase — server validates regardless of source)\n const mnemonic = body.mnemonic;\n if (\n !mnemonic ||\n typeof mnemonic !== 'string' ||\n mnemonic.trim().length === 0\n ) {\n return reply\n .status(400)\n .send({ code: 'mnemonic_invalid', message: 'mnemonic is required.' });\n }\n if (!validateMnemonic(mnemonic.trim(), wordlist)) {\n return reply.status(400).send({\n code: 'mnemonic_invalid',\n message: 'Invalid BIP-39 mnemonic.',\n });\n }\n const cleanMnemonic = mnemonic.trim();\n\n // backup_ack enforcement (Risk R-022) — applies to both generate and import modes\n if (body.backup_ack !== true) {\n return reply.status(400).send({\n code: 'backup_not_acknowledged',\n message:\n 'You must confirm you have backed up your seed phrase before continuing.',\n });\n }\n\n // Validate nodes\n const nodes = body.nodes;\n if (!nodes || typeof nodes !== 'object') {\n return reply\n .status(400)\n .send({ code: 'no_nodes_selected', message: 'nodes is required.' });\n }\n const atLeastOne =\n nodes.town?.enabled || nodes.mill?.enabled || nodes.dvm?.enabled;\n if (!atLeastOne) {\n return reply.status(400).send({\n code: 'no_nodes_selected',\n message: 'At least one node must be enabled.',\n });\n }\n\n // Validate fee ranges\n if (nodes.town?.enabled && nodes.town.feePerEvent !== undefined) {\n if (\n !Number.isInteger(nodes.town.feePerEvent) ||\n nodes.town.feePerEvent < 0 ||\n nodes.town.feePerEvent > 1000\n ) {\n return reply.status(400).send({\n code: 'fee_out_of_range',\n message: 'nodes.town.feePerEvent must be 0–1000.',\n });\n }\n }\n if (nodes.mill?.enabled && nodes.mill.feeBasisPoints !== undefined) {\n if (\n !Number.isInteger(nodes.mill.feeBasisPoints) ||\n nodes.mill.feeBasisPoints < 0 ||\n nodes.mill.feeBasisPoints > 100\n ) {\n return reply.status(400).send({\n code: 'fee_out_of_range',\n message: 'nodes.mill.feeBasisPoints must be 0–100.',\n });\n }\n }\n if (nodes.dvm?.enabled && nodes.dvm.feePerJob !== undefined) {\n if (\n !Number.isInteger(nodes.dvm.feePerJob) ||\n nodes.dvm.feePerJob < 0 ||\n nodes.dvm.feePerJob > 100000\n ) {\n return reply.status(400).send({\n code: 'fee_out_of_range',\n message: 'nodes.dvm.feePerJob must be 0–100000.',\n });\n }\n }\n\n // Validate transport\n const transport = body.transport;\n if (\n !transport ||\n (transport.mode !== 'direct' && transport.mode !== 'ator')\n ) {\n return reply.status(400).send({\n code: 'transport_invalid',\n message: 'transport.mode must be \"direct\" or \"ator\".',\n });\n }\n\n // Conflict checks — TOCTOU-resilient: the wallet.enc write below uses O_EXCL ('wx').\n if (existsSync(deps.walletPath)) {\n return reply.status(409).send({\n code: 'wallet_already_exists',\n message: `A wallet already exists at ${deps.walletPath}. Delete it first.`,\n });\n }\n if (existsSync(deps.configPath)) {\n return reply.status(409).send({\n code: 'config_already_exists',\n message: `A config already exists at ${deps.configPath}. Delete it first.`,\n });\n }\n\n // Create + save wallet. saveWallet writes with mode 0o600 already.\n // Concurrent inits are serialized by `state.initInFlight` above; a stray\n // external process writing wallet.enc between existsSync and saveWallet\n // is an accepted edge case for v1 (single-machine localhost wizard).\n const walletManager = new WalletManager({\n encryptedPath: deps.walletPath,\n });\n await walletManager.fromMnemonic(cleanMnemonic);\n // SECURITY: encrypted immediately, plaintext mnemonic leaves scope after this call\n const encrypted = encryptWallet(cleanMnemonic, password);\n await saveWallet(deps.walletPath, encrypted);\n // Defensive chmod (saveWallet already sets 0o600 but a hostile umask on\n // exotic platforms could still produce a too-open file; cheap to assert).\n try {\n chmodSync(deps.walletPath, 0o600);\n } catch {\n /* best-effort */\n }\n\n // Build + save config — if this throws, roll back the wallet write.\n let config: TownhouseConfig;\n try {\n config = buildConfigFromRequest(\n body as WizardInitRequest,\n deps.configPath\n );\n saveConfig(deps.configPath, config);\n } catch (err) {\n try {\n unlinkSync(deps.walletPath);\n } catch {\n /* best-effort */\n }\n throw err;\n }\n\n // Return 202 before async launch — reply is sent first\n await reply.status(202).send({ status: 'launching' });\n\n // Fire-and-forget: transition to normal mode + start orchestrator.\n // On failure, roll back wallet+config so the operator can retry without\n // hitting 409 wallet_already_exists / config_already_exists.\n if (onInit) {\n const profiles: NodeType[] = [];\n if (nodes.town?.enabled) profiles.push('town');\n if (nodes.mill?.enabled) profiles.push('mill');\n if (nodes.dvm?.enabled) profiles.push('dvm');\n\n onInit(config, walletManager, profiles).catch((err: unknown) => {\n app.log.error({ err }, 'Wizard launch failed');\n try {\n unlinkSync(deps.walletPath);\n } catch {\n /* best-effort */\n }\n try {\n unlinkSync(deps.configPath);\n } catch {\n /* best-effort */\n }\n // Allow a fresh retry: clear the in-flight flag so the next POST passes the guard.\n state.initInFlight = false;\n const errMsg: WizardProgressMessage = {\n type: 'error',\n message: err instanceof Error ? err.message : String(err),\n ts: Date.now(),\n };\n if (state.progressBuffer) {\n state.progressBuffer.push(errMsg);\n if (state.progressBuffer.length > PROGRESS_BUFFER_MAX) {\n state.progressBuffer.splice(\n 0,\n state.progressBuffer.length - PROGRESS_BUFFER_MAX\n );\n }\n }\n if (state.progressSockets) {\n for (const socket of state.progressSockets) {\n try {\n if (socket.readyState === WebSocket.OPEN) {\n socket.send(JSON.stringify(errMsg));\n }\n } catch {\n /* best-effort */\n }\n }\n }\n });\n }\n\n return reply;\n } finally {\n // The success path keeps initInFlight=true to block double-submits while\n // the orchestrator boots; only an error in onInit (above) clears it. If\n // we never reached the onInit branch (e.g., 4xx validation), clear here.\n // We detect this by checking whether reply was already sent with 202.\n // Simpler: the outer success path sets it; validation failures must reset.\n const sent = reply.statusCode;\n if (sent !== 202) {\n state.initInFlight = false;\n }\n }\n });\n\n // WS /wizard/progress — only active in wizard mode (progress sockets are wizard-only).\n // CSWSH defense: enforce Origin header allowlist on upgrade.\n app.get('/wizard/progress', { websocket: true }, (socket, req) => {\n const origin = (req.raw as IncomingMessage).headers?.origin;\n if (!isAllowedWsOrigin(origin)) {\n try {\n socket.close(1008, 'origin_not_allowed');\n } catch {\n /* best-effort */\n }\n return;\n }\n\n const sockets = state.progressSockets;\n if (!sockets) {\n // Normal mode: close immediately (wizard is complete)\n socket.close(1001, 'wizard_complete');\n return;\n }\n\n sockets.add(socket);\n\n // Replay buffered messages for late-connecting clients\n if (state.progressBuffer) {\n for (const msg of state.progressBuffer) {\n try {\n socket.send(JSON.stringify(msg));\n } catch {\n /* best-effort */\n }\n }\n }\n\n socket.on('close', () => {\n sockets.delete(socket);\n });\n\n socket.on('error', () => {\n sockets.delete(socket);\n });\n });\n}\n\n/**\n * Build a TownhouseConfig from a WizardInitRequest by merging with defaults.\n * Uses path.dirname/join for cross-platform path handling.\n */\nexport function buildConfigFromRequest(\n request: WizardInitRequest,\n configPath: string\n): TownhouseConfig {\n const config = getDefaultConfig();\n\n // Point wallet path at the same directory as config — works on POSIX and Windows.\n const configDir = dirname(configPath);\n config.wallet.encrypted_path = join(configDir, 'wallet.enc');\n\n // Apply node selections and fees\n config.nodes.town.enabled = request.nodes.town.enabled;\n if (\n request.nodes.town.enabled &&\n request.nodes.town.feePerEvent !== undefined\n ) {\n config.nodes.town.feePerEvent = request.nodes.town.feePerEvent;\n }\n\n config.nodes.mill.enabled = request.nodes.mill.enabled;\n if (\n request.nodes.mill.enabled &&\n request.nodes.mill.feeBasisPoints !== undefined\n ) {\n config.nodes.mill.feeBasisPoints = request.nodes.mill.feeBasisPoints;\n }\n\n config.nodes.dvm.enabled = request.nodes.dvm.enabled;\n if (request.nodes.dvm.enabled && request.nodes.dvm.feePerJob !== undefined) {\n config.nodes.dvm.feePerJob = request.nodes.dvm.feePerJob;\n }\n\n // Apply transport\n config.transport.mode = request.transport.mode;\n\n return config;\n}\n","/**\n * Transport configuration routes.\n *\n * GET /api/transport — live transport status (mode, reachability, latency)\n * PATCH /api/transport — flip transport mode + trigger connector restart\n */\n\nimport type { FastifyInstance, FastifySchema } from 'fastify';\nimport type {\n ApiDeps,\n NodeType,\n TransportStatusPayload,\n TransportPatchRequest,\n TransportPatchResponse,\n} from '../types.js';\nimport { validateConfig } from '../../config/validator.js';\nimport { saveConfig } from '../../config/loader.js';\nimport { acquireConfigMutex, releaseConfigMutex } from '../config-mutex.js';\n\nconst DEFAULT_ATOR_PROXY = 'socks5h://proxy.ator.io:9050';\n\nexport interface RegisterTransportOptions {\n /**\n * 'wizard' — registers GET only (no PATCH at all). Caller must call\n * registerTransportPatchRoute() after wizard transition completes.\n * 'patch-only'— registers PATCH only (used by the wizard-to-normal transition).\n * 'normal' — registers both GET and PATCH (the standalone-server case).\n */\n mode?: 'normal' | 'wizard' | 'patch-only';\n}\n\n/** Normalize a SOCKS5 URL for byte-comparable equality (trailing slash, casing). */\nfunction normalizeProxyUrl(url: string | undefined): string | undefined {\n if (!url) return undefined;\n try {\n return new URL(url).toString();\n } catch {\n return url;\n }\n}\n\nconst patchBodySchema: FastifySchema = {\n body: {\n type: 'object',\n additionalProperties: false,\n required: ['mode'],\n properties: {\n mode: { type: 'string', enum: ['direct', 'ator'] },\n socksProxy: { type: 'string', minLength: 1, maxLength: 2048 },\n },\n },\n};\n\n/**\n * Register transport routes on the given Fastify instance.\n */\nexport function registerTransportRoutes(\n app: FastifyInstance,\n deps: ApiDeps,\n opts: RegisterTransportOptions = {}\n): void {\n const { mode = 'normal' } = opts;\n\n // ── GET /api/transport ────────────────────────────────────────────────────\n // Skipped for 'patch-only' mode (caller already registered GET earlier).\n if (mode !== 'patch-only') {\n app.get('/api/transport', async (_request, reply) => {\n // Read deps.transportProbe per request — the wizard server swaps the\n // probe instance on transition, so we cannot capture it at registration time.\n const probeStatus = deps.transportProbe.getStatus();\n const configTransport = deps.config.transport;\n\n const payload: TransportStatusPayload = {\n mode: configTransport.mode,\n reachable:\n configTransport.mode === 'direct' ? true : probeStatus.reachable,\n latencyProxyMs:\n configTransport.mode === 'direct' ? null : probeStatus.latencyProxyMs,\n latencyDirectMs:\n configTransport.mode === 'direct'\n ? null\n : probeStatus.latencyDirectMs,\n lastProbedAt: probeStatus.lastProbedAt,\n probeError:\n configTransport.mode === 'direct' ? null : probeStatus.probeError,\n ts: Date.now(),\n };\n\n if (configTransport.mode === 'ator') {\n payload.socksProxy = configTransport.socksProxy ?? DEFAULT_ATOR_PROXY;\n }\n\n return reply.status(200).send(payload);\n });\n }\n\n // ── PATCH /api/transport ──────────────────────────────────────────────────\n // Wizard mode: GET only — PATCH is added later via registerTransportRoutes\n // with mode 'patch-only' once the wizard transitions to normal.\n if (mode === 'wizard') {\n return;\n }\n\n // Probe handle for PATCH operations. Stable: PATCH is only registered for\n // the deps object the caller intends to mutate transport against.\n const probe = deps.transportProbe;\n\n app.patch<{ Body: TransportPatchRequest }>(\n '/api/transport',\n { schema: patchBodySchema },\n async (request, reply) => {\n const body = request.body;\n\n // Reject socksProxy when mode is direct — silent ignore is ambiguous to the caller\n if (body.mode === 'direct' && body.socksProxy !== undefined) {\n return reply.status(400).send({\n error: 'invalid_body',\n message: 'socksProxy is not allowed when mode is direct',\n });\n }\n\n // Validate socksProxy URL format if provided\n if (body.socksProxy !== undefined) {\n try {\n const u = new URL(body.socksProxy);\n if (u.protocol !== 'socks5:' && u.protocol !== 'socks5h:') {\n return reply.status(400).send({\n error: 'invalid_socksProxy',\n message: 'socksProxy must use socks5:// or socks5h:// scheme',\n });\n }\n if (!u.hostname) {\n return reply.status(400).send({\n error: 'invalid_socksProxy',\n message: 'socksProxy must have a hostname',\n });\n }\n if (u.username || u.password) {\n return reply.status(400).send({\n error: 'invalid_socksProxy',\n message: 'socksProxy must not include credentials in the URL',\n });\n }\n if (u.pathname && u.pathname !== '/') {\n return reply.status(400).send({\n error: 'invalid_socksProxy',\n message: 'socksProxy must not include a path',\n });\n }\n if (!u.port) {\n return reply.status(400).send({\n error: 'invalid_socksProxy',\n message: 'socksProxy must specify a port',\n });\n }\n const portNum = Number(u.port);\n if (!Number.isInteger(portNum) || portNum < 1 || portNum > 65535) {\n return reply.status(400).send({\n error: 'invalid_socksProxy',\n message: 'socksProxy port must be in range 1-65535',\n });\n }\n } catch {\n return reply.status(400).send({\n error: 'invalid_socksProxy',\n message: 'socksProxy must be a valid URL',\n });\n }\n }\n\n const prevMode = deps.config.transport.mode;\n const prevSocksProxy = deps.config.transport.socksProxy;\n const newMode = body.mode;\n const newSocksProxy =\n newMode === 'ator'\n ? (body.socksProxy ?? prevSocksProxy ?? DEFAULT_ATOR_PROXY)\n : undefined;\n\n // No-op detection: same mode and (for ATOR) URL-normalized same proxy\n const noOp =\n prevMode === newMode &&\n (newMode === 'direct' ||\n normalizeProxyUrl(prevSocksProxy) ===\n normalizeProxyUrl(newSocksProxy));\n\n if (noOp) {\n const noOpResponse: TransportPatchResponse = {\n mode: newMode,\n ...(newMode === 'ator' ? { socksProxy: newSocksProxy } : {}),\n restartTriggered: false,\n };\n return reply.status(200).send(noOpResponse);\n }\n\n // Acquire shared config mutex\n if (!acquireConfigMutex()) {\n return reply.status(409).send({ error: 'config_mutation_in_flight' });\n }\n\n // Snapshot the prior probe URL so a failed flip can restore it.\n const priorProbeUrl =\n prevMode === 'ator' ? (prevSocksProxy ?? DEFAULT_ATOR_PROXY) : '';\n\n // Snapshot the full prior transport block so a failed flip can restore\n // exactly what the operator had — not just mode + socksProxy.\n // hiddenService / externalUrl / relayHiddenService are operator-managed\n // fields that the YAML may have set up independently (story 7e28ea9);\n // the PATCH must preserve them across mode flips, otherwise toggling\n // direct→ator→direct would silently strip the hidden-service setup.\n const priorTransport = { ...deps.config.transport };\n\n try {\n // Mutate in-memory config — carry forward all non-mode fields, then\n // override mode and socksProxy per the request.\n const {\n mode: _droppedMode,\n socksProxy: _droppedProxy,\n ...carryOver\n } = priorTransport;\n deps.config.transport = {\n ...carryOver,\n mode: newMode,\n ...(newMode === 'ator' ? { socksProxy: newSocksProxy } : {}),\n };\n\n // Defensive round-trip validation\n try {\n validateConfig(deps.config);\n } catch (validationError) {\n // Roll back in-memory edit (restore the FULL prior block — preserves\n // hiddenService/externalUrl/relayHiddenService).\n deps.config.transport = priorTransport;\n return reply.status(500).send({\n error: 'config_validation_error',\n message:\n validationError instanceof Error\n ? validationError.message\n : 'Invalid configuration',\n });\n }\n\n // Persist to disk — if this fails, restore in-memory state before bailing.\n try {\n await saveConfig(deps.configPath, deps.config);\n } catch (saveError) {\n deps.config.transport = priorTransport;\n return reply.status(500).send({\n error: 'config_save_failed',\n message:\n saveError instanceof Error\n ? saveError.message\n : 'Failed to persist config',\n });\n }\n\n // Compute active nodes\n const activeNodes = Object.entries(deps.config.nodes)\n .filter(([, cfg]) => cfg.enabled)\n .map(([t]) => t as NodeType);\n\n // Trigger connector restart\n try {\n await deps.orchestrator.regenerateConnectorConfig(activeNodes);\n } catch (restartError) {\n // Rollback: restore in-memory config and persist the restoration\n deps.config.transport = priorTransport;\n try {\n await saveConfig(deps.configPath, deps.config);\n } catch {\n // Best-effort rollback of disk write — if this fails, system is in\n // a bad state but we can't do anything further here.\n }\n // Restore probe to its prior target as well so the dashboard\n // doesn't keep reporting reachability for the failed-flip URL.\n try {\n probe.setProxyUrl(priorProbeUrl);\n if (prevMode === 'ator') {\n probe.start();\n } else {\n probe.stop();\n }\n } catch {\n /* best-effort */\n }\n return reply.status(500).send({\n error: 'connector_restart_failed',\n message:\n restartError instanceof Error\n ? restartError.message\n : 'Connector restart failed',\n });\n }\n\n // Update probe. ATOR→ATOR with a different proxy must restart the\n // probe loop so the new URL is adopted immediately rather than at\n // the next 30 s tick.\n try {\n if (newMode === 'ator') {\n const newProbeUrl = newSocksProxy ?? DEFAULT_ATOR_PROXY;\n if (prevMode === 'ator') {\n probe.stop();\n }\n probe.setProxyUrl(newProbeUrl);\n probe.start();\n } else {\n probe.stop();\n }\n } catch (probeError) {\n // The connector restart already succeeded — log and continue.\n const msg =\n probeError instanceof Error\n ? probeError.message\n : String(probeError);\n request.log.warn(`transport probe update after flip failed: ${msg}`);\n }\n\n const restartedAt = Date.now();\n const successResponse: TransportPatchResponse = {\n mode: newMode,\n ...(newMode === 'ator' ? { socksProxy: newSocksProxy } : {}),\n restartTriggered: true,\n restartedAt,\n };\n return reply.status(200).send(successResponse);\n } finally {\n releaseConfigMutex();\n }\n }\n );\n}\n","/**\n * GET /api/earnings — earnings aggregator route (Story 47.2).\n *\n * Returns the canonical `{ status, apex, peers }` earnings shape from\n * `aggregateEarnings()`. Reads `nodes.yaml` per request and constructs a\n * fresh `PeerTypeResolver` — matching the pattern in `nodes-lifecycle.ts`.\n *\n * The `?since=` parameter from Story D4 is gone — TODAY/MONTH/YEAR deltas\n * are anchored on UTC boundaries by the snapshot writer (Story 47.3).\n *\n * Failure modes:\n * - connector outage → aggregator returns 200 with\n * `status: 'connector_unavailable'`; the SPA surfaces a banner.\n * - malformed `nodes.yaml` (ZodError on shape violation) → 500 with\n * `{ error: 'nodes_yaml_invalid' }`; logged via `request.log.error`.\n */\n\nimport { dirname, join } from 'node:path';\n\nimport type { FastifyInstance } from 'fastify';\nimport type { ApiDeps } from '../types.js';\nimport { aggregateEarnings } from '../../earnings/aggregator.js';\nimport { createDeltaComputer } from '../../earnings/snapshot-reader.js';\nimport { readNodesYaml } from '../../state/nodes-yaml.js';\nimport { PeerTypeResolver } from '../../registry/peer-type-resolver.js';\nimport { earningsResponseSchema } from '../schemas/earnings.js';\n\n/**\n * Convention shared with `nodes-lifecycle.ts`: `nodes.yaml` lives next to\n * `config.yaml` in the operator's `~/.townhouse` dir. Centralised here so\n * any future reader stays coupled to one resolution rule.\n */\nfunction resolveNodesYamlPath(deps: ApiDeps): string {\n return join(dirname(deps.configPath), 'nodes.yaml');\n}\n\nfunction resolveSnapshotPath(deps: ApiDeps): string {\n return join(dirname(deps.configPath), 'earnings-snapshots.jsonl');\n}\n\nexport function registerEarningsRoutes(\n app: FastifyInstance,\n deps: ApiDeps\n): void {\n app.get(\n '/api/earnings',\n { schema: earningsResponseSchema },\n async (request, reply) => {\n let yaml;\n try {\n yaml = await readNodesYaml(resolveNodesYamlPath(deps));\n } catch (err) {\n request.log.error(\n { err: err instanceof Error ? err.message : String(err) },\n 'earnings: nodes.yaml read/validate failed'\n );\n return reply.status(500).send({ error: 'nodes_yaml_invalid' });\n }\n const peerTypeResolver = new PeerTypeResolver(yaml);\n const deltaComputer = createDeltaComputer({\n snapshotPath: resolveSnapshotPath(deps),\n });\n return aggregateEarnings({\n connectorAdmin: deps.connectorAdmin,\n peerTypeResolver,\n deltaComputer,\n logger: request.log,\n });\n }\n );\n}\n","/**\n * Response schema for GET /api/earnings (Story 47.4).\n *\n * Locks the wire-level contract for the TUI / SPA / future Tauri client.\n * Schema library: raw `FastifySchema` JSON Schema — matches the established\n * pattern in `api/routes/transport.ts:42-52`. See Story 47.4 Open Question 1\n * for the decision trail (TypeBox language in the epic AC was superseded by\n * codebase consistency).\n *\n * AC #1 enforcement: amount fields use `pattern: '^-?\\\\d+$'` (decimal-string\n * bigint, no number coercion); timestamps use `format: 'date-time'` (ISO-8601).\n * Validation requires `ajv-formats` in tests (schema.test.ts registers it).\n *\n * Pass-through subobjects (`recentClaim`, `perAsset`) intentionally OMIT\n * `additionalProperties: false` so future connector-shipped fields (e.g. a\n * `RecentClaim.txHash` added in a connector minor release) survive serialization.\n * `peerSchema` and the top-level remain closed — Townhouse owns those shapes.\n *\n * NOTE: Fastify response schemas run a SERIALIZER (fast-json-stringify), not a\n * validator — unknown fields in the handler return value are silently dropped.\n * To validate against this schema, use Ajv directly (see earnings.test.ts).\n */\n\nimport type { FastifySchema } from 'fastify';\n\nconst DECIMAL_STRING_PATTERN = '^-?\\\\d+$';\n\nconst perAssetSchema = {\n type: 'object',\n properties: {\n lifetime: { type: 'string', pattern: DECIMAL_STRING_PATTERN },\n today: { type: 'string', pattern: DECIMAL_STRING_PATTERN },\n month: { type: 'string', pattern: DECIMAL_STRING_PATTERN },\n year: { type: 'string', pattern: DECIMAL_STRING_PATTERN },\n },\n required: ['lifetime', 'today', 'month', 'year'] as const,\n // Open to future connector-derived fields per D2 decision (2026-05-13).\n} as const;\n\nconst routingFeesSchema = {\n type: 'object',\n additionalProperties: perAssetSchema,\n} as const;\n\nconst peerSchema = {\n type: 'object',\n properties: {\n id: { type: 'string' },\n type: {\n type: 'string',\n enum: ['town', 'mill', 'dvm', 'external'] as const,\n },\n byAsset: routingFeesSchema,\n lastClaimAt: {\n oneOf: [{ type: 'string', format: 'date-time' }, { type: 'null' }],\n },\n },\n required: ['id', 'type', 'byAsset', 'lastClaimAt'] as const,\n additionalProperties: false, // Townhouse owns the peer shape.\n} as const;\n\nconst recentClaimSchema = {\n type: 'object',\n properties: {\n peerId: { type: 'string' },\n assetCode: { type: 'string' },\n assetScale: { type: 'integer', minimum: 0 },\n amount: { type: 'string', pattern: DECIMAL_STRING_PATTERN },\n direction: { type: 'string', enum: ['inbound', 'outbound'] as const },\n at: { type: 'string', format: 'date-time' },\n },\n required: [\n 'peerId',\n 'assetCode',\n 'assetScale',\n 'amount',\n 'direction',\n 'at',\n ] as const,\n // Open to future connector-shipped fields per D2 decision (2026-05-13).\n} as const;\n\nexport const earningsResponseSchema: FastifySchema = {\n response: {\n 200: {\n type: 'object',\n properties: {\n status: { type: 'string', enum: ['ok', 'connector_unavailable'] },\n apex: {\n type: 'object',\n properties: {\n routingFees: routingFeesSchema,\n },\n required: ['routingFees'],\n additionalProperties: false,\n },\n peers: {\n type: 'array',\n items: peerSchema,\n },\n recentClaims: {\n type: 'array',\n items: recentClaimSchema,\n },\n eventsRelayed: { type: 'integer', minimum: 0 },\n uptimeSeconds: { type: 'integer', minimum: 0 },\n },\n required: [\n 'status',\n 'apex',\n 'peers',\n 'recentClaims',\n 'eventsRelayed',\n 'uptimeSeconds',\n ],\n additionalProperties: false,\n },\n },\n};\n","/**\n * Live log-tail SSE route (Story D6).\n *\n * GET /api/logs/stream\n *\n * Streams structured JSON lines from running TOON containers (town, mill,\n * dvm, connector) as Server-Sent Events. Each event is one JSON object on\n * a single `data:` line.\n *\n * The route owns its dockerode connection so we don't have to plumb the\n * Docker handle through ApiDeps; the orchestrator's `docker` field is\n * private. This keeps the change surface contained to the new files.\n */\n\nimport type { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';\nimport Docker from 'dockerode';\nimport type { ApiDeps } from '../types.js';\nimport {\n tailContainerLogs,\n serviceFromContainerName,\n type LogEvent,\n type LogService,\n} from '../../docker/log-tail.js';\n\n/** Allow the test suite to inject a fake Docker. */\nexport interface RegisterLogsRoutesOptions {\n docker?: Docker;\n /**\n * Override the tail driver. Defaults to `tailContainerLogs`. Tests use this\n * to feed deterministic events without a real Docker daemon.\n */\n tailFn?: typeof tailContainerLogs;\n}\n\n/** Heartbeat cadence — keeps proxies/load-balancers from idling out. */\nconst HEARTBEAT_INTERVAL_MS = 15_000;\n\ninterface RunningContainer {\n name: string;\n service: LogService;\n}\n\n/** Discover running townhouse-managed containers (town/mill/dvm/connector). */\nasync function listTownhouseContainers(\n docker: Docker\n): Promise<RunningContainer[]> {\n const containers = await docker.listContainers({ all: false });\n const out: RunningContainer[] = [];\n for (const c of containers) {\n for (const rawName of c.Names) {\n const name = rawName.startsWith('/') ? rawName.slice(1) : rawName;\n const service = serviceFromContainerName(name);\n if (service) {\n out.push({ name, service });\n break;\n }\n }\n }\n return out;\n}\n\nexport function registerLogsRoutes(\n app: FastifyInstance,\n _deps: ApiDeps,\n opts: RegisterLogsRoutesOptions = {}\n): void {\n const docker = opts.docker ?? new Docker();\n const tailFn = opts.tailFn ?? tailContainerLogs;\n\n app.get('/api/logs/stream', async (request, reply) => {\n await streamLogs(request, reply, docker, tailFn);\n });\n}\n\n/**\n * Internal handler — extracted so tests can drive it directly.\n *\n * We bypass Fastify's reply.send and write to the underlying Node response\n * because SSE requires keeping the connection open and flushing each event.\n */\nasync function streamLogs(\n request: FastifyRequest,\n reply: FastifyReply,\n docker: Docker,\n tailFn: typeof tailContainerLogs\n): Promise<void> {\n const raw = reply.raw;\n raw.statusCode = 200;\n raw.setHeader('Content-Type', 'text/event-stream');\n raw.setHeader('Cache-Control', 'no-cache, no-transform');\n raw.setHeader('Connection', 'keep-alive');\n // Defeat buffering on intermediaries (nginx, fly, etc.)\n raw.setHeader('X-Accel-Buffering', 'no');\n raw.flushHeaders?.();\n\n const controller = new AbortController();\n const heartbeat = setInterval(() => {\n if (raw.writableEnded) return;\n try {\n raw.write(`: heartbeat ${Date.now()}\\n\\n`);\n } catch {\n /* best-effort */\n }\n }, HEARTBEAT_INTERVAL_MS);\n\n function teardown(): void {\n clearInterval(heartbeat);\n controller.abort();\n }\n\n request.raw.on('close', teardown);\n request.raw.on('error', teardown);\n\n // Discover running containers up front; if Docker is unreachable we send a\n // single error event and close. This keeps the dashboard demoable even when\n // dockerd is in a weird state.\n let containers: RunningContainer[];\n try {\n containers = await listTownhouseContainers(docker);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n writeEvent(raw, {\n ts: new Date().toISOString(),\n service: 'connector',\n level: 'error',\n msg: `log-tail: docker unavailable (${msg})`,\n });\n teardown();\n raw.end();\n return;\n }\n\n if (containers.length === 0) {\n writeEvent(raw, {\n ts: new Date().toISOString(),\n service: 'connector',\n level: 'warn',\n msg: 'log-tail: no townhouse containers running',\n });\n }\n\n // Spawn one tail per container; merge into the single SSE stream.\n const tasks = containers.map(async (c) => {\n try {\n for await (const evt of tailFn(docker, c.name, c.service, {\n signal: controller.signal,\n tail: 50,\n })) {\n if (raw.writableEnded) break;\n writeEvent(raw, evt);\n }\n } catch (err) {\n if (controller.signal.aborted) return;\n const msg = err instanceof Error ? err.message : String(err);\n writeEvent(raw, {\n ts: new Date().toISOString(),\n service: c.service,\n level: 'error',\n msg: `log-tail: ${c.name} stream error (${msg})`,\n });\n }\n });\n\n // Wait for all tails (typically until client disconnects + abort fires).\n await Promise.allSettled(tasks);\n teardown();\n if (!raw.writableEnded) {\n raw.end();\n }\n}\n\nfunction writeEvent(\n raw: { write: (chunk: string) => boolean; writableEnded: boolean },\n evt: LogEvent\n): void {\n if (raw.writableEnded) return;\n try {\n raw.write(`data: ${JSON.stringify(evt)}\\n\\n`);\n } catch {\n /* best-effort */\n }\n}\n\n// Exported for tests\nexport const __test__ = { listTownhouseContainers, streamLogs };\n","/**\n * Docker log tailing helpers (Story D6).\n *\n * Wraps `dockerode`'s `container.logs({ follow: true })` stream into an\n * AsyncIterable<LogEvent> emitting structured JSON events. The raw Docker\n * stream is multiplexed (8-byte framing per chunk: stdout/stderr) when the\n * container has no TTY; we strip the frame header and split on newlines.\n *\n * Each emitted event is shaped to be drop-in JSON for the SSE endpoint:\n *\n * { ts: ISO8601, service: 'town'|'mill'|'dvm'|'connector',\n * level: 'info'|'warn'|'error'|'debug', msg: string, raw?: string }\n *\n * The parser is exported separately so the unit tests can exercise the\n * stream-decoding logic in pure form (no dockerode required).\n */\nimport type Docker from 'dockerode';\nimport type { Readable } from 'node:stream';\nimport { CONTAINER_PREFIX } from '../constants.js';\n\nexport type LogService = 'town' | 'mill' | 'dvm' | 'connector';\nexport type LogLevel = 'info' | 'warn' | 'error' | 'debug';\n\nexport interface LogEvent {\n ts: string;\n service: LogService;\n level: LogLevel;\n msg: string;\n raw?: string;\n}\n\n/** Possible service identifiers we tail. */\nexport const LOG_SERVICES: readonly LogService[] = [\n 'town',\n 'mill',\n 'dvm',\n 'connector',\n] as const;\n\n/**\n * Strip Docker's multiplexed-stream 8-byte frame headers, if present.\n *\n * Docker's `/containers/{id}/logs?follow=1` response is a \"raw stream\" when\n * the container has a TTY (just bytes) and a \"multiplexed stream\" when not.\n * In the multiplexed form, every payload chunk is preceded by an 8-byte\n * header: [STREAM_TYPE, 0, 0, 0, SIZE_BE_4]. STREAM_TYPE is 1=stdout, 2=stderr.\n *\n * Heuristic: a buffer is considered framed when the first byte is 1 or 2,\n * the next three bytes are zero, and the declared frame length lands within\n * the buffer. Otherwise we return the bytes as-is (TTY case, partial chunks).\n */\nexport function stripDockerFrame(chunk: Buffer): Buffer {\n if (chunk.length < 8) return chunk;\n const streamType = chunk[0];\n if (\n (streamType !== 1 && streamType !== 2) ||\n chunk[1] !== 0 ||\n chunk[2] !== 0 ||\n chunk[3] !== 0\n ) {\n return chunk;\n }\n const out: Buffer[] = [];\n let offset = 0;\n while (offset + 8 <= chunk.length) {\n const st = chunk[offset];\n if (\n (st !== 1 && st !== 2) ||\n chunk[offset + 1] !== 0 ||\n chunk[offset + 2] !== 0 ||\n chunk[offset + 3] !== 0\n ) {\n // Not a frame — return remainder verbatim.\n out.push(chunk.subarray(offset));\n return Buffer.concat(out);\n }\n const size = chunk.readUInt32BE(offset + 4);\n const start = offset + 8;\n const end = start + size;\n if (end > chunk.length) {\n // Partial frame — consumer will see the un-stripped tail next time.\n out.push(chunk.subarray(start));\n return Buffer.concat(out);\n }\n out.push(chunk.subarray(start, end));\n offset = end;\n }\n return Buffer.concat(out);\n}\n\n/**\n * Map a raw log line to a structured LogEvent. The line may be:\n * - JSON-shaped Pino/Bunyan log (preferred)\n * - \"[level] message\"-style text\n * - free-form text (default level: info)\n *\n * `service` is supplied by the caller (we know which container produced it).\n */\nexport function parseLogLine(\n line: string,\n service: LogService\n): LogEvent | null {\n const trimmed = line.replace(/\\r$/, '').trim();\n if (!trimmed) return null;\n\n // Try JSON first (Pino-style logs from connector / mill / dvm)\n if (trimmed.startsWith('{') && trimmed.endsWith('}')) {\n try {\n const obj = JSON.parse(trimmed) as Record<string, unknown>;\n const level = pinoLevelToLevel(obj['level']);\n const ts = pickTimestamp(obj['time'] ?? obj['ts'] ?? obj['@timestamp']);\n const msg =\n pickMsg(obj['msg'] ?? obj['message'] ?? obj['text']) ?? trimmed;\n return { ts, service, level, msg, raw: trimmed };\n } catch {\n // fall through to text parsing\n }\n }\n\n // Bracketed level prefix: \"[ERROR] something happened\" / \"WARN: ...\"\n const levelMatch = trimmed.match(\n /^\\s*\\[?(DEBUG|INFO|WARN|WARNING|ERROR|ERR|FATAL)\\]?[:\\s]+(.*)$/i\n );\n if (levelMatch && levelMatch[1] !== undefined) {\n const lvl = levelMatch[1].toUpperCase();\n const msg = levelMatch[2] ?? '';\n return {\n ts: new Date().toISOString(),\n service,\n level: textLevelToLevel(lvl),\n msg,\n raw: trimmed,\n };\n }\n\n return {\n ts: new Date().toISOString(),\n service,\n level: 'info',\n msg: trimmed,\n raw: trimmed,\n };\n}\n\nfunction pinoLevelToLevel(raw: unknown): LogLevel {\n if (typeof raw === 'number') {\n // Pino numeric levels: 10 trace, 20 debug, 30 info, 40 warn, 50 error, 60 fatal\n if (raw >= 50) return 'error';\n if (raw >= 40) return 'warn';\n if (raw >= 30) return 'info';\n return 'debug';\n }\n if (typeof raw === 'string') {\n return textLevelToLevel(raw.toUpperCase());\n }\n return 'info';\n}\n\nfunction textLevelToLevel(upper: string): LogLevel {\n switch (upper) {\n case 'DEBUG':\n case 'TRACE':\n return 'debug';\n case 'WARN':\n case 'WARNING':\n return 'warn';\n case 'ERR':\n case 'ERROR':\n case 'FATAL':\n case 'CRITICAL':\n return 'error';\n case 'INFO':\n default:\n return 'info';\n }\n}\n\nfunction pickTimestamp(value: unknown): string {\n if (typeof value === 'number' && Number.isFinite(value)) {\n return new Date(value).toISOString();\n }\n if (typeof value === 'string') {\n const d = new Date(value);\n if (!Number.isNaN(d.getTime())) return d.toISOString();\n }\n return new Date().toISOString();\n}\n\nfunction pickMsg(value: unknown): string | null {\n if (typeof value === 'string') return value;\n if (value == null) return null;\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n}\n\n/**\n * Stateful line splitter — accumulates partial chunks across reads and emits\n * complete lines. Used by the live tail to handle TCP frames that don't align\n * with newlines.\n */\nexport class LineSplitter {\n private buffer = '';\n\n push(chunk: Buffer): string[] {\n this.buffer += stripDockerFrame(chunk).toString('utf8');\n const lines = this.buffer.split('\\n');\n this.buffer = lines.pop() ?? '';\n return lines;\n }\n\n flush(): string[] {\n if (!this.buffer) return [];\n const out = [this.buffer];\n this.buffer = '';\n return out;\n }\n}\n\n/**\n * Map a townhouse-managed container name to the service tag used in the SSE\n * events. Returns null when the name doesn't match any known prefix.\n *\n * Examples:\n * townhouse-town -> 'town'\n * townhouse-mill -> 'mill'\n * townhouse-dvm -> 'dvm'\n * townhouse-connector -> 'connector'\n * townhouse-dev-town-01 -> 'town' (preset multi-instance)\n * townhouse-dev-mill-02 -> 'mill'\n */\nexport function serviceFromContainerName(name: string): LogService | null {\n const clean = name.replace(/^\\//, '');\n if (!clean.startsWith(CONTAINER_PREFIX)) return null;\n const suffix = clean.slice(CONTAINER_PREFIX.length);\n for (const svc of LOG_SERVICES) {\n if (\n suffix === svc ||\n suffix.startsWith(`${svc}-`) ||\n suffix.includes(`-${svc}-`) ||\n suffix.endsWith(`-${svc}`)\n ) {\n return svc;\n }\n }\n return null;\n}\n\nexport interface TailOptions {\n /** Number of historical lines to fetch on attach. Default: 50. */\n tail?: number;\n /** AbortSignal for graceful cancellation. */\n signal?: AbortSignal;\n}\n\n/**\n * Tail one container, yielding structured LogEvents. Resolves silently when\n * the underlying stream ends or the signal aborts.\n */\nexport async function* tailContainerLogs(\n docker: Docker,\n containerName: string,\n service: LogService,\n opts: TailOptions = {}\n): AsyncGenerator<LogEvent> {\n const tail = opts.tail ?? 50;\n const container = docker.getContainer(containerName);\n\n // dockerode's typings vary by version — `logs({follow:true})` returns a\n // Readable. Fall back to `any` only here, to keep the surface tiny.\n const stream = (await container.logs({\n follow: true,\n stdout: true,\n stderr: true,\n tail,\n timestamps: false,\n })) as unknown as Readable;\n\n const splitter = new LineSplitter();\n const queue: LogEvent[] = [];\n let waiter: (() => void) | null = null;\n let done = false;\n let err: Error | null = null;\n\n function wake() {\n if (waiter) {\n const w = waiter;\n waiter = null;\n w();\n }\n }\n\n stream.on('data', (chunk: Buffer) => {\n for (const line of splitter.push(chunk)) {\n const evt = parseLogLine(line, service);\n if (evt) queue.push(evt);\n }\n wake();\n });\n stream.on('end', () => {\n for (const line of splitter.flush()) {\n const evt = parseLogLine(line, service);\n if (evt) queue.push(evt);\n }\n done = true;\n wake();\n });\n stream.on('error', (e: Error) => {\n err = e;\n done = true;\n wake();\n });\n\n if (opts.signal) {\n if (opts.signal.aborted) {\n try {\n stream.destroy();\n } catch {\n /* best-effort */\n }\n done = true;\n } else {\n opts.signal.addEventListener(\n 'abort',\n () => {\n try {\n stream.destroy();\n } catch {\n /* best-effort */\n }\n done = true;\n wake();\n },\n { once: true }\n );\n }\n }\n\n while (true) {\n const next = queue.shift();\n if (next !== undefined) {\n yield next;\n continue;\n }\n if (done) break;\n await new Promise<void>((resolve) => {\n waiter = resolve;\n });\n }\n\n if (err) throw err;\n}\n","/**\n * Wizard API Server Factory.\n *\n * Starts in wizard mode (only wizard routes), transitions to normal mode\n * after POST /wizard/init completes and containers are healthy.\n * SECURITY: Wizard mode hard-rejects non-loopback bind regardless of env var.\n */\n\nimport { WebSocket } from 'ws';\nimport type { FastifyBaseLogger, FastifyInstance } from 'fastify';\nimport type Docker from 'dockerode';\nimport { buildFastifyApp, LOOPBACK_HOSTS } from './build-app.js';\nimport { registerWizardRoutes, PROGRESS_BUFFER_MAX } from './routes/wizard.js';\nimport { registerNodeRoutes } from './routes/nodes.js';\nimport { registerWalletRoutes } from './routes/wallet.js';\nimport { registerWalletBalancesRoutes } from './routes/wallet-balances.js';\nimport { registerWalletRevealRoutes } from './routes/wallet-reveal.js';\nimport { registerWalletWithdrawRoutes } from './routes/wallet-withdraw.js';\nimport { registerConfigPatchRoutes } from './routes/nodes-patch.js';\nimport {\n registerMetricsWsRoutes,\n getOpenWebSockets,\n} from './routes/metrics-ws.js';\nimport { registerTransportRoutes } from './routes/transport.js';\nimport {\n ConnectorAdminClient,\n DEFAULT_ATOR_PROXY,\n TransportProbe,\n} from '../connector/index.js';\nimport { DockerOrchestrator } from '../docker/index.js';\nimport type { WalletManager } from '../wallet/index.js';\nimport type { TownhouseConfig } from '../config/schema.js';\nimport type { NodeType } from '../docker/types.js';\nimport type { WizardProgressMessage } from './types.js';\nimport type { ApiServer } from './types.js';\n\nexport interface WizardInitialDeps {\n /** Directory where ~/.townhouse/ (or override) lives */\n configDir: string;\n /** Full path to config.yaml */\n configPath: string;\n /** Full path to wallet.enc */\n walletPath: string;\n /** Port to bind the API */\n port: number;\n /** Bind host — must be a loopback address; defaults to 127.0.0.1 */\n bindHost?: string;\n docker: Docker;\n logger?: FastifyBaseLogger | boolean;\n}\n\nconst CLOSE_TIMEOUT_MS = 5000;\n\n/**\n * Create the wizard API server. Starts in wizard-only mode.\n * After POST /wizard/init + orchestrator launch, transitions to normal mode.\n */\nexport async function createWizardApiServer(\n initialDeps: WizardInitialDeps\n): Promise<ApiServer> {\n const bindHost = initialDeps.bindHost ?? '127.0.0.1';\n\n // SECURITY: Wizard mode hard-rejects non-loopback regardless of TOWNHOUSE_API_ALLOW_REMOTE.\n // This guard is stricter than buildFastifyApp's normal-mode check because the wizard\n // exposes unauthenticated mutating endpoints (POST /wizard/init).\n if (!LOOPBACK_HOSTS.includes(bindHost)) {\n throw new Error(\n 'The wizard refuses remote bind for security. Edit ~/.townhouse/config.yaml after setup if you need remote API access.'\n );\n }\n\n const state: {\n mode: 'wizard' | 'normal';\n progressBuffer: WizardProgressMessage[];\n progressSockets: Set<WebSocket>;\n transitioned: boolean;\n initInFlight: boolean;\n } = {\n mode: 'wizard',\n progressBuffer: [],\n progressSockets: new Set<WebSocket>(),\n transitioned: false,\n initInFlight: false,\n };\n\n const app = await buildFastifyApp({\n logger: initialDeps.logger ?? true,\n bindHost,\n requireLoopback: true,\n });\n\n // Handler that fires after POST /wizard/init: creates orchestrator, transitions mode.\n // NOTE: state.transitioned is flipped only AFTER orchestrator.up() resolves so that\n // a launch failure leaves the wizard recoverable (the route handler clears initInFlight\n // on rejection and rolls back wallet+config files).\n async function onInit(\n config: TownhouseConfig,\n walletManager: WalletManager,\n profiles: NodeType[]\n ): Promise<void> {\n if (state.transitioned) return;\n\n const orchestrator = new DockerOrchestrator(\n initialDeps.docker,\n config,\n walletManager\n );\n\n // Forward orchestrator events to WS buffer + connected sockets\n orchestrator.on(\n 'pullProgress',\n (event: { image: string; status: string; progress?: string }) => {\n const msg: WizardProgressMessage = {\n type: 'pull_progress',\n image: event.image,\n status: event.status,\n progress: event.progress,\n ts: Date.now(),\n };\n broadcastProgress(msg);\n }\n );\n\n orchestrator.on(\n 'containerState',\n (event: {\n name: string;\n state: string;\n detail?: string;\n error?: string;\n }) => {\n const ts = Date.now();\n let msg: WizardProgressMessage;\n if (event.state === 'running' || event.state === 'starting') {\n msg = { type: 'container_starting', name: event.name, ts };\n } else if (event.state === 'error') {\n // Surface the underlying detail/error if the orchestrator provides one;\n // fall back to the generic 'error' state string.\n const reason = event.detail ?? event.error ?? event.state;\n msg = { type: 'container_failed', name: event.name, reason, ts };\n } else if (event.state === 'stopping' || event.state === 'stopped') {\n // During launch, an unexpected stop means a partial failure — surface it.\n msg = {\n type: 'container_failed',\n name: event.name,\n reason: `container ${event.state} during launch`,\n ts,\n };\n } else {\n return;\n }\n broadcastProgress(msg);\n }\n );\n\n // The orchestrator emits 'healthy' via the `healthCheck` event, not `containerState`.\n // Forward that to container_healthy so the wire contract in AC-7 is actually populated.\n orchestrator.on(\n 'healthCheck',\n (event: { name: string; status: string }) => {\n if (event.status === 'healthy') {\n broadcastProgress({\n type: 'container_healthy',\n name: event.name,\n ts: Date.now(),\n });\n }\n }\n );\n\n // Start containers — if this rejects, the route's .catch will roll back files\n // and clear initInFlight so the operator can retry.\n await orchestrator.up(profiles);\n\n // Register all normal routes on the same Fastify instance\n const connectorAdmin = new ConnectorAdminClient(\n `http://127.0.0.1:${config.connector.adminPort}`\n );\n\n // Stop the wizard probe — the normal probe takes over.\n wizardProbe.stop();\n\n // Build probe for normal mode (after wizard completes)\n const normalProbe = new TransportProbe({\n proxyUrl:\n config.transport.mode === 'ator'\n ? (config.transport.socksProxy ?? DEFAULT_ATOR_PROXY)\n : '',\n });\n if (config.transport.mode === 'ator') {\n normalProbe.start();\n }\n // Swap the GET-route's view to the normal probe + real config. The GET\n // closure reads these per request, so subsequent calls reflect live state.\n wizardTransportDeps.config = config;\n wizardTransportDeps.transportProbe = normalProbe;\n activeProbe = normalProbe;\n\n const apiDeps = {\n configPath: initialDeps.configPath,\n config,\n orchestrator,\n wallet: walletManager,\n connectorAdmin,\n transportProbe: normalProbe,\n };\n\n registerNodeRoutes(app as FastifyInstance, apiDeps);\n registerWalletRoutes(app as FastifyInstance, apiDeps);\n registerWalletBalancesRoutes(app as FastifyInstance, apiDeps);\n registerWalletRevealRoutes(app as FastifyInstance, apiDeps);\n registerWalletWithdrawRoutes(app as FastifyInstance, apiDeps);\n registerConfigPatchRoutes(app as FastifyInstance, apiDeps);\n registerMetricsWsRoutes(app as FastifyInstance, apiDeps);\n // GET /api/transport is already registered (wizard mode). Add PATCH only to\n // avoid Fastify's FST_ERR_DUPLICATED_ROUTE on the wizard happy path.\n registerTransportRoutes(app as FastifyInstance, apiDeps, {\n mode: 'patch-only',\n });\n\n // Transition state — only after everything succeeded.\n state.transitioned = true;\n state.mode = 'normal';\n\n // Broadcast launch_complete\n broadcastProgress({ type: 'launch_complete', ts: Date.now() });\n }\n\n function broadcastProgress(msg: WizardProgressMessage): void {\n state.progressBuffer.push(msg);\n if (state.progressBuffer.length > PROGRESS_BUFFER_MAX) {\n state.progressBuffer.splice(\n 0,\n state.progressBuffer.length - PROGRESS_BUFFER_MAX\n );\n }\n for (const socket of state.progressSockets) {\n try {\n if (socket.readyState === WebSocket.OPEN) {\n socket.send(JSON.stringify(msg));\n }\n } catch {\n /* best-effort */\n }\n }\n }\n\n // Lazy ATOR probe for wizard step 3 preview.\n //\n // Privacy: do NOT start the probe at server boot — the wizard fires outbound\n // TCP+HTTPS only when the operator engages the ATOR radio (which calls\n // POST /api/transport/wizard-probe-start). Once started it runs until the\n // wizard transitions to normal mode (when the normal probe takes over) or\n // the server closes.\n const wizardProbe = new TransportProbe({ proxyUrl: DEFAULT_ATOR_PROXY });\n // `activeProbe` lets close() stop whichever probe is currently running.\n let activeProbe: TransportProbe = wizardProbe;\n\n // Wizard transport deps. The route closure reads `transportProbe` and `config`\n // per request, so swapping these on transition is observed by subsequent GETs.\n const wizardTransportConfig = {\n transport: { mode: 'ator' as const, socksProxy: DEFAULT_ATOR_PROXY },\n };\n const wizardTransportDeps = {\n config: wizardTransportConfig,\n transportProbe: wizardProbe,\n } as unknown as Parameters<typeof registerTransportRoutes>[1];\n registerTransportRoutes(app as FastifyInstance, wizardTransportDeps, {\n mode: 'wizard',\n });\n\n // Lazy probe-start endpoint (wizard-only). Idempotent.\n app.post('/api/transport/wizard-probe-start', async (_request, reply) => {\n if (state.mode !== 'wizard') {\n // After transition the normal probe runs based on saved config.\n return reply.status(409).send({ error: 'wizard_not_in_progress' });\n }\n wizardProbe.start();\n return reply.status(200).send({ started: true });\n });\n\n registerWizardRoutes(\n app as FastifyInstance,\n {\n configPath: initialDeps.configPath,\n walletPath: initialDeps.walletPath,\n },\n state,\n onInit\n );\n\n async function close(): Promise<void> {\n try {\n activeProbe.stop();\n } catch {\n /* best-effort */\n }\n // Defensive: stop wizardProbe in case activeProbe is the normal one.\n try {\n wizardProbe.stop();\n } catch {\n /* best-effort */\n }\n\n const openSockets = getOpenWebSockets();\n for (const socket of openSockets) {\n try {\n if (socket.readyState === WebSocket.OPEN) {\n socket.close(1001, 'server_shutdown');\n }\n } catch {\n /* best-effort */\n }\n }\n openSockets.clear();\n for (const socket of state.progressSockets) {\n try {\n socket.close(1001, 'server_shutdown');\n } catch {\n /* best-effort */\n }\n }\n state.progressSockets.clear();\n\n await Promise.race([\n app.close(),\n new Promise<void>((resolve) => setTimeout(resolve, CLOSE_TIMEOUT_MS)),\n ]);\n }\n\n return { app: app as FastifyInstance, close };\n}\n"],"mappings":"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","names":["fs","resolve","dir","platform","arch","runtime","abi","require_node_gyp_build","mask","data","require_fallback","Receiver","Sender","parse","EventEmitter","https","http","net","randomBytes","createHash","URL","Receiver","Sender","parse","WebSocket","websocket","key","WebSocket","createWebSocketStream","err","parse","protocol","EventEmitter","http","createHash","WebSocket","WebSocketServer","writeFileSync","join","resolve","err","SECRET_KEYS","result","join","writeFileSync","prev","resolve","existsSync","readFileSync","writeFileSync","join","parse","yamlStringify","join","existsSync","parse","readFileSync","yamlStringify","writeFileSync","readFileSync","writeFileSync","mkdirSync","chmodSync","existsSync","dirname","join","isAbsolute","homedir","dirname","yamlStringify","fs","dirname","fs","dirname","fs","dirname","stat","dirname","fs","z","mkdir","readFile","stat","writeFile","dirname","dirname","readFile","stat","mkdir","writeFile","resolve","isSnapshotEntry","join","dirname","TIMEOUT_MS","TIMEOUT_MS","entry","after","http","type","fs","dirname","join","bytesToHex","resolve","dirname","join","bytesToHex","fs","WebSocket","sendReq","existsSync","chmodSync","dirname","join","generateMnemonic","validateMnemonic","wordlist","existsSync","generateMnemonic","wordlist","validateMnemonic","chmodSync","WebSocket","dirname","join","DEFAULT_ATOR_PROXY","patchBodySchema","dirname","join","join","dirname","resolve","HEARTBEAT_INTERVAL_MS","join","dirname","CLOSE_TIMEOUT_MS","WebSocket","resolve","WebSocket","resolve"]}