feat: complete 20-track kernel roadmap (P2P ops, POSIX, HRPC, CI, docs)
- Add optional Holepunch clone lag gate (holepunch-freshness-gate.json, verify-holepunch-clone-freshness.mjs) and wire into pretest/docs. - Extend stock ctx.bareOsHrpcRequest with disk.os replication routes; bump hrpc_allowlist_sketch proc to schema 2 with stockRoutes list. - Security posture: blindRelayAudit; hyper_multisig_trust_pointer schema 2 + vault multisig continuity env; login/unlock audit hook. - Syscalls schema 9 alignment (JSON schema, compatibility matrix, conformance matrix clock_gettime); boot budget telemetry schema 2 in metrics_live. - Coreutils hostname -s/--short man/options; rebuild kernel bins/man. - POSIX + P2P dashboard section in docs/README; handbook/DOCUMENTATION/ release-checklist/OTA/KERNEL_CONTRACT/PEAR-RUN and related reference updates. - verify-boot-policy-extension-signer-pins: scan kernel init fragments. Note: vendor drift section removed from kernel/lib/bare/README.md (intentional).
This commit is contained in:
+1
-1
@@ -30,7 +30,7 @@ Files in this directory are **read from disk by the seeder** (or copied into `pa
|
||||
|
||||
## Editing workflow
|
||||
|
||||
**Do not edit `kernel/init.js` by hand.** It is generated from `kernel/lib/boot/*.js` (sorted) plus `kernel/lib/init/init-main.js` via `npm run bundle:kernel` (`scripts/bundle-kernel-init.mjs`). CI and `npm run verify:init-bundle` fail when the file drifts. Edit the fragments under `lib/boot/` and `lib/init/` only, then bundle and rsync to `packages/bare-os-seeder/kernel/` for Pear parity.
|
||||
**Do not edit `kernel/init.js` by hand.** It is generated from `kernel/lib/boot/*.js` (sorted), `kernel/lib/init/fragments/*.js` (sorted), and `kernel/lib/init/init-main.js` via `npm run bundle:kernel` (`scripts/bundle-kernel-init.mjs`). CI and `npm run verify:init-bundle` fail when the file drifts. Edit the fragments under `lib/boot/`, `lib/init/fragments/`, and `lib/init/` only, then bundle and rsync to `packages/bare-os-seeder/kernel/` for Pear parity. See `lib/init/STRUCTURE.md`.
|
||||
|
||||
1. Change sources under `kernel/` or `packages/bare-os-coreutils/src/`.
|
||||
2. Run `npm run build -w bare-os-coreutils` to refresh `kernel/bin/*`.
|
||||
|
||||
+2
-2
@@ -183,10 +183,10 @@ const CONF = {
|
||||
_PC_2_SYMLINKS: '1',
|
||||
/** Comma-separated `ctx.bareOsSyscall` op names implemented in stock booter. */
|
||||
BARE_OS_SYSCALL_OPS:
|
||||
'accept,access,bind,chdir,chmod,connect,exists,fcntl,fdatasync,fsync,ftruncate,getcwd,getsockopt,kill,link,listen,lstat,mkdir,mount,mq_open,mq_receive,mq_send,nanosleep,pathconf,posixPoll,readFile,readdir,readlink,readv,recv,recvfrom,recvmsg,rename,rmdir,select,send,sendmsg,setsockopt,shutdown,socket,stat,symlink,truncate,umask,umount,unlink,utimes,writeFile,writev',
|
||||
'accept,access,bind,chdir,chmod,clock_gettime,connect,exists,fcntl,fdatasync,fsync,ftruncate,getcwd,getsockopt,kill,link,listen,lstat,mkdir,mount,mq_open,mq_receive,mq_send,nanosleep,pathconf,posixPoll,readFile,readdir,readlink,readv,recv,recvfrom,recvmsg,rename,rmdir,select,send,sendmsg,setsockopt,shutdown,socket,stat,symlink,truncate,umask,umount,unlink,utimes,writeFile,writev',
|
||||
/** POSIX.1 XSH-style names documented in `/proc/bare_os/syscalls.json` opsDetail (socket family are syscall probes returning ENOSYS-shaped results). */
|
||||
BARE_OS_POSIX_XSH_OPS:
|
||||
'open,close,read,write,lseek,nanosleep,pipe,dup,dup2,fcntl,poll,select,umask,socket,bind,listen,accept,connect,send,recv,recvfrom,sendmsg,recvmsg,shutdown',
|
||||
'open,close,read,write,lseek,nanosleep,clock_gettime,pipe,dup,dup2,fcntl,poll,select,umask,socket,bind,listen,accept,connect,send,recv,recvfrom,sendmsg,recvmsg,shutdown',
|
||||
/** 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). */
|
||||
|
||||
+13
-1
@@ -88,10 +88,22 @@ function bareOsEmitRaw(ctx, chunk) {
|
||||
}
|
||||
|
||||
async function run(ctx, argv) {
|
||||
const h =
|
||||
let h =
|
||||
globalThis.process?.env?.HOSTNAME ||
|
||||
globalThis.process?.env?.COMPUTERNAME ||
|
||||
ctx.vfs.env.HOSTNAME ||
|
||||
'bare-os'
|
||||
h = String(h)
|
||||
let shortOnly = false
|
||||
for (let i = 1; i < argv.length; i++) {
|
||||
const a = String(argv[i] || '')
|
||||
if (a === '-s' || a === '--short') shortOnly = true
|
||||
else if (a === '-f' || a === '--fqdn') shortOnly = false
|
||||
else if (a === '--') break
|
||||
}
|
||||
if (shortOnly) {
|
||||
const dot = h.indexOf('.')
|
||||
if (dot > 0) h = h.slice(0, dot)
|
||||
}
|
||||
ctx.console.log(h)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"schema": 2,
|
||||
"profileId": "bare-os-posix-like",
|
||||
"generatedAt": "2026-04-05T17:48:11.632Z",
|
||||
"generatedAt": "2026-04-05T18:15:46.515Z",
|
||||
"note": "Sparse POSIX Issue 7 coverage hints for /bin utilities. Omitted command names are not yet profiled here.",
|
||||
"commandIndex": [
|
||||
{
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"schemaVersion": 8,
|
||||
"schemaVersion": 9,
|
||||
"ctxApiVersion": "1.50.0",
|
||||
"posixProfile": {
|
||||
"id": "bare-os-posix-like",
|
||||
"version": "1.0.14"
|
||||
"version": "1.0.15"
|
||||
},
|
||||
"ops": ["readFile", "writeFile", "socket"],
|
||||
"opsDetail": [
|
||||
@@ -29,7 +29,7 @@
|
||||
"posixXsh": {
|
||||
"schema": 2,
|
||||
"note": "POSIX.1 XSH-style names; socket bridge may implement SOCK_DGRAM sendmsg/recvmsg when BARE_OS_POSIX_SOCKET_FD_BRIDGE.",
|
||||
"namesCsv": "open,close,read,write,readv,writev,getsockopt,setsockopt,lseek,nanosleep,pipe,dup,dup2,fcntl,poll,select,umask,socket,bind,listen,accept,connect,send,recv,recvfrom,sendmsg,recvmsg,shutdown"
|
||||
"namesCsv": "open,close,read,write,readv,writev,getsockopt,setsockopt,lseek,nanosleep,clock_gettime,pipe,dup,dup2,fcntl,poll,select,umask,socket,bind,listen,accept,connect,send,recv,recvfrom,sendmsg,recvmsg,shutdown"
|
||||
},
|
||||
"socketMsgSurface": {
|
||||
"schema": 4,
|
||||
|
||||
+1127
-1097
File diff suppressed because it is too large
Load Diff
+212
-212
@@ -38,9 +38,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareEvents.js",
|
||||
"path": "/lib/bare/bundles/bareEncoding.js",
|
||||
"keys": [
|
||||
"bareEvents"
|
||||
"bareEncoding"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -50,15 +50,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareEncoding.js",
|
||||
"path": "/lib/bare/bundles/bareEvents.js",
|
||||
"keys": [
|
||||
"bareEncoding"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareAbort.js",
|
||||
"keys": [
|
||||
"bareAbort"
|
||||
"bareEvents"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -68,15 +62,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareAnsiEscapes.js",
|
||||
"path": "/lib/bare/bundles/bareAbort.js",
|
||||
"keys": [
|
||||
"bareAnsiEscapes"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareAddonResolve.js",
|
||||
"keys": [
|
||||
"bareAddonResolve"
|
||||
"bareAbort"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -85,6 +73,12 @@
|
||||
"bareReadline"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareAddonResolve.js",
|
||||
"keys": [
|
||||
"bareAddonResolve"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareCrypto.js",
|
||||
"keys": [
|
||||
@@ -92,9 +86,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareAtomics.js",
|
||||
"path": "/lib/bare/bundles/bareAnsiEscapes.js",
|
||||
"keys": [
|
||||
"bareAtomics"
|
||||
"bareAnsiEscapes"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -110,15 +104,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/fetch.js",
|
||||
"path": "/lib/bare/bundles/bareAtomics.js",
|
||||
"keys": [
|
||||
"fetch"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareApk.js",
|
||||
"keys": [
|
||||
"bareApk"
|
||||
"bareAtomics"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -128,15 +116,21 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareBmp.js",
|
||||
"path": "/lib/bare/bundles/bareApk.js",
|
||||
"keys": [
|
||||
"bareBmp"
|
||||
"bareApk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareBundleCompile.js",
|
||||
"path": "/lib/bare/bundles/fetch.js",
|
||||
"keys": [
|
||||
"bareBundleCompile"
|
||||
"fetch"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareBmp.js",
|
||||
"keys": [
|
||||
"bareBmp"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -146,15 +140,15 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareBundle.js",
|
||||
"path": "/lib/bare/bundles/bareBundleCompile.js",
|
||||
"keys": [
|
||||
"bareBundle"
|
||||
"bareBundleCompile"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareBundleEvaluate.js",
|
||||
"path": "/lib/bare/bundles/bareBundle.js",
|
||||
"keys": [
|
||||
"bareBundleEvaluate"
|
||||
"bareBundle"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -164,9 +158,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareBoot.js",
|
||||
"path": "/lib/bare/bundles/bareBundleEvaluate.js",
|
||||
"keys": [
|
||||
"bareBoot"
|
||||
"bareBundleEvaluate"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -175,6 +169,12 @@
|
||||
"bareBundleId"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareBoot.js",
|
||||
"keys": [
|
||||
"bareBoot"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareConsole.js",
|
||||
"keys": [
|
||||
@@ -188,9 +188,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareDelta.js",
|
||||
"path": "/lib/bare/bundles/bareDaemon.js",
|
||||
"keys": [
|
||||
"bareDelta"
|
||||
"bareDaemon"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -200,9 +200,15 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareDaemon.js",
|
||||
"path": "/lib/bare/bundles/bareDelta.js",
|
||||
"keys": [
|
||||
"bareDaemon"
|
||||
"bareDelta"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareDiagnosticsChannel.js",
|
||||
"keys": [
|
||||
"bareDiagnosticsChannel"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -212,9 +218,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareDiagnosticsChannel.js",
|
||||
"path": "/lib/bare/bundles/bareCov.js",
|
||||
"keys": [
|
||||
"bareDiagnosticsChannel"
|
||||
"bareCov"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -235,12 +241,6 @@
|
||||
"bareDgram"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareFfmpegEncodings.js",
|
||||
"keys": [
|
||||
"bareFfmpegEncodings"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareFfmpeg.js",
|
||||
"keys": [
|
||||
@@ -248,21 +248,15 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareCov.js",
|
||||
"path": "/lib/bare/bundles/bareFfmpegEncodings.js",
|
||||
"keys": [
|
||||
"bareCov"
|
||||
"bareFfmpegEncodings"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareGif.js",
|
||||
"path": "/lib/bare/bundles/bareFormat.js",
|
||||
"keys": [
|
||||
"bareGif"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareFileLogger.js",
|
||||
"keys": [
|
||||
"bareFileLogger"
|
||||
"bareFormat"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -272,9 +266,15 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareFormat.js",
|
||||
"path": "/lib/bare/bundles/bareFileLogger.js",
|
||||
"keys": [
|
||||
"bareFormat"
|
||||
"bareFileLogger"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareGif.js",
|
||||
"keys": [
|
||||
"bareGif"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -289,12 +289,6 @@
|
||||
"bareHrtime"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareFs.js",
|
||||
"keys": [
|
||||
"bareFs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareHeif.js",
|
||||
"keys": [
|
||||
@@ -308,15 +302,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareIco.js",
|
||||
"path": "/lib/bare/bundles/bareFs.js",
|
||||
"keys": [
|
||||
"bareIco"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareHttp1.js",
|
||||
"keys": [
|
||||
"bareHttp1"
|
||||
"bareFs"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -326,9 +314,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareHttps.js",
|
||||
"path": "/lib/bare/bundles/bareIco.js",
|
||||
"keys": [
|
||||
"bareHttps"
|
||||
"bareIco"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -337,6 +325,18 @@
|
||||
"bareInspect"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareHttp1.js",
|
||||
"keys": [
|
||||
"bareHttp1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareHttps.js",
|
||||
"keys": [
|
||||
"bareHttps"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareJpeg.js",
|
||||
"keys": [
|
||||
@@ -349,12 +349,6 @@
|
||||
"bareIntl"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareLief.js",
|
||||
"keys": [
|
||||
"bareLief"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareIpc.js",
|
||||
"keys": [
|
||||
@@ -362,9 +356,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareLink.js",
|
||||
"path": "/lib/bare/bundles/bareLief.js",
|
||||
"keys": [
|
||||
"bareLink"
|
||||
"bareLief"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -380,9 +374,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareMake.js",
|
||||
"path": "/lib/bare/bundles/bareLink.js",
|
||||
"keys": [
|
||||
"bareMake"
|
||||
"bareLink"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -391,12 +385,6 @@
|
||||
"bareModuleLexer"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareModule.js",
|
||||
"keys": [
|
||||
"bareModule"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareModuleResolve.js",
|
||||
"keys": [
|
||||
@@ -404,15 +392,15 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareModuleTraverse.js",
|
||||
"path": "/lib/bare/bundles/bareMake.js",
|
||||
"keys": [
|
||||
"bareModuleTraverse"
|
||||
"bareMake"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareMedia.js",
|
||||
"path": "/lib/bare/bundles/bareModule.js",
|
||||
"keys": [
|
||||
"bareMedia"
|
||||
"bareModule"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -421,12 +409,6 @@
|
||||
"bareNdk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareNodeFetch.js",
|
||||
"keys": [
|
||||
"bareNodeFetch"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareNative.js",
|
||||
"keys": [
|
||||
@@ -434,9 +416,15 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareOpen.js",
|
||||
"path": "/lib/bare/bundles/bareNodeFetch.js",
|
||||
"keys": [
|
||||
"bareOpen"
|
||||
"bareNodeFetch"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareModuleTraverse.js",
|
||||
"keys": [
|
||||
"bareModuleTraverse"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -445,12 +433,42 @@
|
||||
"bareNet"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareMedia.js",
|
||||
"keys": [
|
||||
"bareMedia"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareOs.js",
|
||||
"keys": [
|
||||
"bareOs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareOpen.js",
|
||||
"keys": [
|
||||
"bareOpen"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/barePerformance.js",
|
||||
"keys": [
|
||||
"barePerformance"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/barePipe.js",
|
||||
"keys": [
|
||||
"barePipe"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/barePng.js",
|
||||
"keys": [
|
||||
"barePng"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/barePack.js",
|
||||
"keys": [
|
||||
@@ -464,33 +482,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/barePerformance.js",
|
||||
"path": "/lib/bare/bundles/barePrebuild.js",
|
||||
"keys": [
|
||||
"barePerformance"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/barePng.js",
|
||||
"keys": [
|
||||
"barePng"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareNodeRuntime.js",
|
||||
"keys": [
|
||||
"bareNodeRuntime"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareDev.js",
|
||||
"keys": [
|
||||
"bareDev"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/barePipe.js",
|
||||
"keys": [
|
||||
"barePipe"
|
||||
"barePrebuild"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -506,15 +500,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/barePrebuild.js",
|
||||
"path": "/lib/bare/bundles/bareProcess.js",
|
||||
"keys": [
|
||||
"barePrebuild"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareQueueMicrotask.js",
|
||||
"keys": [
|
||||
"bareQueueMicrotask"
|
||||
"bareProcess"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -524,21 +512,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareProcess.js",
|
||||
"path": "/lib/bare/bundles/bareQueueMicrotask.js",
|
||||
"keys": [
|
||||
"bareProcess"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareRuntime.js",
|
||||
"keys": [
|
||||
"bareRuntime"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareRpc.js",
|
||||
"keys": [
|
||||
"bareRpc"
|
||||
"bareQueueMicrotask"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -548,21 +524,27 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareSdl.js",
|
||||
"path": "/lib/bare/bundles/bareNodeRuntime.js",
|
||||
"keys": [
|
||||
"bareSdl"
|
||||
"bareNodeRuntime"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareRepl.js",
|
||||
"path": "/lib/bare/bundles/bareDev.js",
|
||||
"keys": [
|
||||
"bareRepl"
|
||||
"bareDev"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareSemver.js",
|
||||
"path": "/lib/bare/bundles/bareRpc.js",
|
||||
"keys": [
|
||||
"bareSemver"
|
||||
"bareRpc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareRuntime.js",
|
||||
"keys": [
|
||||
"bareRuntime"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -572,9 +554,21 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareSidecar.js",
|
||||
"path": "/lib/bare/bundles/bareRepl.js",
|
||||
"keys": [
|
||||
"bareSidecar"
|
||||
"bareRepl"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareSdl.js",
|
||||
"keys": [
|
||||
"bareSdl"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareSemver.js",
|
||||
"keys": [
|
||||
"bareSemver"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -583,24 +577,30 @@
|
||||
"bareSignals"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareSidecar.js",
|
||||
"keys": [
|
||||
"bareSidecar"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareStringDecoder.js",
|
||||
"keys": [
|
||||
"bareStringDecoder"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareStream.js",
|
||||
"keys": [
|
||||
"bareStream"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareStorage.js",
|
||||
"keys": [
|
||||
"bareStorage"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareStream.js",
|
||||
"keys": [
|
||||
"bareStream"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareSvg.js",
|
||||
"keys": [
|
||||
@@ -613,6 +613,12 @@
|
||||
"bareStdio"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareSystemLogger.js",
|
||||
"keys": [
|
||||
"bareSystemLogger"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareStructuredClone.js",
|
||||
"keys": [
|
||||
@@ -620,9 +626,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareTiff.js",
|
||||
"path": "/lib/bare/bundles/bareTcp.js",
|
||||
"keys": [
|
||||
"bareTiff"
|
||||
"bareTcp"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -631,12 +637,6 @@
|
||||
"bareSubprocess"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareSystemLogger.js",
|
||||
"keys": [
|
||||
"bareSystemLogger"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareTap.js",
|
||||
"keys": [
|
||||
@@ -644,15 +644,15 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareThread.js",
|
||||
"path": "/lib/bare/bundles/bareTiff.js",
|
||||
"keys": [
|
||||
"bareThread"
|
||||
"bareTiff"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareTcp.js",
|
||||
"path": "/lib/bare/bundles/bareThread.js",
|
||||
"keys": [
|
||||
"bareTcp"
|
||||
"bareThread"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -673,12 +673,6 @@
|
||||
"bareType"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareUiKit.js",
|
||||
"keys": [
|
||||
"bareUiKit"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareTls.js",
|
||||
"keys": [
|
||||
@@ -686,9 +680,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareTty.js",
|
||||
"path": "/lib/bare/bundles/bareUiKit.js",
|
||||
"keys": [
|
||||
"bareTty"
|
||||
"bareUiKit"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -697,6 +691,12 @@
|
||||
"bareUnpack"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareTty.js",
|
||||
"keys": [
|
||||
"bareTty"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareV8.js",
|
||||
"keys": [
|
||||
@@ -721,12 +721,6 @@
|
||||
"bareWalkHandles"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareWebKitGtk.js",
|
||||
"keys": [
|
||||
"bareWebKitGtk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareUnionBundle.js",
|
||||
"keys": [
|
||||
@@ -739,6 +733,12 @@
|
||||
"bareUtils"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareWebKitGtk.js",
|
||||
"keys": [
|
||||
"bareWebKitGtk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareWebp.js",
|
||||
"keys": [
|
||||
@@ -751,6 +751,12 @@
|
||||
"bareWhich"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareWinUi.js",
|
||||
"keys": [
|
||||
"bareWinUi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareV8ToIstanbul.js",
|
||||
"keys": [
|
||||
@@ -764,21 +770,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareWinUi.js",
|
||||
"path": "/lib/bare/bundles/bareWs.js",
|
||||
"keys": [
|
||||
"bareWinUi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareZlib.js",
|
||||
"keys": [
|
||||
"bareZlib"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareWorker.js",
|
||||
"keys": [
|
||||
"bareWorker"
|
||||
"bareWs"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -788,9 +782,15 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareWs.js",
|
||||
"path": "/lib/bare/bundles/bareWorker.js",
|
||||
"keys": [
|
||||
"bareWs"
|
||||
"bareWorker"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareZlib.js",
|
||||
"keys": [
|
||||
"bareZlib"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -1595,8 +1595,8 @@
|
||||
],
|
||||
"bundleProvenance": {
|
||||
"schemaVersion": 1,
|
||||
"generatedAt": "2026-04-05T17:48:12.581Z",
|
||||
"gitCommit": "551d68ac6cdb72c4a51c0cac56e882cdb53fb900",
|
||||
"generatedAt": "2026-04-05T18:15:47.509Z",
|
||||
"gitCommit": "c15e8fa5be4e6d014a383cff12024e5525e11bbd",
|
||||
"nodeVersion": "v22.22.0",
|
||||
"bundleTier": "all",
|
||||
"normativeManifest": "packages/bare-os-booter/lib/bare-module-manifest.json",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"schema": 1,
|
||||
"atMs": 1775411291632,
|
||||
"atMs": 1775412946515,
|
||||
"commands": [
|
||||
"arch",
|
||||
"awk",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Boot preamble: cooperative lock FSM constants + `invokeCtxBootHooks`.
|
||||
* This file is the **only** source for these symbols; `kernel/init.js` is generated by
|
||||
* `node scripts/bundle-kernel-init.mjs` (concat `lib/boot/*.js` in sorted order, then `lib/init/init-main.js`).
|
||||
* `node scripts/bundle-kernel-init.mjs` (concat `lib/boot/*.js`, `lib/init/fragments/*.js`, then `lib/init/init-main.js`).
|
||||
* CI: `scripts/verify-init-bundle-recipe.mjs` / `verify-kernel-seeder-parity.mjs`.
|
||||
*/
|
||||
/** Boot transaction journal row state (resume / audit / rollback FSM). */
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
# Stock kernel internal layout (`/boot/init.js`)
|
||||
|
||||
The shipped kernel is a **single** Hyperdrive file evaluated with `AsyncFunction` (see `packages/bare-os-booter/lib/kernel-runner.js`). The editable bulk of that script lives in **`init-main.js`** in this directory (staged on the image as **`/lib/init/init-main.js`**); `scripts/bundle-kernel-init.mjs` concatenates `kernel/lib/boot/*.js` + **`init-main.js`** → repo-root **`kernel/init.js`** (staged as **`/boot/init.js`**). Logical modules map to regions of `kernel/init.js` for maintenance:
|
||||
The shipped kernel is a **single** Hyperdrive file evaluated with `AsyncFunction` (see `packages/bare-os-booter/lib/kernel-runner.js`). Sources are concatenated by [`scripts/bundle-kernel-init.mjs`](../../../scripts/bundle-kernel-init.mjs) in this order:
|
||||
|
||||
| Concern | Responsibility |
|
||||
| --- | --- |
|
||||
| Boot policy & env gates | `applyBootPolicyFile`, skip sets, ABI checks |
|
||||
| RC runners | `runRcLines`, `kernel.d`, `kernel.ext.d` |
|
||||
| Extensions | `runKernelExtDropins`, topological ordering |
|
||||
| Selftest | `kernel.d` / `selftest.d` harness |
|
||||
| Interactive loop | readline / REPL after boot stages |
|
||||
1. **`kernel/lib/boot/*.js`** (sorted) — preamble (`BARE_OS_BOOT_TXN_STATE`, multisig shape, `invokeCtxBootHooks`).
|
||||
2. **`kernel/lib/init/fragments/*.js`** (sorted) — large cohesive regions:
|
||||
- **`20-init-boot-policy.js`** — semver helpers, skip merge, `kernel.d` env guards, **`applyBootPolicyFile`**.
|
||||
- **`30-init-kernel-extensions.js`** — **`runKernelExtDropins`**, topological ordering, signer pins, multisig gate.
|
||||
3. **`kernel/lib/init/init-main.js`** — boot trace/journals, RC runners, selftest, **`start(ctx)`**, REPL.
|
||||
|
||||
Future work may **concatenate** split sources into one deliverable; until then, keep this map updated when moving large blocks.
|
||||
Staged paths: **`/lib/init/init-main.js`**, **`/lib/init/fragments/*`**, and bundled **`/boot/init.js`**.
|
||||
|
||||
| Concern | Responsibility | Primary source |
|
||||
| --- | --- | --- |
|
||||
| Boot policy & env gates | `applyBootPolicyFile`, skip sets, ABI checks | `fragments/20-init-boot-policy.js` |
|
||||
| RC runners | `runRcLines`, `kernel.d`, `rc.d` | `init-main.js` |
|
||||
| Extensions | `runKernelExtDropins`, topological ordering | `fragments/30-init-kernel-extensions.js` |
|
||||
| Selftest | `kernel.d` / `selftest.d` harness | `init-main.js` |
|
||||
| Interactive loop | readline / REPL after boot stages | `init-main.js` |
|
||||
|
||||
After editing any fragment or `init-main.js`, run `node scripts/bundle-kernel-init.mjs` and sync the seeder tree (`rsync -a --delete kernel/ packages/bare-os-seeder/kernel/`). CI: `verify-init-bundle-recipe.mjs`, `verify-kernel-seeder-parity.mjs`, `verify-runtime-no-incomplete-markers.mjs`.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,684 @@
|
||||
/**
|
||||
* Concatenated into /boot/init.js before init-main.js by scripts/bundle-kernel-init.mjs.
|
||||
* kernel.ext.d: topological sort, signer pins, multisig gate, runKernelExtDropins.
|
||||
* Depends on: BARE_OS_BOOT_TXN_STATE (boot preamble), bareOsPearMultisigShapeOk (00-pear-multisig-shape),
|
||||
* semverGte from 20-init-boot-policy.js; bootStructuredLog/bootDryRun from init-main.js (same AsyncFunction body; hoisted).
|
||||
* @see kernel/lib/init/STRUCTURE.md
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {{ file: string, extId: string, scripts: string[], dependsOn: string[], signaturePointer?: string }[]} entries
|
||||
* @returns {{ ok: true, ordered: typeof entries } | { ok: false, cycleExtIds: string[] }}
|
||||
*/
|
||||
function topologicalOrderKernelExtEntries(entries) {
|
||||
const sorted = [...entries].sort((a, b) => a.file.localeCompare(b.file))
|
||||
const idToFile = new Map()
|
||||
for (const e of sorted) {
|
||||
if (!idToFile.has(e.extId)) idToFile.set(e.extId, e.file)
|
||||
}
|
||||
/** @type {Map<string, string[]>} */
|
||||
const adj = new Map()
|
||||
/** @type {Map<string, number>} */
|
||||
const indeg = new Map()
|
||||
for (const e of sorted) indeg.set(e.file, 0)
|
||||
for (const e of sorted) {
|
||||
for (const dep of e.dependsOn) {
|
||||
const from = idToFile.get(dep)
|
||||
if (!from || from === e.file) continue
|
||||
if (!adj.has(from)) adj.set(from, [])
|
||||
adj.get(from).push(e.file)
|
||||
indeg.set(e.file, (indeg.get(e.file) || 0) + 1)
|
||||
}
|
||||
}
|
||||
/** @type {string[]} */
|
||||
const q = sorted
|
||||
.map((e) => e.file)
|
||||
.filter((f) => (indeg.get(f) || 0) === 0)
|
||||
.sort()
|
||||
/** @type {string[]} */
|
||||
const out = []
|
||||
while (q.length) {
|
||||
const f = q.shift()
|
||||
out.push(f)
|
||||
for (const to of adj.get(f) || []) {
|
||||
indeg.set(to, (indeg.get(to) || 0) - 1)
|
||||
if (indeg.get(to) === 0) {
|
||||
q.push(to)
|
||||
q.sort()
|
||||
}
|
||||
}
|
||||
}
|
||||
if (out.length !== sorted.length) {
|
||||
const stuck = sorted.filter((e) => !out.includes(e.file))
|
||||
return { ok: false, cycleExtIds: stuck.map((e) => e.extId) }
|
||||
}
|
||||
const byFile = new Map(sorted.map((e) => [e.file, e]))
|
||||
return {
|
||||
ok: true,
|
||||
ordered: out.map((f) => byFile.get(f)).filter(Boolean)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{ extId: string, dependsOn: string[] }[]} entries
|
||||
* @returns {number}
|
||||
*/
|
||||
function kernelExtDependencyDepth(entries) {
|
||||
const idToEntry = new Map(entries.map((e) => [e.extId, e]))
|
||||
const memo = new Map()
|
||||
/**
|
||||
* @param {string} id
|
||||
* @param {Set<string>} stack
|
||||
*/
|
||||
function depth(id, stack) {
|
||||
if (memo.has(id)) return memo.get(id)
|
||||
if (stack.has(id)) return 99
|
||||
const e = idToEntry.get(id)
|
||||
if (!e) return 0
|
||||
stack.add(id)
|
||||
let d = 0
|
||||
for (const dep of e.dependsOn) {
|
||||
d = Math.max(d, depth(dep, stack) + 1)
|
||||
}
|
||||
stack.delete(id)
|
||||
memo.set(id, d)
|
||||
return d
|
||||
}
|
||||
let max = 0
|
||||
for (const e of entries) {
|
||||
max = Math.max(max, depth(e.extId, new Set()))
|
||||
}
|
||||
return max
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge extension signer pin maps from boot policy env mirrors (V2–V5; later JSON wins per key).
|
||||
* @param {Record<string, unknown> | null | undefined} env
|
||||
* @returns {Record<string, string | string[]>}
|
||||
*/
|
||||
function mergeExtensionSignerPinsFromEnv(env) {
|
||||
if (!env || typeof env !== 'object') return {}
|
||||
const keys = [
|
||||
'BARE_OS_BOOT_POLICY_EXTENSION_SIGNER_PINS_V2_JSON',
|
||||
'BARE_OS_BOOT_POLICY_EXTENSION_SIGNER_PINS_V3_JSON',
|
||||
'BARE_OS_BOOT_POLICY_EXTENSION_SIGNER_PINS_V4_JSON',
|
||||
'BARE_OS_BOOT_POLICY_EXTENSION_SIGNER_PINS_V5_JSON'
|
||||
]
|
||||
/** @type {Record<string, string | string[]>} */
|
||||
const out = {}
|
||||
for (const k of keys) {
|
||||
const raw = String(env[k] ?? '').trim()
|
||||
if (!raw) continue
|
||||
try {
|
||||
const o = JSON.parse(raw)
|
||||
if (o && typeof o === 'object' && !Array.isArray(o)) {
|
||||
for (const [ik, iv] of Object.entries(o)) {
|
||||
out[String(ik)] = /** @type {string | string[]} */ (iv)
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
/* ignore malformed JSON */
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
/**
|
||||
* When boot.policy pins an extension id to Ed25519 key(s), verify detached signature over script bytes.
|
||||
* @param {Record<string, unknown>} ctx
|
||||
* @param {{ extId: string, file: string, signaturePointer?: string }} ent
|
||||
* @param {string} imgPath
|
||||
* @param {Record<string, string | string[]>} pins
|
||||
* @param {boolean} strictPol
|
||||
* @returns {Promise<boolean>} true if the script may run
|
||||
*/
|
||||
async function verifyKernelExtSignerPinsForScript(
|
||||
ctx,
|
||||
ent,
|
||||
imgPath,
|
||||
pins,
|
||||
strictPol
|
||||
) {
|
||||
const id = String(ent.extId || '').trim()
|
||||
const need = pins[id]
|
||||
if (need == null) return true
|
||||
/** @type {string[]} */
|
||||
const pubHexList = Array.isArray(need)
|
||||
? need.map((x) =>
|
||||
String(x)
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.replace(/^0x/, '')
|
||||
)
|
||||
: [String(need).trim().toLowerCase().replace(/^0x/, '')]
|
||||
const validKeys = pubHexList.filter(
|
||||
(h) => h.length === 64 && /^[0-9a-f]+$/.test(h)
|
||||
)
|
||||
if (!validKeys.length) {
|
||||
bootStructuredLog(
|
||||
ctx,
|
||||
'error',
|
||||
'kernelExt.signerPinBadKey',
|
||||
`[kernel.ext.d] extensionSignerPins for "${id}" must be 64-char hex pubkey(s)`
|
||||
)
|
||||
return !strictPol
|
||||
}
|
||||
const sigPtr = ent.signaturePointer
|
||||
? String(ent.signaturePointer).trim()
|
||||
: ''
|
||||
if (!sigPtr.startsWith('/')) {
|
||||
bootStructuredLog(
|
||||
ctx,
|
||||
'error',
|
||||
'kernelExt.signerPinNoSigPath',
|
||||
`[kernel.ext.d] "${id}": signaturePointer (absolute path to signature) required when extensionSignerPins lists this id`
|
||||
)
|
||||
return !strictPol
|
||||
}
|
||||
const { drive, b4a } = ctx
|
||||
if (!drive || typeof drive.get !== 'function' || !b4a) {
|
||||
bootStructuredLog(
|
||||
ctx,
|
||||
'error',
|
||||
'kernelExt.signerPinNoDrive',
|
||||
'[kernel.ext.d] signer pin verify requires ctx.drive.get and ctx.b4a'
|
||||
)
|
||||
return !strictPol
|
||||
}
|
||||
let scriptBuf
|
||||
let sigBuf
|
||||
try {
|
||||
scriptBuf = await drive.get(imgPath)
|
||||
sigBuf = await drive.get(sigPtr)
|
||||
} catch (e) {
|
||||
bootStructuredLog(
|
||||
ctx,
|
||||
'error',
|
||||
'kernelExt.signerPinRead',
|
||||
`[kernel.ext.d] signer pin read: ${(e && e.message) || String(e)}`
|
||||
)
|
||||
return !strictPol
|
||||
}
|
||||
if (!scriptBuf || !scriptBuf.byteLength || !sigBuf || !sigBuf.byteLength) {
|
||||
bootStructuredLog(
|
||||
ctx,
|
||||
'error',
|
||||
'kernelExt.signerPinMissingBytes',
|
||||
`[kernel.ext.d] "${id}": script or signature file missing/empty for pin verify`
|
||||
)
|
||||
return !strictPol
|
||||
}
|
||||
const verifyFn = ctx.bareOsVerifyBootManifestSignature
|
||||
if (typeof verifyFn !== 'function') {
|
||||
bootStructuredLog(
|
||||
ctx,
|
||||
'error',
|
||||
'kernelExt.signerPinNoVerify',
|
||||
'[kernel.ext.d] ctx.bareOsVerifyBootManifestSignature unavailable; cannot enforce extensionSignerPins'
|
||||
)
|
||||
return !strictPol
|
||||
}
|
||||
for (const pk of validKeys) {
|
||||
try {
|
||||
if (verifyFn.call(ctx, scriptBuf, sigBuf, pk) === true) return true
|
||||
} catch {
|
||||
/* try next pubkey */
|
||||
}
|
||||
}
|
||||
bootStructuredLog(
|
||||
ctx,
|
||||
'error',
|
||||
'kernelExt.signerPinVerifyFailed',
|
||||
`[kernel.ext.d] "${id}": Ed25519 verify failed for pinned key(s)`
|
||||
)
|
||||
return !strictPol
|
||||
}
|
||||
|
||||
/**
|
||||
* Append one NDJSON line to `/run/bare-os/kernel-ext-audit.ndjson` (strict boot diagnostics).
|
||||
* @param {Record<string, unknown>} ctx
|
||||
* @param {Record<string, unknown>} row
|
||||
*/
|
||||
async function appendKernelExtAuditNdjson(ctx, row) {
|
||||
if (
|
||||
!ctx.vfs ||
|
||||
typeof ctx.vfs.readFile !== 'function' ||
|
||||
typeof ctx.vfs.writeFile !== 'function' ||
|
||||
!ctx.b4a
|
||||
) {
|
||||
return
|
||||
}
|
||||
const line =
|
||||
JSON.stringify({
|
||||
schema: 1,
|
||||
type: 'kernelExtAudit',
|
||||
atMs: Date.now(),
|
||||
sessionId: String((ctx.env && ctx.env.BARE_OS_SESSION_ID) || ''),
|
||||
...row
|
||||
}) + '\n'
|
||||
try {
|
||||
let prev = ''
|
||||
try {
|
||||
prev = ctx.b4a.toString(
|
||||
await ctx.vfs.readFile('/run/bare-os/kernel-ext-audit.ndjson')
|
||||
)
|
||||
} catch {
|
||||
/* absent */
|
||||
}
|
||||
const maxBytes = 96 * 1024
|
||||
let next = prev + line
|
||||
if (next.length > maxBytes) next = next.slice(-maxBytes)
|
||||
await ctx.vfs.writeFile(
|
||||
'/run/bare-os/kernel-ext-audit.ndjson',
|
||||
ctx.b4a.from(next)
|
||||
)
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional `/etc/bare-os/kernel.ext.d/*.json` with `{ "scripts": ["/lib/bare-os/extensions/foo.js"] }`.
|
||||
* @param {Record<string, unknown>} ctx
|
||||
* @param {{ incremental?: boolean, ranScripts?: string[] }} [opts]
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
async function runKernelExtDropins(ctx, opts = {}) {
|
||||
const incremental = opts.incremental === true
|
||||
const ranScripts = opts.ranScripts
|
||||
const { drive } = ctx
|
||||
const run = ctx.bareOsRunImageScript
|
||||
if (typeof run !== 'function') return true
|
||||
if (!ctx.bareOsLoadedKernelExtScripts) {
|
||||
ctx.bareOsLoadedKernelExtScripts = new Set()
|
||||
}
|
||||
/** @type {Set<string>} */
|
||||
const loadedSet = /** @type {Set<string>} */ (ctx.bareOsLoadedKernelExtScripts)
|
||||
const denyRaw = String(
|
||||
ctx.env?.BARE_OS_BOOT_POLICY_DENY_KERNEL_EXT_IDS || ''
|
||||
).trim()
|
||||
const deny = new Set(
|
||||
denyRaw
|
||||
.split(',')
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean)
|
||||
)
|
||||
const strictPol =
|
||||
ctx.env?.BARE_OS_BOOT_POLICY_STRICT === '1' ||
|
||||
ctx.env?.BARE_OS_BOOT_POLICY_STRICT === 'true'
|
||||
const extSignerPins = mergeExtensionSignerPinsFromEnv(ctx.env)
|
||||
const multisigExtGate =
|
||||
ctx.env?.BARE_OS_EXTENSION_MULTISIG_VERIFY === '1' ||
|
||||
ctx.env?.BARE_OS_EXTENSION_MULTISIG_VERIFY === 'true' ||
|
||||
ctx.env?.BARE_OS_BOOT_POLICY_REQUIRE_PEAR_MULTISIG === '1' ||
|
||||
ctx.env?.BARE_OS_BOOT_POLICY_REQUIRE_PEAR_MULTISIG === 'true'
|
||||
if (multisigExtGate) {
|
||||
const requireFile =
|
||||
ctx.env?.BARE_OS_BOOT_POLICY_REQUIRE_PEAR_MULTISIG === '1' ||
|
||||
ctx.env?.BARE_OS_BOOT_POLICY_REQUIRE_PEAR_MULTISIG === 'true'
|
||||
try {
|
||||
const mbuf = await drive.get('/etc/bare-os/pear.multisig.json')
|
||||
if (!mbuf || mbuf.byteLength === 0) {
|
||||
if (strictPol && requireFile) {
|
||||
bootStructuredLog(
|
||||
ctx,
|
||||
'error',
|
||||
'kernelExt.multisigMissing',
|
||||
'[kernel.ext.d] strict boot requires /etc/bare-os/pear.multisig.json (BARE_OS_BOOT_POLICY_REQUIRE_PEAR_MULTISIG)'
|
||||
)
|
||||
if (typeof ctx.bareOsRequestBooterExit === 'function')
|
||||
ctx.bareOsRequestBooterExit(1)
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
const mj = JSON.parse(ctx.b4a.toString(mbuf))
|
||||
if (!bareOsPearMultisigShapeOk(mj)) {
|
||||
bootStructuredLog(
|
||||
ctx,
|
||||
'error',
|
||||
'kernelExt.multisigInvalid',
|
||||
'[kernel.ext.d] pear.multisig.json must be { signers: string[], quorum: number } with 1 ≤ quorum ≤ signers.length'
|
||||
)
|
||||
if (strictPol) {
|
||||
if (typeof ctx.bareOsRequestBooterExit === 'function')
|
||||
ctx.bareOsRequestBooterExit(1)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
bootStructuredLog(
|
||||
ctx,
|
||||
'warn',
|
||||
'kernelExt.multisigRead',
|
||||
'[kernel.ext.d] pear.multisig.json: ' + ((e && e.message) || String(e))
|
||||
)
|
||||
if (strictPol && requireFile) {
|
||||
if (typeof ctx.bareOsRequestBooterExit === 'function')
|
||||
ctx.bareOsRequestBooterExit(1)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
const maxDepthRaw = String(
|
||||
ctx.env?.BARE_OS_BOOT_POLICY_MAX_KERNEL_EXT_DEPTH || ''
|
||||
).trim()
|
||||
const maxDepthCap =
|
||||
maxDepthRaw && Number.parseInt(maxDepthRaw, 10) > 0
|
||||
? Math.min(32, Number.parseInt(maxDepthRaw, 10))
|
||||
: 0
|
||||
/** @type {string[]} */
|
||||
const names = []
|
||||
try {
|
||||
for await (const n of drive.readdir('/etc/bare-os/kernel.ext.d'))
|
||||
names.push(n)
|
||||
} catch {
|
||||
return true
|
||||
}
|
||||
names.sort()
|
||||
/** @type {{ file: string, extId: string, scripts: string[], dependsOn: string[], beforeIds: string[], conflictsWith: string[], signaturePointer?: string }[]} */
|
||||
const collected = []
|
||||
for (const name of names) {
|
||||
if (!name.endsWith('.json')) continue
|
||||
const p = `/etc/bare-os/kernel.ext.d/${name}`
|
||||
try {
|
||||
const buf = await drive.get(p)
|
||||
if (!buf) continue
|
||||
const pol = JSON.parse(ctx.b4a.toString(buf))
|
||||
if (!pol || typeof pol !== 'object') continue
|
||||
const extId =
|
||||
pol.id != null
|
||||
? String(pol.id).trim()
|
||||
: name.replace(/\.json$/i, '')
|
||||
if (deny.has(extId)) {
|
||||
bootStructuredLog(
|
||||
ctx,
|
||||
'error',
|
||||
'kernelExt.denyId',
|
||||
`[kernel.ext.d] denied by policy id: ${extId}`
|
||||
)
|
||||
await appendKernelExtAuditNdjson(ctx, {
|
||||
event: 'deny_id',
|
||||
extId,
|
||||
dropin: name
|
||||
})
|
||||
continue
|
||||
}
|
||||
const scripts = pol.scripts
|
||||
if (!Array.isArray(scripts)) continue
|
||||
/** @type {string[]} */
|
||||
let dependsOn = Array.isArray(pol.dependsOn)
|
||||
? pol.dependsOn.map((d) => String(d).trim()).filter(Boolean).slice(0, 16)
|
||||
: []
|
||||
if (Array.isArray(pol.requires)) {
|
||||
dependsOn = dependsOn.concat(
|
||||
pol.requires.map((d) => String(d).trim()).filter(Boolean).slice(0, 16)
|
||||
)
|
||||
}
|
||||
if (Array.isArray(pol.after)) {
|
||||
dependsOn = dependsOn.concat(
|
||||
pol.after.map((d) => String(d).trim()).filter(Boolean).slice(0, 16)
|
||||
)
|
||||
}
|
||||
const beforeIds = Array.isArray(pol.before)
|
||||
? pol.before.map((d) => String(d).trim()).filter(Boolean).slice(0, 16)
|
||||
: []
|
||||
const sig =
|
||||
typeof pol.signaturePointer === 'string'
|
||||
? pol.signaturePointer.trim().slice(0, 512)
|
||||
: undefined
|
||||
const minExtCtx =
|
||||
typeof pol.minCtxApiVersion === 'string'
|
||||
? pol.minCtxApiVersion.trim()
|
||||
: ''
|
||||
if (minExtCtx) {
|
||||
const haveS =
|
||||
typeof ctx.bareOsCtxApiVersion === 'string'
|
||||
? ctx.bareOsCtxApiVersion.trim()
|
||||
: String(ctx.bareOsCtxApiVersion || '').trim()
|
||||
if (!haveS || !semverGte(haveS, minExtCtx)) {
|
||||
bootStructuredLog(
|
||||
ctx,
|
||||
'error',
|
||||
'kernelExt.minCtxApiVersion',
|
||||
`[kernel.ext.d] ${name}: minCtxApiVersion ${minExtCtx} not satisfied (have ${haveS || 'none'})`
|
||||
)
|
||||
if (strictPol) return false
|
||||
continue
|
||||
}
|
||||
}
|
||||
const conflictsWith = Array.isArray(pol.conflictsWith)
|
||||
? pol.conflictsWith.map((d) => String(d).trim()).filter(Boolean).slice(0, 16)
|
||||
: []
|
||||
collected.push({
|
||||
file: name,
|
||||
extId,
|
||||
scripts: scripts.map((s) => String(s).trim()).filter(Boolean),
|
||||
dependsOn,
|
||||
beforeIds,
|
||||
conflictsWith,
|
||||
signaturePointer: sig || undefined
|
||||
})
|
||||
} catch (e) {
|
||||
bootStructuredLog(
|
||||
ctx,
|
||||
'error',
|
||||
'kernelExt.dropinParse',
|
||||
`kernel.ext.d/${name}: ` + ((e && e.message) || String(e))
|
||||
)
|
||||
}
|
||||
}
|
||||
const idToCollected = new Map(collected.map((e) => [e.extId, e]))
|
||||
for (const e of collected) {
|
||||
for (const bid of e.beforeIds) {
|
||||
const target = idToCollected.get(bid)
|
||||
if (target && target.extId !== e.extId) {
|
||||
const next = new Set(target.dependsOn)
|
||||
next.add(e.extId)
|
||||
target.dependsOn = [...next].slice(0, 24)
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const e of collected) {
|
||||
e.dependsOn = [...new Set(e.dependsOn)]
|
||||
}
|
||||
const idSet = new Set(collected.map((e) => e.extId))
|
||||
for (const e of collected) {
|
||||
for (const c of e.conflictsWith) {
|
||||
if (idSet.has(c)) {
|
||||
bootStructuredLog(
|
||||
ctx,
|
||||
'error',
|
||||
'kernelExt.conflict',
|
||||
`[kernel.ext.d] conflict: extension "${e.extId}" conflictsWith "${c}"`
|
||||
)
|
||||
if (strictPol) {
|
||||
await appendKernelExtAuditNdjson(ctx, {
|
||||
event: 'conflict',
|
||||
extId: e.extId,
|
||||
conflictsWith: c
|
||||
})
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (maxDepthCap > 0) {
|
||||
const d = kernelExtDependencyDepth(collected)
|
||||
if (d > maxDepthCap) {
|
||||
bootStructuredLog(
|
||||
ctx,
|
||||
'error',
|
||||
'kernelExt.maxDepth',
|
||||
`[kernel.ext.d] maxKernelExtensionDepth exceeded (${d} > ${maxDepthCap})`
|
||||
)
|
||||
if (strictPol) return false
|
||||
}
|
||||
}
|
||||
const topo = topologicalOrderKernelExtEntries(collected)
|
||||
/** @type {typeof collected} */
|
||||
let ordered
|
||||
if (!topo.ok) {
|
||||
bootStructuredLog(
|
||||
ctx,
|
||||
'error',
|
||||
'kernelExt.cycle',
|
||||
'[kernel.ext.d] dependency cycle in extension drop-ins; ext ids: ' +
|
||||
topo.cycleExtIds.join(', ')
|
||||
)
|
||||
if (strictPol) {
|
||||
await appendKernelExtAuditNdjson(ctx, {
|
||||
event: 'dependency_cycle',
|
||||
cycleExtIds: [...topo.cycleExtIds].sort()
|
||||
})
|
||||
return false
|
||||
}
|
||||
ordered = [...collected].sort((a, b) => a.file.localeCompare(b.file))
|
||||
} else {
|
||||
ordered = topo.ordered
|
||||
}
|
||||
const dry = bootDryRun(ctx)
|
||||
const traceOn =
|
||||
strictPol &&
|
||||
(ctx.env?.BARE_OS_BOOT_EXT_RESOLUTION_TRACE === '1' ||
|
||||
ctx.env?.BARE_OS_BOOT_EXT_RESOLUTION_TRACE === 'true')
|
||||
const extResolutionFailure = strictPol && !topo.ok
|
||||
if (
|
||||
ctx.vfs &&
|
||||
typeof ctx.vfs.writeFile === 'function' &&
|
||||
ctx.b4a &&
|
||||
(extResolutionFailure || traceOn)
|
||||
) {
|
||||
try {
|
||||
const cycleSorted = topo.ok
|
||||
? []
|
||||
: [...topo.cycleExtIds].sort((a, b) => a.localeCompare(b))
|
||||
const body =
|
||||
JSON.stringify({
|
||||
schema: 2,
|
||||
atMs: Date.now(),
|
||||
sessionId: String((ctx.env && ctx.env.BARE_OS_SESSION_ID) || ''),
|
||||
bootPolicyStrict: !!strictPol,
|
||||
failure: extResolutionFailure
|
||||
? {
|
||||
kind: 'dependency_cycle',
|
||||
cycleExtIds: cycleSorted,
|
||||
provenance: 'kernel.ext.d topological sort (init-main)'
|
||||
}
|
||||
: null,
|
||||
ordered: ordered.map((e) => ({
|
||||
extId: e.extId,
|
||||
file: e.file,
|
||||
dependsOn: e.dependsOn
|
||||
})),
|
||||
cycleExtIds: topo.ok ? [] : cycleSorted
|
||||
}) + '\n'
|
||||
await ctx.vfs.writeFile(
|
||||
'/run/bare-os/kernel-ext-resolution.json',
|
||||
ctx.b4a.from(body)
|
||||
)
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
const extGraphOn =
|
||||
ctx.env?.BARE_OS_KERNEL_EXT_GRAPH === '1' ||
|
||||
ctx.env?.BARE_OS_KERNEL_EXT_GRAPH === 'true'
|
||||
if (extGraphOn && ctx.vfs && typeof ctx.vfs.writeFile === 'function' && ctx.b4a) {
|
||||
let bareModuleTraverse = 'unresolved'
|
||||
try {
|
||||
await import('bare-module-traverse')
|
||||
bareModuleTraverse = 'import_ok'
|
||||
} catch {
|
||||
bareModuleTraverse = 'import_failed'
|
||||
}
|
||||
try {
|
||||
const body =
|
||||
JSON.stringify({
|
||||
schema: 1,
|
||||
atMs: Date.now(),
|
||||
bareModuleTraverse,
|
||||
note: 'Static drop-in DAG; deeper static edges require host tooling on extension sources.',
|
||||
nodes: ordered.map((e) => ({
|
||||
extId: e.extId,
|
||||
file: e.file,
|
||||
scripts: e.scripts,
|
||||
dependsOn: e.dependsOn
|
||||
}))
|
||||
}) + '\n'
|
||||
await ctx.vfs.writeFile(
|
||||
'/run/bare-os/kernel-ext-graph.json',
|
||||
ctx.b4a.from(body)
|
||||
)
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
for (const ent of ordered) {
|
||||
for (const sp of ent.scripts) {
|
||||
const imgPath = String(sp).trim()
|
||||
if (!imgPath.startsWith('/lib/bare-os/extensions/')) {
|
||||
bootStructuredLog(
|
||||
ctx,
|
||||
'error',
|
||||
'kernelExt.rejectedPath',
|
||||
`[kernel.ext.d] rejected script path: ${imgPath}`
|
||||
)
|
||||
continue
|
||||
}
|
||||
if (incremental && loadedSet.has(imgPath)) {
|
||||
continue
|
||||
}
|
||||
if (dry) {
|
||||
bootStructuredLog(
|
||||
ctx,
|
||||
'error',
|
||||
'bootDryRunKernelExt',
|
||||
`[boot-dry-run] skip kernel.ext.d script: ${imgPath}`
|
||||
)
|
||||
continue
|
||||
}
|
||||
if (
|
||||
!(await verifyKernelExtSignerPinsForScript(
|
||||
ctx,
|
||||
ent,
|
||||
imgPath,
|
||||
extSignerPins,
|
||||
strictPol
|
||||
))
|
||||
) {
|
||||
if (strictPol) {
|
||||
if (typeof ctx.bareOsRequestBooterExit === 'function')
|
||||
ctx.bareOsRequestBooterExit(1)
|
||||
return false
|
||||
}
|
||||
continue
|
||||
}
|
||||
try {
|
||||
const extT0 = Date.now()
|
||||
await run(imgPath)
|
||||
const loadMs = Date.now() - extT0
|
||||
loadedSet.add(imgPath)
|
||||
if (Array.isArray(ranScripts)) ranScripts.push(imgPath)
|
||||
if (typeof ctx.bareOsRegisterKernelExtensionRecord === 'function') {
|
||||
ctx.bareOsRegisterKernelExtensionRecord({
|
||||
dropin: ent.file,
|
||||
script: imgPath,
|
||||
id: ent.extId,
|
||||
dependsOn: ent.dependsOn,
|
||||
signaturePointer: ent.signaturePointer,
|
||||
loadMs
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
bootStructuredLog(
|
||||
ctx,
|
||||
'error',
|
||||
'kernelExt.scriptThrown',
|
||||
`[kernel.ext.d] ${ent.file}: ` + ((e && e.message) || String(e))
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
+18
-1936
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user