Files
bare-operating-system/packages/bare-os-booter/lib/tools/bare-os-hrpc-stock-request.js
T
2026-08-18 18:11:28 -04:00

236 lines
8.1 KiB
JavaScript

/**
* Stock `ctx.bareOsHrpcRequest` factory (strict validation + built-in routes).
*/
import b4a from 'b4a'
import {
parseBareOsHrpcAllowlistJson,
bareOsHrpcAllowlistDeniesRoute
} from './bare-os-hrpc-allowlist.js'
import {
BARE_OS_HRPC_ROUTE_TABLE_SCHEMA_VERSION,
BARE_OS_HRPC_STOCK_ROUTES
} from './bare-os-hrpc-route-table.js'
/**
* @typedef {object} StockHrpcDeps
* @property {Record<string, string | undefined>} env
* @property {string} sessionId
* @property {{ readFile?: Function } | null | undefined} vfs
* @property {{ os?: { execRpc?: Function, searchLocal?: Function } } | null | undefined} disk
* @property {unknown} capabilityWords
* @property {string} protocolVersion
* @property {string} booterVersion
* @property {(row: Record<string, unknown>) => unknown} [auditAppend]
*/
/**
* @param {StockHrpcDeps} deps
*/
export function createStockBareOsHrpcRequest(deps) {
const {
env,
sessionId,
vfs,
disk,
capabilityWords,
protocolVersion,
booterVersion,
auditAppend
} = deps
/**
* Built-in hrpc bridge with strict validation and optional host event emission.
* Hosts can still replace `ctx.bareOsHrpcRequest`, but the stock handler is functional.
* @param {string} service
* @param {string} method
* @param {Record<string, unknown>} [payload]
*/
async function stockBareOsHrpcRequest(service, method, payload = {}) {
const svc = String(service || '').trim()
const m = String(method || '').trim()
if (!/^[a-zA-Z0-9._-]{1,64}$/.test(svc)) {
throw new Error('bareOsHrpcRequest: invalid service')
}
if (!/^[a-zA-Z0-9._-]{1,96}$/.test(m)) {
throw new Error('bareOsHrpcRequest: invalid method')
}
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
throw new Error('bareOsHrpcRequest: payload must be an object')
}
const hrpcAuditOn =
env.BARE_OS_HRPC_AUDIT === '1' || env.BARE_OS_HRPC_AUDIT === 'true'
/** @param {Record<string, unknown>} row */
function hrpcAuditAppend(row) {
if (!hrpcAuditOn || typeof auditAppend !== 'function') return
void auditAppend({
type: 'hrpc.request',
service: svc,
method: m,
sessionId,
ts: Date.now(),
...row
})
}
const allowParsed = parseBareOsHrpcAllowlistJson(
String(env.BARE_OS_HRPC_ALLOWLIST_JSON || '')
)
if (allowParsed.parseError) {
throw new Error('bareOsHrpcRequest: invalid BARE_OS_HRPC_ALLOWLIST_JSON')
}
if (bareOsHrpcAllowlistDeniesRoute(allowParsed.allow, svc, m)) {
hrpcAuditAppend({ ok: false, reason: 'allowlist_denied' })
throw new Error('bareOsHrpcRequest: method denied by allowlist')
}
if (svc === 'kernel' && m === 'ping') {
hrpcAuditAppend({ ok: true, route: 'kernel.ping' })
return { ok: true, sessionId, ts: Date.now() }
}
if (svc === 'kernel' && m === 'capabilities') {
hrpcAuditAppend({ ok: true, route: 'kernel.capabilities' })
return {
ok: true,
sessionId,
capabilityWords,
protocolVersion,
booterVersion,
hrpcRouteTableSchemaVersion: BARE_OS_HRPC_ROUTE_TABLE_SCHEMA_VERSION,
hrpcStockRoutes: [...BARE_OS_HRPC_STOCK_ROUTES]
}
}
if (svc === 'vfs' && m === 'readText') {
if (!vfs || typeof vfs.readFile !== 'function') {
hrpcAuditAppend({ ok: false, reason: 'vfs_unavailable' })
throw new Error('bareOsHrpcRequest: vfs.readFile unavailable')
}
const reqPath = String(payload.path || '').trim()
if (!reqPath.startsWith('/')) {
hrpcAuditAppend({ ok: false, reason: 'invalid_path' })
throw new Error('bareOsHrpcRequest: payload.path must be absolute')
}
const buf = await vfs.readFile(reqPath)
hrpcAuditAppend({ ok: true, route: 'vfs.readText' })
return {
ok: true,
path: reqPath,
text: b4a.toString(buf, 'utf8')
}
}
if (svc === 'bare_os' && m === 'echo') {
hrpcAuditAppend({ ok: true, route: 'bare_os.echo' })
return { ok: true, payload }
}
if (svc === 'bare_os' && m === 'disk_os_hints') {
if (!disk?.os || typeof disk.os.execRpc !== 'function') {
hrpcAuditAppend({ ok: false, reason: 'disk_os_unavailable' })
throw new Error('bareOsHrpcRequest: disk.os bridge unavailable')
}
const txt = await disk.os.execRpc('bare_os', 'disk_os_hints', [])
let parsed = null
try {
parsed = JSON.parse(txt)
} catch {
parsed = { error: 'invalid_json' }
}
hrpcAuditAppend({ ok: true, route: 'bare_os.disk_os_hints' })
return { ok: true, text: txt, json: parsed }
}
if (svc === 'bare_os' && m === 'search_local') {
if (!disk?.os || typeof disk.os.searchLocal !== 'function') {
hrpcAuditAppend({ ok: false, reason: 'disk_os_unavailable' })
throw new Error('bareOsHrpcRequest: disk.os searchLocal unavailable')
}
const query = String(payload.query ?? payload.q ?? '').trim()
if (!query) {
hrpcAuditAppend({ ok: false, reason: 'invalid_query' })
throw new Error('bareOsHrpcRequest: payload.query (or q) is required')
}
if (query.length > 512) {
hrpcAuditAppend({ ok: false, reason: 'query_too_long' })
throw new Error('bareOsHrpcRequest: payload.query exceeds 512 chars')
}
const paths = await disk.os.searchLocal(query)
hrpcAuditAppend({ ok: true, route: 'bare_os.search_local' })
return { ok: true, query, paths }
}
if (svc === 'bare_os' && m === 'replication_operator_sketch') {
if (!disk?.os || typeof disk.os.execRpc !== 'function') {
hrpcAuditAppend({ ok: false, reason: 'disk_os_unavailable' })
throw new Error('bareOsHrpcRequest: disk.os bridge unavailable')
}
const txt = await disk.os.execRpc(
'bare_os',
'replication_operator_sketch',
[]
)
let parsed = null
try {
parsed = JSON.parse(txt)
} catch {
parsed = { error: 'invalid_json' }
}
hrpcAuditAppend({
ok: true,
route: 'bare_os.replication_operator_sketch'
})
return { ok: true, text: txt, json: parsed }
}
if (svc === 'bare_os' && m === 'replication_snapshot') {
if (!disk?.os || typeof disk.os.execRpc !== 'function') {
hrpcAuditAppend({ ok: false, reason: 'disk_os_unavailable' })
throw new Error('bareOsHrpcRequest: disk.os bridge unavailable')
}
const txt = await disk.os.execRpc('bare_os', 'replication_snapshot', [])
let parsed = null
try {
parsed = JSON.parse(txt)
} catch {
parsed = { error: 'invalid_json' }
}
hrpcAuditAppend({ ok: true, route: 'bare_os.replication_snapshot' })
return { ok: true, text: txt, json: parsed }
}
if (svc === 'bare_os' && m === 'pkg_index_get') {
if (!disk?.os || typeof disk.os.execRpc !== 'function') {
hrpcAuditAppend({ ok: false, reason: 'disk_os_unavailable' })
throw new Error('bareOsHrpcRequest: disk.os bridge unavailable')
}
const key = String(payload.key ?? payload.name ?? '').trim()
const txt = await disk.os.execRpc('bare_os', 'pkg_index_get', [key])
let parsed = null
try {
parsed = JSON.parse(txt)
} catch {
parsed = { error: 'invalid_json' }
}
hrpcAuditAppend({ ok: true, route: 'bare_os.pkg_index_get' })
return { ok: true, text: txt, json: parsed }
}
const emitUnlisted =
env.BARE_OS_HRPC_EMIT_UNLISTED === '1' ||
env.BARE_OS_HRPC_EMIT_UNLISTED === 'true'
if (emitUnlisted && typeof globalThis.process?.emit === 'function') {
try {
globalThis.process.emit('bare-os:hrpc-request', {
service: svc,
method: m,
payload,
ts: Date.now(),
sessionId
})
} catch {
/* ignore event bus failures */
}
}
hrpcAuditAppend({ ok: false, reason: 'unsupported_route' })
throw new Error(
`bareOsHrpcRequest: unsupported route "${svc}.${m}" (stock handler)`
)
}
stockBareOsHrpcRequest.__bareOsStockHrpcRequest = true
return stockBareOsHrpcRequest
}