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:
Raven Scott
2026-04-05 14:17:20 -04:00
parent c15e8fa5be
commit 8bde745191
87 changed files with 7385 additions and 6725 deletions
+12 -5
View File
@@ -10,7 +10,7 @@ Root **`pretest`** already enforces this sequence; follow the same order when ch
1. **`npm run build -w bare-os-coreutils`** — emit `/bin` sources into **`kernel/bin`** (and man pages).
2. **`npm run build -w bare-os-bare-libs`** — esbuild Holepunch **`bare-*`** IIFEs into **`kernel/lib/bare/`**, refresh **`docs/audit/bundle-health.json`**.
3. **`npm run bundle:kernel`** (part of **`pretest`**) — concatenate **`kernel/lib/boot/*.js`** + **`kernel/lib/init/init-main.js`** → **`kernel/init.js`**.
3. **`npm run bundle:kernel`** (part of **`pretest`**) — concatenate **`kernel/lib/boot/*.js`** + **`kernel/lib/init/fragments/*.js`** + **`kernel/lib/init/init-main.js`** → **`kernel/init.js`**.
4. **`node scripts/verify-kernel-seeder-parity.mjs`** — **`kernel/`** must match **`packages/bare-os-seeder/kernel/`** byte-for-byte (rsync or seeder copy after edits).
If **`verify-kernel-seeder-parity`** fails after bare-libs or init changes, sync the seeder tree before committing (see **`kernel/lib/bare/README.md`** / **`packages/bare-os-bare-libs/README.kernel-lib-bare.md`**).
@@ -56,13 +56,13 @@ Invoked automatically by:
**Usage:** `node scripts/verify-kernel-seeder-parity.mjs`
Exits **0** only when **`kernel/`** and **`packages/bare-os-seeder/kernel/`** contain the **same recursive file set** with **byte-identical** contents (full mirror parity, not a spot check on **`init.js`** alone). Requires **`init.js`**, **`lib/init/init-main.js`**, **`lib/boot/*.js`**, **`lib/bare/manifest.json`**, **`lib/bare/bare-module-manifest.json`**, and asserts **`kernel/init.js`** matches the bundle recipe via **`scripts/lib/kernel-init-bundle.mjs`**. Every **`kernel/bin/*`** must include **`BARE_OS_BIN_API`**. Invoked from the root **`pretest`** hook after **`npm run build -w bare-os-coreutils`**. **Sync:** `rsync -a --delete kernel/ packages/bare-os-seeder/kernel/` (after **`bundle:kernel`** when **`init.js`** is generated).
Exits **0** only when **`kernel/`** and **`packages/bare-os-seeder/kernel/`** contain the **same recursive file set** with **byte-identical** contents (full mirror parity, not a spot check on **`init.js`** alone). Requires **`init.js`**, **`lib/init/init-main.js`**, **`lib/init/fragments/*.js`**, **`lib/boot/*.js`**, **`lib/bare/manifest.json`**, **`lib/bare/bare-module-manifest.json`**, and asserts **`kernel/init.js`** matches the bundle recipe via **`scripts/lib/kernel-init-bundle.mjs`**. Every **`kernel/bin/*`** must include **`BARE_OS_BIN_API`**. Invoked from the root **`pretest`** hook after **`npm run build -w bare-os-coreutils`**. **Sync:** `rsync -a --delete kernel/ packages/bare-os-seeder/kernel/` (after **`bundle:kernel`** when **`init.js`** is generated).
## `verify-init-bundle-recipe.mjs`
**Usage:** `node scripts/verify-init-bundle-recipe.mjs` (also **`npm run verify:init-bundle`**)
Fails when **`kernel/init.js`** does not match the bundle recipe (sorted **`kernel/lib/boot/*.js`** + **`kernel/lib/init/init-main.js`**). Shared logic lives in **`scripts/lib/kernel-init-bundle.mjs`**. Also invoked from **`npm run release-checklist`**.
Fails when **`kernel/init.js`** does not match the bundle recipe (sorted **`kernel/lib/boot/*.js`** + **`kernel/lib/init/fragments/*.js`** + **`kernel/lib/init/init-main.js`**). Shared logic lives in **`scripts/lib/kernel-init-bundle.mjs`**. Also invoked from **`npm run release-checklist`**.
## `verify-banned-terminology.mjs`
@@ -74,7 +74,7 @@ Fails when banned governance tokens (**`Wave N`**, **`wave N`**, **`tranche`**,
**Usage:** `node scripts/bundle-kernel-init.mjs` (also **`npm run bundle:kernel`**)
Concatenates sorted `kernel/lib/boot/*.js` + `kernel/lib/init/init-main.js`**`kernel/init.js`**. Run after editing boot fragments; root **`pretest`** invokes it automatically.
Concatenates sorted `kernel/lib/boot/*.js` + sorted `kernel/lib/init/fragments/*.js` + `kernel/lib/init/init-main.js`**`kernel/init.js`**. Run after editing boot or init fragments; root **`pretest`** invokes it automatically.
## `verify-feature-roadmap-canonical.mjs`
@@ -298,6 +298,12 @@ Fails when **`BARE_OS_POSIX_PROFILE_VERSION`** / **`BARE_OS_POSIX_PROFILE_ID`**
Runs on every **`pretest`**. For each repo basename listed in **`repos`** inside **[`docs/audit/holepunch-drift-repos.json`](../docs/audit/holepunch-drift-repos.json)**, checks that **`origin/main`** is an ancestor of **`HEAD`** (clone not behind remote). With an empty **`repos`** array, the script is a no-op. The same file carries **`suggestedCriticalRepos`** and optional **`goldenPathRepos`** (wire stack + runtime hints); copy **`suggestedCriticalRepos`** into **`repos`** when you maintain local clones and want CI to enforce freshness. **`goldenPathRepos`** is informational only (surfaced in **`holepunch-clone-sync-report.json`**). Set **`BARE_OS_HOLEPUNCH_DRIFT_CHECK=0`** to skip the script entirely (e.g. offline sandboxes). **`sync-holepunch-clones.mjs`** echoes **`suggestedCriticalRepos`** into **`holepunch-clone-sync-report.json`** for review.
## `verify-holepunch-clone-freshness.mjs`
**Usage:** `node scripts/verify-holepunch-clone-freshness.mjs` (chained in root **`pretest`** after drift)
Reads **[`docs/audit/holepunch-freshness-gate.json`](../docs/audit/holepunch-freshness-gate.json)**. Default **`enabled: false`** — the script logs and exits **0** so offline CI never needs local clones. When **`enabled`** is **`true`**, listed repos under **`BARE_OS_HOLEPUNCH_CLONES_ROOT`** (default **`~/dev/pearcli/holepunch-repos/holepunchto_repos`**) that exceed **`maxCommitsBehind`** vs **`origin/main`** cause a **warning**; set **`BARE_OS_HOLEPUNCH_FRESHNESS_STRICT=1`** to **fail** **`pretest`**. Requires **`git fetch origin main`** in each clone for meaningful counts.
## `gen-bare-holepunch-catalog.mjs`
**Usage:** `node scripts/gen-bare-holepunch-catalog.mjs` (also **`npm run gen:bare-catalog`** / **`npm run gen:bare-catalog:check`** from the repo root)
@@ -389,7 +395,8 @@ Root **`npm run pretest`** is the canonical doc/code gate. Run it before pushing
13. **`scripts/verify-pear-no-static-node-import.mjs`**.
14. **`scripts/gen-posix-dashboard.mjs`** + **`verify-posix-dashboard.mjs`** + **`verify-posix-profile-triplet.mjs`** + **`verify-posix-compliance-matrix.mjs`**.
15. **`scripts/verify-holepunch-clone-drift.mjs`** — No-op when **`holepunch-drift-repos.json`** **`repos[]`** is empty; optional release gate when populated.
16. **`npm run smoke:bare-manifest`** — Manifest import smoke.
16. **`scripts/verify-holepunch-clone-freshness.mjs`** — Optional lag gate from **`holepunch-freshness-gate.json`** (**`enabled: false`** by default); strict mode via **`BARE_OS_HOLEPUNCH_FRESHNESS_STRICT=1`**.
17. **`npm run smoke:bare-manifest`** — Manifest import smoke.
**Commit expectation:** check in every regenerated artifact **`pretest` produces** (kernel init bundle, seeder mirror, generated markdown/JSON, dashboard) in the same change set as the source edit.
+21 -3
View File
@@ -1,7 +1,8 @@
#!/usr/bin/env node
/**
* Concatenate kernel/lib/boot/*.js (sorted) + kernel/lib/init/init-main.js → kernel/init.js.
* Run after editing boot fragments or init-main; keep packages/bare-os-seeder/kernel in sync.
* Concatenate kernel/lib/boot/*.js (sorted) + kernel/lib/init/fragments/*.js (sorted) +
* kernel/lib/init/init-main.js → kernel/init.js.
* Run after editing boot fragments, init fragments, or init-main; keep packages/bare-os-seeder/kernel in sync.
*/
import fs from 'node:fs'
import path from 'node:path'
@@ -10,6 +11,7 @@ import { fileURLToPath } from 'node:url'
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
const bootDir = path.join(root, 'kernel', 'lib', 'boot')
const initFragmentsDir = path.join(root, 'kernel', 'lib', 'init', 'fragments')
const mainPath = path.join(root, 'kernel', 'lib', 'init', 'init-main.js')
const outPath = path.join(root, 'kernel', 'init.js')
@@ -28,8 +30,24 @@ function main() {
parts.push(fs.readFileSync(path.join(bootDir, n), 'utf8').trimEnd())
}
}
const fragParts = []
if (fs.existsSync(initFragmentsDir)) {
const fnames = fs
.readdirSync(initFragmentsDir)
.filter((n) => n.endsWith('.js'))
.sort()
for (const n of fnames) {
fragParts.push(
fs.readFileSync(path.join(initFragmentsDir, n), 'utf8').trimEnd()
)
}
}
const main = fs.readFileSync(mainPath, 'utf8')
const out = (parts.length ? parts.join('\n\n') + '\n\n' : '') + main
let out = parts.length ? parts.join('\n\n') + '\n\n' : ''
if (fragParts.length) {
out += fragParts.join('\n\n') + '\n\n'
}
out += main
fs.writeFileSync(outPath, out)
console.log('bundle-kernel-init: OK →', path.relative(root, outPath))
}
+22 -3
View File
@@ -1,5 +1,6 @@
/**
* Single source of truth for the kernel/init.js bundle recipe (lib/boot/*.js + lib/init/init-main.js).
* Single source of truth for the kernel/init.js bundle recipe
* (lib/boot/*.js + lib/init/fragments/*.js + lib/init/init-main.js).
* Used by verify-kernel-seeder-parity and verify-init-bundle-recipe.
*/
import fs from 'node:fs'
@@ -11,6 +12,7 @@ import path from 'node:path'
*/
export function computeKernelInitJsFromRecipe(canonicalKernelDir) {
const bootDir = path.join(canonicalKernelDir, 'lib', 'boot')
const initFragmentsDir = path.join(canonicalKernelDir, 'lib', 'init', 'fragments')
const mainPath = path.join(canonicalKernelDir, 'lib', 'init', 'init-main.js')
if (!fs.existsSync(mainPath)) {
throw new Error(`kernel-init-bundle: missing ${mainPath}`)
@@ -25,8 +27,25 @@ export function computeKernelInitJsFromRecipe(canonicalKernelDir) {
parts.push(fs.readFileSync(path.join(bootDir, n), 'utf8').trimEnd())
}
}
const fragParts = []
if (fs.existsSync(initFragmentsDir)) {
const fnames = fs
.readdirSync(initFragmentsDir)
.filter((n) => n.endsWith('.js'))
.sort()
for (const n of fnames) {
fragParts.push(
fs.readFileSync(path.join(initFragmentsDir, n), 'utf8').trimEnd()
)
}
}
const main = fs.readFileSync(mainPath, 'utf8')
return (parts.length ? parts.join('\n\n') + '\n\n' : '') + main
let out = parts.length ? parts.join('\n\n') + '\n\n' : ''
if (fragParts.length) {
out += fragParts.join('\n\n') + '\n\n'
}
out += main
return out
}
/**
@@ -38,7 +57,7 @@ export function assertKernelInitJsMatchesRecipe(canonicalKernelDir) {
const actual = fs.readFileSync(outPath, 'utf8')
if (actual !== expected) {
throw new Error(
'kernel/init.js does not match bundle recipe (lib/boot/*.js + lib/init/init-main.js). Run: node scripts/bundle-kernel-init.mjs'
'kernel/init.js does not match bundle recipe (lib/boot/*.js + lib/init/fragments/*.js + lib/init/init-main.js). Run: node scripts/bundle-kernel-init.mjs'
)
}
}
@@ -11,6 +11,7 @@ import { fileURLToPath } from 'node:url'
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
const schemaPath = path.join(root, 'docs/schemas/boot.policy.schema.json')
const initMain = path.join(root, 'kernel/lib/init/init-main.js')
const initFragmentsDir = path.join(root, 'kernel/lib/init/fragments')
const EXPECTED = [
['extensionSignerPinsV2', 'BARE_OS_BOOT_POLICY_EXTENSION_SIGNER_PINS_V2_JSON'],
@@ -37,17 +38,30 @@ function main() {
process.exit(1)
}
}
let src
/** @type {string[]} */
const chunks = []
try {
src = fs.readFileSync(initMain, 'utf8')
chunks.push(fs.readFileSync(initMain, 'utf8'))
} catch (e) {
console.error('verify-boot-policy-extension-signer-pins: read init-main', e)
process.exit(1)
}
try {
const names = fs.readdirSync(initFragmentsDir)
for (const n of names.sort()) {
if (!n.endsWith('.js')) continue
chunks.push(
fs.readFileSync(path.join(initFragmentsDir, n), 'utf8')
)
}
} catch {
/* optional fragments dir */
}
const src = chunks.join('\n')
for (const [, envKey] of EXPECTED) {
if (!src.includes(envKey)) {
console.error(
'verify-boot-policy-extension-signer-pins: init-main missing',
'verify-boot-policy-extension-signer-pins: kernel init sources missing',
envKey
)
process.exit(1)
+39 -120
View File
@@ -1,6 +1,7 @@
#!/usr/bin/env node
/**
* CI: ensure booter wires ctx API semver and protocol stock feature mask.
* Stock symbol lists: packages/bare-os-protocol/lib/stock-booter-capability-contract.json
*/
import fs from 'node:fs'
import path from 'node:path'
@@ -18,12 +19,22 @@ const bitsPath = path.join(
root,
'packages/bare-os-protocol/lib/kernel-feature-bits.js'
)
const contractPath = path.join(
root,
'packages/bare-os-protocol/lib/stock-booter-capability-contract.json'
)
function read(p) {
return fs.readFileSync(p, 'utf8')
}
function main() {
const contract = JSON.parse(read(contractPath))
if (contract.schema !== 1) {
console.error('stock-booter-capability-contract.json: unsupported schema')
process.exit(1)
}
const ctxSrc = read(ctxApiPath)
const verMatch = ctxSrc.match(
/export const BARE_OS_CTX_API_VERSION = '([^']+)'/
@@ -34,128 +45,28 @@ function main() {
}
const idx = read(indexPath)
if (!idx.includes('bareOsCtxApiVersion: BARE_OS_CTX_API_VERSION')) {
console.error(
'index.js should assign bareOsCtxApiVersion from BARE_OS_CTX_API_VERSION'
)
process.exit(1)
}
if (!idx.includes('BARE_OS_KERNEL_FEATURES_STOCK_WORD_PRIMARY')) {
console.error('index.js should reference BARE_OS_KERNEL_FEATURES_STOCK_WORD_PRIMARY')
process.exit(1)
}
if (!idx.includes('BARE_OS_KERNEL_FEATURES_STOCK_WORD_EXTENDED_SEEDING_PLATFORM')) {
console.error('index.js should reference BARE_OS_KERNEL_FEATURES_STOCK_WORD_EXTENDED_SEEDING_PLATFORM')
process.exit(1)
}
if (!idx.includes('BARE_OS_KERNEL_FEATURES_STOCK_WORD_RLIMITS_DELEGATES_SHELL')) {
console.error('index.js should reference BARE_OS_KERNEL_FEATURES_STOCK_WORD_RLIMITS_DELEGATES_SHELL')
process.exit(1)
}
if (!idx.includes('bareOsAdvertisedKernelCapabilityWords')) {
console.error('index.js should expose bareOsAdvertisedKernelCapabilityWords (wire v2)')
process.exit(1)
}
if (!idx.includes('bareOsSeedKernelCapabilityWords')) {
console.error('index.js should expose bareOsSeedKernelCapabilityWords (wire v2)')
process.exit(1)
}
if (!idx.includes('KERNEL_CAPABILITY_SEED_STRICT_ROWS')) {
console.error('index.js should define KERNEL_CAPABILITY_SEED_STRICT_ROWS for seed strict checks')
process.exit(1)
}
if (!idx.includes('BARE_OS_KERNEL_FEATURES_STOCK_WORD_REPLICATION_OPERATOR_SURFACE')) {
console.error('index.js should reference BARE_OS_KERNEL_FEATURES_STOCK_WORD_REPLICATION_OPERATOR_SURFACE')
process.exit(1)
}
if (!idx.includes('BARE_OS_KERNEL_FEATURES_STOCK_WORD_PEAR_CORESTORE_HRPC')) {
console.error('index.js should reference BARE_OS_KERNEL_FEATURES_STOCK_WORD_PEAR_CORESTORE_HRPC')
process.exit(1)
}
if (!idx.includes('BARE_OS_KERNEL_FEATURES_STOCK_WORD_BARE_RUNTIME_PROTO_MUX')) {
console.error('index.js should reference BARE_OS_KERNEL_FEATURES_STOCK_WORD_BARE_RUNTIME_PROTO_MUX')
process.exit(1)
}
if (!idx.includes('BARE_OS_KERNEL_FEATURES_STOCK_WORD_BARE_MODULE_CRYPTO_STAGING')) {
console.error('index.js should reference BARE_OS_KERNEL_FEATURES_STOCK_WORD_BARE_MODULE_CRYPTO_STAGING')
process.exit(1)
}
if (!idx.includes('BARE_OS_KERNEL_FEATURES_STOCK_WORD_PEAR_INSPECT_LOGGER_TLS')) {
console.error('index.js should reference BARE_OS_KERNEL_FEATURES_STOCK_WORD_PEAR_INSPECT_LOGGER_TLS')
process.exit(1)
}
if (!idx.includes('BARE_OS_KERNEL_FEATURES_STOCK_WORD_HYPERCORE_PACK_HRPC_LIFECYCLE')) {
console.error('index.js should reference BARE_OS_KERNEL_FEATURES_STOCK_WORD_HYPERCORE_PACK_HRPC_LIFECYCLE')
process.exit(1)
const reqIdx = Array.isArray(contract.requiredIndexSubstrings)
? contract.requiredIndexSubstrings
: []
for (const s of reqIdx) {
if (!idx.includes(s)) {
console.error('index.js should include substring:', s)
process.exit(1)
}
}
const bitsSrc = read(bitsPath)
if (!bitsSrc.includes('export const BARE_OS_KERNEL_FEATURES_STOCK_WORD_PRIMARY')) {
console.error(
'kernel-feature-bits.js missing BARE_OS_KERNEL_FEATURES_STOCK_WORD_PRIMARY'
)
process.exit(1)
}
if (!bitsSrc.includes('export const BARE_OS_KERNEL_FEATURES_STOCK_WORD_EXTENDED_SEEDING_PLATFORM')) {
console.error(
'kernel-feature-bits.js missing BARE_OS_KERNEL_FEATURES_STOCK_WORD_EXTENDED_SEEDING_PLATFORM'
)
process.exit(1)
}
if (!bitsSrc.includes('export const BARE_OS_KERNEL_FEATURES_STOCK_WORD_RLIMITS_DELEGATES_SHELL')) {
console.error(
'kernel-feature-bits.js missing BARE_OS_KERNEL_FEATURES_STOCK_WORD_RLIMITS_DELEGATES_SHELL'
)
process.exit(1)
}
if (!bitsSrc.includes('export const BARE_OS_KERNEL_FEATURES_STOCK_WORD_OFFLINE_NET_EXTENSIONS')) {
console.error(
'kernel-feature-bits.js missing BARE_OS_KERNEL_FEATURES_STOCK_WORD_OFFLINE_NET_EXTENSIONS'
)
process.exit(1)
}
if (!bitsSrc.includes('export const BARE_OS_KERNEL_FEATURES_STOCK_WORD_HOST_TRANSPORT_DELEGATES')) {
console.error(
'kernel-feature-bits.js missing BARE_OS_KERNEL_FEATURES_STOCK_WORD_HOST_TRANSPORT_DELEGATES'
)
process.exit(1)
}
if (!bitsSrc.includes('export const BARE_OS_KERNEL_FEATURES_STOCK_WORD_REPLICATION_OPERATOR_SURFACE')) {
console.error(
'kernel-feature-bits.js missing BARE_OS_KERNEL_FEATURES_STOCK_WORD_REPLICATION_OPERATOR_SURFACE'
)
process.exit(1)
}
if (!bitsSrc.includes('export const BARE_OS_KERNEL_FEATURES_STOCK_WORD_PEAR_CORESTORE_HRPC')) {
console.error(
'kernel-feature-bits.js missing BARE_OS_KERNEL_FEATURES_STOCK_WORD_PEAR_CORESTORE_HRPC'
)
process.exit(1)
}
if (!bitsSrc.includes('export const BARE_OS_KERNEL_FEATURES_STOCK_WORD_BARE_RUNTIME_PROTO_MUX')) {
console.error(
'kernel-feature-bits.js missing BARE_OS_KERNEL_FEATURES_STOCK_WORD_BARE_RUNTIME_PROTO_MUX'
)
process.exit(1)
}
if (!bitsSrc.includes('export const BARE_OS_KERNEL_FEATURES_STOCK_WORD_BARE_MODULE_CRYPTO_STAGING')) {
console.error(
'kernel-feature-bits.js missing BARE_OS_KERNEL_FEATURES_STOCK_WORD_BARE_MODULE_CRYPTO_STAGING'
)
process.exit(1)
}
if (!bitsSrc.includes('export const BARE_OS_KERNEL_FEATURES_STOCK_WORD_PEAR_INSPECT_LOGGER_TLS')) {
console.error(
'kernel-feature-bits.js missing BARE_OS_KERNEL_FEATURES_STOCK_WORD_PEAR_INSPECT_LOGGER_TLS'
)
process.exit(1)
}
if (!bitsSrc.includes('export const BARE_OS_KERNEL_FEATURES_STOCK_WORD_HYPERCORE_PACK_HRPC_LIFECYCLE')) {
console.error(
'kernel-feature-bits.js missing BARE_OS_KERNEL_FEATURES_STOCK_WORD_HYPERCORE_PACK_HRPC_LIFECYCLE'
)
process.exit(1)
const reqBits = Array.isArray(contract.requiredBitsExports)
? contract.requiredBitsExports
: []
for (const name of reqBits) {
const needle = `export const ${name}`
if (!bitsSrc.includes(needle)) {
console.error('kernel-feature-bits.js missing', name)
process.exit(1)
}
}
const docMatch = bitsSrc.match(
/export const BARE_OS_KERNEL_FEATURE_BITS_DOC = (\d+)/
)
@@ -163,8 +74,14 @@ function main() {
console.error('Could not parse BARE_OS_KERNEL_FEATURE_BITS_DOC')
process.exit(1)
}
if (docMatch[1] !== '15') {
console.error('Expected BARE_OS_KERNEL_FEATURE_BITS_DOC = 15')
const wantDoc = String(contract.expectedFeatureBitsDoc ?? '')
if (!wantDoc || docMatch[1] !== wantDoc) {
console.error(
'Expected BARE_OS_KERNEL_FEATURE_BITS_DOC =',
wantDoc,
'got',
docMatch[1]
)
process.exit(1)
}
@@ -209,6 +126,8 @@ function main() {
verMatch[1],
'| bits doc',
docMatch[1],
'| contract',
path.relative(root, contractPath),
'OK'
)
}
@@ -0,0 +1,118 @@
#!/usr/bin/env node
/**
* Optional gate: when docs/audit/holepunch-freshness-gate.json has enabled=true,
* fail if listed clones are more than maxCommitsBehind origin/main.
* Requires BARE_OS_HOLEPUNCH_FRESHNESS_STRICT=1 (or "true") to exit non-zero.
* Clones root: BARE_OS_HOLEPUNCH_CLONES_ROOT (default ~/dev/pearcli/holepunch-repos/holepunchto_repos).
*/
import fs from 'node:fs'
import path from 'node:path'
import process from 'node:process'
import { fileURLToPath } from 'node:url'
import { execSync } from 'node:child_process'
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
const configPath = path.join(root, 'docs/audit/holepunch-freshness-gate.json')
const defaultClones = path.join(
process.env.HOME || '',
'dev/pearcli/holepunch-repos/holepunchto_repos'
)
const clonesRoot = String(
process.env.BARE_OS_HOLEPUNCH_CLONES_ROOT || defaultClones
).trim()
function gitOk(cmd, cwd) {
try {
execSync(cmd, {
cwd,
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'ignore']
})
return true
} catch {
return false
}
}
function gitCommitsBehindOriginMain(dir) {
try {
const o = execSync('git rev-list --count HEAD..origin/main', {
cwd: dir,
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'ignore']
}).trim()
const n = Number.parseInt(o, 10)
return Number.isFinite(n) ? n : -1
} catch {
return -1
}
}
function main() {
if (!fs.existsSync(configPath)) {
console.log('verify-holepunch-clone-freshness: no config — skip')
return
}
let cfg
try {
cfg = JSON.parse(fs.readFileSync(configPath, 'utf8'))
} catch (e) {
console.error('verify-holepunch-clone-freshness: bad JSON', e)
process.exit(1)
}
if (!cfg || cfg.enabled !== true) {
console.log(
'verify-holepunch-clone-freshness: disabled (set enabled:true in holepunch-freshness-gate.json)'
)
return
}
const strict =
process.env.BARE_OS_HOLEPUNCH_FRESHNESS_STRICT === '1' ||
process.env.BARE_OS_HOLEPUNCH_FRESHNESS_STRICT === 'true'
const max = Number.parseInt(String(cfg.maxCommitsBehind ?? ''), 10)
const maxBehind = Number.isFinite(max) && max > 0 ? max : 32
const repos = Array.isArray(cfg.repos)
? cfg.repos.map((r) => String(r).trim()).filter(Boolean)
: []
if (!repos.length) {
console.log('verify-holepunch-clone-freshness: repos[] empty')
return
}
/** @type {string[]} */
const bad = []
for (const name of repos) {
const dir = path.join(clonesRoot, name)
if (!fs.existsSync(dir) || !gitOk('git rev-parse --git-dir', dir)) continue
if (!gitOk('git rev-parse --verify origin/main', dir)) {
console.warn(
'verify-holepunch-clone-freshness: no origin/main in',
name,
'(git fetch origin main)'
)
continue
}
const behind = gitCommitsBehindOriginMain(dir)
if (behind > maxBehind) {
console.warn(
`verify-holepunch-clone-freshness: ${name} behind origin/main by ${behind} (max ${maxBehind})`
)
bad.push(name)
}
}
if (bad.length && strict) {
console.error(
'verify-holepunch-clone-freshness: FAIL — stale clones:',
bad.join(', ')
)
process.exit(1)
}
if (bad.length && !strict) {
console.log(
'verify-holepunch-clone-freshness: warnings only (set BARE_OS_HOLEPUNCH_FRESHNESS_STRICT=1 to fail)'
)
} else {
console.log('verify-holepunch-clone-freshness: OK')
}
}
main()
+2
View File
@@ -80,6 +80,8 @@ function main() {
const required = [
'init.js',
'lib/init/init-main.js',
'lib/init/fragments/20-init-boot-policy.js',
'lib/init/fragments/30-init-kernel-extensions.js',
'lib/boot/00-pear-multisig-shape.js',
'lib/boot/01-invoke-ctx-hooks.js',
'lib/bare/manifest.json',
@@ -73,6 +73,7 @@ function main() {
const files = [
path.join(root, 'kernel', 'init.js'),
path.join(root, 'kernel', 'lib', 'init', 'init-main.js'),
...walk(path.join(root, 'kernel', 'lib', 'init', 'fragments')),
...walk(path.join(root, 'kernel', 'lib', 'boot')),
...walk(path.join(root, 'kernel', 'bin')),
...walk(path.join(root, 'packages', 'bare-os-booter', 'lib')),