feat(booter): OpenMetrics proc export, protomux wire JSON, audit batch API, and doc sync

Expose /proc/bare_os/metrics.prom (kernel counter OpenMetrics) and
/proc/bare_os/protomux.json (alias registry snapshot). Extend VFS routing,
readdir, pseudo watch, and bare_os_proc_index to schema 8; restore flat
/proc entries for syscalls and process_table with sort order matching
tests. Add ctx.bareOsAuditLogAppendBatch and bump BARE_OS_CTX_API_VERSION
to 1.30.0 with d.ts and compatibility-matrix updates.
Harden swarm peer ban backoff with jitter on the exponential window.
Refresh conformance matrix, environment appendix, booter package doc, and
handbook chapters for proc paths, param expansion V3, vault at rest, and
baretop snapshot keys. Align baretop-snapshot with bareOsReadBareTopSnapshot.
This commit is contained in:
Raven Scott
2026-04-04 19:23:06 -04:00
parent b458c6031d
commit 0d7b27bea8
73 changed files with 4103 additions and 571 deletions
+33 -9
View File
@@ -20,9 +20,26 @@ function concatU8(b4a, chunks) {
return out
}
function parseConvList(v) {
return String(v || '')
.split(',')
.map((x) => x.trim().toLowerCase())
.filter(Boolean)
}
function padToBlock(u8, bs, b4a) {
if (bs <= 0) return u8
const rem = u8.byteLength % bs
if (rem === 0) return u8
const pad = bs - rem
const out = b4a.alloc(u8.byteLength + pad)
out.set(u8, 0)
return out
}
async function run(ctx, argv) {
/** @type {{ if?: string, of?: string, bs: number, count?: number, skip: number, seek: number, status: string, conv?: string }} */
const opt = { bs: 512, skip: 0, seek: 0, status: 'progress' }
/** @type {{ if?: string, of?: string, bs: number, count?: number, skip: number, seek: number, status: string, conv: string[] }} */
const opt = { bs: 512, skip: 0, seek: 0, status: 'progress', conv: [] }
for (let i = 1; i < argv.length; i++) {
const a = argv[i]
@@ -77,12 +94,16 @@ async function run(ctx, argv) {
}
opt.status = v
} else if (k === 'conv') {
if (v !== 'notrunc') {
ctx.console.error('dd: unsupported conv=' + v)
ctx.exitCode = 1
return
const list = parseConvList(v)
const allow = new Set(['notrunc', 'sync', 'noerror', 'nocreat'])
for (const c of list) {
if (!allow.has(c)) {
ctx.console.error('dd: unsupported conv=' + c)
ctx.exitCode = 1
return
}
}
opt.conv = v
opt.conv = list
} else {
ctx.console.error('dd: unsupported operand ' + k)
ctx.exitCode = 1
@@ -107,7 +128,10 @@ async function run(ctx, argv) {
opt.count == null
? input.byteLength
: Math.min(input.byteLength, start + opt.count * opt.bs)
const chunk = input.subarray(start, end)
let chunk = input.subarray(start, end)
if (opt.conv.includes('sync')) {
chunk = padToBlock(chunk, opt.bs, ctx.b4a)
}
if (!opt.of) {
const w = globalThis.process?.stdout?.write
@@ -128,7 +152,7 @@ async function run(ctx, argv) {
}
const prevU8 = prev ? (prev instanceof Uint8Array ? prev : ctx.b4a.from(prev)) : ctx.b4a.alloc(0)
let out
if (opt.conv === 'notrunc') {
if (opt.conv.includes('notrunc')) {
const want = Math.max(prevU8.byteLength, outOff + chunk.byteLength)
out = ctx.b4a.alloc(want)
if (prevU8.byteLength) out.set(prevU8, 0)
+3 -3
View File
@@ -38,9 +38,9 @@ const CONF = {
BARE_OS_SPLIT_MAX_FILES: '10000',
/** 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',
/** Encodings accepted by `/bin/iconv` (subset). */
BARE_OS_ICONV_ENCODINGS: 'UTF-8',
'readFile,writeFile,readdir,mkdir,stat,unlink,chmod,chdir,getcwd,readlink,symlink,exists,lstat,rmdir,mount,umount,kill,rename,link,access,utimes,truncate,ftruncate',
/** 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). */
BARE_OS_PROC_PROCESS_TABLE: '/proc/bare_os/process_table.json'
}
+84 -4
View File
@@ -8,9 +8,60 @@ function normEnc(s) {
.toLowerCase()
.replace(/[-_]/g, '')
if (t === 'utf8' || t === 'utf8bom') return 'utf-8'
if (t === 'latin1' || t === 'iso88591' || t === 'cp819') return 'iso-8859-1'
if (t === 'utf16le' || t === 'utf16') return 'utf-16le'
if (t === 'utf16be') return 'utf-16be'
return t
}
/**
* @param {Uint8Array} u8
* @param {string} enc TextDecoder label
* @returns {string}
*/
function decodeToString(u8, enc) {
const dec = new TextDecoder(enc, { fatal: true })
return dec.decode(u8)
}
/**
* @param {string} s
* @param {string} enc TextEncoder only does utf-8; special-case UTF-16
* @param {typeof import('b4a')} b4a
*/
function encodeFromString(s, enc, b4a) {
if (enc === 'utf-8' || enc === 'utf8') {
return b4a.from(s, 'utf8')
}
if (enc === 'utf-16le') {
const out = new Uint8Array(s.length * 2)
for (let i = 0; i < s.length; i++) {
const c = s.charCodeAt(i)
out[i * 2] = c & 0xff
out[i * 2 + 1] = (c >> 8) & 0xff
}
return out
}
if (enc === 'utf-16be') {
const out = new Uint8Array(s.length * 2)
for (let i = 0; i < s.length; i++) {
const c = s.charCodeAt(i)
out[i * 2] = (c >> 8) & 0xff
out[i * 2 + 1] = c & 0xff
}
return out
}
if (enc === 'iso-8859-1') {
const out = new Uint8Array(s.length)
for (let i = 0; i < s.length; i++) {
const c = s.charCodeAt(i)
out[i] = c > 255 ? 0x3f : c & 0xff
}
return out
}
throw new Error('iconv: unsupported output encoding')
}
async function run(ctx, argv) {
let fromEnc = 'utf-8'
let toEnc = 'utf-8'
@@ -47,9 +98,15 @@ async function run(ctx, argv) {
break
}
if (fromEnc !== 'utf-8' || toEnc !== 'utf-8') {
const supported = new Set([
'utf-8',
'iso-8859-1',
'utf-16le',
'utf-16be'
])
if (!supported.has(fromEnc) || !supported.has(toEnc)) {
ctx.console.error(
'iconv: only UTF-8 to UTF-8 is supported in this build (see getconf BARE_OS_ICONV_ENCODINGS)'
'iconv: unsupported encoding pair (supported: utf-8, iso-8859-1, utf-16le, utf-16be)'
)
ctx.exitCode = 1
return
@@ -68,7 +125,30 @@ async function run(ctx, argv) {
u8 = ctx.b4a.from(bareStdin(ctx), 'utf8')
}
let mid = ''
try {
mid = decodeToString(u8, fromEnc)
} catch (e) {
ctx.console.error('iconv: invalid input for ' + fromEnc + ': ' + (e?.message || e))
ctx.exitCode = 1
return
}
let outU8
try {
outU8 = encodeFromString(mid, toEnc, ctx.b4a)
} catch (e) {
ctx.console.error('iconv: ' + (e?.message || e))
ctx.exitCode = 1
return
}
const w = globalThis.process?.stdout?.write
if (typeof w === 'function') w.call(globalThis.process.stdout, u8)
else ctx.console.log(ctx.b4a.toString(u8, 'utf8'))
if (typeof w === 'function') w.call(globalThis.process.stdout, outU8)
else if (toEnc === 'utf-8' || toEnc === 'utf8') {
ctx.console.log(ctx.b4a.toString(outU8, 'utf8'))
} else {
ctx.console.error('iconv: binary output requires a TTY with binary stdout')
ctx.exitCode = 1
}
}
+7
View File
@@ -52,6 +52,13 @@ async function run(ctx, argv) {
message: text
}
const line = JSON.stringify(rec) + '\n'
if (typeof ctx.bareOsAppendLoggerRecord === 'function') {
try {
await ctx.bareOsAppendLoggerRecord(rec)
} catch {
/* best-effort */
}
}
const maxRaw = Number.parseInt(String(ctx.vfs?.env?.BARE_OS_LOGGER_MAX_BYTES || '1048576'), 10)
const maxBytes = Number.isFinite(maxRaw) && maxRaw > 1024 ? Math.min(maxRaw, 8 * 1024 * 1024) : 1048576
let prev = ctx.b4a.from('')
+4
View File
@@ -27,6 +27,10 @@ async function run(ctx, argv) {
const pos = []
for (let i = 0; i < args.length; i++) {
const a = args[i]
if (a === '-l' || a === '--list') {
await printMountTable(ctx)
return
}
if (a === '-t') {
i++
if (i >= args.length) {
+14 -4
View File
@@ -1,11 +1,21 @@
async function run(ctx, argv) {
const args = argv.slice(1).filter((a) => a !== '--')
if (!args.length) {
ctx.console.error('usage: umount </mnt/label>')
let force = false
/** @type {string[]} */
const pos = []
for (const a of args) {
if (a === '-f' || a === '--force') {
force = true
continue
}
pos.push(a)
}
if (!pos.length) {
ctx.console.error('usage: umount [-f] </mnt/label>')
ctx.exitCode = 1
return
}
const target = String(args[0] || '').trim()
const target = String(pos[0] || '').trim()
const m = /^\/mnt\/([a-zA-Z0-9][a-zA-Z0-9._-]{0,62})$/.exec(target)
if (!m) {
ctx.console.error('umount: target must be /mnt/<label>')
@@ -16,7 +26,7 @@ async function run(ctx, argv) {
if (typeof ctx.bareOsSyscall === 'function') {
try {
await ctx.bareOsSyscall('umount', { target, label })
await ctx.bareOsSyscall('umount', { target, label, force })
return
} catch (e) {
ctx.console.error('umount: ' + (e?.message || String(e)))