feat(booter): harden guest vs user isolation and POSIX/P2P parity

- Guest-safe legacy personal-root migration with state file and env gates
- VFS guest deny for sensitive /.bare paths; warm-cache clear on identity switch
- Optional acct/<hash>/ personal layout, guest scrub, vault exclude alignment
- Shell session reset + errexit; fish history reload hook; replication metric
- Audit NDJSON for migration/scrub; structured booter logging paths
- Bump POSIX profile; getconf keys; syscall/socket contract + handbook/docs pass
- Booter tests (Bare vs Node split for identity-session); release-checklist audit hook
This commit is contained in:
Raven Scott
2026-04-05 03:16:33 -04:00
parent 6a4790591d
commit 4af98ef97b
45 changed files with 1139 additions and 450 deletions
+8 -3
View File
@@ -2471,6 +2471,7 @@ async function executeKernel(disk, store, swarm, initSource) {
if (warmFullInvOnRep && (repGrewSys || repGrewPer)) {
try {
this.bareOsInvalidateWarmReadCaches('replication:core-length')
bareOsKernelMetricInc('vfs.replication_warm_full_invalidate')
} catch {
/* ignore */
}
@@ -3741,7 +3742,7 @@ async function executeKernel(disk, store, swarm, initSource) {
vfsMountRef,
bootstrap,
onAfterActivate: async ({ labels }) => {
await persistBareOsMountsSnapshot(vfs, hdmsController)
await persistBareOsMountsSnapshot(vfs, hdmsController, { console })
for (const fn of hdmsLifecycleSubs) {
try {
await fn({ kind: 'activate', labels })
@@ -4983,7 +4984,9 @@ async function executeKernel(disk, store, swarm, initSource) {
} else {
throw new Error('bareOsSyscall: mount source is required')
}
await persistBareOsMountsSnapshot(vfs, hdmsController)
await persistBareOsMountsSnapshot(vfs, hdmsController, {
console: this.console
})
return { ok: true, label, source }
}
if (name === 'umount') {
@@ -5008,7 +5011,9 @@ async function executeKernel(disk, store, swarm, initSource) {
}
}
await hdmsController.remove(this, label)
await persistBareOsMountsSnapshot(vfs, hdmsController)
await persistBareOsMountsSnapshot(vfs, hdmsController, {
console: this.console
})
return { ok: true, label }
}
if (name === 'fsync' || name === 'fdatasync') {
+4
View File
@@ -137,6 +137,10 @@ export interface BareOsKernelContext {
bareOsInvalidateWarmReadCachesFromBareManifestJson?(
buf: Uint8Array | ArrayBuffer
): { ok: boolean }
/**
* Set by fish readline when the TTY editor is active; reloads **`~/.bare/repl_history_<USER>`** after **`login`/`logout`**.
*/
bareOsReloadFishHistoryForIdentity?(): Promise<void>
/** Optional Hyperbee2 index operator hint (P2P metadata; host/extension scoped). */
bareOsHyperbeeGuestHint?(): Record<string, unknown>
/**
@@ -10,8 +10,9 @@ import b4a from 'b4a'
/**
* @param {{ writeFile?: Function, mkdir?: Function } | null | undefined} vfs
* @param {import('./hdms-manager.js').HdmsController | null | undefined} hdms
* @param {{ console?: { warn?: (msg: string) => void } }} [opts] Guest session console; omit to stay silent on failure (tests / minimal VFS).
*/
export async function persistBareOsMountsSnapshot(vfs, hdms) {
export async function persistBareOsMountsSnapshot(vfs, hdms, opts = {}) {
if (!vfs || typeof vfs.writeFile !== 'function') return
if (!hdms?.active || !hdms.registry) return
@@ -58,8 +59,9 @@ export async function persistBareOsMountsSnapshot(vfs, hdms) {
}
if (!personalOk && !systemOk) {
console.warn(
const msg =
'[bare-os] mounts snapshot persist failed: could not write /.bare/os/mounts_last.json or /etc/bare-os/mounts.json'
)
const w = opts.console && typeof opts.console.warn === 'function'
if (w) opts.console.warn(msg)
}
}
@@ -29,7 +29,7 @@ export function buildBareOsSyscallsProcJson(p) {
opsDetail: [...BARE_OS_SYSCALL_OPS_DETAIL_FULL],
posixXsh: {
schema: 2,
note: 'POSIX.1 XSH-style names: logical names in opsDetail; socket family are ctx.bareOsSyscall ops returning ENOSYS-shaped results (not kernel socket FDs) unless BARE_OS_POSIX_SOCKET_FD_BRIDGE. readv/writev are partial posix-pipe facades. getsockopt/setsockopt: ENOSYS without bridge fd; with bridge, partial SO_KEEPALIVE/TCP_NODELAY on logical fds. nanosleep-shaped delay via ctx.bareOsSyscall("nanosleep"). SOCK_DGRAM bridge supports passive bind(2), optional connect(2) after bind, send/sendmsg/recv/recvfrom/recvmsg with bounded queue (BARE_OS_POSIX_DGRAM_RECVQ_MAX); bound-without-connect send requires port+host syscall args. sendmsg/recvmsg: non-empty ancillary control is ENOTSUP (schema 3); BARE_OS_POSIX_SOCKET_SCM_RIGHTS reserved for future bare-ipc logical-fd pass-through.',
note: 'POSIX.1 XSH-style names: logical names in opsDetail; socket family are ctx.bareOsSyscall ops returning ENOSYS-shaped results (not kernel socket FDs) unless BARE_OS_POSIX_SOCKET_FD_BRIDGE. readv/writev are partial posix-pipe facades. getsockopt/setsockopt: ENOSYS without bridge fd; with bridge, partial SO_KEEPALIVE/TCP_NODELAY on logical fds. nanosleep-shaped delay via ctx.bareOsSyscall("nanosleep"). SOCK_DGRAM bridge supports passive bind(2), optional connect(2) after bind, send/sendmsg/recv/recvfrom/recvmsg with bounded queue (BARE_OS_POSIX_DGRAM_RECVQ_MAX); bound-without-connect send requires port+host syscall args. sendmsg/recvmsg: non-empty ancillary control is ENOTSUP (schema 3) unless BARE_OS_POSIX_SOCKET_SCM_RIGHTS=1, then only SCM_RIGHTS-shaped `{ fds: number[] }` logical fd dup is accepted (see bare-os-socket-scm-rights.js and syscall-socket-contract.md).',
namesCsv: bareOsPosixXshOpsCsv()
},
socketMsgSurface: {
@@ -827,6 +827,28 @@ export async function createFishReadLine(ctx, { stdin, stdout, writeScreen }) {
}
})
ctx.bareOsReloadFishHistoryForIdentity = async () => {
const hp = replHistoryDrivePath(ctx)
history = []
try {
const buf =
personalDrive && typeof personalDrive.get === 'function'
? await personalDrive.get(hp)
: null
if (buf && b4a) {
const text = b4a.toString(buf)
history = dedupeConsecutiveHistory(parseHistoryFile(text))
}
} catch {
history = []
}
historyIndex = -1
historySearchActive = false
historySearchQuery = ''
historySearchResults = []
historySearchIndex = -1
}
/**
* @param {string} prompt
* @returns {Promise<string | null>}
+337 -7
View File
@@ -16,6 +16,9 @@ import {
sealBytes,
vaultKeyFromSecret
} from './identity-account.js'
import { bareOsResetShellIdentityState } from './shell.js'
const LEGACY_ROOT_MIGRATION_STATE = '/.bare-os/migration/legacy-root-v1.json'
/**
* Mirror vault NDJSON checkpoints into the tamper-evident host audit chain when the booter exposes it.
@@ -39,6 +42,72 @@ function mirrorVaultCheckpointToAuditChain(ctx, row) {
}
}
/**
* @param {Record<string, unknown>} ctx
* @param {Record<string, unknown>} row
*/
function mirrorMigrationAudit(ctx, row) {
if (typeof ctx.bareOsAuditLogAppendBatch !== 'function') return
try {
ctx.bareOsAuditLogAppendBatch([
{
type: 'identity.personal_root_migration',
schema: 1,
...row,
atMs: Date.now()
}
])
} catch {
/* optional */
}
}
/**
* @param {Record<string, unknown>} ctx
* @param {Record<string, unknown>} row
*/
function mirrorGuestScrubAudit(ctx, row) {
if (typeof ctx.bareOsAuditLogAppendBatch !== 'function') return
try {
ctx.bareOsAuditLogAppendBatch([
{
type: 'identity.guest_scrub',
schema: 1,
...row,
atMs: Date.now()
}
])
} catch {
/* optional */
}
}
/** Keep VFS identity and warm caches aligned with ctx.identity (defense in depth). */
function syncVfsIdentitySession(ctx) {
const st = ctx.identity?.state === 'unlocked' ? 'unlocked' : 'guest'
try {
if (ctx.vfs && typeof ctx.vfs === 'object') {
ctx.vfs.bareOsIdentitySession = st
}
} catch {
/* ignore */
}
try {
if (typeof ctx.vfs?.bareOsClearWarmReadCaches === 'function') {
ctx.vfs.bareOsClearWarmReadCaches()
}
} catch {
/* ignore */
}
try {
if (typeof ctx.bareOsInvalidateWarmReadCaches === 'function') {
ctx.bareOsInvalidateWarmReadCaches('identity-switch')
}
} catch {
/* ignore */
}
}
export const GUEST_USER = 'guest'
export const GUEST_HOME = '/home/guest'
const GUEST_UID = '65534'
@@ -58,6 +127,7 @@ function uidFromPublicKey(pk) {
* @param {Record<string, unknown>} ctx
*/
export async function applyGuestEnv(ctx) {
bareOsResetShellIdentityState(ctx)
const env = ctx.vfs.env
env.USER = GUEST_USER
env.LOGNAME = GUEST_USER
@@ -73,6 +143,17 @@ export async function applyGuestEnv(ctx) {
publicKey: null,
secretKey: null
}
try {
await scrubGuestWorkspaces(ctx)
} catch (e) {
ctx.console?.error?.('[bare-os] guest scrub: ' + (e?.message || e))
}
syncVfsIdentitySession(ctx)
try {
await ctx.bareOsReloadFishHistoryForIdentity?.()
} catch {
/* optional */
}
try {
await ctx.vfs.chdir(GUEST_HOME)
} catch {
@@ -96,6 +177,7 @@ export async function applyGuestEnv(ctx) {
* @param {Uint8Array} secretKey
*/
export async function applyUnlockedEnv(ctx, publicKey, secretKey) {
bareOsResetShellIdentityState(ctx)
const env = ctx.vfs.env
const name = displayNameFromPublicKey(publicKey)
const home = `/home/${name}`
@@ -115,6 +197,12 @@ export async function applyUnlockedEnv(ctx, publicKey, secretKey) {
publicKey: Uint8Array.from(publicKey),
secretKey: Uint8Array.from(secretKey)
}
syncVfsIdentitySession(ctx)
try {
await ctx.bareOsReloadFishHistoryForIdentity?.()
} catch {
/* optional */
}
try {
await ctx.vfs.chdir(home)
} catch {
@@ -172,15 +260,26 @@ export async function ensureGuestHome(ctx) {
await migrateLegacyPersonalHomeIfNeeded(ctx)
const drive = ctx.personalDrive
if (!drive || typeof drive.put !== 'function') return
const keep = '/.bare-os/home/guest/.keep_guest'
let keepPath = '/.bare-os/home/guest/.keep_guest'
try {
const existing = await drive.get(keep)
if (ctx.vfs && typeof ctx.vfs.route === 'function') {
const rr = ctx.vfs.route('/home/guest')
if (rr && rr.path && typeof rr.path === 'string' && !rr.virtualHomeDir) {
const ph = rr.path.replace(/\/+$/, '')
if (ph && ph !== '/') keepPath = `${ph}/.keep_guest`
}
}
} catch {
/* default path */
}
try {
const existing = await drive.get(keepPath)
if (existing) return
} catch {
/* missing */
}
try {
await drive.put(keep, b4a.from(''))
await drive.put(keepPath, b4a.from(''))
} catch {
/* ignore */
}
@@ -300,6 +399,8 @@ const VAULT_EXCLUDE_PREFIXES = [
'.bare',
'.bare-os/var/',
'.bare-os/var',
'.bare-os/migration/',
'.bare-os/migration',
'.vault/',
'.vault',
'bin/',
@@ -311,16 +412,38 @@ const VAULT_EXCLUDE_PREFIXES = [
/**
* @param {string} short path without leading slash
*/
function shouldVaultSkip(short) {
function shouldVaultSkip(short, ctx) {
const n = short.replace(/^\//, '')
for (const ex of VAULT_EXCLUDE_PREFIXES) {
if (n === ex || n.startsWith(ex + '/') || n.startsWith(ex + '\\'))
return true
}
if (n.includes('bare_repl_history') || n.includes('nsh_history')) return true
const want = currentVaultAcctPrefix(ctx)
if (want && n.startsWith('bare-os/acct/') && !n.startsWith(want)) return true
return false
}
/**
* When `BARE_OS_PERSONAL_ACCT_PREFIX` is on, only vault files under the active account subtree.
* @param {Record<string, unknown> | null | undefined} ctx
*/
function currentVaultAcctPrefix(ctx) {
const e = ctx?.vfs?.env
if (!e) return ''
if (e.BARE_OS_PERSONAL_ACCT_PREFIX !== '1' && e.BARE_OS_PERSONAL_ACCT_PREFIX !== 'true')
return ''
const home = String(e.HOME || '')
const seg = home.startsWith('/home/')
? home.slice('/home/'.length).split('/')[0]
: ''
if (!seg) return 'bare-os/acct/_nosession/'
if (seg === 'guest') return 'bare-os/acct/_guest/'
const uid = String(e.UID || '').trim()
if (uid && uid !== '65534') return `bare-os/acct/u${uid}/`
return `bare-os/acct/h${seg}/`
}
/**
* Top-level personal-drive names we do not lift from `/` into `/.bare-os/home/<seg>/`
* during legacy migration (machine metadata, new layout root, old guest marker).
@@ -332,6 +455,31 @@ function legacyRootMigrateSkip(name) {
return false
}
/**
* Physical personal-drive path for `/home/<seg>` (matches VFS routing, including acct prefix).
* @param {Record<string, unknown>} ctx
* @param {string} seg
*/
function resolvePersonalHomeDrivePrefix(ctx, seg) {
try {
if (ctx.vfs && typeof ctx.vfs.route === 'function') {
const rr = ctx.vfs.route(`/home/${seg}`)
if (
rr &&
rr.path &&
typeof rr.path === 'string' &&
!rr.virtualHomeDir &&
rr.drive === ctx.personalDrive
) {
return rr.path.replace(/\/+$/, '') || rr.path
}
}
} catch {
/* fall through */
}
return `/.bare-os/home/${seg}`
}
/**
* @param {import('hyperdrive').default} drive
* @param {string} prefix absolute e.g. /.bare-os/home/guest
@@ -387,28 +535,199 @@ async function moveDriveSubtree(drive, fromAbs, toAbs) {
}
}
/**
* @param {import('hyperdrive').default} drive
* @param {string} absPrefix
*/
async function deleteDriveSubtree(drive, absPrefix) {
if (!drive || typeof absPrefix !== 'string' || !absPrefix.startsWith('/')) return
if (absPrefix !== '/.bare-os' && !absPrefix.startsWith('/.bare-os/')) return
let ent
try {
ent = await drive.entry(absPrefix, { follow: false })
} catch {
return
}
if (!ent?.value) return
const v = ent.value
if (v.blob || v.linkname != null) {
try {
await drive.del(absPrefix)
} catch {
/* ignore */
}
return
}
const names = await driveReaddirNames(drive, absPrefix)
for (const n of names) {
const child =
absPrefix === '/' ? `/${n}` : `${absPrefix.replace(/\/+$/, '')}/${n}`
await deleteDriveSubtree(drive, child)
}
try {
await drive.del(absPrefix)
} catch {
/* ignore */
}
}
/**
* @param {import('hyperdrive').default} drive
*/
async function personalDriveHasAccount(drive) {
try {
const b = await drive.get(ACCOUNT_PATH)
return !!(b && b4a.from(b).length > 0)
} catch {
return false
}
}
/**
* @param {import('hyperdrive').default} drive
*/
async function readLegacyMigrationState(drive) {
try {
const b = await drive.get(LEGACY_ROOT_MIGRATION_STATE)
if (!b) return { schemaVersion: 1, segments: {} }
const j = JSON.parse(b4a.toString(b))
if (j && typeof j === 'object' && j.segments && typeof j.segments === 'object')
return j
return { schemaVersion: 1, segments: {} }
} catch {
return { schemaVersion: 1, segments: {} }
}
}
/**
* @param {import('hyperdrive').default} drive
* @param {Record<string, unknown>} state
*/
async function writeLegacyMigrationState(drive, state) {
try {
await drive.put(
LEGACY_ROOT_MIGRATION_STATE,
b4a.from(JSON.stringify(state, null, 0) + '\n')
)
} catch {
/* ignore */
}
}
/**
* Optional kiosk-style wipe of guest `/.bare-os/tmp/guest` and selected caches under guest `$HOME`.
* @param {Record<string, unknown>} ctx
*/
async function scrubGuestWorkspaces(ctx) {
const e = ctx.env && typeof ctx.env === 'object' ? ctx.env : ctx.vfs?.env
if (
!e ||
(e.BARE_OS_GUEST_SCRUB !== '1' && e.BARE_OS_GUEST_SCRUB !== 'true')
) {
return
}
const drive = ctx.personalDrive
const vfs = ctx.vfs
if (!drive || !vfs || typeof vfs.route !== 'function') return
/** @type {string[]} */
const scrubbed = []
try {
const tr = vfs.route('/tmp')
if (
tr &&
tr.drive === drive &&
typeof tr.path === 'string' &&
tr.path.startsWith('/.bare-os/')
) {
await deleteDriveSubtree(drive, tr.path)
scrubbed.push(tr.path)
}
} catch {
/* ignore */
}
try {
const hr = vfs.route('/home/guest')
if (hr && hr.drive === drive && typeof hr.path === 'string') {
const base = hr.path.replace(/\/+$/, '')
for (const sub of ['.cache', 'tmp']) {
const p = `${base}/${sub}`
await deleteDriveSubtree(drive, p)
scrubbed.push(p)
}
}
} catch {
/* ignore */
}
mirrorGuestScrubAudit(ctx, { paths: scrubbed })
}
/**
* One-time best-effort: flat personal-root files from older booters → `/.bare-os/home/<seg>/`.
* Never moves ambiguous `/` content into **guest** when an account blob exists (unless
* `BARE_OS_PERSONAL_ROOT_MIGRATE=guest`). State: `/.bare-os/migration/legacy-root-v1.json`.
* @param {Record<string, unknown>} ctx
*/
export async function migrateLegacyPersonalHomeIfNeeded(ctx) {
const drive = ctx.personalDrive
const vfs = ctx.vfs
if (!drive || typeof drive.put !== 'function' || !vfs?.env) return
const migrateMode = String(vfs.env.BARE_OS_PERSONAL_ROOT_MIGRATE || '')
.trim()
.toLowerCase()
if (migrateMode === 'skip') return
const home = vfs.env.HOME
if (typeof home !== 'string' || !home.startsWith('/home/')) return
const seg = home.slice('/home/'.length).split('/')[0]
if (!seg) return
const prefix = `/.bare-os/home/${seg}`
if (!(await personalHomePrefixIsEmpty(drive, prefix))) return
const prefix = resolvePersonalHomeDrivePrefix(ctx, seg)
const state = await readLegacyMigrationState(drive)
if (!state.segments || typeof state.segments !== 'object') state.segments = {}
const segState = /** @type {{ completed?: boolean, skipped?: string }} */ (
state.segments[seg] || {}
)
if (segState.completed || segState.skipped === 'account_present_guest') return
const hasAccount = await personalDriveHasAccount(drive)
if (seg === 'guest' && hasAccount && migrateMode !== 'guest') {
state.segments[seg] = {
skipped: 'account_present_guest',
atMs: Date.now()
}
await writeLegacyMigrationState(drive, state)
mirrorMigrationAudit(ctx, {
segment: seg,
action: 'skip_guest_root_migration',
reason: 'account_blob_present'
})
return
}
if (migrateMode === 'unlocked-only' && seg === 'guest') {
state.segments[seg] = {
skipped: 'unlocked_only_policy',
atMs: Date.now()
}
await writeLegacyMigrationState(drive, state)
return
}
if (!(await personalHomePrefixIsEmpty(drive, prefix))) {
state.segments[seg] = { completed: true, atMs: Date.now(), note: 'home_nonempty' }
await writeLegacyMigrationState(drive, state)
return
}
const rootNames = await driveReaddirNames(drive, '/')
let moved = 0
for (const name of rootNames) {
if (legacyRootMigrateSkip(name)) continue
const from = `/${name}`
const to = `${prefix}/${name}`
try {
await moveDriveSubtree(drive, from, to)
moved++
} catch {
/* best-effort */
}
@@ -419,6 +738,17 @@ export async function migrateLegacyPersonalHomeIfNeeded(ctx) {
} catch {
/* ignore */
}
state.segments[seg] = {
completed: true,
atMs: Date.now(),
movedTopLevel: moved
}
await writeLegacyMigrationState(drive, state)
mirrorMigrationAudit(ctx, {
segment: seg,
action: 'legacy_root_migrated',
movedTopLevel: moved
})
}
/**
@@ -458,7 +788,7 @@ export async function saveVaultToDrive(ctx) {
for (const name of names) {
const p = dir === '/' ? `/${name}` : `${dir}/${name}`
const short = p.startsWith('/') ? p.slice(1) : p
if (shouldVaultSkip(short)) continue
if (shouldVaultSkip(short, ctx)) continue
const entry = await drive.entry(p, { follow: true })
const hasBlob = !!(entry && entry.value && entry.value.blob)
+28 -1
View File
@@ -1408,6 +1408,18 @@ async function execParsedPipeline(ctx, pipeline) {
env.BARE_OS_SHELL_NOGLOB = '1'
} else if (args.length === 1 && args[0] === '+f') {
delete env.BARE_OS_SHELL_NOGLOB
} else if (
args.length === 2 &&
args[0] === '-o' &&
args[1] === 'errexit'
) {
env.BARE_OS_SHELL_ERREXIT = '1'
} else if (
args.length === 2 &&
args[0] === '+o' &&
args[1] === 'errexit'
) {
delete env.BARE_OS_SHELL_ERREXIT
} else if (args.length === 1 && args[0] === '-e') {
env.BARE_OS_SHELL_ERREXIT = '1'
} else if (args.length === 1 && args[0] === '+e') {
@@ -1415,7 +1427,7 @@ async function execParsedPipeline(ctx, pipeline) {
} else {
origErr.call(
ctx.console,
'set: unsupported arguments (only -f / +f / -e / +e)'
'set: unsupported arguments (only -f / +f / -e / +e / -o errexit / +o errexit)'
)
ctx.exitCode = 1
}
@@ -2691,3 +2703,18 @@ async function execShellLineInner(ctx, rawTrimmed) {
syncBareOsExitStatusEnv(ctx)
return 'ok'
}
/**
* Clear simulated background jobs on guest ↔ unlocked transitions (POSIX session model).
* @param {Record<string, unknown>} ctx
*/
export function bareOsResetShellIdentityState(ctx) {
if (
ctx.shellBackgroundJobs &&
typeof ctx.shellBackgroundJobs === 'object' &&
Array.isArray(ctx.shellBackgroundJobs.list)
) {
ctx.shellBackgroundJobs.list.length = 0
ctx.shellBackgroundJobs.nextId = 1
}
}
+6 -2
View File
@@ -4,6 +4,7 @@ import {
PROTOCOL_NAME,
PROTOCOL_APP_CHANNEL_NAME
} from 'bare-os-protocol/constants.js'
import { bareOsHostBooterWarn } from './bare-os-host-booter-log.js'
/**
* Host-side booter log for swarm disk (stderr JSON when BARE_OS_BOOT_TRACE=json|ndjson; else stderr or console.warn).
@@ -31,8 +32,11 @@ function emitSwarmDiskHostLog(level, message, detail = null) {
)
}
if (level === 'warn') {
if (typeof console.warn === 'function') console.warn(message)
else if (err && typeof err.write === 'function') err.write(`${message}\n`)
bareOsHostBooterWarn(
'swarm_disk',
message,
detail ? JSON.stringify(detail) : ''
)
return
}
const line = `[bare-os-booter][swarm-disk] ${message}`
+131 -3
View File
@@ -935,6 +935,33 @@ export function createVfs(
const HOME = () => env.HOME || '/home/guest'
let cwd = env.PWD || HOME()
/** @type {{ session: 'guest' | 'unlocked' }} */
const bareOsIdentityVfsRef = { session: 'guest' }
function usePersonalAcctPrefix() {
return (
env &&
(env.BARE_OS_PERSONAL_ACCT_PREFIX === '1' ||
env.BARE_OS_PERSONAL_ACCT_PREFIX === 'true')
)
}
function personalAcctLayoutSubpath() {
if (!usePersonalAcctPrefix()) return ''
const seg = activeHomeBasename()
if (!seg) return 'acct/_nosession'
if (seg === 'guest') return 'acct/_guest'
const uid = String(env.UID || '').trim()
if (uid && uid !== '65534') return `acct/u${uid}`
return `acct/h${seg}`
}
/** `/.bare-os` or `/.bare-os/acct/…` when {@link usePersonalAcctPrefix}. */
function personalLayoutRootAbs() {
const rel = personalAcctLayoutSubpath()
return rel ? `/.bare-os/${rel}` : '/.bare-os'
}
function normalizeHome() {
const h = HOME()
return h.length > 1 && h.endsWith('/') ? h.slice(0, -1) : h
@@ -951,19 +978,57 @@ export function createVfs(
/** Session home tree on the personal drive (isolates guest vs unlocked users). */
function personalHomeStorageRoot() {
const seg = activeHomeBasename()
return seg ? `/.bare-os/home/${seg}` : '/.bare-os/home/_nosession'
const homeRel = seg ? `home/${seg}` : 'home/_nosession'
return `${personalLayoutRootAbs()}/${homeRel}`
}
/** Personal-drive backing for logical `/var/log/…` (per session segment). */
function varLogStorageRoot() {
const seg = activeHomeBasename()
return seg ? `/.bare-os/var/log/${seg}` : '/.bare-os/var/log/_nosession'
const sub = seg ? `var/log/${seg}` : 'var/log/_nosession'
return `${personalLayoutRootAbs()}/${sub}`
}
/** Session-isolated writable `/tmp` on the personal drive. */
function tmpStorageRoot() {
const seg = activeHomeBasename()
return seg ? `/.bare-os/tmp/${seg}` : '/.bare-os/tmp/_nosession'
const sub = seg ? `tmp/${seg}` : 'tmp/_nosession'
return `${personalLayoutRootAbs()}/${sub}`
}
/**
* Guest cannot read sealed identity material on the personal drive (override with BARE_OS_GUEST_BARE_READ_ALL=1).
* @param {string} personalPath absolute on personal Hyperdrive
*/
function bareOsGuestSensitivePersonalDenied(personalPath) {
if (
env &&
(env.BARE_OS_GUEST_BARE_READ_ALL === '1' ||
env.BARE_OS_GUEST_BARE_READ_ALL === 'true')
) {
return false
}
if (bareOsIdentityVfsRef.session === 'unlocked') return false
const norm = String(personalPath || '').replace(/\/+$/, '') || '/'
if (norm === '/.bare/account' || norm.startsWith('/.bare/account/'))
return true
if (norm === '/.bare/vault' || norm.startsWith('/.bare/vault/')) return true
if (norm === '/.bare/vault-rotation-audit.ndjson') return true
return false
}
/**
* @param {unknown} drive
* @param {string} personalPath
* @param {string} op
* @param {string} logicalAbs
*/
function assertGuestSensitivePersonalOp(drive, personalPath, op, logicalAbs) {
if (drive !== personalDrive) return
if (!bareOsGuestSensitivePersonalDenied(personalPath)) return
throw new Error(
`EACCES: ${op} denied for guest on sensitive /.bare path: ` + logicalAbs
)
}
const ENV_SECRET_HINT = new RegExp(
@@ -3682,6 +3747,17 @@ export function createVfs(
return lstatVirtualPseudo(abs, r)
}
const { drive, path: p } = r
if (
drive === personalDrive &&
bareOsGuestSensitivePersonalDenied(p) &&
bareOsIdentityVfsRef.session !== 'unlocked' &&
!(
env?.BARE_OS_GUEST_BARE_READ_ALL === '1' ||
env?.BARE_OS_GUEST_BARE_READ_ALL === 'true'
)
) {
return null
}
const personal = isPersonalRoute(personalDrive, r)
if (isHyperdriveRootPath(p)) {
return { ...synthesizeStat(abs, personal, env, 'directory'), path: abs }
@@ -4392,10 +4468,31 @@ export function createVfs(
}
}
const names = []
assertGuestSensitivePersonalOp(drive, folder, 'readdir', abs)
const stream = drive.readdir(folder)
for await (const name of stream) {
names.push(name)
}
if (
drive === personalDrive &&
bareOsIdentityVfsRef.session !== 'unlocked' &&
!(
env?.BARE_OS_GUEST_BARE_READ_ALL === '1' ||
env?.BARE_OS_GUEST_BARE_READ_ALL === 'true'
) &&
bareAbsNorm === '/.bare'
) {
const hide = new Set(['account', 'vault'])
for (let i = names.length - 1; i >= 0; i--) {
const n = names[i]
if (
hide.has(n) ||
(typeof n === 'string' && n.startsWith('vault-rotation-audit'))
) {
names.splice(i, 1)
}
}
}
if (activeSeg && abs === '/' && !names.includes('home')) {
names.push('home')
}
@@ -4477,6 +4574,7 @@ export function createVfs(
if (isHyperdriveRootPath(p)) {
throw new Error('Cannot unlink directory root')
}
assertGuestSensitivePersonalOp(drive, p, 'unlink', abs)
return drive.del(p)
}
if (drive !== personalDrive) {
@@ -4485,6 +4583,7 @@ export function createVfs(
if (isHyperdriveRootPath(p)) {
throw new Error('Cannot unlink directory root')
}
assertGuestSensitivePersonalOp(drive, p, 'unlink', abs)
return drive.del(p)
}
@@ -4610,6 +4709,7 @@ export function createVfs(
if (await bareOsVfsAclDeniesDriveOp(env, drive, p, 'write')) {
throw new Error('EACCES: ACL enforces deny write: ' + abs)
}
assertGuestSensitivePersonalOp(drive, p, 'write', abs)
const existing = await entryOn(drive, p, { follow: false })
const hadBlob = !!existing?.value?.blob
if (hadBlob) await assertTraverseTo(abs, 'write')
@@ -4646,6 +4746,13 @@ export function createVfs(
env,
get bareOsIdentitySession() {
return bareOsIdentityVfsRef.session
},
set bareOsIdentitySession(v) {
bareOsIdentityVfsRef.session = v === 'unlocked' ? 'unlocked' : 'guest'
},
bareOsClearWarmReadCaches,
bareOsEvictLibBareBundlesFromManifest,
bareOsEvictWarmReadPrefixes,
@@ -4792,6 +4899,12 @@ export function createVfs(
!isHyperdriveRootPath(ur.path)
) {
try {
assertGuestSensitivePersonalOp(
ur.drive,
ur.path,
'read',
absFollowed
)
const ubuf = await ur.drive.get(ur.path, { follow: true })
if (ubuf) return ubuf
} catch {
@@ -4830,6 +4943,7 @@ export function createVfs(
if (await bareOsVfsAclDeniesDriveOp(env, drive, p, 'read')) {
throw new Error('EACCES: ACL enforces deny read: ' + absFollowed)
}
assertGuestSensitivePersonalOp(drive, p, 'read', absFollowed)
if (
warmReadCacheStats &&
drive === systemDrive &&
@@ -4974,6 +5088,7 @@ export function createVfs(
throw new Error('Cannot unlink directory root')
}
await assertUnlink(abs)
assertGuestSensitivePersonalOp(drive, p, 'unlink', abs)
return drive.del(p)
}
if (drive !== personalDrive) {
@@ -4983,6 +5098,7 @@ export function createVfs(
throw new Error('Cannot unlink directory root')
}
await assertUnlink(abs)
assertGuestSensitivePersonalOp(drive, p, 'unlink', abs)
return drive.del(p)
},
@@ -5016,6 +5132,17 @@ export function createVfs(
return true
const { drive, path: p } = r
if (isHyperdriveRootPath(p)) return true
if (
drive === personalDrive &&
bareOsGuestSensitivePersonalDenied(p) &&
bareOsIdentityVfsRef.session !== 'unlocked' &&
!(
env?.BARE_OS_GUEST_BARE_READ_ALL === '1' ||
env?.BARE_OS_GUEST_BARE_READ_ALL === 'true'
)
) {
return false
}
return drive.exists(p)
},
@@ -5061,6 +5188,7 @@ export function createVfs(
if (isHyperdriveRootPath(p)) {
throw new Error('chmod: invalid path')
}
assertGuestSensitivePersonalOp(drive, p, 'chmod', abs)
const st = await lstatFromAbs(abs)
if (!st) throw new Error('chmod: ' + userPath + ': No such file')
const { uid: euid } = parseUidGid(env)
+42
View File
@@ -3,6 +3,11 @@
*/
import test from 'brittle'
import b4a from 'b4a'
import path from 'path'
import { mkdirSync, rmSync } from 'fs'
import { fileURLToPath } from 'url'
import Hyperdrive from 'hyperdrive'
import Corestore from 'corestore'
import {
encodeAccount,
decodeAccount,
@@ -13,6 +18,20 @@ import {
openBytes,
vaultKeyFromSecret
} from './lib/identity-account.js'
import { createVfs } from './lib/vfs.js'
import { migrateLegacyPersonalHomeIfNeeded } from './lib/identity-session.js'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
function testCorestoreDir(name) {
const dir = path.join(
__dirname,
'.test-data',
name + '-' + Date.now() + '-' + Math.random().toString(36).slice(2)
)
mkdirSync(path.dirname(dir), { recursive: true })
return dir
}
test('identity account encode/decode roundtrip', async (t) => {
const pass = 'unit-test-passphrase'
@@ -51,3 +70,26 @@ test('vault sealBytes/openBytes rejects tampered ciphertext', async (t) => {
}
t.ok(threw)
})
test('legacy root migration skips guest home when account blob exists', async (t) => {
const dir = testCorestoreDir('migguest')
const store = new Corestore(dir)
const sys = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('pvmig'))
await sys.ready()
await personal.ready()
await personal.put('/legacy-root.txt', b4a.from('legacy'))
await personal.put('/.bare/account', b4a.from('acc'))
const env = { HOME: '/home/guest', PWD: '/home/guest', PATH: '/bin' }
const vfs = createVfs(sys, personal, env)
const ctx = {
personalDrive: personal,
vfs,
bareOsAuditLogAppendBatch: () => {}
}
await migrateLegacyPersonalHomeIfNeeded(ctx)
t.is(b4a.toString(await personal.get('/legacy-root.txt')), 'legacy')
t.is(await personal.get('/.bare-os/home/guest/legacy-root.txt'), null)
await store.close()
rmSync(dir, { recursive: true, force: true })
})
+53
View File
@@ -2263,6 +2263,59 @@ test('vfs isolates home and /var/log per HOME basename on personal drive', async
rmSync(dir, { recursive: true, force: true })
})
test('guest VFS denies sensitive /.bare paths; unlocked allows', async (t) => {
const dir = testCorestoreDir('vfsbarepol')
const store = new Corestore(dir)
const sys = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('pvpol'))
await sys.ready()
await personal.ready()
await personal.put('/.bare/account', b4a.from('acctblob'))
await personal.put('/.bare/vault/x', b4a.from('v'))
const envG = { HOME: '/home/guest', PWD: '/home/guest', PATH: '/bin' }
const vfsG = createVfs(sys, personal, envG)
vfsG.bareOsIdentitySession = 'guest'
t.is(await vfsG.exists('/.bare/account'), false)
await t.exception(async () => {
await vfsG.readFile('/.bare/account')
})
const envU = {
HOME: '/home/abc123deadbe',
PWD: '/home/abc123deadbe',
PATH: '/bin',
UID: '999',
USER: 'abc123deadbe'
}
const vfsU = createVfs(sys, personal, envU)
vfsU.bareOsIdentitySession = 'unlocked'
t.is(b4a.toString(await vfsU.readFile('/.bare/account')), 'acctblob')
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('BARE_OS_PERSONAL_ACCT_PREFIX nests guest tmp on personal drive', async (t) => {
const dir = testCorestoreDir('vfsacct')
const store = new Corestore(dir)
const sys = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('pvacct'))
await sys.ready()
await personal.ready()
const env = {
HOME: '/home/guest',
PWD: '/home/guest',
PATH: '/bin',
BARE_OS_PERSONAL_ACCT_PREFIX: '1'
}
const vfs = createVfs(sys, personal, env)
await vfs.writeFile('/tmp/acct.txt', b4a.from('ok'))
t.is(
b4a.toString(await personal.get('/.bare-os/acct/_guest/tmp/guest/acct.txt')),
'ok'
)
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('vfs /mnt lists HDMS mounts and allows writable put', async (t) => {
const dir = testCorestoreDir('vfsmnt')
const store = new Corestore(dir)
+9 -1
View File
@@ -94,7 +94,15 @@ const CONF = {
/** Operator hint for hyperblob-style dedup in host pipelines; guest VFS does not enable automatically. */
BARE_OS_VFS_HYPERBLOBS_DEDUP: '0',
/** This binary: fixed catalog. Use `getconf NAME /path` + `ctx.bareOsPathconf` for live pathconf. */
BARE_OS_GETCONF_SOURCE: 'static_catalog'
BARE_OS_GETCONF_SOURCE: 'static_catalog',
/** Guest session denies read/write/unlink/chmod on sealed `/.bare/account` and `/.bare/vault/**` unless BARE_OS_GUEST_BARE_READ_ALL=1. */
BARE_OS_GUEST_SENSITIVE_BARE_DENY: '1',
/** Optional `/.bare-os/acct/…` segment before `home/` and `tmp/` (BARE_OS_PERSONAL_ACCT_PREFIX). */
BARE_OS_PERSONAL_ACCT_PREFIX_FEATURE: '1',
/** Legacy flat-root → `/.bare-os/home/<seg>/` migration state file on the personal drive. */
BARE_OS_PERSONAL_ROOT_MIGRATION_STATE: '/.bare-os/migration/legacy-root-v1.json',
/** Replication hook counter: vfs.replication_warm_full_invalidate (metrics / kernelCounters). */
BARE_OS_REPLICATION_WARM_FULL_INVALIDATE_METRIC: 'vfs.replication_warm_full_invalidate'
}
async function run(ctx, argv) {
@@ -38,6 +38,24 @@ test('getconf _POSIX_SHARED_MEMORY_OBJECTS is 1', async (t) => {
t.is(lines[0], '1')
})
test('getconf BARE_OS_GUEST_SENSITIVE_BARE_DENY catalog entry', async (t) => {
const run = await loadBin('getconf')
const lines = []
const ctx = {
vfs: { env: {} },
console: {
log(s) {
lines.push(String(s))
},
error() {}
},
exitCode: 0,
b4a: await import('b4a')
}
await run(ctx, ['getconf', 'BARE_OS_GUEST_SENSITIVE_BARE_DENY'])
t.is(lines.join('\n').trim(), '1')
})
test('getconf _SC_PAGESIZE static table', async (t) => {
const run = await loadBin('getconf')
const lines = []
+1
View File
@@ -4,6 +4,7 @@ Cross-package **version alignment** (ctx API, feature-bits doc, lifecycle schema
## Documentation (rolling)
- **`BARE_OS_POSIX_PROFILE_VERSION` `1.0.12`** — Guest **`/.bare`** sensitive-path policy, optional **`BARE_OS_PERSONAL_ACCT_PREFIX`** personal-drive layout, safe legacy-root migration (**`BARE_OS_PERSONAL_ROOT_MIGRATE`**, **`/.bare-os/migration/legacy-root-v1.json`**), **`BARE_OS_GUEST_SCRUB`**, shell **`set -o errexit`**, **`vfs.replication_warm_full_invalidate`** metric, and related **`getconf`** catalog keys (see [`bare-os-posix-profile.js`](lib/bare-os-posix-profile.js), [`docs/reference/posix-compliance-matrix.json`](../../docs/reference/posix-compliance-matrix.json)).
- **`BARE_OS_POSIX_PROFILE_VERSION` `1.0.11`** — Adds **`nanosleep`**-shaped syscall, partial bridge **`getsockopt`/`setsockopt`** (**`SO_KEEPALIVE`**, **`TCP_NODELAY`**), Hyperswarm env caps, replication warm-cache invalidation env, and **`disk.os` `replication_operator_sketch` schema 4** notes (see [`bare-os-posix-profile.js`](lib/bare-os-posix-profile.js), [`docs/reference/posix-compliance-matrix.json`](../../docs/reference/posix-compliance-matrix.json)).
- **`BARE_OS_POSIX_PROFILE_VERSION` `1.0.10`** — Declared profile bumps for passive UDP **`bind`**, **`poll`** monotonic clock note, expanded **`sysconf`** / **`getconf`** surface, and normative **`socketMsgSurface` / ancillary `ENOTSUP`** text (see [`bare-os-posix-profile.js`](lib/bare-os-posix-profile.js), [`docs/reference/posix-compliance-matrix.json`](../../docs/reference/posix-compliance-matrix.json)).
- **Seed RPC** — [`lib/channel.js`](lib/channel.js) rejects unknown **`bare_os.*`** method short names against **`BARE_OS_SEED_RPC_METHOD_SHORT_NAME_SET`** (**`bare_os.rpc_unknown_method`**); boot policy / env may add **`denySeedRpcMethods`** denials on the booter path. See [`docs/architecture/KERNEL_CONTRACT.md`](../../docs/architecture/KERNEL_CONTRACT.md).
@@ -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.11'
export const BARE_OS_POSIX_PROFILE_VERSION = '1.0.12'
/** Short identifier for telemetry and `/proc` mirrors. */
export const BARE_OS_POSIX_PROFILE_ID = 'bare-os-posix-like'
+9 -1
View File
@@ -183,7 +183,15 @@ const CONF = {
/** Operator hint for hyperblob-style dedup in host pipelines; guest VFS does not enable automatically. */
BARE_OS_VFS_HYPERBLOBS_DEDUP: '0',
/** This binary: fixed catalog. Use `getconf NAME /path` + `ctx.bareOsPathconf` for live pathconf. */
BARE_OS_GETCONF_SOURCE: 'static_catalog'
BARE_OS_GETCONF_SOURCE: 'static_catalog',
/** Guest session denies read/write/unlink/chmod on sealed `/.bare/account` and `/.bare/vault/**` unless BARE_OS_GUEST_BARE_READ_ALL=1. */
BARE_OS_GUEST_SENSITIVE_BARE_DENY: '1',
/** Optional `/.bare-os/acct/…` segment before `home/` and `tmp/` (BARE_OS_PERSONAL_ACCT_PREFIX). */
BARE_OS_PERSONAL_ACCT_PREFIX_FEATURE: '1',
/** Legacy flat-root → `/.bare-os/home/<seg>/` migration state file on the personal drive. */
BARE_OS_PERSONAL_ROOT_MIGRATION_STATE: '/.bare-os/migration/legacy-root-v1.json',
/** Replication hook counter: vfs.replication_warm_full_invalidate (metrics / kernelCounters). */
BARE_OS_REPLICATION_WARM_FULL_INVALIDATE_METRIC: 'vfs.replication_warm_full_invalidate'
}
async function run(ctx, argv) {
@@ -7,54 +7,54 @@
"safetyCatch"
]
},
{
"path": "/lib/bare/bundles/b4a.js",
"keys": [
"b4a"
]
},
{
"path": "/lib/bare/bundles/hypercoreIdEncoding.js",
"keys": [
"hypercoreIdEncoding"
]
},
{
"path": "/lib/bare/bundles/b4a.js",
"keys": [
"b4a"
]
},
{
"path": "/lib/bare/bundles/compactEncoding.js",
"keys": [
"compactEncoding"
]
},
{
"path": "/lib/bare/bundles/protomux.js",
"keys": [
"protomux"
]
},
{
"path": "/lib/bare/bundles/bareUrl.js",
"keys": [
"bareUrl"
]
},
{
"path": "/lib/bare/bundles/barePath.js",
"keys": [
"barePath"
]
},
{
"path": "/lib/bare/bundles/bareEncoding.js",
"keys": [
"bareEncoding"
]
},
{
"path": "/lib/bare/bundles/protomux.js",
"keys": [
"protomux"
]
},
{
"path": "/lib/bare/bundles/bareEvents.js",
"keys": [
"bareEvents"
]
},
{
"path": "/lib/bare/bundles/barePath.js",
"keys": [
"barePath"
]
},
{
"path": "/lib/bare/bundles/bareAbort.js",
"keys": [
@@ -79,12 +79,6 @@
"bareAddonResolve"
]
},
{
"path": "/lib/bare/bundles/bareAppKit.js",
"keys": [
"bareAppKit"
]
},
{
"path": "/lib/bare/bundles/bareReadline.js",
"keys": [
@@ -103,24 +97,36 @@
"bareApk"
]
},
{
"path": "/lib/bare/bundles/bareAssert.js",
"keys": [
"bareAssert"
]
},
{
"path": "/lib/bare/bundles/bareAsyncHooks.js",
"keys": [
"bareAsyncHooks"
]
},
{
"path": "/lib/bare/bundles/bareAppKit.js",
"keys": [
"bareAppKit"
]
},
{
"path": "/lib/bare/bundles/bareAtomics.js",
"keys": [
"bareAtomics"
]
},
{
"path": "/lib/bare/bundles/bareAssert.js",
"keys": [
"bareAssert"
]
},
{
"path": "/lib/bare/bundles/fetch.js",
"keys": [
"fetch"
]
},
{
"path": "/lib/bare/bundles/bareBmp.js",
"keys": [
@@ -133,12 +139,6 @@
"bareBuffer"
]
},
{
"path": "/lib/bare/bundles/fetch.js",
"keys": [
"fetch"
]
},
{
"path": "/lib/bare/bundles/bareBundle.js",
"keys": [
@@ -170,9 +170,9 @@
]
},
{
"path": "/lib/bare/bundles/bareBundleId.js",
"path": "/lib/bare/bundles/bareDaemon.js",
"keys": [
"bareBundleId"
"bareDaemon"
]
},
{
@@ -181,12 +181,6 @@
"bareConsole"
]
},
{
"path": "/lib/bare/bundles/bareChannel.js",
"keys": [
"bareChannel"
]
},
{
"path": "/lib/bare/bundles/bareDebugLog.js",
"keys": [
@@ -194,15 +188,15 @@
]
},
{
"path": "/lib/bare/bundles/bareDaemon.js",
"path": "/lib/bare/bundles/bareBundleId.js",
"keys": [
"bareDaemon"
"bareBundleId"
]
},
{
"path": "/lib/bare/bundles/bareDelta.js",
"path": "/lib/bare/bundles/bareChannel.js",
"keys": [
"bareDelta"
"bareChannel"
]
},
{
@@ -211,24 +205,30 @@
"bareDiagnosticsChannel"
]
},
{
"path": "/lib/bare/bundles/bareDelta.js",
"keys": [
"bareDelta"
]
},
{
"path": "/lib/bare/bundles/bareDns.js",
"keys": [
"bareDns"
]
},
{
"path": "/lib/bare/bundles/bareEnv.js",
"keys": [
"bareEnv"
]
},
{
"path": "/lib/bare/bundles/bareExif.js",
"keys": [
"bareExif"
]
},
{
"path": "/lib/bare/bundles/bareEnv.js",
"keys": [
"bareEnv"
]
},
{
"path": "/lib/bare/bundles/bareDgram.js",
"keys": [
@@ -248,9 +248,9 @@
]
},
{
"path": "/lib/bare/bundles/bareFormData.js",
"path": "/lib/bare/bundles/bareFormat.js",
"keys": [
"bareFormData"
"bareFormat"
]
},
{
@@ -260,15 +260,9 @@
]
},
{
"path": "/lib/bare/bundles/bareFormat.js",
"path": "/lib/bare/bundles/bareFormData.js",
"keys": [
"bareFormat"
]
},
{
"path": "/lib/bare/bundles/bareFileLogger.js",
"keys": [
"bareFileLogger"
"bareFormData"
]
},
{
@@ -278,15 +272,15 @@
]
},
{
"path": "/lib/bare/bundles/bareHeif.js",
"path": "/lib/bare/bundles/bareFileLogger.js",
"keys": [
"bareHeif"
"bareFileLogger"
]
},
{
"path": "/lib/bare/bundles/bareHrtime.js",
"path": "/lib/bare/bundles/bareHeif.js",
"keys": [
"bareHrtime"
"bareHeif"
]
},
{
@@ -296,9 +290,9 @@
]
},
{
"path": "/lib/bare/bundles/bareFs.js",
"path": "/lib/bare/bundles/bareHrtime.js",
"keys": [
"bareFs"
"bareHrtime"
]
},
{
@@ -308,15 +302,15 @@
]
},
{
"path": "/lib/bare/bundles/bareIco.js",
"path": "/lib/bare/bundles/bareFs.js",
"keys": [
"bareIco"
"bareFs"
]
},
{
"path": "/lib/bare/bundles/bareHttp1.js",
"path": "/lib/bare/bundles/bareIco.js",
"keys": [
"bareHttp1"
"bareIco"
]
},
{
@@ -326,9 +320,9 @@
]
},
{
"path": "/lib/bare/bundles/bareInspect.js",
"path": "/lib/bare/bundles/bareHttp1.js",
"keys": [
"bareInspect"
"bareHttp1"
]
},
{
@@ -338,15 +332,15 @@
]
},
{
"path": "/lib/bare/bundles/bareJpeg.js",
"path": "/lib/bare/bundles/bareInspect.js",
"keys": [
"bareJpeg"
"bareInspect"
]
},
{
"path": "/lib/bare/bundles/bareIpc.js",
"path": "/lib/bare/bundles/bareJpeg.js",
"keys": [
"bareIpc"
"bareJpeg"
]
},
{
@@ -355,12 +349,30 @@
"bareIntl"
]
},
{
"path": "/lib/bare/bundles/bareIpc.js",
"keys": [
"bareIpc"
]
},
{
"path": "/lib/bare/bundles/bareLief.js",
"keys": [
"bareLief"
]
},
{
"path": "/lib/bare/bundles/bareLogger.js",
"keys": [
"bareLogger"
]
},
{
"path": "/lib/bare/bundles/bareInspector.js",
"keys": [
"bareInspector"
]
},
{
"path": "/lib/bare/bundles/bareLink.js",
"keys": [
@@ -374,15 +386,9 @@
]
},
{
"path": "/lib/bare/bundles/bareInspector.js",
"path": "/lib/bare/bundles/bareModuleLexer.js",
"keys": [
"bareInspector"
]
},
{
"path": "/lib/bare/bundles/bareLogger.js",
"keys": [
"bareLogger"
"bareModuleLexer"
]
},
{
@@ -391,24 +397,12 @@
"bareModuleResolve"
]
},
{
"path": "/lib/bare/bundles/bareModuleLexer.js",
"keys": [
"bareModuleLexer"
]
},
{
"path": "/lib/bare/bundles/bareModule.js",
"keys": [
"bareModule"
]
},
{
"path": "/lib/bare/bundles/bareNdk.js",
"keys": [
"bareNdk"
]
},
{
"path": "/lib/bare/bundles/bareModuleTraverse.js",
"keys": [
@@ -416,15 +410,9 @@
]
},
{
"path": "/lib/bare/bundles/bareMedia.js",
"path": "/lib/bare/bundles/bareNdk.js",
"keys": [
"bareMedia"
]
},
{
"path": "/lib/bare/bundles/bareNodeFetch.js",
"keys": [
"bareNodeFetch"
"bareNdk"
]
},
{
@@ -434,9 +422,21 @@
]
},
{
"path": "/lib/bare/bundles/bareOs.js",
"path": "/lib/bare/bundles/bareNodeFetch.js",
"keys": [
"bareOs"
"bareNodeFetch"
]
},
{
"path": "/lib/bare/bundles/bareMedia.js",
"keys": [
"bareMedia"
]
},
{
"path": "/lib/bare/bundles/bareNet.js",
"keys": [
"bareNet"
]
},
{
@@ -446,9 +446,9 @@
]
},
{
"path": "/lib/bare/bundles/bareNet.js",
"path": "/lib/bare/bundles/bareOs.js",
"keys": [
"bareNet"
"bareOs"
]
},
{
@@ -463,6 +463,12 @@
"barePack"
]
},
{
"path": "/lib/bare/bundles/barePipe.js",
"keys": [
"barePipe"
]
},
{
"path": "/lib/bare/bundles/barePackDrive.js",
"keys": [
@@ -475,12 +481,6 @@
"barePng"
]
},
{
"path": "/lib/bare/bundles/barePipe.js",
"keys": [
"barePipe"
]
},
{
"path": "/lib/bare/bundles/barePunycode.js",
"keys": [
@@ -488,9 +488,15 @@
]
},
{
"path": "/lib/bare/bundles/barePrebuild.js",
"path": "/lib/bare/bundles/bareNodeRuntime.js",
"keys": [
"barePrebuild"
"bareNodeRuntime"
]
},
{
"path": "/lib/bare/bundles/bareDev.js",
"keys": [
"bareDev"
]
},
{
@@ -500,9 +506,9 @@
]
},
{
"path": "/lib/bare/bundles/bareNodeRuntime.js",
"path": "/lib/bare/bundles/barePrebuild.js",
"keys": [
"bareNodeRuntime"
"barePrebuild"
]
},
{
@@ -524,15 +530,9 @@
]
},
{
"path": "/lib/bare/bundles/barePromClient.js",
"path": "/lib/bare/bundles/bareSdl.js",
"keys": [
"barePromClient"
]
},
{
"path": "/lib/bare/bundles/bareDev.js",
"keys": [
"bareDev"
"bareSdl"
]
},
{
@@ -542,9 +542,9 @@
]
},
{
"path": "/lib/bare/bundles/bareSdl.js",
"path": "/lib/bare/bundles/bareRpc.js",
"keys": [
"bareSdl"
"bareRpc"
]
},
{
@@ -554,9 +554,9 @@
]
},
{
"path": "/lib/bare/bundles/bareRpc.js",
"path": "/lib/bare/bundles/barePromClient.js",
"keys": [
"bareRpc"
"barePromClient"
]
},
{
@@ -565,6 +565,12 @@
"bareRepl"
]
},
{
"path": "/lib/bare/bundles/bareRun.js",
"keys": [
"bareRun"
]
},
{
"path": "/lib/bare/bundles/bareSidecar.js",
"keys": [
@@ -578,9 +584,9 @@
]
},
{
"path": "/lib/bare/bundles/bareRun.js",
"path": "/lib/bare/bundles/bareStringDecoder.js",
"keys": [
"bareRun"
"bareStringDecoder"
]
},
{
@@ -589,24 +595,12 @@
"bareStorage"
]
},
{
"path": "/lib/bare/bundles/bareStringDecoder.js",
"keys": [
"bareStringDecoder"
]
},
{
"path": "/lib/bare/bundles/bareStream.js",
"keys": [
"bareStream"
]
},
{
"path": "/lib/bare/bundles/bareStdio.js",
"keys": [
"bareStdio"
]
},
{
"path": "/lib/bare/bundles/bareSvg.js",
"keys": [
@@ -620,21 +614,9 @@
]
},
{
"path": "/lib/bare/bundles/bareSystemLogger.js",
"path": "/lib/bare/bundles/bareStdio.js",
"keys": [
"bareSystemLogger"
]
},
{
"path": "/lib/bare/bundles/bareTiff.js",
"keys": [
"bareTiff"
]
},
{
"path": "/lib/bare/bundles/bareTap.js",
"keys": [
"bareTap"
"bareStdio"
]
},
{
@@ -644,9 +626,21 @@
]
},
{
"path": "/lib/bare/bundles/bareTpl.js",
"path": "/lib/bare/bundles/bareSystemLogger.js",
"keys": [
"bareTpl"
"bareSystemLogger"
]
},
{
"path": "/lib/bare/bundles/bareTap.js",
"keys": [
"bareTap"
]
},
{
"path": "/lib/bare/bundles/bareTiff.js",
"keys": [
"bareTiff"
]
},
{
@@ -656,9 +650,9 @@
]
},
{
"path": "/lib/bare/bundles/bareTimers.js",
"path": "/lib/bare/bundles/bareTpl.js",
"keys": [
"bareTimers"
"bareTpl"
]
},
{
@@ -667,24 +661,18 @@
"bareTcp"
]
},
{
"path": "/lib/bare/bundles/bareTimers.js",
"keys": [
"bareTimers"
]
},
{
"path": "/lib/bare/bundles/bareType.js",
"keys": [
"bareType"
]
},
{
"path": "/lib/bare/bundles/bareTls.js",
"keys": [
"bareTls"
]
},
{
"path": "/lib/bare/bundles/bareTty.js",
"keys": [
"bareTty"
]
},
{
"path": "/lib/bare/bundles/bareUiKit.js",
"keys": [
@@ -697,12 +685,30 @@
"bareUnpack"
]
},
{
"path": "/lib/bare/bundles/bareTty.js",
"keys": [
"bareTty"
]
},
{
"path": "/lib/bare/bundles/bareUnionBundle.js",
"keys": [
"bareUnionBundle"
]
},
{
"path": "/lib/bare/bundles/bareV8.js",
"keys": [
"bareV8"
]
},
{
"path": "/lib/bare/bundles/bareTls.js",
"keys": [
"bareTls"
]
},
{
"path": "/lib/bare/bundles/bareVm.js",
"keys": [
@@ -715,24 +721,6 @@
"bareWalkHandles"
]
},
{
"path": "/lib/bare/bundles/bareUnionBundle.js",
"keys": [
"bareUnionBundle"
]
},
{
"path": "/lib/bare/bundles/bareWebKitGtk.js",
"keys": [
"bareWebKitGtk"
]
},
{
"path": "/lib/bare/bundles/bareWebp.js",
"keys": [
"bareWebp"
]
},
{
"path": "/lib/bare/bundles/bareWebKit.js",
"keys": [
@@ -745,6 +733,12 @@
"bareUtils"
]
},
{
"path": "/lib/bare/bundles/bareWebKitGtk.js",
"keys": [
"bareWebKitGtk"
]
},
{
"path": "/lib/bare/bundles/bareV8ToIstanbul.js",
"keys": [
@@ -758,9 +752,9 @@
]
},
{
"path": "/lib/bare/bundles/bareXdiff.js",
"path": "/lib/bare/bundles/bareWebp.js",
"keys": [
"bareXdiff"
"bareWebp"
]
},
{
@@ -769,6 +763,12 @@
"bareWinUi"
]
},
{
"path": "/lib/bare/bundles/bareXdiff.js",
"keys": [
"bareXdiff"
]
},
{
"path": "/lib/bare/bundles/bareZlib.js",
"keys": [
@@ -1595,8 +1595,8 @@
],
"bundleProvenance": {
"schemaVersion": 1,
"generatedAt": "2026-04-05T06:54:57.132Z",
"gitCommit": "6f923f72d1c62d20199d0b8d4a305f8a5e2d0f52",
"generatedAt": "2026-04-05T07:15:10.443Z",
"gitCommit": "6a4790591d840ccc2fb2a3696e3c16fef532b740",
"nodeVersion": "v22.22.0",
"bundleTier": "all",
"normativeManifest": "packages/bare-os-booter/lib/bare-module-manifest.json",
@@ -1,6 +1,6 @@
{
"schema": 1,
"atMs": 1775372096454,
"atMs": 1775373309737,
"commands": [
"arch",
"awk",
File diff suppressed because one or more lines are too long