139 lines
4.8 KiB
JavaScript
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)
|
|
}
|
|
}
|