/** * Concatenated into /boot/init.js before init-main.js by scripts/bundle-kernel-init.mjs. * Boot policy merge: semver helpers, skip merge, kernel.d env guards, applyBootPolicyFile. * @see kernel/lib/init/STRUCTURE.md */ /** * Optional `/etc/bare-os/boot.policy.json` when `BARE_OS_BOOT_POLICY=1`: * `{ "skipBootStages": ["rc.d"], "denyBootStages": ["onboot"], "minKernelCapabilitiesPrimary": 1, "requireSeedCaps": 1, * "maxExecLineDepth": 32, "denyEnvKeys": ["FOO"], "requireProcNodes": ["/proc/bare_os/features"] }`. * Boot policy v5 (optional): `requireKernelCapabilitiesHostTransportDelegates`, `requireInitJsSha256` (hex sha256 of `/boot/init.js` raw bytes). * Boot policy v6 (optional): `requireKernelCapabilitiesReplicationOperatorSurface`, `requireBooterSemver`, `requireCtxApiMin`, `denyKernelExtensionIds`, * `kernelExtensionHashPins`, `offlineLkgIntegrityStrict`. * Boot policy v7 (optional): `requireKernelCapabilitiesPearCorestoreHrpc`, `requirePearRuntimeMin`, `requireProtocolPackageMin`, * `denyCtxMethodPrefixes`, `maxKernelExtensionDepth`, `gitPartialClonePolicy`. * Boot policy v8 (optional): `requireKernelCapabilitiesBareRuntimeProtoMux`, `requireBareRuntimeMin`, `denySeedRpcMethods`, * `maxProtomuxChannelNameLength`. * Boot policy v9 (optional): `requireKernelCapabilitiesBareModuleCryptoStaging`, `requirePearRuntimeRange` (`{ min?, max? }` semver), * `denyBareModuleSpecifierPatterns`, `requireBareCryptoMin`, `denyKernelSyscalls`, `requirePearIpcMin`, * `extensionSignerPinsV2`, `offlineLkgManifestMaxAgeSec`, `bootStagesRequireProcIndexMinSchema` (legacy `bootPhasesRequireProcIndexMinSchema`). * Boot policy v10 (optional): `requireKernelCapabilitiesPearInspectLoggerTls`, `requireBareBootMin`, `denyBareRpcMethodPatterns`, * `maxPearInspectDepth`, `requireBareLoggerMin`, `denyAutobaseDiscoveryChannels`, `requireBareTlsMin`, * `extensionSignerPinsV3`, `offlineLkgRequirePearStamp`, `bootStagesRequireLifecycleMinSchema` (legacy `bootPhasesRequireLifecycleMinSchema`). * Boot policy v11 (optional): `requireKernelCapabilitiesHypercorePackHrpcLifecycle`, `extensionSignerPinsV4`, `requireBarePackMin`, * `requireBareAddonPolicyMin`, `maxHrpcAllowlistDepth`, `offlineLkgRequireHypercorePackHrpcLifecycle`. * @param {Record} ctx * @returns {Promise} false when strict policy fails (caller should abort boot) */ /** * @param {string} s * @returns {number[] | null} */ function parseSemverTriplet(s) { const m = String(s) .trim() .match(/^(\d+)\.(\d+)\.(\d+)/) if (!m) return null return [Number(m[1]), Number(m[2]), Number(m[3])] } /** * @param {string} have * @param {string} need */ function semverGte(have, need) { const a = parseSemverTriplet(have) const b = parseSemverTriplet(need) if (!a || !b) return false for (let i = 0; i < 3; i++) { if (a[i] > b[i]) return true if (a[i] < b[i]) return false } return true } /** * @param {string} have * @param {string} need */ function semverLte(have, need) { const a = parseSemverTriplet(have) const b = parseSemverTriplet(need) if (!a || !b) return false for (let i = 0; i < 3; i++) { if (a[i] < b[i]) return true if (a[i] > b[i]) return false } return true } /** * @param {unknown} m * @param {string} k * @returns {number | undefined} */ function ctxKernelCapWord(m, k) { if (!m || typeof m !== 'object') return undefined const v = /** @type {Record} */ (m)[k] return typeof v === 'number' ? v : undefined } /** * Merge only boot-stage skip fields from a policy fragment (tiered fallbacks / rollback marker). * Honors canonical `skipBootStages` / `denyBootStages` and legacy `skipPhases` / `denyBootPhases`. * @param {Set} merge * @param {Record} pol */ function mergeBootPolicySkipStagesIntoSet(merge, pol) { if (!pol || typeof pol !== 'object') return if (Array.isArray(pol.skipBootStages)) { for (const p of pol.skipBootStages) merge.add(String(p).toLowerCase()) } if (Array.isArray(pol.skipPhases)) { for (const p of pol.skipPhases) merge.add(String(p).toLowerCase()) } if (Array.isArray(pol.denyBootStages)) { for (const p of pol.denyBootStages) merge.add(String(p).toLowerCase()) } if (Array.isArray(pol.denyBootPhases)) { for (const p of pol.denyBootPhases) merge.add(String(p).toLowerCase()) } } /** * Optional dry-run — parse boot snippets but skip trusted execLine / extension scripts. * @param {Record} ctx */ function bootDryRun(ctx) { const v = ctx.env && ctx.env.BARE_OS_BOOT_DRY_RUN return v === '1' || v === 'true' } /** * Named boot stage for journals / checkpoints. * @param {string} label */ function bootStageForPhase(label) { const l = String(label).toLowerCase() if (l === 'boot.policy') return 'policy' if (l === 'os-release' || l === 'motd') return 'preflight' if ( l === 'rc' || l.startsWith('rc.') || l === 'rc.d' || l === 'rc.local' ) return 'rc' if (l === 'kernel.d' || l === 'kernel.ext.d') return 'extensions' if (l === 'onboot' || l === 'selftest') return 'services' if (l === 'banner') return 'shell' return 'misc' } /** * kernel.d: leading `# ConditionEnvironment=KEY=VAL` / `# AssertEnvironment=…` guards. * @param {Record} ctx * @param {string} text */ function kernelSnippetEnvGuardsOk(ctx, text) { const env = ctx.env && typeof ctx.env === 'object' ? ctx.env : {} for (const raw of String(text).split(/\r?\n/)) { const line = raw.trim() if (!line.startsWith('#')) break const m = line.match( /^#\s*ConditionEnvironment=(.+)$/i ) || line.match(/^#\s*AssertEnvironment=(.+)$/i) if (!m) continue const expr = String(m[1]).trim() const eq = expr.indexOf('=') if (eq < 1) return false const key = expr.slice(0, eq).trim() const need = expr.slice(eq + 1).trim() const have = env[key] != null && env[key] !== undefined ? String(env[key]) : '' if (have !== need) return false } return true } async function applyBootPolicyFile(ctx) { const en = ctx.env && ctx.env.BARE_OS_BOOT_POLICY if (en !== '1' && en !== 'true') return true const merge = ctx.bareOsBootPolicySkipStages instanceof Set ? ctx.bareOsBootPolicySkipStages : ctx.bareOsBootPolicySkipPhases if (!(merge instanceof Set)) return true const { drive, b4a } = ctx const strictPol = ctx.env?.BARE_OS_BOOT_POLICY_STRICT === '1' || ctx.env?.BARE_OS_BOOT_POLICY_STRICT === 'true' const advWords = ctx.bareOsAdvertisedKernelCapabilityWords try { const rb = ctx.env?.BARE_OS_BOOT_ROLLBACK_APPLY if ((rb === '1' || rb === 'true') && ctx.vfs && typeof ctx.vfs.readFile === 'function') { try { const mbuf = await ctx.vfs.readFile('/run/bare-os/boot-rollback.marker') const mobj = JSON.parse(b4a.toString(mbuf)) mergeBootPolicySkipStagesIntoSet(merge, mobj) } catch { /* ignore */ } } const policyPathRaw = String(ctx.env?.BARE_OS_BOOT_POLICY_PATH || '').trim() const policyPath = policyPathRaw && policyPathRaw.startsWith('/etc/bare-os/') ? policyPathRaw : '/etc/bare-os/boot.policy.json' const buf = await drive.get(policyPath) if (!buf) return true const pol = JSON.parse(b4a.toString(buf)) if (!pol || typeof pol !== 'object') return true const legacySkip = (Array.isArray(pol.skipPhases) && pol.skipPhases.length > 0) || (Array.isArray(pol.denyBootPhases) && pol.denyBootPhases.length > 0) const modernSkip = (Array.isArray(pol.skipBootStages) && pol.skipBootStages.length > 0) || (Array.isArray(pol.denyBootStages) && pol.denyBootStages.length > 0) if (legacySkip && !modernSkip) { bootStructuredLog( ctx, 'warn', 'deprecatedSkipPhases', '[boot-policy] deprecated: skipPhases/denyBootPhases in boot.policy.json — prefer skipBootStages/denyBootStages' ) } mergeBootPolicySkipStagesIntoSet(merge, pol) if (Array.isArray(pol.policyFallbackPaths)) { for (const rel of pol.policyFallbackPaths) { const p2 = String(rel).trim() if (!p2.startsWith('/etc/bare-os/')) continue try { const fb = await drive.get(p2) if (!fb) continue const pol2 = JSON.parse(b4a.toString(fb)) mergeBootPolicySkipStagesIntoSet(merge, pol2) } catch (e) { bootStructuredLog( ctx, 'error', 'policyFallbackPaths', '[boot-policy] policyFallbackPaths ' + p2 + ': ' + ((e && e.message) || String(e)) ) } } } if ( typeof pol.requireBootBundleSha256Hex === 'string' && String(pol.requireBootBundleSha256Hex).trim() ) { const need = String(pol.requireBootBundleSha256Hex).trim().toLowerCase() const have = String(ctx.env?.BARE_OS_BOOT_BUNDLE_DIGEST_HEX || '') .trim() .toLowerCase() if (!have || have !== need) { bootStructuredLog( ctx, 'error', 'requireBootBundleSha256Hex', '[boot-policy] requireBootBundleSha256Hex not satisfied' ) if (strictPol) { if (typeof ctx.requestBooterExit === 'function') { ctx.requestBooterExit(1) } return false } } } if (pol.initdAdmission && typeof pol.initdAdmission === 'object' && ctx.env) { const a = /** @type {Record} */ (pol.initdAdmission) if (typeof a.maxParallel === 'number' && a.maxParallel >= 1) { ctx.env.BARE_OS_INITD_MAX_PARALLEL = String( Math.min(32, Math.floor(a.maxParallel)) ) } if (typeof a.memoryHintMb === 'number' && a.memoryHintMb >= 1) { ctx.env.BARE_OS_INITD_MEMORY_HINT_MB = String( Math.min(1e6, Math.floor(a.memoryHintMb)) ) } if (typeof a.wallBudgetMsDefault === 'number' && a.wallBudgetMsDefault >= 1) { ctx.env.BARE_OS_INITD_WALL_BUDGET_MS_DEFAULT = String( Math.min(3.6e6, Math.floor(a.wallBudgetMsDefault)) ) } } if (pol.p2pAdmission && typeof pol.p2pAdmission === 'object' && ctx.env) { const p2p = /** @type {Record} */ (pol.p2pAdmission) const allow = typeof p2p.peerAllowlistHex === 'string' ? p2p.peerAllowlistHex.trim() : '' if (allow && !String(ctx.env.BARE_OS_PEER_ALLOWLIST_HEX || '').trim()) { ctx.env.BARE_OS_PEER_ALLOWLIST_HEX = allow } const boot = typeof p2p.hyperswarmBootstrap === 'string' ? p2p.hyperswarmBootstrap.trim() : '' if (boot && !String(ctx.env.HYPERSWARM_BOOTSTRAP || '').trim()) { ctx.env.HYPERSWARM_BOOTSTRAP = boot } } if ( typeof pol.minKernelCapabilitiesPrimary === 'number' && pol.minKernelCapabilitiesPrimary > 0 ) { const adv = ctxKernelCapWord(advWords, 'primary') const need = pol.minKernelCapabilitiesPrimary >>> 0 if (typeof adv === 'number') { const ok = ((adv >>> 0) & need) === need if (!ok) { bootStructuredLog( ctx, 'error', 'minKernelCapabilitiesPrimary', '[boot-policy] minKernelCapabilitiesPrimary not satisfied' ) if (strictPol) { if (typeof ctx.requestBooterExit === 'function') { ctx.requestBooterExit(1) } return false } } } } if (typeof pol.requireSeedCaps === 'number' && pol.requireSeedCaps > 0) { const seed = ctxKernelCapWord(ctx.bareOsSeedKernelCapabilityWords, 'primary') const need = pol.requireSeedCaps >>> 0 const ok = typeof seed === 'number' && ((seed >>> 0) & need) === need if (!ok) { bootStructuredLog( ctx, 'error', 'requireSeedCaps', '[boot-policy] requireSeedCaps not satisfied' ) if (strictPol) { if (typeof ctx.requestBooterExit === 'function') { ctx.requestBooterExit(1) } return false } } } if ( typeof pol.requireKernelCapabilitiesExtendedSeedingPlatform === 'number' && pol.requireKernelCapabilitiesExtendedSeedingPlatform > 0 ) { const adv2 = ctxKernelCapWord(advWords, 'extendedSeedingPlatform') const need2 = pol.requireKernelCapabilitiesExtendedSeedingPlatform >>> 0 const ok = typeof adv2 === 'number' && ((adv2 >>> 0) & need2) === need2 if (!ok) { bootStructuredLog( ctx, 'error', 'requireKernelCapabilitiesExtendedSeedingPlatform', '[boot-policy] requireKernelCapabilitiesExtendedSeedingPlatform not satisfied' ) if (strictPol) { if (typeof ctx.requestBooterExit === 'function') { ctx.requestBooterExit(1) } return false } } } if ( typeof pol.requireKernelCapabilitiesRlimitsDelegatesShell === 'number' && pol.requireKernelCapabilitiesRlimitsDelegatesShell > 0 ) { const adv3 = ctxKernelCapWord(advWords, 'rlimitsDelegatesShell') const need3 = pol.requireKernelCapabilitiesRlimitsDelegatesShell >>> 0 const ok = typeof adv3 === 'number' && ((adv3 >>> 0) & need3) === need3 if (!ok) { bootStructuredLog( ctx, 'error', 'requireKernelCapabilitiesRlimitsDelegatesShell', '[boot-policy] requireKernelCapabilitiesRlimitsDelegatesShell not satisfied' ) if (strictPol) { if (typeof ctx.requestBooterExit === 'function') { ctx.requestBooterExit(1) } return false } } } if ( typeof pol.requireKernelCapabilitiesOfflineNetExtensions === 'number' && pol.requireKernelCapabilitiesOfflineNetExtensions > 0 ) { const adv4 = ctxKernelCapWord(advWords, 'offlineNetExtensions') const need4 = pol.requireKernelCapabilitiesOfflineNetExtensions >>> 0 const ok = typeof adv4 === 'number' && ((adv4 >>> 0) & need4) === need4 if (!ok) { bootStructuredLog( ctx, 'error', 'requireKernelCapabilitiesOfflineNetExtensions', '[boot-policy] requireKernelCapabilitiesOfflineNetExtensions not satisfied' ) if (strictPol) { if (typeof ctx.requestBooterExit === 'function') { ctx.requestBooterExit(1) } return false } } } if ( typeof pol.requireKernelCapabilitiesHostTransportDelegates === 'number' && pol.requireKernelCapabilitiesHostTransportDelegates > 0 ) { const adv5 = ctxKernelCapWord(advWords, 'hostTransportDelegates') const need5 = pol.requireKernelCapabilitiesHostTransportDelegates >>> 0 const ok = typeof adv5 === 'number' && ((adv5 >>> 0) & need5) === need5 if (!ok) { bootStructuredLog( ctx, 'error', 'requireKernelCapabilitiesHostTransportDelegates', '[boot-policy] requireKernelCapabilitiesHostTransportDelegates not satisfied' ) if (strictPol) { if (typeof ctx.requestBooterExit === 'function') { ctx.requestBooterExit(1) } return false } } } if ( typeof pol.requireKernelCapabilitiesReplicationOperatorSurface === 'number' && pol.requireKernelCapabilitiesReplicationOperatorSurface > 0 ) { const adv6 = ctxKernelCapWord(advWords, 'replicationOperatorSurface') const need6 = pol.requireKernelCapabilitiesReplicationOperatorSurface >>> 0 const ok = typeof adv6 === 'number' && ((adv6 >>> 0) & need6) === need6 if (!ok) { bootStructuredLog( ctx, 'error', 'requireKernelCapabilitiesReplicationOperatorSurface', '[boot-policy] requireKernelCapabilitiesReplicationOperatorSurface not satisfied' ) if (strictPol) { if (typeof ctx.requestBooterExit === 'function') { ctx.requestBooterExit(1) } return false } } } if ( typeof pol.requireKernelCapabilitiesPearCorestoreHrpc === 'number' && pol.requireKernelCapabilitiesPearCorestoreHrpc > 0 ) { const adv7 = ctxKernelCapWord(advWords, 'pearCorestoreHrpc') const need7 = pol.requireKernelCapabilitiesPearCorestoreHrpc >>> 0 const ok = typeof adv7 === 'number' && ((adv7 >>> 0) & need7) === need7 if (!ok) { bootStructuredLog( ctx, 'error', 'requireKernelCapabilitiesPearCorestoreHrpc', '[boot-policy] requireKernelCapabilitiesPearCorestoreHrpc not satisfied' ) if (strictPol) { if (typeof ctx.requestBooterExit === 'function') { ctx.requestBooterExit(1) } return false } } } if ( typeof pol.requireKernelCapabilitiesBareRuntimeProtoMux === 'number' && pol.requireKernelCapabilitiesBareRuntimeProtoMux > 0 ) { const adv8 = ctxKernelCapWord(advWords, 'bareRuntimeProtoMux') const need8 = pol.requireKernelCapabilitiesBareRuntimeProtoMux >>> 0 const ok = typeof adv8 === 'number' && ((adv8 >>> 0) & need8) === need8 if (!ok) { bootStructuredLog( ctx, 'error', 'requireKernelCapabilitiesBareRuntimeProtoMux', '[boot-policy] requireKernelCapabilitiesBareRuntimeProtoMux not satisfied' ) if (strictPol) { if (typeof ctx.requestBooterExit === 'function') { ctx.requestBooterExit(1) } return false } } } if ( typeof pol.requireKernelCapabilitiesBareModuleCryptoStaging === 'number' && pol.requireKernelCapabilitiesBareModuleCryptoStaging > 0 ) { const adv9 = ctxKernelCapWord(advWords, 'bareModuleCryptoStaging') const need9 = pol.requireKernelCapabilitiesBareModuleCryptoStaging >>> 0 const ok = typeof adv9 === 'number' && ((adv9 >>> 0) & need9) === need9 if (!ok) { bootStructuredLog( ctx, 'error', 'requireKernelCapabilitiesBareModuleCryptoStaging', '[boot-policy] requireKernelCapabilitiesBareModuleCryptoStaging not satisfied' ) if (strictPol) { if (typeof ctx.requestBooterExit === 'function') { ctx.requestBooterExit(1) } return false } } } if ( typeof pol.requireKernelCapabilitiesPearInspectLoggerTls === 'number' && pol.requireKernelCapabilitiesPearInspectLoggerTls > 0 ) { const adv10 = ctxKernelCapWord(advWords, 'pearInspectLoggerTls') const need10 = pol.requireKernelCapabilitiesPearInspectLoggerTls >>> 0 const ok = typeof adv10 === 'number' && ((adv10 >>> 0) & need10) === need10 if (!ok) { bootStructuredLog( ctx, 'error', 'requireKernelCapabilitiesPearInspectLoggerTls', '[boot-policy] requireKernelCapabilitiesPearInspectLoggerTls not satisfied' ) if (strictPol) { if (typeof ctx.requestBooterExit === 'function') { ctx.requestBooterExit(1) } return false } } } if ( typeof pol.requireKernelCapabilitiesHypercorePackHrpcLifecycle === 'number' && pol.requireKernelCapabilitiesHypercorePackHrpcLifecycle > 0 ) { const adv11 = ctxKernelCapWord(advWords, 'hypercorePackHrpcLifecycle') const need11 = pol.requireKernelCapabilitiesHypercorePackHrpcLifecycle >>> 0 const ok = typeof adv11 === 'number' && ((adv11 >>> 0) & need11) === need11 if (!ok) { bootStructuredLog( ctx, 'error', 'requireKernelCapabilitiesHypercorePackHrpcLifecycle', '[boot-policy] requireKernelCapabilitiesHypercorePackHrpcLifecycle not satisfied' ) if (strictPol) { if (typeof ctx.requestBooterExit === 'function') { ctx.requestBooterExit(1) } return false } } } if ( pol.requirePearRuntimeRange && typeof pol.requirePearRuntimeRange === 'object' ) { const r = pol.requirePearRuntimeRange const minS = typeof r.min === 'string' ? r.min.trim() : '' const maxS = typeof r.max === 'string' ? r.max.trim() : '' const haveS = typeof ctx.bareOsPearRuntimeVersion === 'string' ? ctx.bareOsPearRuntimeVersion.trim() : '' let ok = Boolean(haveS) if (ok && minS) ok = semverGte(haveS, minS) if (ok && maxS) ok = semverLte(haveS, maxS) if (!ok) { bootStructuredLog( ctx, 'error', 'requirePearRuntimeRange', '[boot-policy] requirePearRuntimeRange not satisfied' ) if (strictPol) { if (typeof ctx.requestBooterExit === 'function') { ctx.requestBooterExit(1) } return false } } } if ( typeof pol.requireBareRuntimeMin === 'string' && String(pol.requireBareRuntimeMin).trim() ) { const needS = String(pol.requireBareRuntimeMin).trim() const haveS = typeof ctx.bareOsBareRuntimeVersion === 'string' ? ctx.bareOsBareRuntimeVersion.trim() : '' const ok = haveS && semverGte(haveS, needS) if (!ok) { bootStructuredLog( ctx, 'error', 'requireBareRuntimeMin', '[boot-policy] requireBareRuntimeMin not satisfied' ) if (strictPol) { if (typeof ctx.requestBooterExit === 'function') { ctx.requestBooterExit(1) } return false } } } if ( typeof pol.requirePearRuntimeMin === 'string' && String(pol.requirePearRuntimeMin).trim() ) { const needS = String(pol.requirePearRuntimeMin).trim() const haveS = typeof ctx.bareOsPearRuntimeVersion === 'string' ? ctx.bareOsPearRuntimeVersion.trim() : '' const ok = haveS && semverGte(haveS, needS) if (!ok) { bootStructuredLog( ctx, 'error', 'requirePearRuntimeMin', '[boot-policy] requirePearRuntimeMin not satisfied' ) if (strictPol) { if (typeof ctx.requestBooterExit === 'function') { ctx.requestBooterExit(1) } return false } } } if ( typeof pol.requireProtocolPackageMin === 'string' && String(pol.requireProtocolPackageMin).trim() ) { const needS = String(pol.requireProtocolPackageMin).trim() const haveS = typeof ctx.bareOsProtocolPackageVersion === 'string' ? ctx.bareOsProtocolPackageVersion.trim() : '' const ok = haveS && semverGte(haveS, needS) if (!ok) { bootStructuredLog( ctx, 'error', 'requireProtocolPackageMin', '[boot-policy] requireProtocolPackageMin not satisfied' ) if (strictPol) { if (typeof ctx.requestBooterExit === 'function') { ctx.requestBooterExit(1) } return false } } } if ( typeof pol.requireBooterSemver === 'string' && String(pol.requireBooterSemver).trim() ) { const needS = String(pol.requireBooterSemver).trim() const haveS = typeof ctx.bareOsBooterPackageVersion === 'string' ? ctx.bareOsBooterPackageVersion.trim() : '' const ok = haveS && semverGte(haveS, needS) if (!ok) { bootStructuredLog( ctx, 'error', 'requireBooterSemver', '[boot-policy] requireBooterSemver not satisfied' ) if (strictPol) { if (typeof ctx.requestBooterExit === 'function') { ctx.requestBooterExit(1) } return false } } } if ( typeof pol.requireCtxApiMin === 'string' && String(pol.requireCtxApiMin).trim() ) { const needS = String(pol.requireCtxApiMin).trim() const haveS = typeof ctx.bareOsCtxApiVersion === 'string' ? ctx.bareOsCtxApiVersion.trim() : String(ctx.bareOsCtxApiVersion || '').trim() const ok = haveS && semverGte(haveS, needS) if (!ok) { bootStructuredLog( ctx, 'error', 'requireCtxApiMin', '[boot-policy] requireCtxApiMin not satisfied' ) if (strictPol) { if (typeof ctx.requestBooterExit === 'function') { ctx.requestBooterExit(1) } return false } } } if (Array.isArray(pol.denyKernelExtensionIds) && ctx.env) { const parts = pol.denyKernelExtensionIds .map((x) => String(x).trim()) .filter(Boolean) if (parts.length) ctx.env.BARE_OS_BOOT_POLICY_DENY_KERNEL_EXT_IDS = parts.join(',') } if ( pol.kernelExtensionHashPins && typeof pol.kernelExtensionHashPins === 'object' && ctx.env ) { try { ctx.env.BARE_OS_BOOT_POLICY_EXTENSION_HASH_PINS_JSON = JSON.stringify( pol.kernelExtensionHashPins ) } catch { /* ignore */ } } if (pol.offlineLkgIntegrityStrict === true && ctx.env) { ctx.env.BARE_OS_OFFLINE_LKG_INTEGRITY_STRICT = '1' } if ( typeof pol.requireInitJsSha256 === 'string' && String(pol.requireInitJsSha256).trim() ) { const need = String(pol.requireInitJsSha256).trim().toLowerCase() const buf = await drive.get('/boot/init.js') const hashFn = ctx.bareOsBootFileSha256Hex if (typeof hashFn !== 'function') { bootStructuredLog( ctx, 'error', 'requireInitJsSha256MissingHasher', '[boot-policy] requireInitJsSha256 needs ctx.bareOsBootFileSha256Hex' ) if (strictPol) { if (typeof ctx.requestBooterExit === 'function') { ctx.requestBooterExit(1) } return false } } else { const hex = hashFn(buf || new Uint8Array()) if (hex !== need) { bootStructuredLog( ctx, 'error', 'requireInitJsSha256Mismatch', '[boot-policy] requireInitJsSha256 mismatch for /boot/init.js' ) if (strictPol) { if (typeof ctx.requestBooterExit === 'function') { ctx.requestBooterExit(1) } return false } } } } if (Array.isArray(pol.allowedPearIpcChannels) && ctx.env) { const parts = pol.allowedPearIpcChannels .map((c) => String(c).trim()) .filter(Boolean) if (parts.length) ctx.env.BARE_OS_BOOT_POLICY_PEAR_IPC_CHANNELS = parts.join(',') } if (Array.isArray(pol.denyVfsPrefixes) && ctx.env) { const parts = pol.denyVfsPrefixes .map((p) => String(p).trim()) .filter(Boolean) if (parts.length) ctx.env.BARE_OS_BOOT_POLICY_DENY_VFS = parts.join(',') } if (Array.isArray(pol.denyExecLineBuiltins) && ctx.env) { const parts = pol.denyExecLineBuiltins .map((b) => String(b).trim()) .filter(Boolean) if (parts.length) { ctx.env.BARE_OS_BOOT_POLICY_DENY_EXEC_LINE_BUILTINS = parts.join(',') } } if (Array.isArray(pol.allowedCtxMethods) && ctx.env) { const parts = pol.allowedCtxMethods .map((m) => String(m).trim()) .filter(Boolean) if (parts.length) { ctx.env.BARE_OS_BOOT_POLICY_ALLOWED_CTX_METHODS = parts.join(',') } } if (Array.isArray(pol.denyCtxMethodPrefixes) && ctx.env) { const parts = pol.denyCtxMethodPrefixes .map((m) => String(m).trim()) .filter(Boolean) if (parts.length) { ctx.env.BARE_OS_BOOT_POLICY_DENY_CTX_PREFIXES = parts.join(',') } } if (Array.isArray(pol.denySeedRpcMethods) && ctx.env) { const parts = pol.denySeedRpcMethods .map((m) => String(m).trim()) .filter(Boolean) const existing = String( ctx.env.BARE_OS_BOOT_POLICY_DENY_SEED_RPC_METHODS || '' ) .split(',') .map((s) => s.trim()) .filter(Boolean) const merged = [...new Set([...existing, ...parts])] if (merged.length) { ctx.env.BARE_OS_BOOT_POLICY_DENY_SEED_RPC_METHODS = merged.join(',') } } if ( Array.isArray(pol.denyBareModuleSpecifierPatterns) && ctx.env ) { try { ctx.env.BARE_OS_BOOT_POLICY_DENY_BARE_MODULE_SPECS_JSON = JSON.stringify(pol.denyBareModuleSpecifierPatterns) } catch { /* ignore */ } } if ( Array.isArray(pol.denyKernelSyscalls) && ctx.env ) { const parts = pol.denyKernelSyscalls .map((s) => String(s).trim()) .filter(Boolean) if (parts.length) { ctx.env.BARE_OS_BOOT_POLICY_DENY_KERNEL_SYSCALLS = parts.join(',') } } if ( typeof pol.requireBareCryptoMin === 'string' && String(pol.requireBareCryptoMin).trim() ) { const needS = String(pol.requireBareCryptoMin).trim() const haveS = ctx.env && String(ctx.env.BARE_OS_BARE_CRYPTO_VERSION || '').trim() const ok = haveS && semverGte(haveS, needS) if (!ok) { bootStructuredLog( ctx, 'error', 'requireBareCryptoMin', '[boot-policy] requireBareCryptoMin not satisfied' ) if (strictPol) { if (typeof ctx.requestBooterExit === 'function') { ctx.requestBooterExit(1) } return false } } } if ( typeof pol.requirePearIpcMin === 'string' && String(pol.requirePearIpcMin).trim() ) { const needS = String(pol.requirePearIpcMin).trim() const haveS = ctx.env && String(ctx.env.BARE_OS_PEAR_IPC_PACKAGE_VERSION || '').trim() const ok = haveS && semverGte(haveS, needS) if (!ok) { bootStructuredLog( ctx, 'error', 'requirePearIpcMin', '[boot-policy] requirePearIpcMin not satisfied' ) if (strictPol) { if (typeof ctx.requestBooterExit === 'function') { ctx.requestBooterExit(1) } return false } } } if ( pol.extensionSignerPinsV2 && typeof pol.extensionSignerPinsV2 === 'object' && ctx.env ) { try { ctx.env.BARE_OS_BOOT_POLICY_EXTENSION_SIGNER_PINS_V2_JSON = JSON.stringify(pol.extensionSignerPinsV2) } catch { /* ignore */ } } if ( pol.extensionSignerPinsV3 && typeof pol.extensionSignerPinsV3 === 'object' && ctx.env ) { try { ctx.env.BARE_OS_BOOT_POLICY_EXTENSION_SIGNER_PINS_V3_JSON = JSON.stringify(pol.extensionSignerPinsV3) } catch { /* ignore */ } } if ( pol.extensionSignerPinsV4 && typeof pol.extensionSignerPinsV4 === 'object' && ctx.env ) { try { ctx.env.BARE_OS_BOOT_POLICY_EXTENSION_SIGNER_PINS_V4_JSON = JSON.stringify(pol.extensionSignerPinsV4) } catch { /* ignore */ } } if ( pol.extensionSignerPinsV5 && typeof pol.extensionSignerPinsV5 === 'object' && ctx.env ) { try { ctx.env.BARE_OS_BOOT_POLICY_EXTENSION_SIGNER_PINS_V5_JSON = JSON.stringify(pol.extensionSignerPinsV5) } catch { /* ignore */ } } if ( typeof pol.requireBareBootMin === 'string' && String(pol.requireBareBootMin).trim() ) { const needS = String(pol.requireBareBootMin).trim() const haveS = ctx.env && String(ctx.env.BARE_OS_BARE_BOOT_VERSION || '').trim() const ok = haveS && semverGte(haveS, needS) if (!ok) { bootStructuredLog( ctx, 'error', 'requireBareBootMin', '[boot-policy] requireBareBootMin not satisfied' ) if (strictPol) { if (typeof ctx.requestBooterExit === 'function') { ctx.requestBooterExit(1) } return false } } } if ( Array.isArray(pol.denyBareRpcMethodPatterns) && ctx.env ) { try { ctx.env.BARE_OS_BOOT_POLICY_DENY_BARE_RPC_PATTERNS_JSON = JSON.stringify(pol.denyBareRpcMethodPatterns) } catch { /* ignore */ } } if ( typeof pol.maxPearInspectDepth === 'number' && pol.maxPearInspectDepth >= 0 && ctx.env ) { ctx.env.BARE_OS_BOOT_POLICY_MAX_PEAR_INSPECT_DEPTH = String( Math.min(64, Math.floor(pol.maxPearInspectDepth)) ) } if ( typeof pol.maxHrpcAllowlistDepth === 'number' && pol.maxHrpcAllowlistDepth >= 0 && ctx.env ) { ctx.env.BARE_OS_BOOT_POLICY_MAX_HRPC_ALLOWLIST_DEPTH = String( Math.min(64, Math.floor(pol.maxHrpcAllowlistDepth)) ) } if ( typeof pol.requireBareLoggerMin === 'string' && String(pol.requireBareLoggerMin).trim() ) { const needS = String(pol.requireBareLoggerMin).trim() const haveS = ctx.env && String(ctx.env.BARE_OS_BARE_LOGGER_VERSION || '').trim() const ok = haveS && semverGte(haveS, needS) if (!ok) { bootStructuredLog( ctx, 'error', 'requireBareLoggerMin', '[boot-policy] requireBareLoggerMin not satisfied' ) if (strictPol) { if (typeof ctx.requestBooterExit === 'function') { ctx.requestBooterExit(1) } return false } } } if ( Array.isArray(pol.denyAutobaseDiscoveryChannels) && ctx.env ) { try { ctx.env.BARE_OS_BOOT_POLICY_DENY_AUTOBASE_DISCOVERY_CHANNELS_JSON = JSON.stringify(pol.denyAutobaseDiscoveryChannels) } catch { /* ignore */ } } if ( typeof pol.requireBareTlsMin === 'string' && String(pol.requireBareTlsMin).trim() ) { const needS = String(pol.requireBareTlsMin).trim() const haveS = ctx.env && String(ctx.env.BARE_OS_BARE_TLS_VERSION || '').trim() const ok = haveS && semverGte(haveS, needS) if (!ok) { bootStructuredLog( ctx, 'error', 'requireBareTlsMin', '[boot-policy] requireBareTlsMin not satisfied' ) if (strictPol) { if (typeof ctx.requestBooterExit === 'function') { ctx.requestBooterExit(1) } return false } } } if ( typeof pol.requireBarePackMin === 'string' && String(pol.requireBarePackMin).trim() ) { const needS = String(pol.requireBarePackMin).trim() const haveS = ctx.env && String(ctx.env.BARE_OS_BARE_PACK_VERSION || '').trim() const ok = haveS && semverGte(haveS, needS) if (!ok) { bootStructuredLog( ctx, 'error', 'requireBarePackMin', '[boot-policy] requireBarePackMin not satisfied' ) if (strictPol) { if (typeof ctx.requestBooterExit === 'function') { ctx.requestBooterExit(1) } return false } } } if ( typeof pol.requireBareAddonPolicyMin === 'string' && String(pol.requireBareAddonPolicyMin).trim() ) { const needS = String(pol.requireBareAddonPolicyMin).trim() const haveS = ctx.env && String(ctx.env.BARE_OS_BARE_ADDON_POLICY_VERSION || '').trim() const ok = haveS && semverGte(haveS, needS) if (!ok) { bootStructuredLog( ctx, 'error', 'requireBareAddonPolicyMin', '[boot-policy] requireBareAddonPolicyMin not satisfied' ) if (strictPol) { if (typeof ctx.requestBooterExit === 'function') { ctx.requestBooterExit(1) } return false } } } if (pol.offlineLkgRequirePearStamp === true && ctx.env) { ctx.env.BARE_OS_OFFLINE_LKG_REQUIRE_PEAR_STAMP = '1' } if (pol.offlineLkgRequireHypercorePackHrpcLifecycle === true && ctx.env) { ctx.env.BARE_OS_OFFLINE_LKG_REQUIRE_HYPERCORE_PACK_HRPC_LIFECYCLE = '1' } if ( typeof pol.offlineLkgManifestMaxAgeSec === 'number' && pol.offlineLkgManifestMaxAgeSec > 0 && ctx.env ) { ctx.env.BARE_OS_OFFLINE_LKG_MANIFEST_MAX_AGE_SEC = String( Math.min(1e9, Math.floor(pol.offlineLkgManifestMaxAgeSec)) ) } if ( typeof pol.maxProtomuxChannelNameLength === 'number' && pol.maxProtomuxChannelNameLength >= 8 && ctx.env ) { const n = Math.min(512, Math.floor(pol.maxProtomuxChannelNameLength)) ctx.env.BARE_OS_BOOT_POLICY_MAX_PROTO_MUX_CHANNEL_NAME_LENGTH = String(n) } if ( typeof pol.maxKernelExtensionDepth === 'number' && pol.maxKernelExtensionDepth >= 1 && ctx.env ) { const d = Math.min(32, Math.floor(pol.maxKernelExtensionDepth)) ctx.env.BARE_OS_BOOT_POLICY_MAX_KERNEL_EXT_DEPTH = String(d) } if ( typeof pol.gitPartialClonePolicy === 'string' && String(pol.gitPartialClonePolicy).trim() && ctx.env ) { ctx.env.BARE_OS_BOOT_POLICY_GIT_PARTIAL_CLONE = String( pol.gitPartialClonePolicy ).trim() } if ( typeof pol.maxInitdRestartsPerUnit === 'number' && pol.maxInitdRestartsPerUnit >= 1 && ctx.env ) { const cap = Math.min(32, Math.floor(pol.maxInitdRestartsPerUnit)) ctx.env.BARE_OS_INITD_RESTART_MAX_DEFAULT = String(cap) } if ( typeof pol.maxExecLineDepth === 'number' && pol.maxExecLineDepth > 0 && ctx.env ) { const cap = Math.min(256, Math.floor(pol.maxExecLineDepth)) ctx.env.BARE_OS_EXEC_MAX_DEPTH = String(cap) } if (Array.isArray(pol.denyEnvKeys) && ctx.env) { for (const k of pol.denyEnvKeys) { const key = String(k).trim() if (key && Object.prototype.hasOwnProperty.call(ctx.env, key)) { delete ctx.env[key] } } } const needLifecycleSchema = Math.max( typeof pol.bootStagesRequireLifecycleMinSchema === 'number' ? pol.bootStagesRequireLifecycleMinSchema : 0, typeof pol.bootPhasesRequireLifecycleMinSchema === 'number' ? pol.bootPhasesRequireLifecycleMinSchema : 0 ) if (needLifecycleSchema > 0) { const needSch = Math.floor(needLifecycleSchema) const raw = ctx.env && String(ctx.env.BARE_OS_LIFECYCLE_SCHEMA_VERSION || '').trim() const haveSch = Number.parseInt(raw, 10) const ok = Number.isFinite(haveSch) && haveSch >= needSch if (!ok) { bootStructuredLog( ctx, 'error', 'bootStagesRequireLifecycleMinSchema', '[boot-policy] bootStagesRequireLifecycleMinSchema not satisfied' ) if (strictPol) { if (typeof ctx.requestBooterExit === 'function') { ctx.requestBooterExit(1) } return false } } } const needProcIndexSchema = Math.max( typeof pol.bootStagesRequireProcIndexMinSchema === 'number' ? pol.bootStagesRequireProcIndexMinSchema : 0, typeof pol.bootPhasesRequireProcIndexMinSchema === 'number' ? pol.bootPhasesRequireProcIndexMinSchema : 0 ) if (needProcIndexSchema > 0) { const vfs = ctx.vfs const needSch = Math.floor(needProcIndexSchema) if (vfs && typeof vfs.readFile === 'function') { try { const raw = await vfs.readFile('/proc/bare_os/index.json') const txt = raw && ctx.b4a && typeof ctx.b4a.toString === 'function' ? ctx.b4a.toString(raw) : '' const j = txt ? JSON.parse(txt) : null const sch = j && typeof j === 'object' && typeof j.schema === 'number' ? j.schema : 0 if (sch < needSch) throw new Error('proc_index schema too old') } catch { bootStructuredLog( ctx, 'error', 'bootStagesRequireProcIndexMinSchema', '[boot-policy] bootStagesRequireProcIndexMinSchema not satisfied' ) if (strictPol) { if (typeof ctx.requestBooterExit === 'function') { ctx.requestBooterExit(1) } return false } } } } if (Array.isArray(pol.requireProcNodes)) { const vfs = ctx.vfs if (vfs && typeof vfs.readFile === 'function') { for (const rel of pol.requireProcNodes) { const raw = String(rel).trim() const p = raw.startsWith('/') ? raw : '/proc/' + raw.replace(/^\/*/, '') try { const b = await vfs.readFile(p) const empty = !b || (ctx.b4a && typeof ctx.b4a.from === 'function' && ctx.b4a.from(b).length === 0) if (empty) throw new Error('empty or missing') } catch { bootStructuredLog( ctx, 'error', 'requireProcNodes', '[boot-policy] requireProcNodes not satisfied: ' + p ) if (strictPol) { if (typeof ctx.requestBooterExit === 'function') { ctx.requestBooterExit(1) } return false } } } } } } catch (e) { bootStructuredLog( ctx, 'error', 'bootPolicyParse', '[boot-policy] boot.policy.json: ' + ((e && e.message) || String(e)) ) } return true }