Files
bare-operating-system/packages/bare-os-booter/lib/tools/bare-os-bin-index.js
T
2026-08-18 18:11:28 -04:00

145 lines
4.2 KiB
JavaScript

/**
* Optional `/bin` manifest on the personal drive for large images (`BARE_OS_VFS_BIN_INDEX_BUILD=1`).
*/
/**
* Deterministic SHA-256 over UTF-8 names with `\0` terminators (Web Crypto — Node 20 + Bare).
* @param {string[]} sortedNames
*/
async function bareOsBinIndexNamesDigestAsync(sortedNames) {
const enc = new TextEncoder()
let totalLen = 0
for (const n of sortedNames) {
totalLen += enc.encode(String(n)).byteLength + 1
}
const u8 = new Uint8Array(totalLen)
let o = 0
for (const n of sortedNames) {
const s = enc.encode(String(n))
u8.set(s, o)
o += s.byteLength
u8[o++] = 0
}
const subtle = globalThis.crypto && globalThis.crypto.subtle
if (!subtle || typeof subtle.digest !== 'function') {
throw new Error('bare-os bin index: Web Crypto subtle.digest unavailable')
}
const digest = await subtle.digest('SHA-256', u8)
return Array.from(new Uint8Array(digest), (b) =>
b.toString(16).padStart(2, '0')
).join('')
}
/**
* When **`BARE_OS_BIN_HYPERBEE_INDEX=1`**, writes **`/.bare-os/index/bin-hyperbee-hint.json`**
* (module resolution probe + name list) for operators integrating Hyperbee/Corestore offline.
*/
export async function maybeBareOsBuildBinHyperbeeHint(ctx) {
const e = ctx.env
if (
e.BARE_OS_BIN_HYPERBEE_INDEX !== '1' &&
e.BARE_OS_BIN_HYPERBEE_INDEX !== 'true'
) {
return
}
const vfs = ctx.vfs
const drive = ctx.drive
const b4 = ctx.b4a
if (!vfs || !drive || typeof drive.readdir !== 'function' || !b4) return
let moduleResolves = false
try {
await import('hyperbee')
moduleResolves = true
} catch {
moduleResolves = false
}
const names = []
try {
const stream = drive.readdir('/bin')
for await (const n of stream) names.push(String(n))
} catch {
return
}
names.sort()
const digest = await bareOsBinIndexNamesDigestAsync(names)
const body =
JSON.stringify({
schema: 2,
atMs: Date.now(),
namesDigest: digest,
namesDigestAlgo: 'sha256-null-suffixed-utf8',
moduleResolves,
count: names.length,
names: names.slice(0, 8000),
invalidation: {
schema: 1,
triggers: ['bareOsVfsBatchWrite bin/*', 'BARE_OS_VFS_BIN_INDEX_BUILD'],
note: 'Rebuild when batch writes touch /bin or on boot when index env flags are set.'
},
note:
'Hyperbee-backed KV index is host-wired; this file lists /bin names and whether hyperbee imports on the booter runtime.'
}) + '\n'
try {
await vfs.writeFile('/.bare-os/index/bin-hyperbee-hint.json', b4.from(body))
} catch (err) {
ctx.console?.error?.(
'[bare-os] bin hyperbee hint: ' + ((err && err.message) || String(err))
)
}
try {
const idx =
JSON.stringify({
schema: 2,
atMs: Date.now(),
namesDigest: digest,
primaryHint: '/.bare-os/index/bin-hyperbee-hint.json',
note: 'Secondary metadata path for `/.bare-os/indexes/` namespace (Hyperbee operator integration).'
}) + '\n'
await vfs.writeFile('/.bare-os/indexes/hyperbee_status.json', b4.from(idx))
} catch {
/* personal tree may deny path on some images */
}
}
export async function maybeBareOsBuildBinManifest(ctx) {
const e = ctx.env
if (
e.BARE_OS_VFS_BIN_INDEX_BUILD !== '1' &&
e.BARE_OS_VFS_BIN_INDEX_BUILD !== 'true'
) {
return
}
const vfs = ctx.vfs
const drive = ctx.drive
const b4 = ctx.b4a
if (!vfs || !drive || typeof drive.readdir !== 'function' || !b4) return
/** @type {string[]} */
const names = []
try {
const stream = drive.readdir('/bin')
for await (const n of stream) names.push(String(n))
} catch {
return
}
names.sort()
const digest = await bareOsBinIndexNamesDigestAsync(names)
const body =
JSON.stringify({
schema: 2,
atMs: Date.now(),
path: '/bin',
namesDigest: digest,
namesDigestAlgo: 'sha256-null-suffixed-utf8',
count: names.length,
names: names.slice(0, 8000),
note: 'Sorted lexicographic /bin listing; digest covers full sorted set before truncation.'
}) + '\n'
try {
await vfs.writeFile('/.bare-os/index/bin-manifest.json', b4.from(body))
} catch (err) {
ctx.console?.error?.(
'[bare-os] bin index: ' + ((err && err.message) || String(err))
)
}
}