Align the booter and docs with the kernel POSIX and stack-hardening roadmap: refresh

vendored Bare bundles and seeder parity; tighten the no-`node:` verifier and host-vs-image
docs; expand syscall/proc JSON with POSIX XSH-style ops, errno hints, signals, and schema
updates; add simulated FD table hooks, pathconf/getconf coverage, IPC message-queue surface,
swarm/protomux pool metrics and HyperDHT stats env; extend systemctl verbs, cron TZ-aware
matching, Pear updater and corestore snapshot hints, WASM instantiate behind a gate, and
runBin default timeout merging with caller abort options.

Bump BARE_OS_CTX_API_VERSION to 1.34.0 and BARE_OS_POSIX_PROFILE_VERSION to 1.0.1; sync
declared profile, compliance matrix, handbook, example syscalls JSON, CHANGELOG, and
generated ctx client helper; extend bare-os-ctx.d.ts for new ctx members.
This commit is contained in:
Raven Scott
2026-04-04 22:39:33 -04:00
parent 8a6c1e7b09
commit 985eadfda3
45 changed files with 1418 additions and 478 deletions
+1
View File
@@ -15,6 +15,7 @@ Authoritative **version alignment** with protocol and telemetry schema numbers l
| Version | Booter (workspace) | Notes |
| ------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| 1.34.0 | 0.1.0 | **`ctx.bareOsPosixFdSimPipe`** / **`bareOsPosixFdSimRead`** / **`Write`** / **`Dup`** when **`BARE_OS_POSIX_FD_SIM`**; **`ctx.bareOsSigaction`** (**`IGNORE`** / **`DEFAULT`**) + **`process_table.sigactionSurface`**; expanded POSIX signal names; **`ctx.bareOsWasmKernelInstantiate`** (bounded **`Memory`** + timeout); **`/proc/bare_os/syscalls.json`** **`posixXsh`** + XSH **`opsDetail`** rows; **`bareOsPathconf`** **`_PC_*`** coverage; protocol **`BARE_OS_POSIX_PROFILE_VERSION` `1.0.1`**. |
| 1.33.0 | 0.1.0 | **`ctx.bareOsListMirrorMounts`**, **`bareOsVaultAuditSummary`**, **`bareOsWasmKernelCompile`** ( **`BARE_OS_WASM_KERNEL`** ); **`/proc/bare_os/syscalls.json`** documents **`fdModel.posixLike`** mapping; **`process_table`** **`exitStatusModel`**; pipeline exit = last stage ( **`BARE_OS_SHELL_PIPEFAIL`** for bash-like early stop); script errors stop pipeline; **`corestore_snapshot`** operator proc id; Pear inspect + **`bareOsDiagnosticsEmit`**; IPC **`stats.ipcBackpressure`**. |
| 1.32.1 | 0.1.0 | **`/proc/bare_os/process_table.json`** schema **4**: **`signalRouting`**, **`initdBinding`**, initd units as logical pids **5100+**; **`bareOsSendSignal`** accepts **41008999** (shell jobs / initd) without session exit. |
| 1.32.0 | 0.1.0 | **`ctx.bareOsPearUpdaterDelegate`** is **async**: operator snapshot (**`BARE_OS_PEAR_UPDATER_SNAPSHOT_JSON`**) or dynamic ESM (**`BARE_OS_PEAR_UPDATER_MODULE`** / **`BARE_OS_PEAR_UPDATER_DELEGATE`**) via [`lib/bare-os-pear-updater-bridge.js`](lib/bare-os-pear-updater-bridge.js). Passthrough env keys for those variables. Host **`#host-fs`** / **`#host-path`** import aliases (Bare-first). |
+239 -14
View File
@@ -46,7 +46,8 @@ import {
import { SwarmDisk } from './lib/swarm-disk.js'
import {
bareOsInstallCorestoreSuspendResumeHooks,
bareOsRegisterCorestoreSuspendResumeHooks
bareOsRegisterCorestoreSuspendResumeHooks,
bareOsCorestoreSnapshotOperatorHint
} from './lib/corestore-host-lifecycle.js'
import { BareOsSwarmConnectionManager } from './lib/bare-os-swarm-connection-manager.js'
import { createBareOsProtomuxAliasRegistry } from './lib/bare-os-protomux-alias-registry.js'
@@ -133,7 +134,14 @@ import {
BARE_OS_SYSCALL_OPS_DETAIL
} from './lib/bare-os-syscall-ops.js'
import { buildBareOsSyscallsProcJson } from './lib/bare-os-syscalls-proc-json.js'
import { bareOsWasmKernelCompile as compileBareOsWasmKernelModule } from './lib/bare-os-wasm-kernel.js'
import {
bareOsIsPosixSignalName,
bareOsNormalizeSignalName
} from './lib/bare-os-posix-signals.js'
import {
bareOsWasmKernelCompile as compileBareOsWasmKernelModule,
bareOsWasmKernelInstantiate as instantiateBareOsWasmKernelModule
} from './lib/bare-os-wasm-kernel.js'
import { BARE_OS_CTX_API_VERSION } from './lib/bare-os-ctx-api.js'
import { buildBareOsPearCorestoreHrpcProcJson } from './lib/bare-os-proc-pear-corestore-hrpc.js'
import { buildBareOsRuntimeCaps } from './lib/bare-os-runtime-caps.js'
@@ -145,6 +153,7 @@ import {
verifyBareModuleLockfile
} from './lib/bare-os-ctx-bare.js'
import { raceWithAbortAndTimeout } from './lib/bare-os-abort.js'
import { bareOsHrtimeSnapshot } from './lib/bare-os-monotonic-time.js'
import {
coerceBareFetchExport,
ensureBareFetchGlobals,
@@ -588,6 +597,10 @@ async function executeKernel(disk, store, swarm, initSource) {
'BARE_OS_WARM_REBOOT',
'BARE_OS_WARM_REBOOT_EXTRA_CYCLES',
'BARE_OS_WASM_KERNEL',
'BARE_OS_POSIX_FD_SIM',
'BARE_OS_POSIX_FD_SIM_MAX_BYTES',
'BARE_OS_HYPERDHT_STATS_JSON',
'BARE_OS_RUNBIN_DEFAULT_TIMEOUT_MS',
'BARE_OS_VFS_SNAPSHOTS',
'BARE_OS_VFS_QUOTA_MODE',
'BARE_OS_VFS_QUOTA_GRACE_MS',
@@ -2111,6 +2124,7 @@ async function executeKernel(disk, store, swarm, initSource) {
),
processTable: (() => {
const { shellJobs, ipcStats } = bareOsLiveShellJobsAndIpcStats()
const liveCtx = bareOsInteractiveCtxRef.ctx
return bareOsProcessTableSnapshot({
sessionId: bareOsSessionId,
bootStartedMs,
@@ -2118,7 +2132,11 @@ async function executeKernel(disk, store, swarm, initSource) {
shellJobs,
ipcStats,
logicalFdRows: bareOsCollectLogicalFdRows(),
initdReadiness: bareInitdReadinessSnapshot()
initdReadiness: bareInitdReadinessSnapshot(),
sigactionHandlers:
liveCtx && liveCtx.bareOsLogicalSigaction
? liveCtx.bareOsLogicalSigaction
: null
})
})(),
initdReadiness: bareInitdReadinessSnapshot(),
@@ -2328,9 +2346,32 @@ async function executeKernel(disk, store, swarm, initSource) {
const BARE_OS_SIGNAL_EXIT = {
HUP: 1,
INT: 2,
QUIT: 3,
ABRT: 6,
KILL: 9,
TERM: 15
SEGV: 11,
ALRM: 14,
TERM: 15,
XCPU: 24,
XFSZ: 25,
VTALRM: 26,
PROF: 27
}
/** Signals that end the Bare OS session when delivered to pids 13. */
const BARE_OS_SESSION_EXIT_SIGNALS = new Set([
'HUP',
'INT',
'QUIT',
'ABRT',
'KILL',
'TERM',
'SEGV',
'XCPU',
'XFSZ',
'ALRM',
'VTALRM',
'PROF'
])
/**
* Built-in hrpc bridge with strict validation and optional host event emission.
@@ -2446,8 +2487,21 @@ async function executeKernel(disk, store, swarm, initSource) {
bareOsBootStartedMs: bootStartedMs,
/** Logical open-file targets for `/proc/self/fd/*` beyond stdio (see `bareOsRegisterLogicalFd`). */
bareOsLogicalFds: /** @type {Record<string, string>} */ ({}),
/** In-memory POSIX pipe simulation (`BARE_OS_POSIX_FD_SIM`); see `bareOsPosixFdSimPipe`. */
bareOsPosixFdSimState: {
nextFd: 10,
nextPairId: 1,
/** @type {Record<string, string[]>} */
queues: Object.create(null),
/** @type {Record<string, number>} */
byteTotals: Object.create(null)
},
/** Shell `trap` COMMAND → SIGNAL map (logical job control; delivery is host-specific). */
shellTrapHandlers: Object.create(null),
/** Logical sigaction: signal name → `IGNORE` (see `ctx.bareOsSigaction`). */
bareOsLogicalSigaction: Object.create(null),
/** Recent host **`bare-subprocess`** spawn metadata (non-secret). */
bareOsHostSubprocessJobs: /** @type {Record<string, unknown>[]} */ ([]),
/** True when `BARE_OS_SKIP_REPL=1` — stdin is non-interactive; `readLine` yields EOF immediately after boot. */
bareOsSkipRepl: skipInteractive,
disk,
@@ -2889,7 +2943,8 @@ async function executeKernel(disk, store, swarm, initSource) {
shellJobs,
ipcStats,
logicalFdRows: bareOsCollectLogicalFdRows(),
initdReadiness: bareInitdReadinessSnapshot()
initdReadiness: bareInitdReadinessSnapshot(),
sigactionHandlers: this.bareOsLogicalSigaction
})
},
/**
@@ -2911,10 +2966,8 @@ async function executeKernel(disk, store, swarm, initSource) {
* Signals: HUP, INT, KILL, TERM, PIPE, CHLD, USR1, USR2; `PIPE`/`CHLD`/`USR*` update state only (no session exit).
*/
bareOsSendSignal(target, signal = 'TERM') {
const sig = String(signal || 'TERM')
.replace(/^SIG/i, '')
.toUpperCase()
if (!/^(HUP|INT|KILL|TERM|PIPE|CHLD|USR1|USR2|0)$/.test(sig)) {
const sig = bareOsNormalizeSignalName(signal)
if (!bareOsIsPosixSignalName(signal)) {
throw new Error('bareOsSendSignal: unsupported signal')
}
const raw = String(target == null ? '' : target).trim()
@@ -2935,6 +2988,19 @@ async function executeKernel(disk, store, swarm, initSource) {
if (sig === '0') {
return { ok: true, pid, signal: sig, delivered: false, exists: true }
}
if (
this.bareOsLogicalSigaction &&
this.bareOsLogicalSigaction[sig] === 'IGNORE'
) {
return {
ok: true,
pid,
signal: sig,
delivered: false,
ignored: true,
atMs: Date.now()
}
}
const atMs = Date.now()
bareOsVirtualSignalState.set(pid, { signal: sig, atMs })
if (typeof globalThis.process?.emit === 'function') {
@@ -2946,10 +3012,8 @@ async function executeKernel(disk, store, swarm, initSource) {
}
const noSessionExit =
extendedSynthetic ||
sig === 'PIPE' ||
sig === 'CHLD' ||
sig === 'USR1' ||
sig === 'USR2'
sig === '0' ||
!BARE_OS_SESSION_EXIT_SIGNALS.has(sig)
if (!noSessionExit) {
const signum = BARE_OS_SIGNAL_EXIT[sig] || 15
this.requestBooterExit(128 + signum)
@@ -2974,6 +3038,14 @@ async function executeKernel(disk, store, swarm, initSource) {
...(opts && typeof opts === 'object' ? opts : {}),
stdio: 'pipe'
})
if (!Array.isArray(this.bareOsHostSubprocessJobs)) {
this.bareOsHostSubprocessJobs = []
}
this.bareOsHostSubprocessJobs.push({
hostDelegated: true,
argv: av.slice(0, 48),
atMs: Date.now()
})
return { ok: true, child }
} catch (e) {
return { ok: false, reason: (e && e.message) || String(e) }
@@ -2997,6 +3069,126 @@ async function executeKernel(disk, store, swarm, initSource) {
delete this.bareOsLogicalFds[k]
}
},
bareOsPosixFdSimCapBytes() {
const raw = String(shellEnv.BARE_OS_POSIX_FD_SIM_MAX_BYTES || '').trim()
const n = raw ? Number.parseInt(raw, 10) : 1048576
return Number.isFinite(n) && n > 0 ? Math.min(n, 16 * 1024 * 1024) : 1048576
},
bareOsPosixFdSimEnabled() {
const v = shellEnv.BARE_OS_POSIX_FD_SIM
return v === '1' || v === 'true'
},
/** @returns {{ readFd: number, writeFd: number, pairId: number } | null} */
bareOsPosixFdSimPipe() {
if (!this.bareOsPosixFdSimEnabled()) return null
const st = this.bareOsPosixFdSimState
if (!st || typeof st !== 'object') return null
const pairId = st.nextPairId++
const readFd = st.nextFd++
const writeFd = st.nextFd++
if (st.nextFd > 65530) {
st.nextFd = 10
return null
}
const pk = String(pairId)
st.queues[pk] = []
st.byteTotals[pk] = 0
this.bareOsRegisterLogicalFd(readFd, `posix-pipe:${pairId}:r`)
this.bareOsRegisterLogicalFd(writeFd, `posix-pipe:${pairId}:w`)
return { readFd, writeFd, pairId }
},
/** @param {number} fd */
bareOsPosixFdSimDup(fd) {
if (!this.bareOsPosixFdSimEnabled()) return null
const k = String(Number(fd) >>> 0)
const target = this.bareOsLogicalFds[k]
if (!target || !String(target).startsWith('posix-pipe:')) return null
const st = this.bareOsPosixFdSimState
const newFd = st.nextFd++
if (st.nextFd > 65530) return null
this.bareOsRegisterLogicalFd(newFd, String(target))
return newFd
},
/**
* @param {number} writeFd
* @param {string | Uint8Array} data
*/
bareOsPosixFdSimWrite(writeFd, data) {
if (!this.bareOsPosixFdSimEnabled()) return { ok: false, reason: 'disabled' }
const k = String(Number(writeFd) >>> 0)
const t = this.bareOsLogicalFds[k]
const m = /^posix-pipe:(\d+):w$/.exec(String(t || ''))
if (!m) return { ok: false, reason: 'not_write_end' }
const pairId = m[1]
const st = this.bareOsPosixFdSimState
const q = st.queues[pairId]
if (!q) return { ok: false, reason: 'bad_pair' }
const buf =
typeof data === 'string'
? b4a.from(data, 'utf8')
: data instanceof Uint8Array
? data
: b4a.from(String(data), 'utf8')
const add = buf.byteLength
const cap = this.bareOsPosixFdSimCapBytes()
const prev = st.byteTotals[pairId] || 0
if (prev + add > cap) return { ok: false, reason: 'EAGAIN' }
st.byteTotals[pairId] = prev + add
q.push(b4a.toString(buf, 'utf8'))
return { ok: true, bytes: add }
},
/**
* Logical `sigaction`: `IGNORE` suppresses synthetic delivery for this signal.
* @param {string} signal
* @param {'IGNORE' | 'DEFAULT' | 'IGN' | 'DEF'} mode
*/
bareOsSigaction(signal, mode) {
const s = bareOsNormalizeSignalName(signal)
if (!bareOsIsPosixSignalName(signal) || s === '0') {
throw new Error('bareOsSigaction: unsupported signal')
}
const m = String(mode || 'DEFAULT')
.toUpperCase()
.replace(/^SIG/i, '')
if (m === 'DEFAULT' || m === 'DEF') {
if (this.bareOsLogicalSigaction) delete this.bareOsLogicalSigaction[s]
return { ok: true, signal: s, mode: 'DEFAULT' }
}
if (m === 'IGNORE' || m === 'IGN') {
if (!this.bareOsLogicalSigaction) {
throw new Error('bareOsSigaction: internal state missing')
}
this.bareOsLogicalSigaction[s] = 'IGNORE'
return { ok: true, signal: s, mode: 'IGNORE' }
}
throw new Error('bareOsSigaction: mode must be IGNORE or DEFAULT')
},
/**
* @param {number} readFd
* @param {number} [maxBytes]
*/
bareOsPosixFdSimRead(readFd, maxBytes = 65536) {
if (!this.bareOsPosixFdSimEnabled()) return { ok: false, reason: 'disabled' }
const cap = Math.max(1, Math.min(Number(maxBytes) || 65536, 1024 * 1024))
const k = String(Number(readFd) >>> 0)
const t = this.bareOsLogicalFds[k]
const m = /^posix-pipe:(\d+):r$/.exec(String(t || ''))
if (!m) return { ok: false, reason: 'not_read_end' }
const pairId = m[1]
const st = this.bareOsPosixFdSimState
const q = st.queues[pairId]
if (!q || q.length === 0) return { ok: true, data: '', eof: false }
let chunk = q.shift()
if (!chunk) return { ok: true, data: '', eof: false }
if (chunk.length > cap) {
const rest = chunk.slice(cap)
chunk = chunk.slice(0, cap)
q.unshift(rest)
}
const used = b4a.from(chunk, 'utf8').byteLength
st.byteTotals[pairId] = Math.max(0, (st.byteTotals[pairId] || 0) - used)
return { ok: true, data: chunk, eof: false }
},
/** @type {Record<string, unknown>[]} */
bareOsSnapshotHandles: [],
/** @param {Record<string, unknown>} desc */
@@ -3016,8 +3208,9 @@ async function executeKernel(disk, store, swarm, initSource) {
* @param {string} [_path]
* @param {string} name
*/
bareOsPathconf(_path, name) {
bareOsPathconf(pathArg, name) {
const k = String(name || '').trim()
const p = String(pathArg || '/').trim() || '/'
/** @type {Record<string, number>} */
const table = {
PATH_MAX: 4096,
@@ -3025,9 +3218,20 @@ async function executeKernel(disk, store, swarm, initSource) {
_PC_PATH_MAX: 4096,
_PC_NAME_MAX: 255,
_PC_SYMLINK_MAX: 1024,
_PC_CHOWN_RESTRICTED: 1,
_PC_NO_TRUNC: 1,
_PC_FILESIZEBITS: 64,
_PC_LINK_MAX: 1,
_PC_MAX_CANON: 255,
_PC_MAX_INPUT: 512,
_PC_PIPE_BUF: 4096,
_PC_2_SYMLINKS: 1,
_POSIX_VERSION: 200809,
_POSIX_THREAD_ATTR_STACKSIZE: 65536
}
if (k === '_PC_CHOWN_RESTRICTED' && p.startsWith('/mirror/')) {
return 0
}
if (!k || table[k] === undefined) {
throw new Error('bareOsPathconf: unknown name: ' + k)
}
@@ -3348,6 +3552,12 @@ async function executeKernel(disk, store, swarm, initSource) {
'Use upstream mirror-drive / pear-pack with the system key; guest returns this operator-facing descriptor only — replication runs on the host.'
}
},
bareOsCorestoreSnapshotHint() {
return bareOsCorestoreSnapshotOperatorHint(store)
},
bareOsReadHrtimeSnapshot() {
return bareOsHrtimeSnapshot()
},
/** Read-only auxiliary Hyperdrive mounts under `/mirror/auxN` (same ordering as `/proc/mounts`). */
bareOsListMirrorMounts() {
const aux = disk.auxiliaryDrives || []
@@ -3359,6 +3569,11 @@ async function executeKernel(disk, store, swarm, initSource) {
index: i,
readOnly: true
})),
mirrorDriveHint: {
schema: 1,
note: 'Host read-through replication may use holepunch mirror-drive; guest lists static aux keys only.',
dedupOperatorEnv: 'BARE_OS_VFS_HYPERBLOBS_DEDUP'
},
atMs: Date.now()
}
},
@@ -3387,6 +3602,16 @@ async function executeKernel(disk, store, swarm, initSource) {
const u8 = buf instanceof Uint8Array ? buf : new Uint8Array(buf || [])
return compileBareOsWasmKernelModule(u8, opts)
},
async bareOsWasmKernelInstantiate(buf, opts) {
if (
shellEnv.BARE_OS_WASM_KERNEL !== '1' &&
shellEnv.BARE_OS_WASM_KERNEL !== 'true'
) {
return { ok: false, reason: 'disabled_set_BARE_OS_WASM_KERNEL' }
}
const u8 = buf instanceof Uint8Array ? buf : new Uint8Array(buf || [])
return instantiateBareOsWasmKernelModule(u8, this, opts)
},
/**
* HDMS activate/deactivate hooks (`kind`: `activate` | `deactivate`; `labels` on activate).
* @param {(ev: Record<string, unknown>) => void | Promise<void>} fn
+56 -8
View File
@@ -24,6 +24,51 @@ let cronExecCtx = null
/** @type {Set<number>} */
const cronRebootLinesDone = new Set()
/**
* Wall-clock fields for cron matching (uses `TZ` when set).
* @param {Date} d
* @param {Record<string, string> | undefined} env
*/
export function cronWallClockParts(d, env) {
const tz = env && String(env.TZ || '').trim()
if (!tz) {
return {
minute: d.getMinutes(),
hour: d.getHours(),
date: d.getDate(),
month: d.getMonth() + 1,
dow: d.getDay()
}
}
try {
const parts = new Intl.DateTimeFormat('en-US', {
timeZone: tz,
minute: 'numeric',
hour: 'numeric',
day: 'numeric',
month: 'numeric',
weekday: 'short',
hourCycle: 'h23'
}).formatToParts(d)
/** @type {Record<string, string>} */
const map = Object.create(null)
for (const p of parts) {
if (p.type !== 'literal') map[p.type] = p.value
}
const wd = String(map.weekday || 'Sun').slice(0, 3)
const dowMap = { Sun: 0, Mon: 1, Tue: 2, Wed: 3, Thu: 4, Fri: 5, Sat: 6 }
return {
minute: Number.parseInt(map.minute, 10),
hour: Number.parseInt(map.hour, 10),
date: Number.parseInt(map.day, 10),
month: Number.parseInt(map.month, 10),
dow: dowMap[wd] ?? 0
}
} catch {
return cronWallClockParts(d, undefined)
}
}
export function stopBareCron() {
if (cronTimeoutId != null) {
clearTimeout(cronTimeoutId)
@@ -211,18 +256,20 @@ export function parseCronLine(line) {
/**
* @param {{ minute: string, hour: string, dom: string, month: string, dow: string }} job
* @param {Date} date
* @param {Record<string, string> | undefined} [env] optional `TZ` for wall-clock fields
*/
export function jobMatchesDate(job, date) {
export function jobMatchesDate(job, date, env) {
if (job.minute === '@reboot') return false
const z = cronWallClockParts(date, env)
if (
!fieldMatches(job.minute, date.getMinutes(), 0, 59) ||
!fieldMatches(job.hour, date.getHours(), 0, 23) ||
!fieldMatches(job.month, date.getMonth() + 1, 1, 12)
!fieldMatches(job.minute, z.minute, 0, 59) ||
!fieldMatches(job.hour, z.hour, 0, 23) ||
!fieldMatches(job.month, z.month, 1, 12)
) {
return false
}
const domOk = fieldMatches(job.dom, date.getDate(), 1, 31)
const dowOk = dowFieldMatches(job.dow, date.getDay())
const domOk = fieldMatches(job.dom, z.date, 1, 31)
const dowOk = dowFieldMatches(job.dow, z.dow)
const domStar = String(job.dom || '').trim() === '*'
const dowStar = String(job.dow || '').trim() === '*'
if (domStar && dowStar) return true
@@ -621,7 +668,7 @@ function startBareCron(ctx) {
const now = new Date()
for (const job of jobs) {
if (!jobMatchesDate(job, now)) continue
if (!jobMatchesDate(job, now, c.env)) continue
const key = job.lineIndex
if (cronRunningJobs.has(key)) continue
cronRunningJobs.add(key)
@@ -667,7 +714,8 @@ registerBareInitdDisposer(stopBareCron)
registerBareService({
name: 'bare-cron',
description: 'User crontab scheduler (minute-aligned; ~/.crontab)',
description:
'User crontab scheduler (minute-aligned; ~/.crontab); honors TZ for wall-clock fields',
logPath: CRON_LOG,
start: startBareCron,
stop: stopBareCron
@@ -53,6 +53,18 @@ export async function maybeBareOsBuildBinHyperbeeHint(ctx) {
'[bare-os] bin hyperbee hint: ' + ((err && err.message) || String(err))
)
}
try {
const idx =
JSON.stringify({
schema: 1,
atMs: Date.now(),
primaryHint: '/.bare-os/index/bin-hyperbee-hint.json',
note: 'Secondary metadata path for `/.bare-os/indexes/` namespace (Hyperbee operator integration).'
}) + '\n'
await vfs.writeFile('/.bare-os/indexes/hyperbee_status.json', b4.from(idx))
} catch {
/* personal tree may deny path on some images */
}
}
export async function maybeBareOsBuildBinManifest(ctx) {
@@ -2,4 +2,4 @@
* Semantic version of the booter `ctx` contract for custom kernels.
* Bump when adding/removing/renaming documented `ctx` fields or changing behavior.
*/
export const BARE_OS_CTX_API_VERSION = '1.33.0'
export const BARE_OS_CTX_API_VERSION = '1.34.0'
+41
View File
@@ -337,12 +337,15 @@ export interface BareOsKernelContext {
signal: string
delivered: boolean
exists?: boolean
ignored?: boolean
atMs?: number
}
/** FD → logical VFS/IPC target; see `/proc/bare_os/syscalls.json` fdModel. */
bareOsLogicalFds?: Record<string, string>
/** Shell `trap` COMMAND → SIGNAL map (logical; delivery is synthetic). */
shellTrapHandlers?: Record<string, string>
bareOsLogicalSigaction?: Record<string, string>
bareOsHostSubprocessJobs?: Record<string, unknown>[]
bareOsAppendVaultRotationCheckpoint?(
row: Record<string, unknown>
): Promise<void>
@@ -355,6 +358,25 @@ export interface BareOsKernelContext {
>
bareOsRegisterLogicalFd?(fd: number, target: string): void
bareOsUnregisterLogicalFd?(fd: number): void
bareOsPosixFdSimEnabled?(): boolean
bareOsPosixFdSimPipe?(): {
readFd: number
writeFd: number
pairId: number
} | null
bareOsPosixFdSimDup?(fd: number): number | null
bareOsPosixFdSimWrite?(
writeFd: number,
data: string | Uint8Array
): { ok: boolean; bytes?: number; reason?: string }
bareOsPosixFdSimRead?(
readFd: number,
maxBytes?: number
): { ok: boolean; data?: string; eof?: boolean; reason?: string }
bareOsSigaction?(
signal: string,
mode: 'IGNORE' | 'DEFAULT' | 'IGN' | 'DEF'
): { ok: boolean; signal: string; mode: string }
bareOsPathconf?(path: string, name: string): number
bareOsSyscall?(
op: string,
@@ -385,6 +407,8 @@ export interface BareOsKernelContext {
labels: string[]
}
bareOsMirrorDriveExportSketch?(): { schema: number; note: string }
bareOsCorestoreSnapshotHint?(): Record<string, unknown>
bareOsReadHrtimeSnapshot?(): Record<string, unknown>
bareOsListMirrorMounts?(): {
schema: number
count: number
@@ -405,6 +429,23 @@ export interface BareOsKernelContext {
reason?: string
exportNames?: string[]
}>
bareOsWasmKernelInstantiate?(
buf: Uint8Array | ArrayBuffer,
opts?: {
maxBytes?: number
memoryInitialPages?: number
memoryMaxPages?: number
timeoutMs?: number
}
): Promise<{
ok: boolean
reason?: string
exportNames?: string[]
instance?: WebAssembly.Instance
memory?: WebAssembly.Memory
memoryInitialPages?: number
memoryMaxPages?: number
}>
bareOsRegisterKernelExtensionRecord?(rec: {
dropin: string
script: string
@@ -175,6 +175,9 @@ export function createBareOsIpc(opts = {}) {
/** @type {Map<string, FanoutHub>} */
const fanouts = new Map()
/** @type {Map<string, { maxmsg: number, maxBytes: number, msgs: { prio: number, data: Uint8Array }[], curBytes: number }>} */
const mqQueues = new Map()
return {
/**
* @param {string} name
@@ -354,6 +357,82 @@ export function createBareOsIpc(opts = {}) {
return hub ? hub.subscribers.length : 0
},
/**
* POSIX-like named message queue (separate from FIFO byte streams).
* @param {string} name
* @param {{ maxmsg?: number, maxBytes?: number }} [mqOpts]
*/
mqOpen(name, mqOpts = {}) {
assertSafeIpcName(name)
if (mqQueues.has(name)) {
const q = mqQueues.get(name)
return {
name,
maxmsg: q.maxmsg,
maxBytes: q.maxBytes,
curmsgs: q.msgs.length
}
}
const maxmsg = Math.min(
256,
Math.max(1, Math.floor(Number(mqOpts.maxmsg) || 32))
)
const maxBytes = Math.min(
1024 * 1024,
Math.max(256, Math.floor(Number(mqOpts.maxBytes) || 65536))
)
mqQueues.set(name, { maxmsg, maxBytes, msgs: [], curBytes: 0 })
return { name, maxmsg, maxBytes, curmsgs: 0 }
},
/** @param {string} name */
mqAttrs(name) {
const q = mqQueues.get(name)
if (!q) return null
return {
maxmsg: q.maxmsg,
maxBytes: q.maxBytes,
curmsgs: q.msgs.length,
curBytes: q.curBytes
}
},
/**
* @param {string} name
* @param {number} prio
* @param {Uint8Array | string} data
*/
mqSend(name, prio, data) {
const q = mqQueues.get(name)
if (!q) throw new Error('bare-os ipc mq: unknown queue (mqOpen first)')
const u8 =
typeof data === 'string' ? b4a.from(data, 'utf8') : new Uint8Array(data)
const p = Math.max(0, Math.min(32767, Math.floor(Number(prio) || 0)))
if (u8.byteLength > q.maxBytes) {
throw new Error('bare-os ipc mq: message exceeds maxBytes')
}
if (q.msgs.length >= q.maxmsg) {
throw new Error('bare-os ipc mq: queue full (maxmsg)')
}
if (q.curBytes + u8.byteLength > q.maxmsg * q.maxBytes) {
throw new Error('bare-os ipc mq: queue byte budget exceeded')
}
q.msgs.push({ prio: p, data: u8 })
q.msgs.sort((a, b) => b.prio - a.prio)
q.curBytes += u8.byteLength
return { ok: true }
},
/** @param {string} name */
mqReceive(name) {
const q = mqQueues.get(name)
if (!q || q.msgs.length === 0) return null
const m = q.msgs.shift()
if (!m) return null
q.curBytes -= m.data.byteLength
return { prio: m.prio, data: m.data }
},
stats() {
let queuedBytesTotal = 0
/** @type {{ name: string, queuedBytes: number, waiters: number }[]} */
@@ -402,6 +481,12 @@ export function createBareOsIpc(opts = {}) {
schema: 1,
byPgid,
note: 'Channels tagged via assignProcessGroup; consumers handle bareOsProcessGroupSignal RPC lines.'
},
posixMessageQueues: {
schema: 1,
count: mqQueues.size,
names: [...mqQueues.keys()].sort().slice(0, 128),
note: 'Separate from FIFOs; use mqOpen/mqSend/mqReceive on the ipc object.'
}
}
},
@@ -14,3 +14,29 @@ export function bareOsMonotonicNowMs() {
export function bareOsMonotonicElapsedMs(startMs) {
return Math.max(0, bareOsMonotonicNowMs() - startMs)
}
/**
* High-resolution time for `/proc` hints: `process.hrtime.bigint` when available, else `performance.now`.
*/
export function bareOsHrtimeSnapshot() {
try {
const hr = globalThis.process?.hrtime?.bigint
if (typeof hr === 'function') {
const ns = hr.call(globalThis.process)
return {
schema: 1,
source: 'process.hrtime.bigint',
bigintNs: ns.toString(),
atMs: Date.now()
}
}
} catch {
/* ignore */
}
return {
schema: 1,
source: 'performance.now',
monotonicMs: bareOsMonotonicNowMs(),
atMs: Date.now()
}
}
@@ -37,6 +37,17 @@ export function resolvePearUpdaterImportUrl(raw, cwd) {
* @param {Record<string, string | undefined> | undefined} env
* @returns {Promise<Record<string, unknown>>}
*/
function pearUpdaterBootCoordinationHint() {
return {
bootTransactionCoordination: {
schema: 1,
channel: 'pear_runtime_updater',
atMs: Date.now(),
note: 'Align with pear-runtime-updater staging; kernel boot transaction journal may record phases when enabled.'
}
}
}
export async function invokeBareOsPearUpdaterDelegate(opts, env) {
const E =
env && typeof env === 'object'
@@ -56,7 +67,8 @@ export async function invokeBareOsPearUpdaterDelegate(opts, env) {
source: 'env',
snapshot,
optsEcho:
opts && typeof opts === 'object' ? { ...opts } : {}
opts && typeof opts === 'object' ? { ...opts } : {},
...pearUpdaterBootCoordinationHint()
}
} catch (err) {
return {
@@ -98,7 +110,8 @@ export async function invokeBareOsPearUpdaterDelegate(opts, env) {
code: 'ok',
phase: 'module',
source: 'module',
.../** @type {Record<string, unknown>} */ (out)
.../** @type {Record<string, unknown>} */ (out),
...pearUpdaterBootCoordinationHint()
}
}
return {
@@ -106,7 +119,8 @@ export async function invokeBareOsPearUpdaterDelegate(opts, env) {
code: 'ok',
phase: 'module',
source: 'module',
value: out
value: out,
...pearUpdaterBootCoordinationHint()
}
} catch (err) {
return {
@@ -0,0 +1,56 @@
/**
* POSIX.1 Issue 7 signal names accepted by synthetic `kill` / `ctx.bareOsSendSignal`
* (delivery is logical; many only update guest state without host semantics).
*/
export const BARE_OS_POSIX_SIGNAL_NAMES = Object.freeze([
'HUP',
'INT',
'QUIT',
'ILL',
'TRAP',
'ABRT',
'BUS',
'FPE',
'KILL',
'USR1',
'SEGV',
'USR2',
'PIPE',
'ALRM',
'TERM',
'STKFLT',
'CHLD',
'CONT',
'STOP',
'TSTP',
'TTIN',
'TTOU',
'URG',
'XCPU',
'XFSZ',
'VTALRM',
'PROF',
'WINCH',
'IO',
'SYS'
])
/** @type {ReadonlySet<string>} */
const SET = new Set(BARE_OS_POSIX_SIGNAL_NAMES)
/**
* @param {string} signal
*/
export function bareOsNormalizeSignalName(signal) {
return String(signal || 'TERM')
.replace(/^SIG/i, '')
.toUpperCase()
}
/**
* @param {string} signal
*/
export function bareOsIsPosixSignalName(signal) {
const s = bareOsNormalizeSignalName(signal)
return SET.has(s) || s === '0'
}
@@ -10,7 +10,8 @@
* shellJobs?: Array<{ id?: number, done?: boolean, stopped?: boolean, pgid?: number, sid?: number, label?: string }>,
* ipcStats?: { processGroups?: Record<string, unknown> } | null,
* logicalFdRows?: Array<{ fd: number, target: string }>,
* initdReadiness?: { units?: Array<{ name: string, phase: string, startedAtMs: number, error?: string }>, schema?: number } | null
* initdReadiness?: { units?: Array<{ name: string, phase: string, startedAtMs: number, error?: string }>, schema?: number } | null,
* sigactionHandlers?: Record<string, string> | null
* }} opts
*/
export function bareOsProcessTableSnapshot(opts = {}) {
@@ -92,6 +93,11 @@ export function bareOsProcessTableSnapshot(opts = {}) {
target: String(r.target || '').slice(0, 1024)
}))
const sigHandlers =
opts.sigactionHandlers && typeof opts.sigactionHandlers === 'object'
? opts.sigactionHandlers
: null
const initd = opts.initdReadiness
const initdUnitsRaw =
initd && Array.isArray(initd.units) ? initd.units : []
@@ -147,6 +153,14 @@ export function bareOsProcessTableSnapshot(opts = {}) {
'Shell builtins use ctx.shellTrapHandlers; signals are synthetic (no host kill).',
sessionShellPgid: 3
},
sigactionSurface: {
schema: 1,
note: 'Logical dispositions via ctx.bareOsSigaction(signal, IGNORE|DEFAULT); IGNORE suppresses delivery and session exit side effects.',
handlers:
sigHandlers && Object.keys(sigHandlers).length
? { ...sigHandlers }
: {}
},
exitStatusModel: {
schema: 1,
note: 'POSIX-like: pipeline exit is last stage exitCode; && stops on non-zero prior; || stops on zero prior.',
@@ -12,6 +12,13 @@ export class BareOsSwarmConnectionManager {
*/
constructor(swarm, opts = {}) {
this._engine = new BareOsSwarmPeerPolicyEngine(swarm, opts)
/** Protomux RPC poolstyle reuse counter (call {@link recordRpcPoolReuse} from hot paths). */
this._rpcPoolReuse = 0
}
/** Increment when a logical RPC channel is reused instead of opened fresh. */
recordRpcPoolReuse() {
this._rpcPoolReuse++
}
get swarm() {
@@ -57,7 +64,15 @@ export class BareOsSwarmConnectionManager {
}
snapshot() {
return this._engine.snapshot()
const base = this._engine.snapshot()
return {
...base,
protomuxPoolMetrics: {
schema: 1,
rpcPoolReuseCount: this._rpcPoolReuse,
note: 'Aligns with protomux-rpc-client-pool-style accounting; stock booter increments only when callers invoke recordRpcPoolReuse.'
}
}
}
/**
@@ -16,12 +16,34 @@ export function bareOsSwarmLifecycleSnapshot(env, peerCount) {
if (degraded) state = 'degraded'
else if (syncing) state = 'syncing'
else if (peers === 0) state = 'discovering'
/** @type {number | undefined} */
let dhtActiveQueries
/** @type {number | undefined} */
let udxEphemeralPort
const raw = env && env.BARE_OS_HYPERDHT_STATS_JSON
if (raw != null && String(raw).trim()) {
try {
const j = JSON.parse(String(raw))
if (typeof j.activeQueries === 'number') dhtActiveQueries = j.activeQueries
if (typeof j.udpPort === 'number') udxEphemeralPort = j.udpPort
} catch {
/* ignore invalid operator JSON */
}
}
return {
schema: 1,
state,
peerCount: peers,
note:
'Lifecycle is derived from BARE_OS_SWARM_* env hints and live peer count; host may override via env.',
hyperdhtSketch: {
schema: 1,
note: 'Capped non-secret hints from BARE_OS_HYPERDHT_STATS_JSON when the host sets it.',
dhtActiveQueries,
udxEphemeralPort
},
atMs: Date.now()
}
}
@@ -40,6 +40,99 @@ export const BARE_OS_SYSCALL_OPS_DETAIL = Object.freeze([
{ name: 'pathconf', category: 'fs', stability: 'experimental' }
])
/**
* POSIX.1 XSH-style names mapped to guest behavior (not separate `ctx.bareOsSyscall` ops).
* Merged into `/proc/bare_os/syscalls.json` **`opsDetail`** for Issue 7 traceability.
* @type {ReadonlyArray<{
* name: string,
* category: BareOsSyscallCategory,
* stability: BareOsSyscallStability,
* posixAlignment: 'simulated' | 'partial' | 'ENOTSUP',
* mapsTo: string[],
* errnoHint?: string
* }>}
*/
export const BARE_OS_POSIX_XSH_OPS_DETAIL = Object.freeze([
{
name: 'open',
category: 'fs',
stability: 'stable',
posixAlignment: 'simulated',
mapsTo: ['vfs.readFile', 'vfs.writeFile', 'vfs.exists', 'vfs.lstat'],
errnoHint: 'ENOENT'
},
{
name: 'close',
category: 'fs',
stability: 'stable',
posixAlignment: 'partial',
mapsTo: ['logical fd table eviction', 'IPC channel teardown'],
errnoHint: 'EBADF'
},
{
name: 'read',
category: 'fs',
stability: 'stable',
posixAlignment: 'partial',
mapsTo: ['vfs.readFile', 'ctx.bareOsBinReadStdin', 'IPC fifo consumer'],
errnoHint: 'EAGAIN'
},
{
name: 'write',
category: 'fs',
stability: 'stable',
posixAlignment: 'partial',
mapsTo: ['vfs.writeFile', 'ctx.bareOsBinWrite', 'IPC fifo producer'],
errnoHint: 'EIO'
},
{
name: 'lseek',
category: 'fs',
stability: 'stable',
posixAlignment: 'ENOTSUP',
mapsTo: ['no byte-offset FD cursor in guest'],
errnoHint: 'ESPIPE'
},
{
name: 'pipe',
category: 'ipc',
stability: 'stable',
posixAlignment: 'simulated',
mapsTo: ['mkfifo', '/run/bare-os/ipc/*', 'BARE_OS_POSIX_FD_SIM optional channel pairs'],
errnoHint: 'EMFILE'
},
{
name: 'dup',
category: 'fs',
stability: 'stable',
posixAlignment: 'partial',
mapsTo: ['ctx.bareOsRegisterLogicalFd'],
errnoHint: 'EBADF'
},
{
name: 'dup2',
category: 'fs',
stability: 'stable',
posixAlignment: 'partial',
mapsTo: ['shell redirect table', '2>&1 merge'],
errnoHint: 'EBADF'
},
{
name: 'fcntl',
category: 'fs',
stability: 'stable',
posixAlignment: 'partial',
mapsTo: ['F_GETFL noop where honored', 'other cmds ENOTSUP'],
errnoHint: 'ENOTSUP'
}
])
/** Full `opsDetail` for proc JSON (ctx ops + POSIX XSH surface). */
export const BARE_OS_SYSCALL_OPS_DETAIL_FULL = Object.freeze([
...BARE_OS_SYSCALL_OPS_DETAIL,
...BARE_OS_POSIX_XSH_OPS_DETAIL
])
export const BARE_OS_STOCK_SYSCALL_OPS = Object.freeze(
BARE_OS_SYSCALL_OPS_DETAIL.map((o) => o.name)
)
@@ -47,3 +140,7 @@ export const BARE_OS_STOCK_SYSCALL_OPS = Object.freeze(
export function bareOsStockSyscallOpsCsv() {
return BARE_OS_STOCK_SYSCALL_OPS.join(',')
}
export function bareOsPosixXshOpsCsv() {
return BARE_OS_POSIX_XSH_OPS_DETAIL.map((o) => o.name).join(',')
}
@@ -6,9 +6,10 @@ import {
BARE_OS_POSIX_PROFILE_VERSION
} from 'bare-os-protocol/bare-os-posix-profile.js'
import {
BARE_OS_SYSCALL_OPS_DETAIL,
BARE_OS_SYSCALL_OPS_DETAIL_FULL,
BARE_OS_STOCK_SYSCALL_OPS
} from './bare-os-syscall-ops.js'
import { BARE_OS_POSIX_SIGNAL_NAMES } from './bare-os-posix-signals.js'
import { bareOsErrnoTableForProc } from './bare-os-posix-errno.js'
/**
@@ -24,7 +25,12 @@ export function buildBareOsSyscallsProcJson(p) {
utilitiesIndexPath: '/etc/bare-os/posix_utilities.json'
},
ops: [...BARE_OS_STOCK_SYSCALL_OPS],
opsDetail: [...BARE_OS_SYSCALL_OPS_DETAIL],
opsDetail: [...BARE_OS_SYSCALL_OPS_DETAIL_FULL],
posixXsh: {
schema: 1,
note: 'POSIX.1 XSH names in opsDetail with posixAlignment; not additional ctx.bareOsSyscall op strings.',
namesCsv: 'open,close,read,write,lseek,pipe,dup,dup2,fcntl'
},
errnoHints: bareOsErrnoTableForProc(),
fdModel: {
schema: 1,
@@ -72,8 +78,8 @@ export function buildBareOsSyscallsProcJson(p) {
},
signalModel: {
schema: 1,
note: 'ctx.bareOsSendSignal delivers to synthetic pids 13; see process_table.signalModel.sigpendingAnalog.',
names: ['HUP', 'INT', 'KILL', 'TERM', 'PIPE', 'CHLD', 'USR1', 'USR2']
note: 'ctx.bareOsSendSignal delivers to synthetic pids 13; ctx.bareOsSigaction may set IGNORE; see process_table.sigactionSurface.',
names: [...BARE_OS_POSIX_SIGNAL_NAMES]
},
caps: {
note: 'VFS path classes and boot policy may deny individual ops at runtime.'
@@ -1,5 +1,5 @@
/**
* Optional WebAssembly compile probe for feature-gated kernel extensions.
* Optional WebAssembly compile / instantiate for feature-gated kernel extensions.
* Uses global WebAssembly when present (Bare/V8); no Node built-ins.
*/
@@ -36,3 +36,84 @@ export async function bareOsWasmKernelCompile(source, opts = {}) {
return { ok: false, reason: String(e && e.message ? e.message : e) }
}
}
/**
* Bounded `instantiate` with an isolated `Memory` and minimal `env` imports.
* Guest modules cannot call `ctx.bareOsSyscall` until a future import type is added;
* this establishes memory limits and timeout bounds only.
*
* @param {Uint8Array} source
* @param {Record<string, unknown>} _ctx
* @param {{
* maxBytes?: number,
* memoryInitialPages?: number,
* memoryMaxPages?: number,
* timeoutMs?: number
* }} [opts]
*/
export async function bareOsWasmKernelInstantiate(source, _ctx, opts = {}) {
void _ctx
const maxB = Math.min(
DEFAULT_MAX,
Math.max(1024, Number(opts.maxBytes) || DEFAULT_MAX)
)
if (!(source instanceof Uint8Array) || source.byteLength === 0) {
return { ok: false, reason: 'invalid_source' }
}
if (source.byteLength > maxB) {
return { ok: false, reason: 'source_too_large' }
}
const W = globalThis.WebAssembly
if (!W || typeof W.instantiate !== 'function' || typeof W.Memory !== 'function') {
return { ok: false, reason: 'webassembly_unavailable' }
}
const memInitial = Math.min(
256,
Math.max(1, Number(opts.memoryInitialPages) || 1)
)
const memMax = Math.min(
256,
Math.max(memInitial, Number(opts.memoryMaxPages) || 8)
)
const timeoutMs = Math.min(
60000,
Math.max(50, Number(opts.timeoutMs) || 5000)
)
try {
const mod = await W.compile(source)
/** @type {{ name: string }[]} */
let exportNames = []
if (typeof W.Module.exports === 'function') {
exportNames = W.Module.exports(mod).map((e) => String(e.name || ''))
}
const memory = new W.Memory({ initial: memInitial, maximum: memMax })
const importObject = {
env: {
memory,
bare_os_nop: () => 0
}
}
const instP = W.instantiate(mod, importObject)
const done = await Promise.race([
instP,
new Promise((_, rej) =>
setTimeout(
() => rej(new Error('wasm_instantiate_timeout')),
timeoutMs
)
)
])
const instance = /** @type {{ instance: WebAssembly.Instance }} */ (done)
.instance
return {
ok: true,
exportNames,
instance,
memory,
memoryInitialPages: memInitial,
memoryMaxPages: memMax
}
} catch (e) {
return { ok: false, reason: String(e && e.message ? e.message : e) }
}
}
@@ -44,3 +44,17 @@ export function bareOsInstallCorestoreSuspendResumeHooks(ctx) {
await callSuspendResume(store, 'resume')
})
}
/**
* Non-secret operator hint for integrating **`corestore-snapshot`** on the host.
* @param {unknown} store
*/
export function bareOsCorestoreSnapshotOperatorHint(store) {
return {
schema: 1,
note: 'Wire the holepunch corestore-snapshot package on the host; guests receive this descriptor only.',
corestoreSnapshotPackage: 'corestore-snapshot',
hasLiveStore: !!(store && typeof store === 'object'),
atMs: Date.now()
}
}
@@ -195,6 +195,14 @@ export async function unlockIdentity(ctx, passphrase) {
wipeSecret(ctx)
await applyUnlockedEnv(ctx, publicKey, secretKey)
await ensureBareDir(ctx)
try {
await bareOsAppendVaultRotationCheckpoint(ctx, {
kind: 'identity_unlock',
user: String(ctx.vfs?.env?.USER || '')
})
} catch {
/* ignore audit failures */
}
ctx.console.log(`Unlocked as ${ctx.vfs.env.USER}`)
}
+14 -1
View File
@@ -284,9 +284,22 @@ export async function runUserScriptFromSource(ctx, src, argv, _label = argv[0])
* @param {import('./bare-os-abort.js').BareOsAbortOpts} [runOpts]
*/
export async function runBinCommand(ctx, argv, runOpts) {
const env = ctx?.vfs?.env
const defTo = Number.parseInt(
String(env?.BARE_OS_RUNBIN_DEFAULT_TIMEOUT_MS || ''),
10
)
/** @type {import('./bare-os-abort.js').BareOsAbortOpts | undefined} */
let merged =
runOpts && typeof runOpts === 'object' ? { ...runOpts } : undefined
if (Number.isFinite(defTo) && defTo > 0) {
const cap = Math.min(defTo, 3_600_000)
if (!merged) merged = { timeoutMs: cap }
else if (merged.timeoutMs == null) merged.timeoutMs = cap
}
return raceWithAbortAndTimeout(
runBinCommandInner(ctx, argv),
runOpts,
merged,
'runBinCommand'
)
}
@@ -63,6 +63,9 @@ function printHelp(ctx, prog) {
ctx.console.log(
`Usage: ${prog} list|list-units
${prog} status [UNIT] [--lines N]
${prog} show|cat UNIT
${prog} is-failed UNIT
${prog} reset-failed [UNIT]
${prog} logs UNIT [--lines N]
${prog} start|stop|restart UNIT
${prog} enable|disable UNIT
@@ -240,6 +243,61 @@ export async function runSystemctlCli(ctx, argv) {
return
}
if (sub === 'show' || sub === 'cat') {
const unit = rest[0]
if (!unit) {
ctx.console.error(`${prog}: ${sub} requires a UNIT`)
ctx.exitCode = 2
return
}
const def = findBareServiceDefinition(unit)
if (!def) {
ctx.console.error(`${prog}: unknown unit: ${unit}`)
ctx.exitCode = 1
return
}
const rt = getBareServiceRuntime(unit)
ctx.console.log(`Id=${unit}`)
ctx.console.log(`Description=${def.description || ''}`)
ctx.console.log(`LoadState=loaded`)
ctx.console.log(`ActiveState=${rt?.phase || 'unknown'}`)
if (def.logPath) ctx.console.log(`LogPath=${def.logPath}`)
ctx.exitCode = 0
return
}
if (sub === 'is-failed') {
const unit = rest[0]
if (!unit) {
ctx.console.error(`${prog}: is-failed requires a UNIT`)
ctx.exitCode = 2
return
}
const def = findBareServiceDefinition(unit)
if (!def) {
ctx.console.error(`${prog}: unknown unit: ${unit}`)
ctx.exitCode = 1
return
}
const rt = getBareServiceRuntime(unit)
if (rt?.phase === 'failed') {
ctx.console.log('failed')
ctx.exitCode = 0
} else {
ctx.console.log('active')
ctx.exitCode = 1
}
return
}
if (sub === 'reset-failed') {
ctx.console.log(
'reset-failed: no persistent failed state in stock bare-initd (logical no-op).'
)
ctx.exitCode = 0
return
}
if (sub === 'logs') {
const unit = rest[0]
let lines = 40
+3
View File
@@ -840,6 +840,9 @@ test('buildBareOsSyscallsProcJson exposes posixLike fd mapping', async (t) => {
t.ok(j.fdModel && j.fdModel.posixLike && j.fdModel.posixLike.pipe)
t.ok(j.fdModel.posixLike.read && j.fdModel.posixLike.write)
t.ok(j.fdModel.posixLike.fcntl)
t.ok(j.posixXsh && j.posixXsh.schema === 1 && j.posixXsh.namesCsv.includes('open'))
const xsh = j.opsDetail.filter((r) => r.posixAlignment)
t.ok(xsh.length >= 9)
})
test('ls -l long listing uses session user and regular file mode', async (t) => {
@@ -893,7 +893,7 @@ function systemctlPage() {
'systemd'
],
bareOsNotes:
'journalctl only supports -u UNIT and optional --lines / -n. Unknown systemd verbs are not implemented.',
'journalctl supports -u UNIT and --lines / -n. Other systemd verbs are unavailable on Bare OS (exit 2).',
seeAlso: [
{ name: 'crontab', section: 1 },
{ name: 'bare-os-shell', section: 1 }
+11
View File
@@ -40,9 +40,20 @@ const CONF = {
_POSIX_THREAD_ATTR_STACKSIZE: '65536',
_PC_PATH_MAX: '4096',
_PC_NAME_MAX: '255',
_PC_CHOWN_RESTRICTED: '1',
_PC_NO_TRUNC: '1',
_PC_FILESIZEBITS: '64',
_PC_LINK_MAX: '1',
_PC_MAX_CANON: '255',
_PC_MAX_INPUT: '512',
_PC_PIPE_BUF: '4096',
_PC_2_SYMLINKS: '1',
/** Comma-separated `ctx.bareOsSyscall` op names implemented in stock booter. */
BARE_OS_SYSCALL_OPS:
'readFile,writeFile,readdir,mkdir,stat,unlink,chmod,chdir,getcwd,readlink,symlink,exists,lstat,rmdir,mount,umount,kill,rename,link,access,utimes,truncate,ftruncate,fsync,fdatasync,pathconf',
/** POSIX.1 XSH-style names documented in `/proc/bare_os/syscalls.json` opsDetail (not separate ctx ops). */
BARE_OS_POSIX_XSH_OPS:
'open,close,read,write,lseek,pipe,dup,dup2,fcntl',
/** Encodings accepted by `/bin/iconv` (subset; case-insensitive names). */
BARE_OS_ICONV_ENCODINGS: 'UTF-8,ISO-8859-1,UTF-16LE,UTF-16BE',
/** Synthetic process table JSON path (logical VFS). */
@@ -4,7 +4,7 @@
*/
/** Semver for the documented POSIX-like surface (handbook ch.9 + environment appendix). */
export const BARE_OS_POSIX_PROFILE_VERSION = '1.0.0'
export const BARE_OS_POSIX_PROFILE_VERSION = '1.0.1'
/** Short identifier for telemetry and `/proc` mirrors. */
export const BARE_OS_POSIX_PROFILE_ID = 'bare-os-posix-like'
@@ -129,9 +129,20 @@ const CONF = {
_POSIX_THREAD_ATTR_STACKSIZE: '65536',
_PC_PATH_MAX: '4096',
_PC_NAME_MAX: '255',
_PC_CHOWN_RESTRICTED: '1',
_PC_NO_TRUNC: '1',
_PC_FILESIZEBITS: '64',
_PC_LINK_MAX: '1',
_PC_MAX_CANON: '255',
_PC_MAX_INPUT: '512',
_PC_PIPE_BUF: '4096',
_PC_2_SYMLINKS: '1',
/** Comma-separated `ctx.bareOsSyscall` op names implemented in stock booter. */
BARE_OS_SYSCALL_OPS:
'readFile,writeFile,readdir,mkdir,stat,unlink,chmod,chdir,getcwd,readlink,symlink,exists,lstat,rmdir,mount,umount,kill,rename,link,access,utimes,truncate,ftruncate,fsync,fdatasync,pathconf',
/** POSIX.1 XSH-style names documented in `/proc/bare_os/syscalls.json` opsDetail (not separate ctx ops). */
BARE_OS_POSIX_XSH_OPS:
'open,close,read,write,lseek,pipe,dup,dup2,fcntl',
/** Encodings accepted by `/bin/iconv` (subset; case-insensitive names). */
BARE_OS_ICONV_ENCODINGS: 'UTF-8,ISO-8859-1,UTF-16LE,UTF-16BE',
/** Synthetic process table JSON path (logical VFS). */
@@ -1,15 +1,28 @@
{
"schemaVersion": 4,
"ctxApiVersion": "1.33.0",
"ctxApiVersion": "1.34.0",
"posixProfile": {
"id": "bare-os-posix-like",
"version": "1.0.0"
"version": "1.0.1"
},
"ops": ["readFile", "writeFile"],
"opsDetail": [
{ "name": "readFile", "category": "fs", "stability": "stable" },
{ "name": "writeFile", "category": "fs", "stability": "stable" }
{ "name": "writeFile", "category": "fs", "stability": "stable" },
{
"name": "open",
"category": "fs",
"stability": "stable",
"posixAlignment": "simulated",
"mapsTo": ["vfs.readFile", "vfs.writeFile"],
"errnoHint": "ENOENT"
}
],
"posixXsh": {
"schema": 1,
"note": "POSIX.1 XSH names in opsDetail with posixAlignment; not ctx.bareOsSyscall op strings.",
"namesCsv": "open,close,read,write,lseek,pipe,dup,dup2,fcntl"
},
"errnoHints": {
"ENOENT": 2,
"EACCES": 13
@@ -20,7 +33,7 @@
"stdio": [0, 1, 2],
"register": "ctx.bareOsRegisterLogicalFd",
"processTablePath": "/proc/bare_os/process_table.json",
"processTableSchema": 3
"processTableSchema": 4
},
"signalModel": {
"schema": 1,
@@ -7,18 +7,18 @@
"b4a"
]
},
{
"path": "/lib/bare/bundles/hypercoreIdEncoding.js",
"keys": [
"hypercoreIdEncoding"
]
},
{
"path": "/lib/bare/bundles/safetyCatch.js",
"keys": [
"safetyCatch"
]
},
{
"path": "/lib/bare/bundles/hypercoreIdEncoding.js",
"keys": [
"hypercoreIdEncoding"
]
},
{
"path": "/lib/bare/bundles/compactEncoding.js",
"keys": [
@@ -43,12 +43,6 @@
"bareEncoding"
]
},
{
"path": "/lib/bare/bundles/barePath.js",
"keys": [
"barePath"
]
},
{
"path": "/lib/bare/bundles/bareEvents.js",
"keys": [
@@ -56,15 +50,15 @@
]
},
{
"path": "/lib/bare/bundles/bareAbort.js",
"path": "/lib/bare/bundles/barePath.js",
"keys": [
"bareAbort"
"barePath"
]
},
{
"path": "/lib/bare/bundles/bareReadline.js",
"path": "/lib/bare/bundles/bareAbort.js",
"keys": [
"bareReadline"
"bareAbort"
]
},
{
@@ -79,36 +73,42 @@
"bareAnsiEscapes"
]
},
{
"path": "/lib/bare/bundles/bareCrypto.js",
"keys": [
"bareCrypto"
]
},
{
"path": "/lib/bare/bundles/bareAddonResolve.js",
"keys": [
"bareAddonResolve"
]
},
{
"path": "/lib/bare/bundles/bareReadline.js",
"keys": [
"bareReadline"
]
},
{
"path": "/lib/bare/bundles/bareCrypto.js",
"keys": [
"bareCrypto"
]
},
{
"path": "/lib/bare/bundles/bareAsyncHooks.js",
"keys": [
"bareAsyncHooks"
]
},
{
"path": "/lib/bare/bundles/fetch.js",
"keys": [
"fetch"
]
},
{
"path": "/lib/bare/bundles/bareAppKit.js",
"keys": [
"bareAppKit"
]
},
{
"path": "/lib/bare/bundles/bareApk.js",
"keys": [
"bareApk"
]
},
{
"path": "/lib/bare/bundles/bareAtomics.js",
"keys": [
@@ -116,9 +116,9 @@
]
},
{
"path": "/lib/bare/bundles/bareApk.js",
"path": "/lib/bare/bundles/fetch.js",
"keys": [
"bareApk"
"fetch"
]
},
{
@@ -139,18 +139,18 @@
"bareBundleCompile"
]
},
{
"path": "/lib/bare/bundles/bareBundle.js",
"keys": [
"bareBundle"
]
},
{
"path": "/lib/bare/bundles/bareBuffer.js",
"keys": [
"bareBuffer"
]
},
{
"path": "/lib/bare/bundles/bareBundle.js",
"keys": [
"bareBundle"
]
},
{
"path": "/lib/bare/bundles/bareBluetoothApple.js",
"keys": [
@@ -181,30 +181,30 @@
"bareBundleId"
]
},
{
"path": "/lib/bare/bundles/bareChannel.js",
"keys": [
"bareChannel"
]
},
{
"path": "/lib/bare/bundles/bareDebugLog.js",
"keys": [
"bareDebugLog"
]
},
{
"path": "/lib/bare/bundles/bareDaemon.js",
"keys": [
"bareDaemon"
]
},
{
"path": "/lib/bare/bundles/bareDelta.js",
"keys": [
"bareDelta"
]
},
{
"path": "/lib/bare/bundles/bareChannel.js",
"keys": [
"bareChannel"
]
},
{
"path": "/lib/bare/bundles/bareDaemon.js",
"keys": [
"bareDaemon"
]
},
{
"path": "/lib/bare/bundles/bareDiagnosticsChannel.js",
"keys": [
@@ -217,30 +217,24 @@
"bareDns"
]
},
{
"path": "/lib/bare/bundles/bareDgram.js",
"keys": [
"bareDgram"
]
},
{
"path": "/lib/bare/bundles/bareEnv.js",
"keys": [
"bareEnv"
]
},
{
"path": "/lib/bare/bundles/bareCov.js",
"keys": [
"bareCov"
]
},
{
"path": "/lib/bare/bundles/bareExif.js",
"keys": [
"bareExif"
]
},
{
"path": "/lib/bare/bundles/bareCov.js",
"keys": [
"bareCov"
]
},
{
"path": "/lib/bare/bundles/bareFfmpeg.js",
"keys": [
@@ -248,9 +242,9 @@
]
},
{
"path": "/lib/bare/bundles/bareFormData.js",
"path": "/lib/bare/bundles/bareDgram.js",
"keys": [
"bareFormData"
"bareDgram"
]
},
{
@@ -259,30 +253,24 @@
"bareFfmpegEncodings"
]
},
{
"path": "/lib/bare/bundles/bareFormData.js",
"keys": [
"bareFormData"
]
},
{
"path": "/lib/bare/bundles/bareFormat.js",
"keys": [
"bareFormat"
]
},
{
"path": "/lib/bare/bundles/bareFileLogger.js",
"keys": [
"bareFileLogger"
]
},
{
"path": "/lib/bare/bundles/bareGif.js",
"keys": [
"bareGif"
]
},
{
"path": "/lib/bare/bundles/bareHrtime.js",
"keys": [
"bareHrtime"
]
},
{
"path": "/lib/bare/bundles/bareHeif.js",
"keys": [
@@ -296,9 +284,15 @@
]
},
{
"path": "/lib/bare/bundles/bareHttpParser.js",
"path": "/lib/bare/bundles/bareFileLogger.js",
"keys": [
"bareHttpParser"
"bareFileLogger"
]
},
{
"path": "/lib/bare/bundles/bareHrtime.js",
"keys": [
"bareHrtime"
]
},
{
@@ -307,6 +301,12 @@
"bareFs"
]
},
{
"path": "/lib/bare/bundles/bareHttpParser.js",
"keys": [
"bareHttpParser"
]
},
{
"path": "/lib/bare/bundles/bareIco.js",
"keys": [
@@ -331,12 +331,6 @@
"bareHttp1"
]
},
{
"path": "/lib/bare/bundles/bareIpc.js",
"keys": [
"bareIpc"
]
},
{
"path": "/lib/bare/bundles/bareHttps.js",
"keys": [
@@ -349,18 +343,18 @@
"bareJpeg"
]
},
{
"path": "/lib/bare/bundles/bareInspector.js",
"keys": [
"bareInspector"
]
},
{
"path": "/lib/bare/bundles/bareIntl.js",
"keys": [
"bareIntl"
]
},
{
"path": "/lib/bare/bundles/bareIpc.js",
"keys": [
"bareIpc"
]
},
{
"path": "/lib/bare/bundles/bareLief.js",
"keys": [
@@ -379,12 +373,24 @@
"bareLink"
]
},
{
"path": "/lib/bare/bundles/bareInspector.js",
"keys": [
"bareInspector"
]
},
{
"path": "/lib/bare/bundles/bareMake.js",
"keys": [
"bareMake"
]
},
{
"path": "/lib/bare/bundles/bareModuleResolve.js",
"keys": [
"bareModuleResolve"
]
},
{
"path": "/lib/bare/bundles/bareModuleLexer.js",
"keys": [
@@ -397,12 +403,6 @@
"bareModule"
]
},
{
"path": "/lib/bare/bundles/bareModuleResolve.js",
"keys": [
"bareModuleResolve"
]
},
{
"path": "/lib/bare/bundles/bareModuleTraverse.js",
"keys": [
@@ -427,18 +427,18 @@
"bareNodeFetch"
]
},
{
"path": "/lib/bare/bundles/bareOs.js",
"keys": [
"bareOs"
]
},
{
"path": "/lib/bare/bundles/bareOpen.js",
"keys": [
"bareOpen"
]
},
{
"path": "/lib/bare/bundles/bareOs.js",
"keys": [
"bareOs"
]
},
{
"path": "/lib/bare/bundles/bareMedia.js",
"keys": [
@@ -452,9 +452,15 @@
]
},
{
"path": "/lib/bare/bundles/bareDev.js",
"path": "/lib/bare/bundles/barePackDrive.js",
"keys": [
"bareDev"
"barePackDrive"
]
},
{
"path": "/lib/bare/bundles/barePack.js",
"keys": [
"barePack"
]
},
{
@@ -475,12 +481,6 @@
"barePipe"
]
},
{
"path": "/lib/bare/bundles/barePackDrive.js",
"keys": [
"barePackDrive"
]
},
{
"path": "/lib/bare/bundles/bareNodeRuntime.js",
"keys": [
@@ -493,48 +493,42 @@
"barePunycode"
]
},
{
"path": "/lib/bare/bundles/barePack.js",
"keys": [
"barePack"
]
},
{
"path": "/lib/bare/bundles/barePrebuild.js",
"keys": [
"barePrebuild"
]
},
{
"path": "/lib/bare/bundles/bareQueueMicrotask.js",
"keys": [
"bareQueueMicrotask"
]
},
{
"path": "/lib/bare/bundles/bareQuerystring.js",
"keys": [
"bareQuerystring"
]
},
{
"path": "/lib/bare/bundles/bareQueueMicrotask.js",
"keys": [
"bareQueueMicrotask"
]
},
{
"path": "/lib/bare/bundles/bareRealm.js",
"keys": [
"bareRealm"
]
},
{
"path": "/lib/bare/bundles/bareDev.js",
"keys": [
"bareDev"
]
},
{
"path": "/lib/bare/bundles/bareProcess.js",
"keys": [
"bareProcess"
]
},
{
"path": "/lib/bare/bundles/bareRuntime.js",
"keys": [
"bareRuntime"
]
},
{
"path": "/lib/bare/bundles/barePromClient.js",
"keys": [
@@ -542,9 +536,15 @@
]
},
{
"path": "/lib/bare/bundles/bareRpc.js",
"path": "/lib/bare/bundles/bareRuntime.js",
"keys": [
"bareRpc"
"bareRuntime"
]
},
{
"path": "/lib/bare/bundles/bareRepl.js",
"keys": [
"bareRepl"
]
},
{
@@ -560,15 +560,9 @@
]
},
{
"path": "/lib/bare/bundles/bareRun.js",
"path": "/lib/bare/bundles/bareRpc.js",
"keys": [
"bareRun"
]
},
{
"path": "/lib/bare/bundles/bareRepl.js",
"keys": [
"bareRepl"
"bareRpc"
]
},
{
@@ -578,15 +572,15 @@
]
},
{
"path": "/lib/bare/bundles/bareSidecar.js",
"path": "/lib/bare/bundles/bareRun.js",
"keys": [
"bareSidecar"
"bareRun"
]
},
{
"path": "/lib/bare/bundles/bareStream.js",
"path": "/lib/bare/bundles/bareSidecar.js",
"keys": [
"bareStream"
"bareSidecar"
]
},
{
@@ -596,15 +590,9 @@
]
},
{
"path": "/lib/bare/bundles/bareStringDecoder.js",
"path": "/lib/bare/bundles/bareStream.js",
"keys": [
"bareStringDecoder"
]
},
{
"path": "/lib/bare/bundles/bareStdio.js",
"keys": [
"bareStdio"
"bareStream"
]
},
{
@@ -613,12 +601,30 @@
"bareSvg"
]
},
{
"path": "/lib/bare/bundles/bareStdio.js",
"keys": [
"bareStdio"
]
},
{
"path": "/lib/bare/bundles/bareStringDecoder.js",
"keys": [
"bareStringDecoder"
]
},
{
"path": "/lib/bare/bundles/bareStructuredClone.js",
"keys": [
"bareStructuredClone"
]
},
{
"path": "/lib/bare/bundles/bareSystemLogger.js",
"keys": [
"bareSystemLogger"
]
},
{
"path": "/lib/bare/bundles/bareTap.js",
"keys": [
@@ -637,36 +643,30 @@
"bareTiff"
]
},
{
"path": "/lib/bare/bundles/bareTimers.js",
"keys": [
"bareTimers"
]
},
{
"path": "/lib/bare/bundles/bareSystemLogger.js",
"keys": [
"bareSystemLogger"
]
},
{
"path": "/lib/bare/bundles/bareTcp.js",
"keys": [
"bareTcp"
]
},
{
"path": "/lib/bare/bundles/bareTpl.js",
"keys": [
"bareTpl"
]
},
{
"path": "/lib/bare/bundles/bareThread.js",
"keys": [
"bareThread"
]
},
{
"path": "/lib/bare/bundles/bareTimers.js",
"keys": [
"bareTimers"
]
},
{
"path": "/lib/bare/bundles/bareTpl.js",
"keys": [
"bareTpl"
]
},
{
"path": "/lib/bare/bundles/bareType.js",
"keys": [
@@ -697,24 +697,12 @@
"bareTty"
]
},
{
"path": "/lib/bare/bundles/bareVm.js",
"keys": [
"bareVm"
]
},
{
"path": "/lib/bare/bundles/bareV8.js",
"keys": [
"bareV8"
]
},
{
"path": "/lib/bare/bundles/bareWalkHandles.js",
"keys": [
"bareWalkHandles"
]
},
{
"path": "/lib/bare/bundles/bareUnionBundle.js",
"keys": [
@@ -722,15 +710,21 @@
]
},
{
"path": "/lib/bare/bundles/bareWebKit.js",
"path": "/lib/bare/bundles/bareWalkHandles.js",
"keys": [
"bareWebKit"
"bareWalkHandles"
]
},
{
"path": "/lib/bare/bundles/bareWebKitGtk.js",
"path": "/lib/bare/bundles/bareVm.js",
"keys": [
"bareWebKitGtk"
"bareVm"
]
},
{
"path": "/lib/bare/bundles/bareWebKit.js",
"keys": [
"bareWebKit"
]
},
{
@@ -740,27 +734,9 @@
]
},
{
"path": "/lib/bare/bundles/bareWinUi.js",
"path": "/lib/bare/bundles/bareWebKitGtk.js",
"keys": [
"bareWinUi"
]
},
{
"path": "/lib/bare/bundles/bareWhich.js",
"keys": [
"bareWhich"
]
},
{
"path": "/lib/bare/bundles/bareV8ToIstanbul.js",
"keys": [
"bareV8ToIstanbul"
]
},
{
"path": "/lib/bare/bundles/bareXdiff.js",
"keys": [
"bareXdiff"
"bareWebKitGtk"
]
},
{
@@ -770,9 +746,27 @@
]
},
{
"path": "/lib/bare/bundles/bareWorker.js",
"path": "/lib/bare/bundles/bareWhich.js",
"keys": [
"bareWorker"
"bareWhich"
]
},
{
"path": "/lib/bare/bundles/bareWinUi.js",
"keys": [
"bareWinUi"
]
},
{
"path": "/lib/bare/bundles/bareXdiff.js",
"keys": [
"bareXdiff"
]
},
{
"path": "/lib/bare/bundles/bareV8ToIstanbul.js",
"keys": [
"bareV8ToIstanbul"
]
},
{
@@ -781,6 +775,12 @@
"bareZlib"
]
},
{
"path": "/lib/bare/bundles/bareWs.js",
"keys": [
"bareWs"
]
},
{
"path": "/lib/bare/bundles/bareZmq.js",
"keys": [
@@ -788,9 +788,9 @@
]
},
{
"path": "/lib/bare/bundles/bareWs.js",
"path": "/lib/bare/bundles/bareWorker.js",
"keys": [
"bareWs"
"bareWorker"
]
}
],
@@ -1595,8 +1595,8 @@
],
"bundleProvenance": {
"schemaVersion": 1,
"generatedAt": "2026-04-05T02:28:01.351Z",
"gitCommit": "433cb2176d8f8518af10393c83b205627127db86",
"generatedAt": "2026-04-05T02:38:13.070Z",
"gitCommit": "8a6c1e7b0947b1a70fc837892c76d67222e44119",
"nodeVersion": "v22.22.0",
"bundleTier": "all",
"normativeManifest": "packages/bare-os-booter/lib/bare-module-manifest.json",
@@ -1,6 +1,6 @@
{
"schema": 1,
"atMs": 1775356080442,
"atMs": 1775356692404,
"commands": [
"arch",
"awk",
File diff suppressed because one or more lines are too long