Files
bare-operating-system/packages/bare-os-seeder/lib/pear-multisig-hint.js
T
2026-04-05 04:33:14 -04:00

139 lines
4.8 KiB
JavaScript

import { readFile } from '#host-fs-promises'
import path from '#host-path'
import b4a from 'b4a'
import { pearMultisigShapeOk } from 'bare-os-protocol/bare-os-pear-multisig-shape.js'
/**
* @returns {Promise<((cmd: string, args: string[], opts?: object) => object) | null>}
*/
async function loadSpawnSync() {
try {
const m = await import('bare-subprocess')
if (m && typeof m.spawnSync === 'function') return m.spawnSync
} catch {
/* bare-subprocess missing or native addon unavailable */
}
try {
const m = await import('child_process')
if (m && typeof m.spawnSync === 'function') return m.spawnSync
} catch {
/* Node child_process unavailable */
}
return null
}
/**
* @param {object} r spawnSync result (Node or bare-subprocess)
* @param {'stdout' | 'stderr'} key
*/
function pipeText(r, key) {
const v = r[key]
if (v == null) return ''
if (typeof v === 'string') return v
try {
return b4a.toString(v)
} catch {
return String(v)
}
}
/**
* When `pear.multisig.json` exists next to the kernel tree, validate shape and log status.
* Mirrors Holepunch `pear-multisig-link` style metadata (operational hint, not cryptography).
* Subprocess: prefers **`bare-subprocess`** (Pear/Bare); falls back to **`child_process`** on Node.
* Do not use **`node:child_process`** — it is not resolvable under Bare.
* @param {string} kernelRoot
*/
export async function logPearMultisigKernelHint(kernelRoot) {
const envOut = globalThis.process?.env
const f = path.join(kernelRoot, 'pear.multisig.json')
try {
const raw = await readFile(f, 'utf8')
const j = JSON.parse(raw)
if (!pearMultisigShapeOk(j)) {
console.warn(
'[seeder] pear.multisig.json: expected { signers: string[], quorum: number } with 1 ≤ quorum ≤ signers.length'
)
if (envOut) envOut.BARE_OS_SEEDER_MULTISIG_VERIFY_RESULT = 'skipped'
return
}
const signers = j.signers
const quorum = j.quorum
console.log(
`[seeder] pear.multisig.json OK (${signers.length} signers, quorum ${quorum})`
)
const env = globalThis.process?.env
if (
env?.BARE_OS_HYPER_MULTISIG_VERIFY === '1' ||
env?.BARE_OS_HYPER_MULTISIG_VERIFY === 'true'
) {
const spawnSync = await loadSpawnSync()
if (!spawnSync) {
console.warn(
'[seeder] hyper-multisig verify skipped (no subprocess: install bare-subprocess or use Node)'
)
if (envOut) envOut.BARE_OS_SEEDER_MULTISIG_VERIFY_RESULT = 'skipped'
return
}
const r = spawnSync('hyper-multisig', ['verify', f], {
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'pipe']
})
if (r.error) {
console.warn(
'[seeder] hyper-multisig verify skipped (CLI not on PATH):',
r.error.message
)
if (envOut) envOut.BARE_OS_SEEDER_MULTISIG_VERIFY_RESULT = 'skipped'
} else if (r.status !== 0) {
const msg = (pipeText(r, 'stderr') || pipeText(r, 'stdout')).trim().slice(0, 400)
console.warn('[seeder] hyper-multisig verify failed:', msg || `exit ${r.status}`)
if (envOut) envOut.BARE_OS_SEEDER_MULTISIG_VERIFY_RESULT = 'failed'
} else {
console.log('[seeder] hyper-multisig verify OK')
if (envOut) envOut.BARE_OS_SEEDER_MULTISIG_VERIFY_RESULT = 'ok'
}
} else if (envOut) {
envOut.BARE_OS_SEEDER_MULTISIG_VERIFY_RESULT = 'skipped'
}
} catch (e) {
if (/** @type {NodeJS.ErrnoException} */ (e).code === 'ENOENT') return
console.warn('[seeder] pear.multisig.json:', e?.message || e)
}
}
/**
* Same shape checks as {@link logPearMultisigKernelHint}, reading **`/kernel/pear.multisig.json`**
* via Pear app drive IPC (**`pear-appdrive`**). Subprocess **`hyper-multisig verify`** is skipped.
* @param {{ get: (key: string) => Promise<Uint8Array | Buffer | null> }} appDrive
*/
export async function logPearMultisigKernelHintFromPearAppDrive(appDrive) {
const envOut = globalThis.process?.env
let raw
try {
const buf = await appDrive.get('/kernel/pear.multisig.json')
if (!buf) return
raw = b4a.toString(buf, 'utf8')
} catch {
return
}
try {
const j = JSON.parse(raw)
if (!pearMultisigShapeOk(j)) {
console.warn(
'[seeder] pear.multisig.json: expected { signers: string[], quorum: number } with 1 ≤ quorum ≤ signers.length'
)
if (envOut) envOut.BARE_OS_SEEDER_MULTISIG_VERIFY_RESULT = 'skipped'
return
}
const signers = j.signers
const quorum = j.quorum
console.log(
`[seeder] pear.multisig.json OK (${signers.length} signers, quorum ${quorum}) [from Pear bundle]`
)
if (envOut) envOut.BARE_OS_SEEDER_MULTISIG_VERIFY_RESULT = 'skipped'
} catch (e) {
console.warn('[seeder] pear.multisig.json (bundle):', e?.message || e)
}
}