- disk.os: path manifest search, metrics schema bump, docs

- HRPC: bare_os.search_local, route table/schema updates; ctx API 1.52.0
- VFS: optional path-capability enforcement; pathcap-verify coreutil
- Replication/boot: warm-cache adaptive metrics, boot budget NDJSON v2
- Identity: savevault pre-snapshot host hint
- POSIX: profile 1.0.16, socket connect timeout env, Wasm ctx API peek import
- Security/ops: peer admission test, personal-drive path policy verifier in pretest
- P2P UX: pkg-swarm-index; multisig rc.proposals/enabled gate + audit rows
- Docs/scripts: README, handbook, env appendix, developer-guide, ctx helper sync
- Examples: syscalls.example ctx version; seeder kernel rsync parity
This commit is contained in:
Raven Scott
2026-04-05 15:27:39 -04:00
parent 169df862f7
commit 41526508df
86 changed files with 3116 additions and 551 deletions
+1 -1
View File
@@ -46,7 +46,7 @@ The [developer guide](developer-guide/README.md) is the **how-to** for `start(ct
### POSIX surface
Intentional coverage and gaps for utilities, shell, and VFS: [handbook — Chapter 9](handbook/09-posix-utilities-shell-and-vfs.md). Declared profile **`1.0.15`** (**`BARE_OS_POSIX_PROFILE_VERSION`**) with **`ctx`** API **`1.51.1`** — see [POSIX declared profile](docs/architecture/POSIX_DECLARED_PROFILE.md) and [compatibility matrix](docs/reference/compatibility-matrix.md). Machine-readable summary: [docs/reference/posix-compliance-matrix.json](docs/reference/posix-compliance-matrix.json) (verified in **`pretest`**).
Intentional coverage and gaps for utilities, shell, and VFS: [handbook — Chapter 9](handbook/09-posix-utilities-shell-and-vfs.md). Declared profile **`1.0.16`** (**`BARE_OS_POSIX_PROFILE_VERSION`**) with **`ctx`** API **`1.52.0`** — see [POSIX declared profile](docs/architecture/POSIX_DECLARED_PROFILE.md) and [compatibility matrix](docs/reference/compatibility-matrix.md). Machine-readable summary: [docs/reference/posix-compliance-matrix.json](docs/reference/posix-compliance-matrix.json) (verified in **`pretest`**).
## Quick start
+2
View File
@@ -72,6 +72,8 @@ Default **guest** sessions have predictable **`HOME=/home/guest`** and no Ed2551
**`pear.multisig.json`** metadata (signer list + quorum) is validated by the same shape predicate in **`bare-os-protocol`** (seeder hint) and the bundled guest helper **`bareOsPearMultisigShapeOk`** in **`kernel/lib/boot/00-pear-multisig-shape.js`**; keep those files aligned when changing rules.
**`BARE_OS_RC_PROPOSAL_MULTISIG_STRICT`** ties **`/etc/bare-os/rc.proposals/enabled/*.json`** to that policy before **`rc.d`**: each proposal lists approved signer public keys in **`signaturesFrom`**; the guest checks membership and quorum only (host tooling should verify signatures over proposal payloads before writing **`enabled/`**).
---
## Reporting issues
+1 -1
View File
@@ -70,7 +70,7 @@ Items **1100** map to the first approved plan (boot graph, journal, safe mode
## Optional Wasm kernel bridge
**`ctx.bareOsWasmKernelCompile`** / **`ctx.bareOsWasmKernelInstantiate`** (see **`packages/bare-os-booter/lib/bare-os-wasm-kernel.js`**) compile or instantiate a **bounded** Wasm module when **`BARE_OS_WASM_KERNEL=1`**. The booter applies **`maxBytes`**, **memory page caps**, and a **wall-clock instantiate timeout**; syscall-shaped imports (**`bare_os_pathconf`**, **`bare_os_wall_time_ms32`**, **`bare_os_umask_get`**, optional **`bare_os_monotonic_ms`** when **`BARE_OS_WASM_KERNEL_MONOTONIC_MS`**, optional **`bare_os_hostname_peek`** when **`BARE_OS_WASM_KERNEL_HOSTNAME_IMPORT`**) are only constructed when **`BARE_OS_WASM_KERNEL_SYSCALL`** is enabled and the corresponding **`ctx`** hooks exist. **Parity note:** the full **`posixXsh.namesCsv`** surface ( **`clock_gettime`**, **`nanosleep`**, socket bridge ops, …) lives on **`ctx.bareOsSyscall`** in JS — it is **not** exported into the Wasm import object because those paths are async or bridge-heavy. Wasm guests should call back into JS/host shims when they need syscall parity beyond the **sync** clock / umask / pathconf imports. Treat Wasm as **untrusted**: keep modules small, gate on env, and never pass secrets into guest linear memory without a separate review.
**`ctx.bareOsWasmKernelCompile`** / **`ctx.bareOsWasmKernelInstantiate`** (see **`packages/bare-os-booter/lib/bare-os-wasm-kernel.js`**) compile or instantiate a **bounded** Wasm module when **`BARE_OS_WASM_KERNEL=1`**. The booter applies **`maxBytes`**, **memory page caps**, and a **wall-clock instantiate timeout**; syscall-shaped imports (**`bare_os_pathconf`**, **`bare_os_wall_time_ms32`**, **`bare_os_umask_get`**, optional **`bare_os_monotonic_ms`** when **`BARE_OS_WASM_KERNEL_MONOTONIC_MS`**, optional **`bare_os_hostname_peek`** when **`BARE_OS_WASM_KERNEL_HOSTNAME_IMPORT`**, optional **`bare_os_ctx_api_version_peek`** when **`BARE_OS_WASM_KERNEL_CTX_API_PEEK`**) are only constructed when **`BARE_OS_WASM_KERNEL_SYSCALL`** is enabled and the corresponding **`ctx`** hooks exist. **Parity note:** the full **`posixXsh.namesCsv`** surface ( **`clock_gettime`**, **`nanosleep`**, socket bridge ops, …) lives on **`ctx.bareOsSyscall`** in JS — it is **not** exported into the Wasm import object because those paths are async or bridge-heavy. Wasm guests should call back into JS/host shims when they need syscall parity beyond the **sync** clock / umask / pathconf imports. Treat Wasm as **untrusted**: keep modules small, gate on env, and never pass secrets into guest linear memory without a separate review.
## See also
+1 -1
View File
@@ -72,7 +72,7 @@ Shared error classes (stable `code` field): [`packages/bare-os-booter/lib/bare-o
## Seed RPC and hrpc bridges
- **Seed RPC** (`bare_os.*` methods on the seeder wire): [`packages/bare-os-protocol/lib/channel.js`](../../packages/bare-os-protocol/lib/channel.js) answers only methods listed in [`seed-rpc-methods.js`](../../packages/bare-os-protocol/lib/seed-rpc-methods.js); unknown names return **`bare_os.rpc_unknown_method`** (default deny). Boot policy may add **`denySeedRpcMethods`** / env **`BARE_OS_BOOT_POLICY_DENY_SEED_RPC_METHODS`** so the stock booter skips selected calls.
- **hrpc** (`ctx.bareOsHrpcRequest`): the stock booter implementation validates inputs, enforces optional **`BARE_OS_HRPC_ALLOWLIST_JSON`** (JSON **array** of **`"service.method"`** strings, or **object** whose **keys** are route names and values are truthy — see [`docs/schemas/bare-os-hrpc-allowlist.schema.json`](../schemas/bare-os-hrpc-allowlist.schema.json); wildcards **`"*"`**, **`"kernel.*"`**, etc.), and serves a built-in route table (**`kernel.ping`**, **`kernel.capabilities`**, **`vfs.readText`**, **`bare_os.echo`**, **`bare_os.disk_os_hints`**, **`bare_os.replication_operator_sketch`**, **`bare_os.replication_snapshot`** — disk.os-backed routes return the same JSON strings as **`execRpc`**). **Deny-by-default:** any **`service.method`** not in the stock table throws **`bareOsHrpcRequest: unsupported route …`** unless the host replaces **`ctx.bareOsHrpcRequest`** or **`BARE_OS_HRPC_EMIT_UNLISTED`** is **`1`** / **`true`** (then **`process.emit('bare-os:hrpc-request', …)`** fires for unlisted routes). Set **`BARE_OS_HRPC_AUDIT`** to append tamper-evident audit rows for attempts (via **`bareOsAuditChainLinkAppend`**). Treat new routes as security-sensitive: extend only with explicit allowlist semantics and tests.
- **hrpc** (`ctx.bareOsHrpcRequest`): the stock booter implementation validates inputs, enforces optional **`BARE_OS_HRPC_ALLOWLIST_JSON`** (JSON **array** of **`"service.method"`** strings, or **object** whose **keys** are route names and values are truthy — see [`docs/schemas/bare-os-hrpc-allowlist.schema.json`](../schemas/bare-os-hrpc-allowlist.schema.json); wildcards **`"*"`**, **`"kernel.*"`**, etc.), and serves a built-in route table (**`kernel.ping`**, **`kernel.capabilities`**, **`vfs.readText`**, **`bare_os.echo`**, **`bare_os.disk_os_hints`**, **`bare_os.search_local`**, **`bare_os.replication_operator_sketch`**, **`bare_os.replication_snapshot`** — disk.os-backed routes return the same JSON strings as **`execRpc`** except **`bare_os.search_local`**, which returns **`{ ok, query, paths }`** from **`disk.os.searchLocal`**). **Deny-by-default:** any **`service.method`** not in the stock table throws **`bareOsHrpcRequest: unsupported route …`** unless the host replaces **`ctx.bareOsHrpcRequest`** or **`BARE_OS_HRPC_EMIT_UNLISTED`** is **`1`** / **`true`** (then **`process.emit('bare-os:hrpc-request', …)`** fires for unlisted routes). Set **`BARE_OS_HRPC_AUDIT`** to append tamper-evident audit rows for attempts (via **`bareOsAuditChainLinkAppend`**). Treat new routes as security-sensitive: extend only with explicit allowlist semantics and tests.
## Related docs
+3 -1
View File
@@ -6,7 +6,7 @@ This document is the **normative contract** for how closely the stock Bare OS ke
| Constant | Value |
| -------- | ----- |
| `BARE_OS_POSIX_PROFILE_VERSION` | `1.0.15` |
| `BARE_OS_POSIX_PROFILE_VERSION` | `1.0.16` |
| `BARE_OS_POSIX_PROFILE_ID` | `bare-os-posix-like` |
| `BARE_OS_POSIX_PROFILE_REFERENCE` | Open Group Issue 7 index URL |
@@ -72,3 +72,5 @@ Signal names accepted by **`kill`** and shell builtins follow POSIX **names** wh
## 6. Profile evolution
Bump **`BARE_OS_POSIX_PROFILE_VERSION`** in `bare-os-protocol` when **intentional** utility, shell, VFS, or proc-contract behavior changes. Update this file, handbook ch.9, and §14a in the same change set.
**1.0.16** — Stock **`pathcap-verify`**, **`pkg-swarm-index`**, optional **path-capability** VFS gate (**`BARE_OS_PATH_CAPABILITY_*`**), **`ctx.bareOsVerifyPathCapabilityEnvelope`**, **`disk.os` path manifest + `bare_os.search_local` hrpc**, **`metrics_live.replicationLive` schema 4** (**`warmReplAdaptive`**), boot budget transaction **`bootBudgetSchemaVersion` 2** (**`bootBudgetViolations`**), Wasm **`env.bare_os_ctx_api_version_peek`**, optional **`BARE_OS_POSIX_SOCKET_CONNECT_TIMEOUT_MS`** on bridge TCP **`connect`**, **`BARE_OS_SAVEVAULT_PRESNAPSHOT_HINT`** host event, optional **`BARE_OS_RC_PROPOSAL_MULTISIG_STRICT`** + **`rc.proposals/enabled`** audit rows (**`rc.proposal.multisig_*`**).
+6
View File
@@ -58,6 +58,12 @@ Upstream-generated IIFEs may contain `TODO` comments, `NOT_IMPLEMENTED` helpers,
Re-run a full-repo scan before each release and append a dated section here when new gaps are introduced or closed. Maintainer automation: **`npm run audit:placeholder-baseline`** writes [`placeholder-baseline-scan.json`](placeholder-baseline-scan.json) (first-party TODO/stub phrase sweep including **`packages/bare-os-coreutils/src`**). Root **`pretest`** runs **`npm run audit:placeholder-baseline:check`**, which fails if the live scan finds hits not already recorded in the committed scan JSON or in [`placeholder-baseline-allowlist.json`](placeholder-baseline-allowlist.json) (use allowlist only for time-boxed exceptions). Compare hits to this document and **`verify-runtime-no-incomplete-markers.mjs`**. **`npm run audit:holepunch-clones`** refreshes [`holepunch-clone-sync-report.json`](holepunch-clone-sync-report.json) and [`holepunch-lockfile-drift.json`](holepunch-lockfile-drift.json). Optional strict clone lag: [`holepunch-freshness-gate.json`](holepunch-freshness-gate.json) + **`scripts/verify-holepunch-clone-freshness.mjs`** (root **`pretest`**, default **`enabled: false`**).
## 2026-04-05 — P3 bundle allowlists at target
| Closure | Notes |
| ------- | ----- |
| **Empty marker/throw allowlists** | **`docs/audit/bundle-marker-allowlist.json`** and **`bundle-throw-allowlist.json`** remain **empty**; **`node scripts/verify-bundle-markers.mjs`** and **`verify-bundle-throws.mjs`** pass on stock tree. Vendored IIFEs stay sanitized via **`scripts/sanitize-bare-bundles.mjs`** during **`npm run build -w bare-os-bare-libs`**. |
## 2026-04-05 — Placeholder-baseline scan hygiene
| Closure | Notes |
+1 -1
View File
@@ -1,6 +1,6 @@
{
"schemaVersion": 1,
"generatedAt": "2026-04-05T19:03:42.323Z",
"generatedAt": "2026-04-05T19:26:43.567Z",
"normativeManifest": "packages/bare-os-booter/lib/bare-module-manifest.json",
"buildTool": "packages/bare-os-bare-libs/build.mjs",
"bundles": [
+1 -1
View File
@@ -26,7 +26,7 @@ After replication, the booter records **`vfs`** → **`ctx`** → **`repl`** →
### Swarm `disk.os` bridge (post-`initd`)
[`createBareOsDiskOsBridge`](../../packages/bare-os-booter/lib/bare-os-disk-os-bridge.js) is assigned to **`disk.os`**. Peer **`searchLocal`** aggregates path substring matches from the **system** Hyperdrive and every **`SwarmDisk.auxiliaryDrives`** entry (read-only mirrors under **`/mirror/aux*`**), dedupes, and caps results. Optional host env **`BARE_OS_DISK_OS_SEARCH_THROTTLE_MS`** (0500) yields between per-drive scans for large images. Whitelisted RPC **`bare_os.disk_os_hints`** exposes advisory **`mirrorDriveHintV2`** and **`httpDhtProxyHint`** objects when the booter populated them from seed RPCs — operators only; guests do not auto-apply them. **`bare_os.replication_operator_sketch`** returns **schema 5** JSON (seed replication status, **`swarmPeerCount`**, Hyperswarm **`connectionBudget`**, protomux operator sketch, pause/backpressure env mirrors, optional **`hyperblobsDedupSketch`** / **`corestoreOperatorSketch`** / **`peerPrioritySketch`**). **`bare_os.replication_operator_intent`** appends an audit row when **`BARE_OS_DISK_OS_OPERATOR_INTENT_RPC=1`** and the booter wires **`auditBatch`**.
[`createBareOsDiskOsBridge`](../../packages/bare-os-booter/lib/bare-os-disk-os-bridge.js) is assigned to **`disk.os`**. Peer **`searchLocal`** aggregates path substring matches from the **system** Hyperdrive and every **`SwarmDisk.auxiliaryDrives`** entry (read-only mirrors under **`/mirror/aux*`**), dedupes, and caps results. The system drive may ship **`/etc/bare-os/path-manifest.json`** (or **`BARE_OS_DISK_OS_PATH_MANIFEST`**) so common paths match **without** a full recursive **`list()`** on the primary drive; **`BARE_OS_DISK_OS_SEARCH_MANIFEST_ONLY`** skips primary listing when a complete manifest is maintained. Optional host env **`BARE_OS_DISK_OS_SEARCH_THROTTLE_MS`** (0500) yields between **auxiliary** drive scans. Whitelisted RPC **`bare_os.disk_os_hints`** (**schema 3**) exposes advisory **`mirrorDriveHintV2`**, **`httpDhtProxyHint`**, and **`pathSearchLocal`** metrics — operators only; guests do not auto-apply mirror hints. Stock **`ctx.bareOsHrpcRequest`** includes **`bare_os.search_local`** (**`payload.query`**) returning **`paths`** from **`disk.os.searchLocal`**. **`bare_os.replication_operator_sketch`** returns **schema 5** JSON (seed replication status, **`swarmPeerCount`**, Hyperswarm **`connectionBudget`**, protomux operator sketch, pause/backpressure env mirrors, optional **`hyperblobsDedupSketch`** / **`corestoreOperatorSketch`** / **`peerPrioritySketch`**). **`bare_os.replication_operator_intent`** appends an audit row when **`BARE_OS_DISK_OS_OPERATOR_INTENT_RPC=1`** and the booter wires **`auditBatch`**.
```mermaid
flowchart TB
+2 -2
View File
@@ -14,7 +14,7 @@ Single place to compare **protocol**, **booter `ctx` API**, **feature-bit docume
## Version artifacts
**`bareOsCtxApiVersion`** (current stock: **`1.51.1`**) — Defined in [`packages/bare-os-booter/lib/bare-os-ctx-api.js`](../../packages/bare-os-booter/lib/bare-os-ctx-api.js). This is the semver string for the documented **`ctx`** contract; bump it when stable guest-visible behavior changes.
**`bareOsCtxApiVersion`** (current stock: **`1.52.0`**) — Defined in [`packages/bare-os-booter/lib/bare-os-ctx-api.js`](../../packages/bare-os-booter/lib/bare-os-ctx-api.js). This is the semver string for the documented **`ctx`** contract; bump it when stable guest-visible behavior changes.
**`BARE_OS_KERNEL_FEATURE_BITS_DOC`** (current stock: **`15`**) — Exported from [`packages/bare-os-protocol/lib/kernel-feature-bits.js`](../../packages/bare-os-protocol/lib/kernel-feature-bits.js). It versions the feature-bit documentation and related governance; keep it aligned with [`developer-guide/adr/001-kernel-feature-bits-governance.md`](../../developer-guide/adr/001-kernel-feature-bits-governance.md).
@@ -24,7 +24,7 @@ Single place to compare **protocol**, **booter `ctx` API**, **feature-bit docume
**Protocol package** — Workspace version in [`bare-os-protocol` `package.json`](../../packages/bare-os-protocol/package.json). Pair it with seeder and booter releases when you change wire behavior.
**POSIX-like profile** — **`BARE_OS_POSIX_PROFILE_VERSION`** (**`1.0.15`**) and **`BARE_OS_POSIX_PROFILE_ID`** (**`bare-os-posix-like`**) in [`bare-os-protocol/lib/bare-os-posix-profile.js`](../../packages/bare-os-protocol/lib/bare-os-posix-profile.js). Surfaced under **`/proc/bare_os_features`** as **`capabilitySurface.posixProfile`** and in **`/proc/bare_os/syscalls.json`**.
**POSIX-like profile** — **`BARE_OS_POSIX_PROFILE_VERSION`** (**`1.0.16`**) and **`BARE_OS_POSIX_PROFILE_ID`** (**`bare-os-posix-like`**) in [`bare-os-protocol/lib/bare-os-posix-profile.js`](../../packages/bare-os-protocol/lib/bare-os-posix-profile.js). Surfaced under **`/proc/bare_os_features`** as **`capabilitySurface.posixProfile`** and in **`/proc/bare_os/syscalls.json`**.
**`/proc/bare_os/syscalls.json`** — Schema **10** (see also [handbook ch.9 §2.3](../../handbook/09-posix-utilities-shell-and-vfs.md#23-pseudo-proc-sys-run-dev-and-session-tmp)) adds **`posix_fadvise`** as a simulated **`ctx.bareOsSyscall`** no-op and retains **`clock_gettime`** (REALTIME / MONOTONIC sketch); earlier schema fields remain: cooperative **`fcntl`** advisory locks (optional **`BARE_OS_POSIX_FCNTL_BLOCKING_WAIT`** for **`F_SETLKW`**-shaped waits), optional **`BARE_OS_POSIX_SOCKET_FD_BRIDGE`** **`socket`/`connect`** (**`SOCK_STREAM`** via **`bareTcp`**, **`SOCK_DGRAM`** via **`bareDgram`** when merged) with bounded **`send`/`sendmsg`/`recv`/`recvfrom`/`recvmsg`** on bridged datagram fds (**`BARE_OS_POSIX_DGRAM_RECVQ_MAX`**, **`BARE_OS_POSIX_DGRAM_RECV_BLOCK_MS_MAX`**), **`socketMsgSurface`** schema **5** (**`sendmsg`/`recvmsg`** partial on bridge; binary ancillary **`ENOTSUP`**; optional **`logicalScmRightsOnSend`** when **`BARE_OS_POSIX_SOCKET_SCM_RIGHTS`**; **`recvmsg`** **`controllen` 0**), and retains socket-family **probes** plus **`posixXsh`**. Earlier schemas added **`fdModel`**, **`signalModel`**, **`opsDetail`**, **`errnoHints`**, and **`posixProfile`**. The stock booter **caches** the JSON text until **`vfs.bareOsClearWarmReadCaches()`** / **`ctx.bareOsInvalidateWarmReadCaches()`** (same invalidation path as **`/bin`** warm cache; **`boot/init.js`** and **`lib/init/**` batch puts also invalidate). Example payload: [`kernel/etc/bare-os/syscalls.example.json`](../../kernel/etc/bare-os/syscalls.example.json); JSON Schema: [`docs/schemas/bare-os-syscalls.schema.json`](../schemas/bare-os-syscalls.schema.json).
@@ -1,16 +1,16 @@
// Auto-generated by scripts/gen-ctx-client-helper.mjs — do not edit by hand.
/** Canonical `ctx` API semver from `packages/bare-os-booter/lib/bare-os-ctx-api.js`. */
export const BARE_OS_CTX_API_CLIENT_VERSION = '1.51.1' as const
export const BARE_OS_CTX_API_CLIENT_VERSION = '1.52.0' as const
/** Relative path to the hand-maintained TypeScript contract (schema source of truth). */
export const BARE_OS_CTX_DTS_SOURCE = 'packages/bare-os-booter/lib/bare-os-ctx.d.ts' as const
/** Approximate count of `bareOs…` members in the DTS (diagnostic only; regenerate on contract edits). */
export const BARE_OS_CTX_DTS_BAREOS_MEMBER_APPROX = 154 as const
export const BARE_OS_CTX_DTS_BAREOS_MEMBER_APPROX = 155 as const
/** Byte length of `bare-os-ctx.d.ts` when this file was generated. */
export const BARE_OS_CTX_DTS_BYTES = 19868 as const
export const BARE_OS_CTX_DTS_BYTES = 20046 as const
/** Pointers for external client generators (OpenAPI / JSON Schema follow-ups). */
export const BARE_OS_CTX_SCHEMA_HINT = {
@@ -40,6 +40,16 @@ The list below is one **bullet per variable** in the form **name — component
- `BARE_OS_MBR_READ_TIMEOUT_MS` — Booter (host) — Max wait in milliseconds for replicated **block 0** / MBR before boot fails (**≥ 3000**, hard cap **600000**); when unset, defaults to **60000** unless adaptive mode adjusts it.
- `BARE_OS_MBR_READ_TIMEOUT_ADAPTIVE` — Booter — When **`1`** / **`true`** and **`BARE_OS_MBR_READ_TIMEOUT_MS`** is unset, adjusts the MBR wait from live **peer count** (sparse swarms get a longer budget; multi-peer sessions may use a shorter one).
- `BARE_OS_DISK_OS_SEARCH_THROTTLE_MS` — Booter / **`disk.os`** — Optional **0500** ms delay between scanning the system Hyperdrive and each **`auxiliaryDrives`** entry during peer **`searchLocal`** fan-out (default **0**); softens load on large offline mirrors.
- `BARE_OS_DISK_OS_PATH_MANIFEST` — Booter / **`disk.os`** — Absolute path on the **system** Hyperdrive to a JSON manifest (**default** **`/etc/bare-os/path-manifest.json`**). The file may be **`{ "schema": 1, "paths": ["/bin/sh", …] }`** or a bare JSON array of path strings. Substring matches from the manifest are merged **before** the primary drives recursive listing. Set to **empty** to disable manifest acceleration (full listing only on the primary drive).
- `BARE_OS_DISK_OS_SEARCH_MANIFEST_ONLY` — Booter — When **`1`** / **`true`**, **`searchLocal` skips the primary drives recursive `list()` scan** and uses only manifest hits on that drive (auxiliary drives are still listed). For tests and operator-tuned images with a complete manifest.
- `BARE_OS_DISK_OS_MANIFEST_CACHE_MS` — Booter — Cache parsed manifest paths per system drive (**default** **30000**, max **3600000**). Reduces **`drive.get`** churn when peers issue many searches.
- `BARE_OS_PATH_CAPABILITY_ENFORCE_READ` — Booter / VFS — When **`1`** / **`true`**, reads on the **personal** drive under **`BARE_OS_PATH_CAPABILITY_PREFIX`** require a valid **`user.bareos.cap_v1`** entry in **`PATH.bare_xattr.json`** (see **`path-capability.envelope.example.json`** and **`ctx.bareOsVerifyPathCapabilityEnvelope`**). Sidecar and ACL files are exempt from the check.
- `BARE_OS_PATH_CAPABILITY_PREFIX` — Booter — Logical path prefix for the capability gate (**default** **`/home/`**). Only applies with **`BARE_OS_PATH_CAPABILITY_ENFORCE_READ`**.
- `BARE_OS_SAVEVAULT_PRESNAPSHOT_HINT` — Booter — When **`1`** / **`true`**, **`saveVaultToDrive`** emits **`process.emit('bare-os:vault-pre-save-snapshot-hint', …)`** so the host may snapshot the personal Corestore namespace before sealing vault blobs.
- `BARE_OS_POSIX_SOCKET_CONNECT_TIMEOUT_MS` — Booter — Optional **SOCK_STREAM** bridge **`connect`** wall timeout in milliseconds (**default** **0** = unlimited). On expiry the socket is destroyed and **`connect`** returns **`ETIMEDOUT`**.
- `BARE_OS_WASM_KERNEL_CTX_API_PEEK` — Booter / guest env — With **`BARE_OS_WASM_KERNEL_SYSCALL`**, adds Wasm import **`env.bare_os_ctx_api_version_peek`** writing **`ctx.bareOsCtxApiVersion`** NUL-terminated (**bounded**).
- `BARE_OS_PKG_SWARM_TOPIC_HEX` — Guest — Optional **64-hex** topic class hint for **`pkg-swarm-index`** operator documentation (not wired to Hyperswarm by the stock utility).
- `BARE_OS_RC_PROPOSAL_MULTISIG_STRICT` — Stock kernel — When **`1`** / **`true`**, before **`rc.d`** validate **`/etc/bare-os/rc.proposals/enabled/*.json`** against **`/etc/bare-os/pear.multisig.json`** (**`signaturesFrom`** ⊆ **`signers`**, distinct count ≥ **`quorum`**). Emits audit rows **`rc.proposal.multisig_*`** via **`ctx.bareOsAuditLogAppend`** when present.
- `BARE_OS_NO_SPLASH` — Booter — If `1`, skip TTY splash (plain logs / non-TTY behavior unchanged)
- `BARE_OS_LOCAL_SEED` — paths — Overrides local seed path helper (`defaultLocalSeedCorestorePath`); booter does not local-boot
- `BARE_OS_SKIP_REPL` — Booter — If `1`, readline returns null — non-interactive exit
+2 -2
View File
@@ -65,8 +65,8 @@ See also [Kernel subsystem map](../architecture/kernel-subsystems.md) and [ADR 0
- **`saveVault()`** — encrypt personal-drive files into `/.bare/vault/` (skips `/.bare`, `bin`, `boot`, history paths).
- **`registerKernelShutdownHook(fn)`** — register async/sync teardown before **`stopBareInitd`** when the REPL session cleans up.
- After **`createKernelReplSession`**: wires **`readLine`**, **`console`**, **`execLine`**, **`suspendReplForSubprocess`** / **`resumeReplAfterSubprocess`**, then **`await startBareInitd(ctx)`** (services such as kernel log mirroring).
- Sets **`disk.os`** **after initd** with **`createBareOsDiskOsBridge`** (**`searchLocal`**, whitelisted **`execRpc`** / **`bare_os.*`**, **`disk_os_hints`** schema **2**, **`replication_operator_sketch`** schema **5**, cap-gated **`replication_operator_intent`** when **`BARE_OS_DISK_OS_OPERATOR_INTENT_RPC=1`**) — peers use **`SwarmDisk`** messages **36**; see [`bare-os-disk-os-bridge.js`](../../packages/bare-os-booter/lib/bare-os-disk-os-bridge.js) and §12.4.
- **`ctx.bareOsHrpcRequest`** (stock) — validates **`service`**, **`method`**, object **`payload`**; optional **`BARE_OS_HRPC_ALLOWLIST_JSON`**; built-in routes **`kernel.ping`**, **`kernel.capabilities`**, **`vfs.readText`**, **`bare_os.echo`**, **`bare_os.disk_os_hints`**, **`bare_os.replication_operator_sketch`**, **`bare_os.replication_snapshot`**. **`BARE_OS_HRPC_EMIT_UNLISTED`** forwards unknown routes to **`process.emit('bare-os:hrpc-request', …)`** on Node when enabled.
- Sets **`disk.os`** **after initd** with **`createBareOsDiskOsBridge`** (**`searchLocal`** with optional path manifest, whitelisted **`execRpc`** / **`bare_os.*`**, **`disk_os_hints`** schema **3**, **`replication_operator_sketch`** schema **5**, cap-gated **`replication_operator_intent`** when **`BARE_OS_DISK_OS_OPERATOR_INTENT_RPC=1`**) — peers use **`SwarmDisk`** messages **36**; see [`bare-os-disk-os-bridge.js`](../../packages/bare-os-booter/lib/bare-os-disk-os-bridge.js) and §12.4.
- **`ctx.bareOsHrpcRequest`** (stock) — validates **`service`**, **`method`**, object **`payload`**; optional **`BARE_OS_HRPC_ALLOWLIST_JSON`**; built-in routes **`kernel.ping`**, **`kernel.capabilities`**, **`vfs.readText`**, **`bare_os.echo`**, **`bare_os.disk_os_hints`**, **`bare_os.search_local`**, **`bare_os.replication_operator_sketch`**, **`bare_os.replication_snapshot`** (**`BARE_OS_HRPC_ROUTE_TABLE_SCHEMA_VERSION` 2**). **`BARE_OS_HRPC_EMIT_UNLISTED`** forwards unknown routes to **`process.emit('bare-os:hrpc-request', …)`** on Node when enabled.
- `try { await runKernelFromSource(...) } finally { await session.cleanup() }` — cleanup runs **`bareInitdShutdownActiveUnitsReverse`**, **`runKernelShutdownHooks`**, **`stopBareInitd`**, fish TTY teardown.
**`boot-splash.js`** — TTY splash (disabled when `stdout` is not a TTY or `BARE_OS_NO_SPLASH=1`): initial full-screen clear and hidden cursor, then centered redraws from the top-left without erasing the whole screen each tick (reduces flicker); one full clear again if the terminal is resized. Vertically and horizontally centered “card” (Unicode box frame when wide enough; compact rules on very narrow widths). On very wide terminals the framed card (and progress bar) width is capped so the bar does not span the entire display. Layout scales with `stdout.columns` / `stdout.rows` (sensible fallbacks): more log lines on tall terminals (capped), word-wrapped phase text, ANSI-safe centering. **`createBootSplash(stdout, { bootLimitMs?, tagline?, footerLines? })`** — optional **`tagline`** and dim **`footerLines`**; **`main()`** passes booter and protocol package versions. Braille spinner, elapsed boot timer, bar vs `BARE_OS_BOOT_TIMEOUT_MS` (default 60s), shimmer title. `prepareForKernel()` stops the splash ticker, clears again, and shows the cursor before the fish shell; `fail()` centers a wrapped error message.
+1 -1
View File
@@ -1,7 +1,7 @@
{
"schema": 1,
"profileId": "bare-os-posix-like",
"note": "Machine-readable summary; normative detail remains in handbook ch.9 and environment POSIX appendix. Profile 1.0.15 / syscalls schema 10: adds ctx.bareOsSyscall posix_fadvise simulated no-op; guest session read/write/chmod/unlink deny on /.bare/account and /.bare/vault/** (override BARE_OS_GUEST_BARE_READ_ALL); optional BARE_OS_PERSONAL_ACCT_PREFIX personal-drive layout; BARE_OS_PERSONAL_ROOT_MIGRATE + /.bare-os/migration/legacy-root-v1.json for safe legacy-root lift; BARE_OS_GUEST_SCRUB; vfs.replication_warm_full_invalidate metric; ctx.bareOsSyscall nanosleep-shaped delay; bridge getsockopt/setsockopt partial SO_KEEPALIVE/TCP_NODELAY when BARE_OS_POSIX_SOCKET_FD_BRIDGE; cooperative fcntl locks (optional BARE_OS_POSIX_FCNTL_BLOCKING_WAIT), SOCK_DGRAM passive bind via bareDgram; sendmsg/recvmsg partial; socketMsgSurface schema 5 (logicalScmRightsOnSend on SOCK_STREAM + SOCK_DGRAM when BARE_OS_POSIX_SOCKET_SCM_RIGHTS); ctx.bareOsSyscall select mirrors pollClock when timeoutMs>0. BARE_OS_VFS_WARM_CACHE_INVALIDATE_ON_REPLICATION full warm flush on core-length growth; optional BARE_OS_VFS_WARM_REPL_ADAPTIVE_WINDOW_MS coalesces repeated full flushes; warm invalidation reasons containing replication increment vfs.warm_read_cache_invalidate_replication. Peer admission schema 2: BARE_OS_PEER_DENYLIST_HEX, BARE_OS_PEER_REQUIRE_CAPS_JSON (+ security_posture peerAdmission.requireCapsTokenCount), optional BARE_OS_PEER_ADMISSION_AUDIT_NDJSON (+ rate ms). Tier-1 /bin/test: -u -g -k when st.mode exposes setuid/setgid/sticky. Hyperswarm caps via BARE_OS_SWARM_MAX_* env. disk.os replication_operator_sketch schema 5 (BARE_OS_CORESTORE_STATS_JSON corestoreOperatorSketch); replication_snapshot schema 2 (seed snapshot hints + BARE_OS_CORESTORE_SNAPSHOT_JSON). ctx API 1.51.1. Tier-1 nice (simulated niceness via BARE_OS_SIMULATED_NICE). Shell BARE_OS_SHELL_NOUNSET / set -u. Dynamic sysconf via ctx.bareOsGetconfSysconf.",
"note": "Machine-readable summary; normative detail remains in handbook ch.9 and environment POSIX appendix. Profile 1.0.16 / syscalls schema 10: adds ctx.bareOsSyscall posix_fadvise simulated no-op; guest session read/write/chmod/unlink deny on /.bare/account and /.bare/vault/** (override BARE_OS_GUEST_BARE_READ_ALL); optional BARE_OS_PERSONAL_ACCT_PREFIX personal-drive layout; BARE_OS_PERSONAL_ROOT_MIGRATE + /.bare-os/migration/legacy-root-v1.json for safe legacy-root lift; BARE_OS_GUEST_SCRUB; vfs.replication_warm_full_invalidate metric; ctx.bareOsSyscall nanosleep-shaped delay; bridge getsockopt/setsockopt partial SO_KEEPALIVE/TCP_NODELAY when BARE_OS_POSIX_SOCKET_FD_BRIDGE; cooperative fcntl locks (optional BARE_OS_POSIX_FCNTL_BLOCKING_WAIT), SOCK_DGRAM passive bind via bareDgram; sendmsg/recvmsg partial; socketMsgSurface schema 5 (logicalScmRightsOnSend on SOCK_STREAM + SOCK_DGRAM when BARE_OS_POSIX_SOCKET_SCM_RIGHTS); ctx.bareOsSyscall select mirrors pollClock when timeoutMs>0. BARE_OS_VFS_WARM_CACHE_INVALIDATE_ON_REPLICATION full warm flush on core-length growth; optional BARE_OS_VFS_WARM_REPL_ADAPTIVE_WINDOW_MS coalesces repeated full flushes; warm invalidation reasons containing replication increment vfs.warm_read_cache_invalidate_replication. Peer admission schema 2: BARE_OS_PEER_DENYLIST_HEX, BARE_OS_PEER_REQUIRE_CAPS_JSON (+ security_posture peerAdmission.requireCapsTokenCount), optional BARE_OS_PEER_ADMISSION_AUDIT_NDJSON (+ rate ms). Tier-1 /bin/test: -u -g -k when st.mode exposes setuid/setgid/sticky. Hyperswarm caps via BARE_OS_SWARM_MAX_* env. disk.os replication_operator_sketch schema 5 (BARE_OS_CORESTORE_STATS_JSON corestoreOperatorSketch); replication_snapshot schema 2 (seed snapshot hints + BARE_OS_CORESTORE_SNAPSHOT_JSON). ctx API 1.52.0. Tier-1 nice (simulated niceness via BARE_OS_SIMULATED_NICE). Shell BARE_OS_SHELL_NOUNSET / set -u. Dynamic sysconf via ctx.bareOsGetconfSysconf.",
"utilities": {
"tier1_bin": "high",
"sed_awk_grep": "issue7_inspired_js_engines",
+1 -1
View File
@@ -10,7 +10,7 @@ Normative narrative: [`docs/architecture/POSIX_DECLARED_PROFILE.md`](../architec
| --- | --- | --- |
| Profile | profileId | bare-os-posix-like |
| Profile | schema | 1 |
| Profile | note | Machine-readable summary; normative detail remains in handbook ch.9 and environment POSIX appendix. Profile 1.0.15 / syscalls schema 10: adds ctx.bareOsSyscall posix_fadvise simulated no-op; guest session read/write/chmod/unlink deny on /.bare/account and /.bare/vault/** (override BARE_OS_GUEST_BARE_READ_ALL); optional BARE_OS_PERSONAL_ACCT_PREFIX personal-drive layout; BARE_OS_PERSONAL_ROOT_MIGRATE + /.bare-os/migration/legacy-root-v1.json for safe legacy-root lift; BARE_OS_GUEST_SCRUB; vfs.replication_warm_full_invalidate metric; ctx.bareOsSyscall nanosleep-shaped delay; bridge getsockopt/setsockopt partial SO_KEEPALIVE/TCP_NODELAY when BARE_OS_POSIX_SOCKET_FD_BRIDGE; cooperative fcntl locks (optional BARE_OS_POSIX_FCNTL_BLOCKING_WAIT), SOCK_DGRAM passive bind via bareDgram; sendmsg/recvmsg partial; socketMsgSurface schema 5 (logicalScmRightsOnSend on SOCK_STREAM + SOCK_DGRAM when BARE_OS_POSIX_SOCKET_SCM_RIGHTS); ctx.bareOsSyscall select mirrors pollClock when timeoutMs>0. BARE_OS_VFS_WARM_CACHE_INVALIDATE_ON_REPLICATION full warm flush on core-length growth; optional BARE_OS_VFS_WARM_REPL_ADAPTIVE_WINDOW_MS coalesces repeated full flushes; warm invalidation reasons containing replication increment vfs.warm_read_cache_invalidate_replication. Peer admission schema 2: BARE_OS_PEER_DENYLIST_HEX, BARE_OS_PEER_REQUIRE_CAPS_JSON (+ security_posture peerAdmission.requireCapsTokenCount), optional BARE_OS_PEER_ADMISSION_AUDIT_NDJSON (+ rate ms). Tier-1 /bin/test: -u -g -k when st.mode exposes setuid/setgid/sticky. Hyperswarm caps via BARE_OS_SWARM_MAX_* env. disk.os replication_operator_sketch schema 5 (BARE_OS_CORESTORE_STATS_JSON corestoreOperatorSketch); replication_snapshot schema 2 (seed snapshot hints + BARE_OS_CORESTORE_SNAPSHOT_JSON). ctx API 1.51.1. Tier-1 nice (simulated niceness via BARE_OS_SIMULATED_NICE). Shell BARE_OS_SHELL_NOUNSET / set -u. Dynamic sysconf via ctx.bareOsGetconfSysconf. |
| Profile | note | Machine-readable summary; normative detail remains in handbook ch.9 and environment POSIX appendix. Profile 1.0.16 / syscalls schema 10: adds ctx.bareOsSyscall posix_fadvise simulated no-op; guest session read/write/chmod/unlink deny on /.bare/account and /.bare/vault/** (override BARE_OS_GUEST_BARE_READ_ALL); optional BARE_OS_PERSONAL_ACCT_PREFIX personal-drive layout; BARE_OS_PERSONAL_ROOT_MIGRATE + /.bare-os/migration/legacy-root-v1.json for safe legacy-root lift; BARE_OS_GUEST_SCRUB; vfs.replication_warm_full_invalidate metric; ctx.bareOsSyscall nanosleep-shaped delay; bridge getsockopt/setsockopt partial SO_KEEPALIVE/TCP_NODELAY when BARE_OS_POSIX_SOCKET_FD_BRIDGE; cooperative fcntl locks (optional BARE_OS_POSIX_FCNTL_BLOCKING_WAIT), SOCK_DGRAM passive bind via bareDgram; sendmsg/recvmsg partial; socketMsgSurface schema 5 (logicalScmRightsOnSend on SOCK_STREAM + SOCK_DGRAM when BARE_OS_POSIX_SOCKET_SCM_RIGHTS); ctx.bareOsSyscall select mirrors pollClock when timeoutMs>0. BARE_OS_VFS_WARM_CACHE_INVALIDATE_ON_REPLICATION full warm flush on core-length growth; optional BARE_OS_VFS_WARM_REPL_ADAPTIVE_WINDOW_MS coalesces repeated full flushes; warm invalidation reasons containing replication increment vfs.warm_read_cache_invalidate_replication. Peer admission schema 2: BARE_OS_PEER_DENYLIST_HEX, BARE_OS_PEER_REQUIRE_CAPS_JSON (+ security_posture peerAdmission.requireCapsTokenCount), optional BARE_OS_PEER_ADMISSION_AUDIT_NDJSON (+ rate ms). Tier-1 /bin/test: -u -g -k when st.mode exposes setuid/setgid/sticky. Hyperswarm caps via BARE_OS_SWARM_MAX_* env. disk.os replication_operator_sketch schema 5 (BARE_OS_CORESTORE_STATS_JSON corestoreOperatorSketch); replication_snapshot schema 2 (seed snapshot hints + BARE_OS_CORESTORE_SNAPSHOT_JSON). ctx API 1.52.0. Tier-1 nice (simulated niceness via BARE_OS_SIMULATED_NICE). Shell BARE_OS_SHELL_NOUNSET / set -u. Dynamic sysconf via ctx.bareOsGetconfSysconf. |
| Utilities | tier1_bin | high |
| Utilities | sed_awk_grep | issue7_inspired_js_engines |
| Utilities | coreutils | see packages/bare-os-coreutils/src |
+1
View File
@@ -17,6 +17,7 @@ Use this page to jump to the right doc for your situation. **Symptom-first** pat
| **`ctx`**, scripts, `/bin` development | [developer-guide/08-testing-and-debugging.md](../developer-guide/08-testing-and-debugging.md) · [developer-guide/02-the-context-object.md](../developer-guide/02-the-context-object.md) |
| CI scripts, verifiers, release checklist | [scripts/README.md](../scripts/README.md) · [docs/release-checklist.md](release-checklist.md) |
| Version skew, feature bits, API alignment | [docs/reference/compatibility-matrix.md](reference/compatibility-matrix.md) · [docs/architecture/KERNEL_CONTRACT.md](architecture/KERNEL_CONTRACT.md) |
| **`curl` / `wget` exit 127** (“unavailable in this session”) | Stock booter must expose **`ctx.bareOsRunCurlCli`** / **`ctx.bareOsRunWgetCli`**; upgrade **`bare-os-booter`** and match **`ctx` API** in [compatibility-matrix.md](reference/compatibility-matrix.md). **`BARE_OS_DELEGATE_ALLOW`** excluding **`curl`**/`wget` does **not** disable HTTP fetch — only host delegation order changes. |
---
+10 -2
View File
@@ -114,15 +114,23 @@ Once **initd** has started, the stock booter assigns **`disk.os`** on **`SwarmDi
- **`bare_os.ipc_list`** → JSON array of FIFO channel names
- **`bare_os.service_names`** → JSON array of **initd** unit names
- **`bare_os.echo`** → JSON `{"args":[…]}` (debug)
- **`bare_os.disk_os_hints`** → JSON **schema 2**: **`protocolPackageVersion`**, **`protomuxChannelSchemaVersion`**, **`booterPackageVersion`**, plus **`mirrorDriveHintV2`**, **`httpDhtProxyHint`**, **`auxiliaryDriveCount`** — advisory for operators only (parity with seed **`kernel_info`** / **`capabilities`** semver fields).
- **`bare_os.disk_os_hints`** → JSON **schema 3**: schema **2** fields plus **`pathSearchLocal`** (last **`searchLocal`** metrics: manifest path, match counts, scan counts). **`searchLocal`** consults optional **`/etc/bare-os/path-manifest.json`** on the system drive first (**`BARE_OS_DISK_OS_PATH_MANIFEST`**, **`BARE_OS_DISK_OS_SEARCH_MANIFEST_ONLY`**, **`BARE_OS_DISK_OS_MANIFEST_CACHE_MS`**).
- **`bare_os.replication_operator_sketch`** → JSON **schema 5** (superset of **4**): adds optional **`corestoreOperatorSketch`** from **`BARE_OS_CORESTORE_STATS_JSON`**; retains **`peerPrioritySketch`** (**`BARE_OS_REPLICATION_PEER_PRIORITY_JSON`**), **`blindTopologySketchV3`**, **`hyperblobsDedupSketch`**, Hyperswarm budget fields — non-secret operator JSON only.
- **`bare_os.replication_operator_intent`** — cap-gated (**`BARE_OS_DISK_OS_OPERATOR_INTENT_RPC=1`**): accepts a JSON object string, appends one **`disk_os.replication_operator_intent`** row via **`ctx.bareOsAuditLogAppendBatch`** when the booter wires **`auditBatch`**; **`EPERM`** when disabled.
- Unknown pairs throw **`BareOsKernelError`** with code **`BARE_OS_EXEC_RPC_UNKNOWN`** (surfaced as RPC error text).
**`searchLocal`:** scans the **system** Hyperdrive, then each **`SwarmDisk.auxiliaryDrives`** entry (read-only mirrors), dedupes paths, caps at **256** hits. Optional **`BARE_OS_DISK_OS_SEARCH_THROTTLE_MS`** (0500) inserts a short **`setTimeout`** between drive scans to reduce load on slow replicas (offline-first friendly).
**`searchLocal`:** matches optional **`/etc/bare-os/path-manifest.json`** (or **`BARE_OS_DISK_OS_PATH_MANIFEST`**) on the **system** drive first, then (unless **`BARE_OS_DISK_OS_SEARCH_MANIFEST_ONLY`**) recursively lists the **system** Hyperdrive, then each **`SwarmDisk.auxiliaryDrives`** entry, dedupes paths, caps at **256** hits. Optional **`BARE_OS_DISK_OS_SEARCH_THROTTLE_MS`** (0500) inserts a short **`setTimeout`** between **auxiliary** drive scans.
Implementation: [`packages/bare-os-booter/lib/bare-os-disk-os-bridge.js`](../packages/bare-os-booter/lib/bare-os-disk-os-bridge.js). Architecture overview: [`docs/architecture/KERNEL_CONTRACT.md`](../docs/architecture/KERNEL_CONTRACT.md).
### Experimental — P2P-native package index
Stock **`pkg-swarm-index`** documents how operators can compose **Hyperbee + hyperswarm** to publish **`name@version → { driveKey, manifestHash }`** records signed with **Ed25519**, verified against **boot policy** instead of a central registry. See [`kernel/etc/bare-os/hyperbee-index-hint.example.json`](../kernel/etc/bare-os/hyperbee-index-hint.example.json), **`BARE_OS_PKG_SWARM_TOPIC_HEX`**, and **`kernel/etc/bare-os/bundlebee-extension-sidecar.example.json`** for extension sidecars.
### Experimental — multisig-gated `rc.d` proposals
Collaborative fleets install JSON under **`/etc/bare-os/rc.proposals/enabled/*.json`** (see [`kernel/etc/bare-os/rc.proposal.multisig.example.json`](../kernel/etc/bare-os/rc.proposal.multisig.example.json)). With **`BARE_OS_RC_PROPOSAL_MULTISIG_STRICT=1`**, the guest kernel requires **`/etc/bare-os/pear.multisig.json`**, validates each proposals **`signaturesFrom`** keys against that policys **`signers`**, and requires at least **`quorum`** distinct matches before **`rc.d`** runs. **`ctx.bareOsAuditLogAppend`** receives **`rc.proposal.multisig_ok`** / **`rc.proposal.multisig_fail`** rows when wired. Cryptographic attestation of proposal bytes remains a **host / seeder** responsibility.
---
## Kernel feature bitmask (documentation)
+1 -1
View File
@@ -259,7 +259,7 @@ Stable paths are also listed under **`/proc/bare_os/`** in **`packages/bare-os-b
### 5.2b HTTP clients (`curl` / `wget`)
**`curl`** and **`wget`** are **booter-delegated** CLIs, not coreutils engines. They implement Fetch-based **subsets** of the familiar tools; the **`/bin/curl`** and **`/bin/wget`** files on the system image exist for **`ls /bin`** / **`which`** parity and are **stubs** if ever executed without delegation.
**`curl`** and **`wget`** are **booter-delegated** CLIs first, not coreutils engines. They implement Fetch-based **subsets** of the familiar tools. The drive-resident **`/bin/curl`** and **`/bin/wget`** scripts call **`ctx.bareOsRunCurlCli`** / **`ctx.bareOsRunWgetCli`** (always present on stock booters) so **`BARE_OS_DELEGATE_ALLOW`** can exclude host delegation while **keeping the same fetch implementation**. If those **`ctx`** hooks are absent (prehistoric booter), the utilities **fail closed** with exit **127** and an explicit upgrade message — they are not silent no-ops.
- **Delegation order, fetch resolution, policy env** — [Reference — HTTP: curl and wget](../docs/reference/http-curl-and-wget.md).
- **Flag parity** — [`CLI_PARITY.md`](../packages/bare-os-booter/CLI_PARITY.md).
+2 -2
View File
@@ -98,7 +98,7 @@ async function run(ctx, argv) {
return
}
ctx.console.error(
'curl: ctx.bareOsRunCurlCli missing — upgrade booter / ctx API'
'curl: unavailable in this session (stock booter exposes ctx.bareOsRunCurlCli; upgrade bare-os-booter or run with a current ctx API)'
)
ctx.exitCode = 1
ctx.exitCode = 127
}
+1 -1
View File
@@ -87,7 +87,7 @@ function bareOsEmitRaw(ctx, chunk) {
return false
}
var BARE_OS_HELP_BIN_SPACED = "arch awk baretop base32 base64 basename basenc btop bundlebee cat chgrp chmod chown cksum clear cmp comm cp crontab curl cut date dd df diff dir dircolors dirname du echo edit env exit expand expr factor false find fmt fold getconf getfacl git git-pear grep groups hdms head help hostid hostname hrpc iconv id install join journalctl jq kernel-boot-diff kernel-doctor kernel-explain kernel-fsck kernel-home-snapshot kernel-manifest-validate kernel-preflight kernel-triage kill link ln logger login logname logout ls man md5sum mkdir mkfifo mktemp mount mv nano nice nl nohup nproc numfmt od oidc-publish openssl openssl paste patch pathchk pear-runtime-matrix pr printenv printf procstat ps pwd readlink realpath rev rm rmdir savevault sed seq setfacl sh sha1sum sha256sum sha512sum shuf sidecar sleep sort split ssh-keygen ssh-keygen stat sum sync systemctl tac tail tar tar tee test theme time timeout touch tr true truncate tsort tty ulimit umount uname unexpand uniq unlink uptime users vdir wc wget which who whoami xargs xattr yes"
var BARE_OS_HELP_BIN_SPACED = "arch awk baretop base32 base64 basename basenc btop bundlebee cat chgrp chmod chown cksum clear cmp comm cp crontab curl cut date dd df diff dir dircolors dirname du echo edit env exit expand expr factor false find fmt fold getconf getfacl git git-pear grep groups hdms head help hostid hostname hrpc iconv id install join journalctl jq kernel-boot-diff kernel-doctor kernel-explain kernel-fsck kernel-home-snapshot kernel-manifest-validate kernel-preflight kernel-triage kill link ln logger login logname logout ls man md5sum mkdir mkfifo mktemp mount mv nano nice nl nohup nproc numfmt od oidc-publish openssl openssl paste patch pathcap-verify pathchk pear-runtime-matrix pkg-swarm-index pr printenv printf procstat ps pwd readlink realpath rev rm rmdir savevault sed seq setfacl sh sha1sum sha256sum sha512sum shuf sidecar sleep sort split ssh-keygen ssh-keygen stat sum sync systemctl tac tail tar tar tee test theme time timeout touch tr true truncate tsort tty ulimit umount uname unexpand uniq unlink uptime users vdir wc wget which who whoami xargs xattr yes"
async function run(ctx, argv) {
ctx.console.log(
'Bare OS — default user: guest | shell builtins: alias, barerc, cd, command, export, exit, login, logout, readonly, type, umask, unalias, unset, : | /bin: ' +
+163
View File
@@ -0,0 +1,163 @@
/* BARE_OS_BIN_API 1.0.0 — bump when staged /bin script semantics change (see developer guide). */
/** Shared helpers for drive-resident /bin scripts (prepended before each command). */
function bareStdin(ctx) {
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
}
/** @param {number} mode @param {'file' | 'directory' | 'symlink'} type */
function bareFormatModeString(mode, type) {
const typeChar = type === 'directory' ? 'd' : type === 'symlink' ? 'l' : '-'
const perm = mode & 0o777
const r = (bit) => (perm & bit ? 'r' : '-')
const w = (bit) => (perm & bit ? 'w' : '-')
const x = (bit) => (perm & bit ? 'x' : '-')
return (
typeChar +
r(0o400) +
w(0o200) +
x(0o100) +
r(0o040) +
w(0o020) +
x(0o010) +
r(0o004) +
w(0o002) +
x(0o001)
)
}
/** @param {number} mtimeMs @param {number} [nowMs] */
function bareFormatLsMtime(mtimeMs, nowMs) {
const now = nowMs != null ? nowMs : Date.now()
const d = new Date(mtimeMs)
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
]
const mon = months[d.getMonth()]
const day = String(d.getDate()).padStart(2, ' ')
const sixMo = 180 * 24 * 3600 * 1000
if (Math.abs(now - mtimeMs) > sixMo) {
const yr = String(d.getFullYear()).padStart(4, ' ')
return mon + ' ' + day + ' ' + yr
}
const hh = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
return mon + ' ' + day + ' ' + hh + ':' + mm
}
/** @param {number} size */
function barePosixBlocks(size) {
return Math.ceil(Number(size) / 512) || 0
}
/**
* Raw stdout for NUL/binary when **`process.stdout.write`** is missing.
* If **`ctx.bareOsBinWrite(Uint8Array|string)`** is set (tests / host), use it.
* @param {Record<string, unknown>} ctx
* @param {string | Uint8Array} chunk
* @returns {boolean}
*/
function bareOsEmitRaw(ctx, chunk) {
if (typeof ctx.bareOsBinWrite === 'function') {
const b4 = ctx.b4a
const u8 =
typeof chunk === 'string'
? b4 && typeof b4.from === 'function'
? b4.from(chunk)
: new TextEncoder().encode(chunk)
: chunk
ctx.bareOsBinWrite(u8 instanceof Uint8Array ? u8 : new Uint8Array(u8))
return true
}
const w = globalThis.process?.stdout?.write
if (typeof w === 'function') {
w.call(globalThis.process.stdout, chunk)
return true
}
return false
}
/**
* Verify a path-capability envelope (JSON) using ctx.bareOsVerifyPathCapabilityEnvelope.
* Usage: pathcap-verify FILE.json (or stdin JSON when FILE is -)
*/
async function run(ctx, argv) {
let path = ''
for (let i = 1; i < argv.length; i++) {
const a = argv[i]
if (a === '--help' || a === '-h') {
ctx.console.log(
'usage: pathcap-verify FILE.json\n pathcap-verify - (read envelope JSON from stdin)'
)
return
}
if (!a.startsWith('-')) {
path = a
break
}
ctx.console.error('pathcap-verify: unknown option ' + a)
ctx.exitCode = 1
return
}
if (!path) {
ctx.console.error(
'usage: pathcap-verify FILE.json\n pathcap-verify -'
)
ctx.exitCode = 1
return
}
if (typeof ctx.bareOsVerifyPathCapabilityEnvelope !== 'function') {
ctx.console.error(
'pathcap-verify: ctx.bareOsVerifyPathCapabilityEnvelope missing'
)
ctx.exitCode = 1
return
}
let text = ''
try {
if (path === '-') {
text = String(ctx.shellStdin || '')
} else {
const buf = await ctx.vfs.readFile(path)
if (!buf || !buf.byteLength) {
ctx.console.error('pathcap-verify: empty or missing: ' + path)
ctx.exitCode = 1
return
}
text = ctx.b4a.toString(buf)
}
} catch (e) {
ctx.console.error(
'pathcap-verify: read failed: ' + ((e && e.message) || String(e))
)
ctx.exitCode = 1
return
}
let env
try {
env = JSON.parse(text)
} catch {
ctx.console.error('pathcap-verify: invalid JSON')
ctx.exitCode = 1
return
}
const r = ctx.bareOsVerifyPathCapabilityEnvelope(env)
if (r.ok) {
ctx.console.log(
'ok prefix=' + r.payload.prefix + ' ops=' + r.payload.ops.join(',')
)
return
}
ctx.console.error('pathcap-verify: FAIL ' + r.reason)
ctx.exitCode = 1
}
+120
View File
@@ -0,0 +1,120 @@
/* BARE_OS_BIN_API 1.0.0 — bump when staged /bin script semantics change (see developer guide). */
/** Shared helpers for drive-resident /bin scripts (prepended before each command). */
function bareStdin(ctx) {
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
}
/** @param {number} mode @param {'file' | 'directory' | 'symlink'} type */
function bareFormatModeString(mode, type) {
const typeChar = type === 'directory' ? 'd' : type === 'symlink' ? 'l' : '-'
const perm = mode & 0o777
const r = (bit) => (perm & bit ? 'r' : '-')
const w = (bit) => (perm & bit ? 'w' : '-')
const x = (bit) => (perm & bit ? 'x' : '-')
return (
typeChar +
r(0o400) +
w(0o200) +
x(0o100) +
r(0o040) +
w(0o020) +
x(0o010) +
r(0o004) +
w(0o002) +
x(0o001)
)
}
/** @param {number} mtimeMs @param {number} [nowMs] */
function bareFormatLsMtime(mtimeMs, nowMs) {
const now = nowMs != null ? nowMs : Date.now()
const d = new Date(mtimeMs)
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
]
const mon = months[d.getMonth()]
const day = String(d.getDate()).padStart(2, ' ')
const sixMo = 180 * 24 * 3600 * 1000
if (Math.abs(now - mtimeMs) > sixMo) {
const yr = String(d.getFullYear()).padStart(4, ' ')
return mon + ' ' + day + ' ' + yr
}
const hh = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
return mon + ' ' + day + ' ' + hh + ':' + mm
}
/** @param {number} size */
function barePosixBlocks(size) {
return Math.ceil(Number(size) / 512) || 0
}
/**
* Raw stdout for NUL/binary when **`process.stdout.write`** is missing.
* If **`ctx.bareOsBinWrite(Uint8Array|string)`** is set (tests / host), use it.
* @param {Record<string, unknown>} ctx
* @param {string | Uint8Array} chunk
* @returns {boolean}
*/
function bareOsEmitRaw(ctx, chunk) {
if (typeof ctx.bareOsBinWrite === 'function') {
const b4 = ctx.b4a
const u8 =
typeof chunk === 'string'
? b4 && typeof b4.from === 'function'
? b4.from(chunk)
: new TextEncoder().encode(chunk)
: chunk
ctx.bareOsBinWrite(u8 instanceof Uint8Array ? u8 : new Uint8Array(u8))
return true
}
const w = globalThis.process?.stdout?.write
if (typeof w === 'function') {
w.call(globalThis.process.stdout, chunk)
return true
}
return false
}
/**
* pkg-swarm-index — document Hyperbee/Hyperdrive package index pattern for P2P-first registries.
* Prints static guidance; real indexes are built with hyperbee + hyperswarm outside this utility.
*/
async function run(ctx, argv) {
const topic = String(
(ctx.env && ctx.env.BARE_OS_PKG_SWARM_TOPIC_HEX) || ''
).trim()
for (let i = 1; i < argv.length; i++) {
if (argv[i] === '--help' || argv[i] === '-h') {
ctx.console.log(`pkg-swarm-index — P2P package index notes for Bare OS
Set BARE_OS_PKG_SWARM_TOPIC_HEX (64 hex chars) to pin a swarm topic class for index peers.
Architecture (operators):
• Writers publish signed records: name@version -> { driveKey, manifestHash } in a Hyperbee.
• Readers join the hyperswarm topic, replicate the feed, verify Ed25519 signatures against boot policy.
• See handbook § P2P-native package index (experimental) and kernel/etc/bare-os/hyperbee-index-hint.example.json.
${topic ? 'Current BARE_OS_PKG_SWARM_TOPIC_HEX prefix: ' + topic.slice(0, 16) + '…' : 'BARE_OS_PKG_SWARM_TOPIC_HEX is unset (documentation mode).'}
`)
return
}
}
ctx.console.log(
'pkg-swarm-index: P2P registry pattern — use `pkg-swarm-index --help`. ' +
(topic
? 'Topic pin active (' + topic.slice(0, 12) + '…).'
: 'No topic pin (set BARE_OS_PKG_SWARM_TOPIC_HEX).')
)
}
+2 -2
View File
@@ -97,7 +97,7 @@ async function run(ctx, argv) {
return
}
ctx.console.error(
'wget: ctx.bareOsRunWgetCli missing — upgrade booter / ctx API'
'wget: unavailable in this session (stock booter exposes ctx.bareOsRunWgetCli; upgrade bare-os-booter or run with a current ctx API)'
)
ctx.exitCode = 1
ctx.exitCode = 127
}
@@ -0,0 +1,8 @@
{
"schema": 1,
"note": "Optional sidecar next to a bundlebee-published extension: merge hints for kernel.ext.d strict verification (signer pins, drive key sketch). Non-secret metadata only.",
"extensionId": "example.p2p.extension",
"bundlebeeDriveKeyHexPrefix": "replace_with_discovery_or_id_prefix",
"signerPubkeysHex": ["replace_with_ed25519_public_key_64_hex"],
"publishedAtMs": 0
}
@@ -0,0 +1,11 @@
{
"schema": 1,
"note": "Sign bareOsPathCapabilityPayloadCanonicalUtf8(payload) with Ed25519; set pubkeyHex (64 hex) and signatureHex (128 hex). Inner payload fields: prefix (absolute path prefix), ops (include read or all), optional expMs (epoch ms). Store on a file as xattr user.bareos.cap_v1 = base64(JSON.stringify(this object)) via /bin/xattr -w. Enable enforcement: BARE_OS_PATH_CAPABILITY_ENFORCE_READ=1 and BARE_OS_PATH_CAPABILITY_PREFIX=/home/",
"pubkeyHex": "REPLACE_WITH_SIGNER_PUBLIC_KEY_64_HEX_CHARS",
"signatureHex": "REPLACE_WITH_SIGNATURE_OVER_CANONICAL_PAYLOAD_128_HEX_CHARS",
"payload": {
"expMs": 1893456000000,
"ops": ["read"],
"prefix": "/home/guest/"
}
}
+51
View File
@@ -0,0 +1,51 @@
{
"schema": 1,
"note": "Optional path list for disk.os searchLocal: substring matches are returned before (or instead of) full-drive listing. Maintainer: extend when adding Tier-1 /bin or boot-critical paths. Disable manifest use with BARE_OS_DISK_OS_PATH_MANIFEST= (empty) on the host.",
"paths": [
"/boot/init.js",
"/etc/os-release",
"/etc/motd",
"/etc/bare-os/rc",
"/etc/bare-os/path-manifest.json",
"/etc/bare-os/boot.policy.example.json",
"/lib/bare/README.md",
"/bin/sh",
"/bin/ls",
"/bin/cat",
"/bin/grep",
"/bin/sed",
"/bin/awk",
"/bin/curl",
"/bin/wget",
"/bin/man",
"/bin/login",
"/bin/logout",
"/bin/savevault",
"/bin/help",
"/bin/env",
"/bin/test",
"/bin/kill",
"/bin/ps",
"/bin/mkdir",
"/bin/cp",
"/bin/mv",
"/bin/rm",
"/bin/chmod",
"/bin/chown",
"/bin/tar",
"/bin/gzip",
"/bin/gunzip",
"/bin/find",
"/bin/xargs",
"/bin/sort",
"/bin/uniq",
"/bin/wc",
"/bin/head",
"/bin/tail",
"/bin/sleep",
"/bin/echo",
"/bin/printf",
"/bin/baretop",
"/bin/btop"
]
}
@@ -0,0 +1,7 @@
{
"schema": 1,
"note": "Golden-path alignment hint for operators: expected pear-runtime semver from local Holepunch clones (not enforced by stock booter). Compare with BARE_OS_PEAR_RUNTIME_VERSION / pear doctor.",
"expectedPearRuntimeSemver": "1.1.1",
"observedAt": "2026-04-05",
"clonePathHint": "~/dev/pearcli/holepunch-repos/holepunchto_repos/pear-runtime"
}
+9 -1
View File
@@ -1,7 +1,7 @@
{
"schema": 2,
"profileId": "bare-os-posix-like",
"generatedAt": "2026-04-05T19:03:41.000Z",
"generatedAt": "2026-04-05T19:26:42.531Z",
"note": "Sparse POSIX Issue 7 coverage hints for /bin utilities. Omitted command names are not yet profiled here.",
"commandIndex": [
{
@@ -356,10 +356,18 @@
"name": "patch",
"tier": "tier1_bin"
},
{
"name": "pathcap-verify",
"tier": "tier1_bin"
},
{
"name": "pathchk",
"tier": "tier1_bin"
},
{
"name": "pkg-swarm-index",
"tier": "tier1_bin"
},
{
"name": "pr",
"tier": "tier1_bin"
@@ -0,0 +1,11 @@
{
"schema": 1,
"note": "Place approved JSON as /etc/bare-os/rc.proposals/enabled/<name>.json when BARE_OS_RC_PROPOSAL_MULTISIG_STRICT=1. pear.multisig.json must exist; each signaturesFrom entry must match a signer key in that file (case-insensitive); distinct count must be ≥ pear.quorum. Ed25519 / hyper-multisig verification of the proposal payload is a host or seeder step — the guest gate is policy alignment only.",
"proposalId": "example-2026-04-05",
"targetRcSnippet": "/etc/bare-os/rc.d/99-proposed.sh",
"payloadSha256Hex": "replace_with_sha256_of_script_bytes",
"signaturesFrom": [
"full_signer_public_key_hex_from_pear_multisig_json",
"second_signer_public_key_hex_from_pear_multisig_json"
]
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"schemaVersion": 10,
"ctxApiVersion": "1.51.1",
"ctxApiVersion": "1.52.0",
"posixProfile": {
"id": "bare-os-posix-like",
"version": "1.0.15"
+269 -1
View File
@@ -2195,6 +2195,12 @@ async function runKernelExtDropins(ctx, opts = {}) {
* BARE_OS_RC_D_SKIP: comma-separated rc.d basenames to skip; a pattern ending with * skips
* names with that prefix (e.g. 10-* skips 10-foo).
*
* BARE_OS_RC_PROPOSAL_MULTISIG_STRICT=1: before **rc.d**, require **`/etc/bare-os/pear.multisig.json`**
* ( **`bareOsPearMultisigShapeOk`** ) and validate every **`/etc/bare-os/rc.proposals/enabled/*.json`**
* (**`schema`**: **1**, **`proposalId`**, **`targetRcSnippet`** under **`/etc/bare-os/rc.d/`**, **`signaturesFrom`**: distinct
* public keys each listed in **`pear.multisig.json`** **`signers`**, count **`quorum`**). Emits **`ctx.bareOsAuditLogAppend`**
* rows **`rc.proposal.multisig_ok`** / **`rc.proposal.multisig_fail`** when the hook exists.
*
* Use ctx.registerKernelShutdownHook(fn) for teardown before initd disposers.
*/
@@ -3223,6 +3229,246 @@ async function runBareOsKernelDir(ctx) {
return true
}
/**
* @param {Record<string, unknown>} ctx
* @param {Record<string, unknown>} entry
*/
function bareOsAppendRcProposalAudit(ctx, entry) {
try {
const fn = ctx.bareOsAuditLogAppend
if (typeof fn === 'function') fn(entry)
} catch {
/* ignore */
}
}
/**
* When **`BARE_OS_RC_PROPOSAL_MULTISIG_STRICT`**, every enabled proposal must cite enough
* **`pear.multisig.json`** signers (distinct keys). Cryptographic signature verification is host/seeder responsibility.
* @param {Record<string, unknown>} ctx
* @returns {Promise<boolean>}
*/
async function verifyRcProposalsMultisigStrict(ctx) {
const strict = ctx.env?.BARE_OS_RC_PROPOSAL_MULTISIG_STRICT
if (strict !== '1' && strict !== 'true') return true
const { drive, b4a } = ctx
let pearBuf
try {
pearBuf = await drive.get('/etc/bare-os/pear.multisig.json')
} catch {
pearBuf = null
}
if (!pearBuf) {
bootStructuredLog(
ctx,
'error',
'rc.proposal.multisig.pear_missing',
'[rc.proposals] BARE_OS_RC_PROPOSAL_MULTISIG_STRICT requires /etc/bare-os/pear.multisig.json'
)
bareOsAppendRcProposalAudit(ctx, {
type: 'rc.proposal.multisig_fail',
reason: 'pear_multisig_missing',
ts: Date.now()
})
return false
}
/** @type {unknown} */
let pearParsed
try {
pearParsed = JSON.parse(b4a.toString(pearBuf))
} catch (e) {
bootStructuredLog(
ctx,
'error',
'rc.proposal.multisig.pear_parse',
'[rc.proposals] pear.multisig.json: ' + ((e && e.message) || String(e))
)
bareOsAppendRcProposalAudit(ctx, {
type: 'rc.proposal.multisig_fail',
reason: 'pear_multisig_parse',
ts: Date.now()
})
return false
}
if (!bareOsPearMultisigShapeOk(pearParsed)) {
bootStructuredLog(
ctx,
'error',
'rc.proposal.multisig.pear_shape',
'[rc.proposals] pear.multisig.json must be { signers: string[], quorum: number } with 1 ≤ quorum ≤ signers.length'
)
bareOsAppendRcProposalAudit(ctx, {
type: 'rc.proposal.multisig_fail',
reason: 'pear_multisig_shape',
ts: Date.now()
})
return false
}
const pear = /** @type {{ signers: string[]; quorum: number }} */ (pearParsed)
const signerSet = new Set(pear.signers.map((s) => String(s).toLowerCase()))
const quorum = pear.quorum
/** @type {string[]} */
const proposalNames = []
try {
for await (const n of drive.readdir('/etc/bare-os/rc.proposals/enabled')) {
if (String(n).endsWith('.json')) proposalNames.push(String(n))
}
} catch {
return true
}
proposalNames.sort()
if (!proposalNames.length) return true
for (const fn of proposalNames) {
const p = `/etc/bare-os/rc.proposals/enabled/${fn}`
/** @type {unknown} */
let raw
try {
const buf = await drive.get(p)
if (!buf) {
bootStructuredLog(ctx, 'error', 'rc.proposal.missing', `[rc.proposals] empty: ${fn}`)
bareOsAppendRcProposalAudit(ctx, {
type: 'rc.proposal.multisig_fail',
reason: 'proposal_missing',
proposalFile: fn,
ts: Date.now()
})
return false
}
raw = JSON.parse(b4a.toString(buf))
} catch (e) {
bootStructuredLog(
ctx,
'error',
'rc.proposal.parse',
`[rc.proposals] ${fn}: ` + ((e && e.message) || String(e))
)
bareOsAppendRcProposalAudit(ctx, {
type: 'rc.proposal.multisig_fail',
reason: 'proposal_parse',
proposalFile: fn,
ts: Date.now()
})
return false
}
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
bootStructuredLog(ctx, 'error', 'rc.proposal.shape', `[rc.proposals] ${fn}: expected object`)
return false
}
const o = /** @type {Record<string, unknown>} */ (raw)
if (o.schema !== 1) {
bootStructuredLog(
ctx,
'error',
'rc.proposal.schema',
`[rc.proposals] ${fn}: schema must be 1`
)
bareOsAppendRcProposalAudit(ctx, {
type: 'rc.proposal.multisig_fail',
reason: 'proposal_schema',
proposalFile: fn,
ts: Date.now()
})
return false
}
const proposalId = String(o.proposalId || '').trim()
if (!proposalId) {
bootStructuredLog(ctx, 'error', 'rc.proposal.id', `[rc.proposals] ${fn}: proposalId required`)
bareOsAppendRcProposalAudit(ctx, {
type: 'rc.proposal.multisig_fail',
reason: 'proposal_id',
proposalFile: fn,
ts: Date.now()
})
return false
}
const targetRcSnippet = String(o.targetRcSnippet || '').trim()
if (!targetRcSnippet.startsWith('/etc/bare-os/rc.d/')) {
bootStructuredLog(
ctx,
'error',
'rc.proposal.target',
`[rc.proposals] ${fn}: targetRcSnippet must start with /etc/bare-os/rc.d/`
)
bareOsAppendRcProposalAudit(ctx, {
type: 'rc.proposal.multisig_fail',
reason: 'proposal_target',
proposalFile: fn,
proposalId,
ts: Date.now()
})
return false
}
const sigFrom = o.signaturesFrom
if (!Array.isArray(sigFrom)) {
bootStructuredLog(
ctx,
'error',
'rc.proposal.signaturesFrom',
`[rc.proposals] ${fn}: signaturesFrom must be an array of signer public keys`
)
bareOsAppendRcProposalAudit(ctx, {
type: 'rc.proposal.multisig_fail',
reason: 'signatures_from_shape',
proposalFile: fn,
proposalId,
ts: Date.now()
})
return false
}
/** @type {Set<string>} */
const approved = new Set()
for (const s of sigFrom) {
const k = String(s || '').toLowerCase().trim()
if (!k) continue
if (!signerSet.has(k)) {
bootStructuredLog(
ctx,
'error',
'rc.proposal.signer_unknown',
`[rc.proposals] ${fn}: signer not in pear.multisig.json`
)
bareOsAppendRcProposalAudit(ctx, {
type: 'rc.proposal.multisig_fail',
reason: 'signer_not_in_policy',
proposalFile: fn,
proposalId,
ts: Date.now()
})
return false
}
approved.add(k)
}
if (approved.size < quorum) {
bootStructuredLog(
ctx,
'error',
'rc.proposal.quorum',
`[rc.proposals] ${fn}: need ≥ pear.multisig quorum (${quorum}) distinct approved signers, got ${approved.size}`
)
bareOsAppendRcProposalAudit(ctx, {
type: 'rc.proposal.multisig_fail',
reason: 'quorum_not_met',
proposalFile: fn,
proposalId,
ts: Date.now()
})
return false
}
bareOsAppendRcProposalAudit(ctx, {
type: 'rc.proposal.multisig_ok',
proposalFile: fn,
proposalId,
targetRcSnippet,
signerCount: approved.size,
quorum,
ts: Date.now()
})
}
return true
}
/**
* Optional snippets under /etc/bare-os/rc.d/ executed in lexicographic order.
* @param {Record<string, unknown>} ctx
@@ -3230,6 +3476,7 @@ async function runBareOsKernelDir(ctx) {
*/
async function runBareOsRcDir(ctx) {
const { drive, b4a } = ctx
if (!(await verifyRcProposalsMultisigStrict(ctx))) return false
const skip = parseRcDSkipPatterns(ctx)
try {
/** @type {string[]} */
@@ -3978,6 +4225,26 @@ async function start(ctx) {
}
}
if (coldExceeded || stdlibExceeded) {
/** @type {{ kind: string, wallMs: number, limitMs: number }[]} */
const violations = []
if (coldExceeded && Number.isFinite(budget) && budget > 0) {
violations.push({
kind: 'cold',
wallMs: Date.now() - bootT0,
limitMs: budget
})
}
if (stdlibExceeded && Number.isFinite(sb) && sb > 0) {
const sw = Number.parseInt(
String(ctx.env?.BARE_OS_BOOT_BARE_STDLIB_RESOLUTION_MS || ''),
10
)
violations.push({
kind: 'bare_stdlib',
wallMs: Number.isFinite(sw) ? sw : 0,
limitMs: sb
})
}
await maybeAppendBootTransactionJournal(ctx, {
phase: 'boot.budget',
stage: 'boot.budget',
@@ -3985,9 +4252,10 @@ async function start(ctx) {
ms: 0,
ok: false,
bootBudgetViolation: true,
bootBudgetSchemaVersion: 1,
bootBudgetSchemaVersion: 2,
coldBudgetExceeded: coldExceeded,
bareStdlibBudgetExceeded: stdlibExceeded,
bootBudgetViolations: violations,
transactionState: BARE_OS_BOOT_TXN_STATE.STAGE_COMMITTED
})
}
+216 -216
View File
@@ -2,9 +2,9 @@
"version": 1,
"bundles": [
{
"path": "/lib/bare/bundles/safetyCatch.js",
"path": "/lib/bare/bundles/hypercoreIdEncoding.js",
"keys": [
"safetyCatch"
"hypercoreIdEncoding"
]
},
{
@@ -14,9 +14,9 @@
]
},
{
"path": "/lib/bare/bundles/hypercoreIdEncoding.js",
"path": "/lib/bare/bundles/safetyCatch.js",
"keys": [
"hypercoreIdEncoding"
"safetyCatch"
]
},
{
@@ -37,6 +37,12 @@
"protomux"
]
},
{
"path": "/lib/bare/bundles/barePath.js",
"keys": [
"barePath"
]
},
{
"path": "/lib/bare/bundles/bareEvents.js",
"keys": [
@@ -49,18 +55,6 @@
"bareEncoding"
]
},
{
"path": "/lib/bare/bundles/barePath.js",
"keys": [
"barePath"
]
},
{
"path": "/lib/bare/bundles/bareAbort.js",
"keys": [
"bareAbort"
]
},
{
"path": "/lib/bare/bundles/bareAbortController.js",
"keys": [
@@ -68,9 +62,9 @@
]
},
{
"path": "/lib/bare/bundles/bareCrypto.js",
"path": "/lib/bare/bundles/bareAbort.js",
"keys": [
"bareCrypto"
"bareAbort"
]
},
{
@@ -85,6 +79,12 @@
"bareAddonResolve"
]
},
{
"path": "/lib/bare/bundles/bareCrypto.js",
"keys": [
"bareCrypto"
]
},
{
"path": "/lib/bare/bundles/bareAnsiEscapes.js",
"keys": [
@@ -109,24 +109,30 @@
"bareApk"
]
},
{
"path": "/lib/bare/bundles/bareAsyncHooks.js",
"keys": [
"bareAsyncHooks"
]
},
{
"path": "/lib/bare/bundles/bareAtomics.js",
"keys": [
"bareAtomics"
]
},
{
"path": "/lib/bare/bundles/bareAsyncHooks.js",
"keys": [
"bareAsyncHooks"
]
},
{
"path": "/lib/bare/bundles/bareAssert.js",
"keys": [
"bareAssert"
]
},
{
"path": "/lib/bare/bundles/bareBluetoothApple.js",
"keys": [
"bareBluetoothApple"
]
},
{
"path": "/lib/bare/bundles/bareBmp.js",
"keys": [
@@ -139,12 +145,6 @@
"bareBuffer"
]
},
{
"path": "/lib/bare/bundles/bareBundleCompile.js",
"keys": [
"bareBundleCompile"
]
},
{
"path": "/lib/bare/bundles/bareBundle.js",
"keys": [
@@ -152,15 +152,9 @@
]
},
{
"path": "/lib/bare/bundles/bareBluetoothApple.js",
"path": "/lib/bare/bundles/bareBundleCompile.js",
"keys": [
"bareBluetoothApple"
]
},
{
"path": "/lib/bare/bundles/bareBundleEvaluate.js",
"keys": [
"bareBundleEvaluate"
"bareBundleCompile"
]
},
{
@@ -170,15 +164,9 @@
]
},
{
"path": "/lib/bare/bundles/bareConsole.js",
"path": "/lib/bare/bundles/bareBundleEvaluate.js",
"keys": [
"bareConsole"
]
},
{
"path": "/lib/bare/bundles/bareDebugLog.js",
"keys": [
"bareDebugLog"
"bareBundleEvaluate"
]
},
{
@@ -188,9 +176,15 @@
]
},
{
"path": "/lib/bare/bundles/bareChannel.js",
"path": "/lib/bare/bundles/bareDebugLog.js",
"keys": [
"bareChannel"
"bareDebugLog"
]
},
{
"path": "/lib/bare/bundles/bareConsole.js",
"keys": [
"bareConsole"
]
},
{
@@ -199,12 +193,24 @@
"bareDaemon"
]
},
{
"path": "/lib/bare/bundles/bareChannel.js",
"keys": [
"bareChannel"
]
},
{
"path": "/lib/bare/bundles/bareDelta.js",
"keys": [
"bareDelta"
]
},
{
"path": "/lib/bare/bundles/bareCov.js",
"keys": [
"bareCov"
]
},
{
"path": "/lib/bare/bundles/bareDiagnosticsChannel.js",
"keys": [
@@ -235,12 +241,6 @@
"bareExif"
]
},
{
"path": "/lib/bare/bundles/bareCov.js",
"keys": [
"bareCov"
]
},
{
"path": "/lib/bare/bundles/bareFfmpeg.js",
"keys": [
@@ -259,24 +259,12 @@
"bareFormData"
]
},
{
"path": "/lib/bare/bundles/bareFormat.js",
"keys": [
"bareFormat"
]
},
{
"path": "/lib/bare/bundles/bareGif.js",
"keys": [
"bareGif"
]
},
{
"path": "/lib/bare/bundles/bareGtk.js",
"keys": [
"bareGtk"
]
},
{
"path": "/lib/bare/bundles/bareFileLogger.js",
"keys": [
@@ -284,9 +272,9 @@
]
},
{
"path": "/lib/bare/bundles/bareHrtime.js",
"path": "/lib/bare/bundles/bareFormat.js",
"keys": [
"bareHrtime"
"bareFormat"
]
},
{
@@ -301,6 +289,18 @@
"bareFs"
]
},
{
"path": "/lib/bare/bundles/bareHrtime.js",
"keys": [
"bareHrtime"
]
},
{
"path": "/lib/bare/bundles/bareGtk.js",
"keys": [
"bareGtk"
]
},
{
"path": "/lib/bare/bundles/bareHttpParser.js",
"keys": [
@@ -313,12 +313,6 @@
"bareIco"
]
},
{
"path": "/lib/bare/bundles/bareHttp1.js",
"keys": [
"bareHttp1"
]
},
{
"path": "/lib/bare/bundles/bareImageResample.js",
"keys": [
@@ -326,9 +320,9 @@
]
},
{
"path": "/lib/bare/bundles/bareHttps.js",
"path": "/lib/bare/bundles/bareHttp1.js",
"keys": [
"bareHttps"
"bareHttp1"
]
},
{
@@ -338,9 +332,9 @@
]
},
{
"path": "/lib/bare/bundles/bareJpeg.js",
"path": "/lib/bare/bundles/bareHttps.js",
"keys": [
"bareJpeg"
"bareHttps"
]
},
{
@@ -349,30 +343,30 @@
"bareIntl"
]
},
{
"path": "/lib/bare/bundles/bareJpeg.js",
"keys": [
"bareJpeg"
]
},
{
"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/bareLief.js",
"keys": [
"bareLief"
]
},
{
"path": "/lib/bare/bundles/bareLink.js",
"keys": [
@@ -380,9 +374,9 @@
]
},
{
"path": "/lib/bare/bundles/bareModuleLexer.js",
"path": "/lib/bare/bundles/bareLogger.js",
"keys": [
"bareModuleLexer"
"bareLogger"
]
},
{
@@ -397,30 +391,18 @@
"bareModule"
]
},
{
"path": "/lib/bare/bundles/bareModuleLexer.js",
"keys": [
"bareModuleLexer"
]
},
{
"path": "/lib/bare/bundles/bareModuleResolve.js",
"keys": [
"bareModuleResolve"
]
},
{
"path": "/lib/bare/bundles/bareNdk.js",
"keys": [
"bareNdk"
]
},
{
"path": "/lib/bare/bundles/bareNative.js",
"keys": [
"bareNative"
]
},
{
"path": "/lib/bare/bundles/bareNodeFetch.js",
"keys": [
"bareNodeFetch"
]
},
{
"path": "/lib/bare/bundles/bareModuleTraverse.js",
"keys": [
@@ -428,15 +410,27 @@
]
},
{
"path": "/lib/bare/bundles/bareOpen.js",
"path": "/lib/bare/bundles/bareMedia.js",
"keys": [
"bareOpen"
"bareMedia"
]
},
{
"path": "/lib/bare/bundles/bareOs.js",
"path": "/lib/bare/bundles/bareNdk.js",
"keys": [
"bareOs"
"bareNdk"
]
},
{
"path": "/lib/bare/bundles/bareDev.js",
"keys": [
"bareDev"
]
},
{
"path": "/lib/bare/bundles/bareNative.js",
"keys": [
"bareNative"
]
},
{
@@ -446,15 +440,21 @@
]
},
{
"path": "/lib/bare/bundles/bareMedia.js",
"path": "/lib/bare/bundles/bareNodeFetch.js",
"keys": [
"bareMedia"
"bareNodeFetch"
]
},
{
"path": "/lib/bare/bundles/barePerformance.js",
"path": "/lib/bare/bundles/bareOs.js",
"keys": [
"barePerformance"
"bareOs"
]
},
{
"path": "/lib/bare/bundles/bareOpen.js",
"keys": [
"bareOpen"
]
},
{
@@ -464,9 +464,9 @@
]
},
{
"path": "/lib/bare/bundles/barePackDrive.js",
"path": "/lib/bare/bundles/barePerformance.js",
"keys": [
"barePackDrive"
"barePerformance"
]
},
{
@@ -476,15 +476,15 @@
]
},
{
"path": "/lib/bare/bundles/barePipe.js",
"path": "/lib/bare/bundles/barePackDrive.js",
"keys": [
"barePipe"
"barePackDrive"
]
},
{
"path": "/lib/bare/bundles/bareDev.js",
"path": "/lib/bare/bundles/barePipe.js",
"keys": [
"bareDev"
"barePipe"
]
},
{
@@ -505,36 +505,36 @@
"barePrebuild"
]
},
{
"path": "/lib/bare/bundles/bareQueueMicrotask.js",
"keys": [
"bareQueueMicrotask"
]
},
{
"path": "/lib/bare/bundles/bareQuerystring.js",
"keys": [
"bareQuerystring"
]
},
{
"path": "/lib/bare/bundles/bareProcess.js",
"keys": [
"bareProcess"
]
},
{
"path": "/lib/bare/bundles/bareRealm.js",
"keys": [
"bareRealm"
]
},
{
"path": "/lib/bare/bundles/bareQueueMicrotask.js",
"keys": [
"bareQueueMicrotask"
]
},
{
"path": "/lib/bare/bundles/barePromClient.js",
"keys": [
"barePromClient"
]
},
{
"path": "/lib/bare/bundles/bareProcess.js",
"keys": [
"bareProcess"
]
},
{
"path": "/lib/bare/bundles/bareRuntime.js",
"keys": [
@@ -547,6 +547,18 @@
"bareSdl"
]
},
{
"path": "/lib/bare/bundles/bareRun.js",
"keys": [
"bareRun"
]
},
{
"path": "/lib/bare/bundles/bareRpc.js",
"keys": [
"bareRpc"
]
},
{
"path": "/lib/bare/bundles/bareRepl.js",
"keys": [
@@ -559,54 +571,42 @@
"bareSemver"
]
},
{
"path": "/lib/bare/bundles/bareRpc.js",
"keys": [
"bareRpc"
]
},
{
"path": "/lib/bare/bundles/bareSignals.js",
"keys": [
"bareSignals"
]
},
{
"path": "/lib/bare/bundles/bareSidecar.js",
"keys": [
"bareSidecar"
]
},
{
"path": "/lib/bare/bundles/bareRun.js",
"keys": [
"bareRun"
]
},
{
"path": "/lib/bare/bundles/bareStorage.js",
"keys": [
"bareStorage"
]
},
{
"path": "/lib/bare/bundles/bareSignals.js",
"keys": [
"bareSignals"
]
},
{
"path": "/lib/bare/bundles/bareStream.js",
"keys": [
"bareStream"
]
},
{
"path": "/lib/bare/bundles/bareStringDecoder.js",
"keys": [
"bareStringDecoder"
]
},
{
"path": "/lib/bare/bundles/bareStdio.js",
"keys": [
"bareStdio"
]
},
{
"path": "/lib/bare/bundles/bareStringDecoder.js",
"keys": [
"bareStringDecoder"
]
},
{
"path": "/lib/bare/bundles/bareSvg.js",
"keys": [
@@ -619,6 +619,12 @@
"bareTap"
]
},
{
"path": "/lib/bare/bundles/bareStructuredClone.js",
"keys": [
"bareStructuredClone"
]
},
{
"path": "/lib/bare/bundles/bareSystemLogger.js",
"keys": [
@@ -638,9 +644,9 @@
]
},
{
"path": "/lib/bare/bundles/bareTimers.js",
"path": "/lib/bare/bundles/bareThread.js",
"keys": [
"bareTimers"
"bareThread"
]
},
{
@@ -650,33 +656,9 @@
]
},
{
"path": "/lib/bare/bundles/bareTpl.js",
"path": "/lib/bare/bundles/bareTimers.js",
"keys": [
"bareTpl"
]
},
{
"path": "/lib/bare/bundles/bareStructuredClone.js",
"keys": [
"bareStructuredClone"
]
},
{
"path": "/lib/bare/bundles/bareThread.js",
"keys": [
"bareThread"
]
},
{
"path": "/lib/bare/bundles/bareTty.js",
"keys": [
"bareTty"
]
},
{
"path": "/lib/bare/bundles/bareType.js",
"keys": [
"bareType"
"bareTimers"
]
},
{
@@ -685,12 +667,36 @@
"bareTls"
]
},
{
"path": "/lib/bare/bundles/bareTpl.js",
"keys": [
"bareTpl"
]
},
{
"path": "/lib/bare/bundles/bareType.js",
"keys": [
"bareType"
]
},
{
"path": "/lib/bare/bundles/bareUiKit.js",
"keys": [
"bareUiKit"
]
},
{
"path": "/lib/bare/bundles/bareTty.js",
"keys": [
"bareTty"
]
},
{
"path": "/lib/bare/bundles/bareUnpack.js",
"keys": [
"bareUnpack"
]
},
{
"path": "/lib/bare/bundles/bareV8.js",
"keys": [
@@ -698,9 +704,9 @@
]
},
{
"path": "/lib/bare/bundles/bareUnpack.js",
"path": "/lib/bare/bundles/bareUnionBundle.js",
"keys": [
"bareUnpack"
"bareUnionBundle"
]
},
{
@@ -715,18 +721,18 @@
"bareWalkHandles"
]
},
{
"path": "/lib/bare/bundles/bareUnionBundle.js",
"keys": [
"bareUnionBundle"
]
},
{
"path": "/lib/bare/bundles/bareV8ToIstanbul.js",
"keys": [
"bareV8ToIstanbul"
]
},
{
"path": "/lib/bare/bundles/bareWebKit.js",
"keys": [
"bareWebKit"
]
},
{
"path": "/lib/bare/bundles/bareUtils.js",
"keys": [
@@ -739,24 +745,18 @@
"bareWebp"
]
},
{
"path": "/lib/bare/bundles/bareWhich.js",
"keys": [
"bareWhich"
]
},
{
"path": "/lib/bare/bundles/bareWebKit.js",
"keys": [
"bareWebKit"
]
},
{
"path": "/lib/bare/bundles/bareWebKitGtk.js",
"keys": [
"bareWebKitGtk"
]
},
{
"path": "/lib/bare/bundles/bareWhich.js",
"keys": [
"bareWhich"
]
},
{
"path": "/lib/bare/bundles/bareWinUi.js",
"keys": [
@@ -769,12 +769,6 @@
"bareXdiff"
]
},
{
"path": "/lib/bare/bundles/bareWorker.js",
"keys": [
"bareWorker"
]
},
{
"path": "/lib/bare/bundles/bareZlib.js",
"keys": [
@@ -787,6 +781,12 @@
"bareZmq"
]
},
{
"path": "/lib/bare/bundles/bareWorker.js",
"keys": [
"bareWorker"
]
},
{
"path": "/lib/bare/bundles/bareWs.js",
"keys": [
@@ -1595,8 +1595,8 @@
],
"bundleProvenance": {
"schemaVersion": 1,
"generatedAt": "2026-04-05T19:03:42.280Z",
"gitCommit": "39dbf0f1be135931add4414edfc66b593559be83",
"generatedAt": "2026-04-05T19:26:43.551Z",
"gitCommit": "169df862f7c3579c6eb28d62c3a6b05ab38ef0f5",
"nodeVersion": "v22.22.0",
"bundleTier": "all",
"normativeManifest": "packages/bare-os-booter/lib/bare-module-manifest.json",
+3 -1
View File
@@ -1,6 +1,6 @@
{
"schema": 1,
"atMs": 1775415820999,
"atMs": 1775417202531,
"commands": [
"arch",
"awk",
@@ -90,7 +90,9 @@
"openssl",
"paste",
"patch",
"pathcap-verify",
"pathchk",
"pkg-swarm-index",
"pr",
"printenv",
"printf",
+269 -1
View File
@@ -70,6 +70,12 @@
* BARE_OS_RC_D_SKIP: comma-separated rc.d basenames to skip; a pattern ending with * skips
* names with that prefix (e.g. 10-* skips 10-foo).
*
* BARE_OS_RC_PROPOSAL_MULTISIG_STRICT=1: before **rc.d**, require **`/etc/bare-os/pear.multisig.json`**
* ( **`bareOsPearMultisigShapeOk`** ) and validate every **`/etc/bare-os/rc.proposals/enabled/*.json`**
* (**`schema`**: **1**, **`proposalId`**, **`targetRcSnippet`** under **`/etc/bare-os/rc.d/`**, **`signaturesFrom`**: distinct
* public keys each listed in **`pear.multisig.json`** **`signers`**, count **`quorum`**). Emits **`ctx.bareOsAuditLogAppend`**
* rows **`rc.proposal.multisig_ok`** / **`rc.proposal.multisig_fail`** when the hook exists.
*
* Use ctx.registerKernelShutdownHook(fn) for teardown before initd disposers.
*/
@@ -1098,6 +1104,246 @@ async function runBareOsKernelDir(ctx) {
return true
}
/**
* @param {Record<string, unknown>} ctx
* @param {Record<string, unknown>} entry
*/
function bareOsAppendRcProposalAudit(ctx, entry) {
try {
const fn = ctx.bareOsAuditLogAppend
if (typeof fn === 'function') fn(entry)
} catch {
/* ignore */
}
}
/**
* When **`BARE_OS_RC_PROPOSAL_MULTISIG_STRICT`**, every enabled proposal must cite enough
* **`pear.multisig.json`** signers (distinct keys). Cryptographic signature verification is host/seeder responsibility.
* @param {Record<string, unknown>} ctx
* @returns {Promise<boolean>}
*/
async function verifyRcProposalsMultisigStrict(ctx) {
const strict = ctx.env?.BARE_OS_RC_PROPOSAL_MULTISIG_STRICT
if (strict !== '1' && strict !== 'true') return true
const { drive, b4a } = ctx
let pearBuf
try {
pearBuf = await drive.get('/etc/bare-os/pear.multisig.json')
} catch {
pearBuf = null
}
if (!pearBuf) {
bootStructuredLog(
ctx,
'error',
'rc.proposal.multisig.pear_missing',
'[rc.proposals] BARE_OS_RC_PROPOSAL_MULTISIG_STRICT requires /etc/bare-os/pear.multisig.json'
)
bareOsAppendRcProposalAudit(ctx, {
type: 'rc.proposal.multisig_fail',
reason: 'pear_multisig_missing',
ts: Date.now()
})
return false
}
/** @type {unknown} */
let pearParsed
try {
pearParsed = JSON.parse(b4a.toString(pearBuf))
} catch (e) {
bootStructuredLog(
ctx,
'error',
'rc.proposal.multisig.pear_parse',
'[rc.proposals] pear.multisig.json: ' + ((e && e.message) || String(e))
)
bareOsAppendRcProposalAudit(ctx, {
type: 'rc.proposal.multisig_fail',
reason: 'pear_multisig_parse',
ts: Date.now()
})
return false
}
if (!bareOsPearMultisigShapeOk(pearParsed)) {
bootStructuredLog(
ctx,
'error',
'rc.proposal.multisig.pear_shape',
'[rc.proposals] pear.multisig.json must be { signers: string[], quorum: number } with 1 ≤ quorum ≤ signers.length'
)
bareOsAppendRcProposalAudit(ctx, {
type: 'rc.proposal.multisig_fail',
reason: 'pear_multisig_shape',
ts: Date.now()
})
return false
}
const pear = /** @type {{ signers: string[]; quorum: number }} */ (pearParsed)
const signerSet = new Set(pear.signers.map((s) => String(s).toLowerCase()))
const quorum = pear.quorum
/** @type {string[]} */
const proposalNames = []
try {
for await (const n of drive.readdir('/etc/bare-os/rc.proposals/enabled')) {
if (String(n).endsWith('.json')) proposalNames.push(String(n))
}
} catch {
return true
}
proposalNames.sort()
if (!proposalNames.length) return true
for (const fn of proposalNames) {
const p = `/etc/bare-os/rc.proposals/enabled/${fn}`
/** @type {unknown} */
let raw
try {
const buf = await drive.get(p)
if (!buf) {
bootStructuredLog(ctx, 'error', 'rc.proposal.missing', `[rc.proposals] empty: ${fn}`)
bareOsAppendRcProposalAudit(ctx, {
type: 'rc.proposal.multisig_fail',
reason: 'proposal_missing',
proposalFile: fn,
ts: Date.now()
})
return false
}
raw = JSON.parse(b4a.toString(buf))
} catch (e) {
bootStructuredLog(
ctx,
'error',
'rc.proposal.parse',
`[rc.proposals] ${fn}: ` + ((e && e.message) || String(e))
)
bareOsAppendRcProposalAudit(ctx, {
type: 'rc.proposal.multisig_fail',
reason: 'proposal_parse',
proposalFile: fn,
ts: Date.now()
})
return false
}
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
bootStructuredLog(ctx, 'error', 'rc.proposal.shape', `[rc.proposals] ${fn}: expected object`)
return false
}
const o = /** @type {Record<string, unknown>} */ (raw)
if (o.schema !== 1) {
bootStructuredLog(
ctx,
'error',
'rc.proposal.schema',
`[rc.proposals] ${fn}: schema must be 1`
)
bareOsAppendRcProposalAudit(ctx, {
type: 'rc.proposal.multisig_fail',
reason: 'proposal_schema',
proposalFile: fn,
ts: Date.now()
})
return false
}
const proposalId = String(o.proposalId || '').trim()
if (!proposalId) {
bootStructuredLog(ctx, 'error', 'rc.proposal.id', `[rc.proposals] ${fn}: proposalId required`)
bareOsAppendRcProposalAudit(ctx, {
type: 'rc.proposal.multisig_fail',
reason: 'proposal_id',
proposalFile: fn,
ts: Date.now()
})
return false
}
const targetRcSnippet = String(o.targetRcSnippet || '').trim()
if (!targetRcSnippet.startsWith('/etc/bare-os/rc.d/')) {
bootStructuredLog(
ctx,
'error',
'rc.proposal.target',
`[rc.proposals] ${fn}: targetRcSnippet must start with /etc/bare-os/rc.d/`
)
bareOsAppendRcProposalAudit(ctx, {
type: 'rc.proposal.multisig_fail',
reason: 'proposal_target',
proposalFile: fn,
proposalId,
ts: Date.now()
})
return false
}
const sigFrom = o.signaturesFrom
if (!Array.isArray(sigFrom)) {
bootStructuredLog(
ctx,
'error',
'rc.proposal.signaturesFrom',
`[rc.proposals] ${fn}: signaturesFrom must be an array of signer public keys`
)
bareOsAppendRcProposalAudit(ctx, {
type: 'rc.proposal.multisig_fail',
reason: 'signatures_from_shape',
proposalFile: fn,
proposalId,
ts: Date.now()
})
return false
}
/** @type {Set<string>} */
const approved = new Set()
for (const s of sigFrom) {
const k = String(s || '').toLowerCase().trim()
if (!k) continue
if (!signerSet.has(k)) {
bootStructuredLog(
ctx,
'error',
'rc.proposal.signer_unknown',
`[rc.proposals] ${fn}: signer not in pear.multisig.json`
)
bareOsAppendRcProposalAudit(ctx, {
type: 'rc.proposal.multisig_fail',
reason: 'signer_not_in_policy',
proposalFile: fn,
proposalId,
ts: Date.now()
})
return false
}
approved.add(k)
}
if (approved.size < quorum) {
bootStructuredLog(
ctx,
'error',
'rc.proposal.quorum',
`[rc.proposals] ${fn}: need ≥ pear.multisig quorum (${quorum}) distinct approved signers, got ${approved.size}`
)
bareOsAppendRcProposalAudit(ctx, {
type: 'rc.proposal.multisig_fail',
reason: 'quorum_not_met',
proposalFile: fn,
proposalId,
ts: Date.now()
})
return false
}
bareOsAppendRcProposalAudit(ctx, {
type: 'rc.proposal.multisig_ok',
proposalFile: fn,
proposalId,
targetRcSnippet,
signerCount: approved.size,
quorum,
ts: Date.now()
})
}
return true
}
/**
* Optional snippets under /etc/bare-os/rc.d/ executed in lexicographic order.
* @param {Record<string, unknown>} ctx
@@ -1105,6 +1351,7 @@ async function runBareOsKernelDir(ctx) {
*/
async function runBareOsRcDir(ctx) {
const { drive, b4a } = ctx
if (!(await verifyRcProposalsMultisigStrict(ctx))) return false
const skip = parseRcDSkipPatterns(ctx)
try {
/** @type {string[]} */
@@ -1853,6 +2100,26 @@ async function start(ctx) {
}
}
if (coldExceeded || stdlibExceeded) {
/** @type {{ kind: string, wallMs: number, limitMs: number }[]} */
const violations = []
if (coldExceeded && Number.isFinite(budget) && budget > 0) {
violations.push({
kind: 'cold',
wallMs: Date.now() - bootT0,
limitMs: budget
})
}
if (stdlibExceeded && Number.isFinite(sb) && sb > 0) {
const sw = Number.parseInt(
String(ctx.env?.BARE_OS_BOOT_BARE_STDLIB_RESOLUTION_MS || ''),
10
)
violations.push({
kind: 'bare_stdlib',
wallMs: Number.isFinite(sw) ? sw : 0,
limitMs: sb
})
}
await maybeAppendBootTransactionJournal(ctx, {
phase: 'boot.budget',
stage: 'boot.budget',
@@ -1860,9 +2127,10 @@ async function start(ctx) {
ms: 0,
ok: false,
bootBudgetViolation: true,
bootBudgetSchemaVersion: 1,
bootBudgetSchemaVersion: 2,
coldBudgetExceeded: coldExceeded,
bareStdlibBudgetExceeded: stdlibExceeded,
bootBudgetViolations: violations,
transactionState: BARE_OS_BOOT_TXN_STATE.STAGE_COMMITTED
})
}
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -22,7 +22,7 @@
"verify:manifest-data": "node scripts/verify-bare-module-manifest-data.mjs",
"smoke:bare-manifest": "node scripts/smoke-bare-manifest-imports.mjs",
"bundle:kernel": "node scripts/bundle-kernel-init.mjs",
"pretest": "npm run build -w bare-os-coreutils && npm run verify:manifest-data && npm run build -w bare-os-bare-libs && npm run bundle:kernel && node scripts/gen-kernel-extensions-index.mjs && node scripts/verify-kernel-seeder-parity.mjs && node scripts/verify-banned-terminology.mjs && node scripts/verify-naming-alias-matrix.mjs && node scripts/verify-feature-roadmap-canonical.mjs && node scripts/verify-no-new-giant-phase-identifiers.mjs && node scripts/verify-runtime-no-incomplete-markers.mjs && node scripts/audit-placeholder-baseline.mjs --check && node scripts/verify-bare-imports.mjs && node scripts/verify-booter-boot-steps-alignment.mjs && node scripts/gen-ctx-client-helper.mjs && node scripts/verify-extension-manifest-schema.mjs && node scripts/verify-boot-policy-extension-signer-pins.mjs && node scripts/verify-ctx-api-feature-bits.mjs && node scripts/verify-kernel-capabilities-contract.mjs && node scripts/verify-kernel-capabilities-word-6.mjs && node scripts/verify-kernel-capabilities-word-7.mjs && node scripts/verify-kernel-capabilities-word-8.mjs && node scripts/verify-kernel-capabilities-word-9.mjs && node scripts/verify-kernel-capabilities-word-10.mjs && node scripts/verify-kernel-capabilities-word-11.mjs && node scripts/verify-ctx-dts.mjs && node scripts/validate-example-schemas.mjs && node scripts/verify-doc-links.mjs && node scripts/verify-man-coverage.mjs && node scripts/verify-compat-matrix.mjs && node scripts/verify-doc-contracts.mjs && node scripts/verify-bundle-health.mjs && node scripts/verify-bundle-markers.mjs && node scripts/verify-bundle-throws.mjs && node scripts/verify-kernel-program-doc.mjs && node scripts/verify-kernel-program-proc-schema.mjs && node scripts/verify-kernel-program-roadmap-table.mjs && node scripts/verify-pear-no-static-node-import.mjs && node scripts/gen-posix-dashboard.mjs && node scripts/verify-posix-dashboard.mjs && node scripts/verify-posix-profile-triplet.mjs && node scripts/verify-posix-compliance-matrix.mjs && node scripts/verify-holepunch-clone-drift.mjs && node scripts/verify-holepunch-clone-freshness.mjs && npm run smoke:bare-manifest",
"pretest": "npm run build -w bare-os-coreutils && npm run verify:manifest-data && npm run build -w bare-os-bare-libs && npm run bundle:kernel && node scripts/gen-kernel-extensions-index.mjs && node scripts/verify-kernel-seeder-parity.mjs && node scripts/verify-banned-terminology.mjs && node scripts/verify-naming-alias-matrix.mjs && node scripts/verify-feature-roadmap-canonical.mjs && node scripts/verify-no-new-giant-phase-identifiers.mjs && node scripts/verify-runtime-no-incomplete-markers.mjs && node scripts/audit-placeholder-baseline.mjs --check && node scripts/verify-bare-imports.mjs && node scripts/verify-booter-boot-steps-alignment.mjs && node scripts/gen-ctx-client-helper.mjs && node scripts/verify-ctx-client-helper-sync.mjs && node scripts/verify-personal-drive-path-policy.mjs && node scripts/verify-extension-manifest-schema.mjs && node scripts/verify-boot-policy-extension-signer-pins.mjs && node scripts/verify-ctx-api-feature-bits.mjs && node scripts/verify-kernel-capabilities-contract.mjs && node scripts/verify-kernel-capabilities-word-6.mjs && node scripts/verify-kernel-capabilities-word-7.mjs && node scripts/verify-kernel-capabilities-word-8.mjs && node scripts/verify-kernel-capabilities-word-9.mjs && node scripts/verify-kernel-capabilities-word-10.mjs && node scripts/verify-kernel-capabilities-word-11.mjs && node scripts/verify-ctx-dts.mjs && node scripts/validate-example-schemas.mjs && node scripts/verify-doc-links.mjs && node scripts/verify-man-coverage.mjs && node scripts/verify-compat-matrix.mjs && node scripts/verify-doc-contracts.mjs && node scripts/verify-bundle-health.mjs && node scripts/verify-bundle-markers.mjs && node scripts/verify-bundle-throws.mjs && node scripts/verify-kernel-program-doc.mjs && node scripts/verify-kernel-program-proc-schema.mjs && node scripts/verify-kernel-program-roadmap-table.mjs && node scripts/verify-pear-no-static-node-import.mjs && node scripts/gen-posix-dashboard.mjs && node scripts/verify-posix-dashboard.mjs && node scripts/verify-posix-profile-triplet.mjs && node scripts/verify-posix-compliance-matrix.mjs && node scripts/verify-holepunch-clone-drift.mjs && node scripts/verify-holepunch-clone-freshness.mjs && npm run smoke:bare-manifest",
"test": "npm run test --workspaces --if-present",
"test:bare": "npm run test -w bare-os-protocol && npm run test:bare -w bare-os-booter && npm run test -w bare-os-seeder",
"verify-kernel-seeder": "node scripts/verify-kernel-seeder-parity.mjs",
+1
View File
@@ -18,6 +18,7 @@ Authoritative **version alignment** with protocol and telemetry schema numbers l
| Version | Booter (workspace) | Notes |
| ------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| 1.52.0 | 0.1.0 | **`disk.os` `searchLocal`** — optional **`/etc/bare-os/path-manifest.json`** acceleration (**`BARE_OS_DISK_OS_PATH_MANIFEST`**, **`BARE_OS_DISK_OS_SEARCH_MANIFEST_ONLY`**, **`BARE_OS_DISK_OS_MANIFEST_CACHE_MS`**) + **`bare_os.disk_os_hints`** JSON **schema 3** (**`pathSearchLocal`** metrics). Stock **`ctx.bareOsHrpcRequest`** route **`bare_os.search_local`** (**`payload.query`**, max 512 chars). **`BARE_OS_HRPC_ROUTE_TABLE_SCHEMA_VERSION` `2`**. Optional **path capabilities**: **`ctx.bareOsVerifyPathCapabilityEnvelope`**, VFS gate **`BARE_OS_PATH_CAPABILITY_ENFORCE_READ`** + **`BARE_OS_PATH_CAPABILITY_PREFIX`**, **`/bin/pathcap-verify`**, example **`kernel/etc/bare-os/path-capability.envelope.example.json`**. |
| 1.51.1 | 0.1.0 | **`disk.os` `bare_os.replication_snapshot`** JSON **schema 2** — merges seed snapshot hints / manifest slice / chain + **`BARE_OS_CORESTORE_SNAPSHOT_JSON`**. Shell **`BARE_OS_SHELL_NOUNSET`** / **`set -u`** / **`set -o nounset`**. Wasm **`BARE_OS_WASM_KERNEL_HOSTNAME_IMPORT`** → **`env.bare_os_hostname_peek`**. **`ctx.bareOsTrySpawnHostSubprocess`** merges default **`timeoutMs`** from **`BARE_OS_BARE_SUBPROCESS_TIMEOUT_MS`**. **`/proc/bare_os_features`**: **`protomuxAppChannel`**. **`/proc/bare_os/security_posture.json`** schema **5** (**`blindPeeringOperatorSketch`**). Swarm disk: **`BARE_OS_MBR_READ_TIMEOUT_MS`**, **`BARE_OS_MBR_READ_TIMEOUT_ADAPTIVE`**. Kernel **`kernel-ext-resolution.json`** schema **3** + **`BARE_OS_KERNEL_EXT_RESOLUTION_JSON_ALWAYS`**. Seeder multisig logs via **`seedLog`**. |
| 1.51.0 | 0.1.0 | **`disk.os` `bare_os.replication_operator_sketch`** JSON **schema 5** — optional **`corestoreOperatorSketch`** from host **`BARE_OS_CORESTORE_STATS_JSON`**. **`/proc/bare_os/syscalls.json`** schema **10****`ctx.bareOsSyscall('posix_fadvise')`** simulated no-op; **`getconf`** / **`posix-compliance-matrix`** syscall op list includes **`posix_fadvise`**. **`hrpc_allowlist_sketch`** proc **schema 3** + **`routeTableSchemaVersion`** ( **`bare-os-hrpc-route-table.js`** ). **`export -p`** shell builtin. Optional **`kernel.ext.d` `provides`** version conflict detection (strict boot). Runtime caps: **`swarm_health`** proc paths. |
| 1.50.0 | 0.1.0 | Optional **`BARE_OS_SHELL_READ_BUILTIN`** **`read`** builtin (bounded stdin / **`readLine`**). Expanded **`bareOsGetconfSysconf`** **`_SC_BARE_OS_*`** runtime caps (pipeline, glob, swarm, socket queues). **`pathconf`**: **`BARE_OS_PERSONAL_ACCT_PREFIX`** + union — **`_PC_NO_TRUNC`** stays **1** under **`/.bare-os/acct/…`**. Socket bridge **`SO_SNDBUF`/`SO_RCVBUF`** logical **`getsockopt`/`setsockopt`**. **`metrics_live.replicationLive`** schema **3** + **`corestoreSnapshotSurface`**; **`bootBudget*`** schema **2** + **`bootBudgetTelemetry`**. Wasm **`BARE_OS_WASM_KERNEL_MONOTONIC_MS`** → **`env.bare_os_monotonic_ms`**. MQ priority tie-break FIFO (**`posixMessageQueues` schema 2**). Blind-relay swarm proc **schema 3** (**`blindPeerStack`**). Hyperbee guest hint **schema 2** (**`hyperbee2ReadSurface`**). **`/run/bare-os/boot-perf.json`**: **`bootBudgetStrict`** / **`bootPolicyStrict`**. |
+95 -2
View File
@@ -67,6 +67,7 @@ import { createBareOsDiskOsBridge } from './lib/bare-os-disk-os-bridge.js'
import { buildBareOsHyperbeeGuestHint } from './lib/bare-os-hyperbee-guest-hint.js'
import { invokeBareOsPearUpdaterDelegate } from './lib/bare-os-pear-updater-bridge.js'
import { verifyBootManifestEd25519 } from '#bare-os-boot-manifest-sig'
import { verifyPathCapabilityEnvelope } from './lib/bare-os-path-capability.js'
import { getBareInitdJournalNdjson } from './lib/bare-initd-journal.js'
import { createBareOsSandboxContext } from './lib/bare-os-sandbox.js'
import {
@@ -652,9 +653,11 @@ async function executeKernel(disk, store, swarm, initSource) {
'BARE_OS_WASM_KERNEL_SYSCALL',
'BARE_OS_WASM_KERNEL_MONOTONIC_MS',
'BARE_OS_WASM_KERNEL_HOSTNAME_IMPORT',
'BARE_OS_WASM_KERNEL_CTX_API_PEEK',
'BARE_OS_POSIX_FD_SIM',
'BARE_OS_POSIX_FD_SIM_MAX_BYTES',
'BARE_OS_POSIX_SOCKET_FD_BRIDGE',
'BARE_OS_POSIX_SOCKET_CONNECT_TIMEOUT_MS',
'BARE_OS_POSIX_SOCKET_SCM_RIGHTS',
'BARE_OS_POSIX_SOCKET_SCM_RIGHTS_MAX_FDS',
'BARE_OS_POSIX_FCNTL_BLOCKING_WAIT',
@@ -731,6 +734,12 @@ async function executeKernel(disk, store, swarm, initSource) {
'BARE_OS_PEER_REQUIRE_CAPS_JSON',
'BARE_OS_PEER_ADMISSION_AUDIT_NDJSON',
'BARE_OS_PEER_ADMISSION_AUDIT_RATE_MS',
'BARE_OS_DISK_OS_PATH_MANIFEST',
'BARE_OS_DISK_OS_SEARCH_MANIFEST_ONLY',
'BARE_OS_DISK_OS_MANIFEST_CACHE_MS',
'BARE_OS_PATH_CAPABILITY_ENFORCE_READ',
'BARE_OS_PATH_CAPABILITY_PREFIX',
'BARE_OS_SAVEVAULT_PRESNAPSHOT_HINT',
'BARE_OS_VFS_WARM_CACHE_INVALIDATE_ON_APPEND',
'BARE_OS_KERNEL_DEBUG',
'BARE_OS_PEAR_INSPECT',
@@ -994,6 +1003,8 @@ async function executeKernel(disk, store, swarm, initSource) {
const replicationWarmCacheLensRef = { system: null, personal: null }
/** @type {{ current: number }} */
const replicationWarmLastFullInvMsRef = { current: 0 }
/** Count of replication warm full flushes skipped due to adaptive window coalescing. */
const replicationWarmAdaptiveSkipRef = { current: 0 }
/** Cached `/proc/bare_os/syscalls.json` text; cleared with warm read cache invalidation. */
let bareOsSyscallsProcJsonCache = null
function bareOsBlindRelayRuntimeHintsForProc() {
@@ -2636,6 +2647,7 @@ async function executeKernel(disk, store, swarm, initSource) {
const nowMs = Date.now()
if (nowMs - replicationWarmLastFullInvMsRef.current < adaptWin) {
doFullInv = false
replicationWarmAdaptiveSkipRef.current++
} else {
replicationWarmLastFullInvMsRef.current = nowMs
}
@@ -2665,7 +2677,7 @@ async function executeKernel(disk, store, swarm, initSource) {
session: { ...sessionStatsRef },
peers: peerRep,
replicationLive: {
schema: 3,
schema: 4,
peerCount: peerRep,
liveHyperdriveSketchSchema: repLive.schema,
systemCoreLength: repLive.systemCoreLength,
@@ -2684,6 +2696,13 @@ async function executeKernel(disk, store, swarm, initSource) {
enabled: warmFullInvOnRep,
envGate: 'BARE_OS_VFS_WARM_CACHE_INVALIDATE_ON_REPLICATION',
note: 'When enabled, increasing replicated core lengths call ctx.bareOsInvalidateWarmReadCaches(replication:core-length) for full /bin + /lib/bare warm flush.'
},
warmReplAdaptive: {
schema: 1,
windowMs: adaptiveReplWarm ? adaptWin : null,
skippedFullFlushCount: replicationWarmAdaptiveSkipRef.current,
envGate: 'BARE_OS_VFS_WARM_REPL_ADAPTIVE_WINDOW_MS',
note: 'Coalesces full warm-cache invalidation bursts when replication core lengths grow faster than the adaptive window.'
}
},
pipeline: pl,
@@ -3268,6 +3287,24 @@ async function executeKernel(disk, store, swarm, initSource) {
hrpcAuditAppend({ ok: true, route: 'bare_os.disk_os_hints' })
return { ok: true, text: txt, json: parsed }
}
if (svc === 'bare_os' && m === 'search_local') {
if (!disk?.os || typeof disk.os.searchLocal !== 'function') {
hrpcAuditAppend({ ok: false, reason: 'disk_os_unavailable' })
throw new Error('bareOsHrpcRequest: disk.os searchLocal unavailable')
}
const query = String(payload.query ?? payload.q ?? '').trim()
if (!query) {
hrpcAuditAppend({ ok: false, reason: 'invalid_query' })
throw new Error('bareOsHrpcRequest: payload.query (or q) is required')
}
if (query.length > 512) {
hrpcAuditAppend({ ok: false, reason: 'query_too_long' })
throw new Error('bareOsHrpcRequest: payload.query exceeds 512 chars')
}
const paths = await disk.os.searchLocal(query)
hrpcAuditAppend({ ok: true, route: 'bare_os.search_local' })
return { ok: true, query, paths }
}
if (svc === 'bare_os' && m === 'replication_operator_sketch') {
if (!disk?.os || typeof disk.os.execRpc !== 'function') {
hrpcAuditAppend({ ok: false, reason: 'disk_os_unavailable' })
@@ -6157,7 +6194,19 @@ async function executeKernel(disk, store, swarm, initSource) {
}
}
const sock = new Tcp()
await new Promise((resolve, reject) => {
const connectTimeoutMs = Math.min(
300000,
Math.max(
0,
Number.parseInt(
String(
shellEnv.BARE_OS_POSIX_SOCKET_CONNECT_TIMEOUT_MS || '0'
).trim(),
10
) || 0
)
)
const connectPromise = new Promise((resolve, reject) => {
try {
sock.connect(port, host || '127.0.0.1', {}, (err) =>
err ? reject(err) : resolve(undefined)
@@ -6166,6 +6215,46 @@ async function executeKernel(disk, store, swarm, initSource) {
reject(e)
}
})
try {
if (connectTimeoutMs > 0) {
let to
const timeoutP = new Promise((_, reject) => {
to = setTimeout(
() =>
reject(
Object.assign(new Error('ETIMEDOUT'), {
code: 'ETIMEDOUT'
})
),
connectTimeoutMs
)
})
await Promise.race([connectPromise, timeoutP])
clearTimeout(to)
} else {
await connectPromise
}
} catch (e) {
try {
sock.destroy?.()
} catch {
/* ignore */
}
if (
e &&
/** @type {{ code?: string }} */ (e).code === 'ETIMEDOUT'
) {
return {
ok: false,
op: name,
code: 'ETIMEDOUT',
errnoHint: 'ETIMEDOUT',
posixAlignment: 'partial',
note: `connect: timed out after ${connectTimeoutMs}ms (BARE_OS_POSIX_SOCKET_CONNECT_TIMEOUT_MS).`
}
}
throw e
}
slot.state = 'connected'
slot.sock = sock
slot.transport = 'tcp'
@@ -7827,6 +7916,10 @@ async function executeKernel(disk, store, swarm, initSource) {
: null
return verifyBootManifestEd25519(m, s, pub)
},
/** Verify a signed path-capability envelope (schema 1); see `kernel/etc/bare-os/path-capability.envelope.example.json`. */
bareOsVerifyPathCapabilityEnvelope(envelope) {
return verifyPathCapabilityEnvelope(envelope)
},
/**
* Ask host to attach a mirror read key (Pear / Holepunch); emits process event when available.
* @param {{ key?: string, label?: string }} [opts]
@@ -2,4 +2,4 @@
* Semantic version of the booter `ctx` contract for custom kernels.
* Bump when adding/removing/renaming documented `ctx` fields or changing behavior.
*/
export const BARE_OS_CTX_API_VERSION = '1.51.1'
export const BARE_OS_CTX_API_VERSION = '1.52.0'
+4 -1
View File
@@ -1,6 +1,6 @@
/**
* Stable shapes for custom kernels and `/bin` utilities (best-effort; full `ctx` is assembled in `index.js`).
* Match `BARE_OS_CTX_API_VERSION` in `bare-os-ctx-api.js` (stock **1.51.1**).
* Match `BARE_OS_CTX_API_VERSION` in `bare-os-ctx-api.js` (stock **1.52.0**).
*/
export interface BareOsAbortOpts {
@@ -185,6 +185,9 @@ export interface BareOsKernelContext {
signatureBytes: Uint8Array | ArrayBuffer | null | undefined,
publicKeyHex?: string
): boolean
bareOsVerifyPathCapabilityEnvelope(envelope: unknown):
| { ok: true; payload: { prefix: string; ops: string[]; expMs: number | null } }
| { ok: false; reason: string }
bareOsRequestMirror(opts?: {
key?: string
label?: string
@@ -47,6 +47,64 @@ async function collectSearchMatches(drive, q, matches, cap) {
}
}
/** @type {WeakMap<object, { paths: string[], atMs: number }>} */
const pathManifestCache = new WeakMap()
/**
* @param {import('hyperdrive').default | null | undefined} drive
* @param {string} manifestRel absolute path on drive
* @param {number} ttlMs
* @returns {Promise<string[]>}
*/
async function loadPathManifestPaths(drive, manifestRel, ttlMs) {
if (!drive || typeof drive.get !== 'function' || !manifestRel) return []
const now = Date.now()
const cached = pathManifestCache.get(drive)
if (cached && now - cached.atMs < ttlMs) return cached.paths
/** @type {string[]} */
let paths = []
try {
const buf = await drive.get(manifestRel)
if (!buf || !(buf.byteLength > 0)) {
pathManifestCache.set(drive, { paths: [], atMs: now })
return []
}
const text = b4a.toString(buf)
const j = JSON.parse(text)
const arr = Array.isArray(j)
? j
: j && typeof j === 'object' && Array.isArray(j.paths)
? j.paths
: []
paths = arr
.map((p) => String(p || '').trim())
.filter((p) => p.startsWith('/'))
} catch {
paths = []
}
pathManifestCache.set(drive, { paths, atMs: now })
return paths
}
/**
* @param {string[]} manifestPaths
* @param {string} q normalized lowercase query
* @param {string[]} matches
* @param {number} cap
* @returns {number} number of manifest matches appended
*/
function collectManifestMatches(manifestPaths, q, matches, cap) {
let n = 0
for (const p of manifestPaths) {
if (matches.length >= cap) break
if (p.toLowerCase().includes(q)) {
matches.push(p)
n++
}
}
return n
}
/**
* @param {number} ms
* @returns {Promise<void>}
@@ -77,6 +135,19 @@ function sleepMs(ms) {
* }} opts
*/
export function createBareOsDiskOsBridge(opts) {
/** Last `searchLocal` metrics for `disk_os_hints` (schema 3). */
const pathSearchLocalMetrics = {
schema: 1,
lastAtMs: 0,
lastQueryLen: 0,
manifestRel: /** @type {string | null} */ (null),
manifestOnly: false,
manifestEntryCount: 0,
manifestMatchCount: 0,
primaryScanMatchCount: 0,
auxiliaryScanMatchCount: 0
}
return {
/**
* @param {string} query
@@ -90,24 +161,92 @@ export function createBareOsDiskOsBridge(opts) {
0,
Math.min(500, Number(env?.BARE_OS_DISK_OS_SEARCH_THROTTLE_MS) || 0)
)
const manifestDefault = '/etc/bare-os/path-manifest.json'
const manifestRelRaw = String(
env?.BARE_OS_DISK_OS_PATH_MANIFEST != null
? env.BARE_OS_DISK_OS_PATH_MANIFEST
: manifestDefault
).trim()
const manifestRel =
manifestRelRaw === ''
? ''
: manifestRelRaw.startsWith('/')
? manifestRelRaw
: `/${manifestRelRaw}`
const manifestOnly =
String(env?.BARE_OS_DISK_OS_SEARCH_MANIFEST_ONLY || '') === '1' ||
String(env?.BARE_OS_DISK_OS_SEARCH_MANIFEST_ONLY || '') === 'true'
const cacheMs = Math.max(
0,
Math.min(3600000, Number(env?.BARE_OS_DISK_OS_MANIFEST_CACHE_MS) || 30000)
)
/** @type {import('hyperdrive').default[]} */
const drives = [opts.drive]
const drives = []
if (opts.drive && typeof opts.drive.list === 'function') {
drives.push(opts.drive)
}
if (Array.isArray(opts.auxiliaryDrives)) {
for (const d of opts.auxiliaryDrives) {
if (d && typeof d.list === 'function') drives.push(d)
}
}
/** @type {string[]} */
const raw = []
for (let i = 0; i < drives.length && raw.length < MAX_SEARCH_RESULTS; i++) {
if (i > 0 && throttleMs > 0) await sleepMs(throttleMs)
let manifestMatchCount = 0
let primaryScanCount = 0
let auxScanCount = 0
let manifestEntryCount = 0
const primary = drives[0]
if (primary && manifestRel) {
const mpaths = await loadPathManifestPaths(
primary,
manifestRel,
cacheMs
)
manifestEntryCount = mpaths.length
manifestMatchCount = collectManifestMatches(
mpaths,
q,
raw,
MAX_SEARCH_RESULTS
)
}
if (primary && raw.length < MAX_SEARCH_RESULTS && !manifestOnly) {
const before = raw.length
await collectSearchMatches(
primary,
q,
raw,
MAX_SEARCH_RESULTS - raw.length
)
primaryScanCount += raw.length - before
}
for (let i = 1; i < drives.length && raw.length < MAX_SEARCH_RESULTS; i++) {
if (throttleMs > 0) await sleepMs(throttleMs)
const before = raw.length
await collectSearchMatches(
drives[i],
q,
raw,
MAX_SEARCH_RESULTS - raw.length
)
auxScanCount += raw.length - before
}
pathSearchLocalMetrics.lastAtMs = Date.now()
pathSearchLocalMetrics.lastQueryLen = q.length
pathSearchLocalMetrics.manifestRel = manifestRel || null
pathSearchLocalMetrics.manifestOnly = manifestOnly
pathSearchLocalMetrics.manifestEntryCount = manifestEntryCount
pathSearchLocalMetrics.manifestMatchCount = manifestMatchCount
pathSearchLocalMetrics.primaryScanMatchCount = primaryScanCount
pathSearchLocalMetrics.auxiliaryScanMatchCount = auxScanCount
return [...new Set(raw)]
},
@@ -174,7 +313,7 @@ export function createBareOsDiskOsBridge(opts) {
: 0
const booterVer = String(opts.booterPackageVersion || '').trim()
return JSON.stringify({
schema: 2,
schema: 3,
ok: true,
auxiliaryDriveCount: aux,
protocolPackageVersion: BARE_OS_PROTOCOL_PACKAGE_VERSION,
@@ -190,7 +329,8 @@ export function createBareOsDiskOsBridge(opts) {
typeof opts.seedHttpDhtProxyHint === 'object'
? opts.seedHttpDhtProxyHint
: null,
note: 'Schema 2 adds protocolPackageVersion, protomuxChannelSchemaVersion, booterPackageVersion (parity with bare_os.kernel_info / capabilities seed RPC). Advisory hints only.'
pathSearchLocal: { ...pathSearchLocalMetrics },
note: 'Schema 3: adds pathSearchLocal metrics (path-manifestaccelerated disk.os searchLocal). Schema 2 fields retained. Advisory hints only.'
})
}
@@ -1,7 +1,7 @@
/**
* Stock ctx.bareOsHrpcRequest built-in routes (versioned for guest tooling).
*/
export const BARE_OS_HRPC_ROUTE_TABLE_SCHEMA_VERSION = 1
export const BARE_OS_HRPC_ROUTE_TABLE_SCHEMA_VERSION = 2
export const BARE_OS_HRPC_STOCK_ROUTES = Object.freeze([
'kernel.ping',
@@ -9,6 +9,7 @@ export const BARE_OS_HRPC_STOCK_ROUTES = Object.freeze([
'vfs.readText',
'bare_os.echo',
'bare_os.disk_os_hints',
'bare_os.search_local',
'bare_os.replication_operator_sketch',
'bare_os.replication_snapshot'
])
@@ -0,0 +1,135 @@
/**
* Ed25519-signed path capability envelopes (schema 1) for optional VFS read enforcement.
* Store **`user.bareos.cap_v1`** in **`PATH.bare_xattr.json`** (see `/bin/xattr`) as base64 UTF-8 JSON.
*/
import b4a from 'b4a'
import { verifyBootManifestEd25519 } from '#bare-os-boot-manifest-sig'
/**
* @param {unknown} v
* @returns {string}
*/
function stableJson(v) {
if (v === null || typeof v !== 'object') return JSON.stringify(v)
if (Array.isArray(v)) {
return '[' + v.map((x) => stableJson(x)).join(',') + ']'
}
const o = /** @type {Record<string, unknown>} */ (v)
const keys = Object.keys(o).sort()
return (
'{' +
keys.map((k) => JSON.stringify(k) + ':' + stableJson(o[k])).join(',') +
'}'
)
}
/**
* @param {Record<string, unknown>} payload
*/
export function bareOsPathCapabilityPayloadCanonicalUtf8(payload) {
return b4a.from(stableJson(payload), 'utf8')
}
/**
* @param {unknown} envelope
* @returns {{ ok: true, payload: { prefix: string, ops: string[], expMs: number | null } } | { ok: false, reason: string }}
*/
export function verifyPathCapabilityEnvelope(envelope) {
if (!envelope || typeof envelope !== 'object' || Array.isArray(envelope)) {
return { ok: false, reason: 'envelope_not_object' }
}
const e = /** @type {Record<string, unknown>} */ (envelope)
if (Number(e.schema) !== 1) return { ok: false, reason: 'bad_schema' }
const payload = e.payload
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
return { ok: false, reason: 'bad_payload' }
}
const pl = /** @type {Record<string, unknown>} */ (payload)
const prefix = String(pl.prefix || '').trim()
if (!prefix.startsWith('/')) return { ok: false, reason: 'bad_prefix' }
const ops = Array.isArray(pl.ops) ? pl.ops.map((x) => String(x)) : []
if (!ops.includes('read') && !ops.includes('all')) {
return { ok: false, reason: 'no_read_op' }
}
const expRaw = pl.expMs
const expMs =
expRaw != null && Number.isFinite(Number(expRaw)) ? Number(expRaw) : null
if (expMs != null && Date.now() > expMs) return { ok: false, reason: 'expired' }
const msg = bareOsPathCapabilityPayloadCanonicalUtf8(pl)
const sig = String(e.signatureHex || '').trim().replace(/^0x/, '')
const pub = String(e.pubkeyHex || '').trim().replace(/^0x/, '')
if (!/^[0-9a-f]{128}$/i.test(sig)) return { ok: false, reason: 'bad_signature_len' }
if (!/^[0-9a-f]{64}$/i.test(pub)) return { ok: false, reason: 'bad_pubkey_len' }
const ok = verifyBootManifestEd25519(msg, sig, pub)
if (!ok) return { ok: false, reason: 'bad_signature' }
return { ok: true, payload: { prefix, ops, expMs } }
}
/**
* @param {string} b64
*/
function xattrB64ToUtf8(b64) {
const t = String(b64 || '').replace(/\s+/g, '')
try {
return b4a.toString(b4a.from(t, 'base64'), 'utf8')
} catch {
return ''
}
}
/**
* When **`BARE_OS_PATH_CAPABILITY_ENFORCE_READ`** is set, personal-drive reads under
* **`BARE_OS_PATH_CAPABILITY_PREFIX`** require a valid **`user.bareos.cap_v1`** xattr envelope
* on **`PATH.bare_xattr.json`** authorizing the logical path prefix.
*
* @param {Record<string, string>} env
* @param {import('hyperdrive').default} drive
* @param {string} drivePath path segment on drive (as `drive.get` expects)
* @param {string} logicalAbs resolved logical absolute path (e.g. `/home/u/f`)
*/
export async function bareOsVfsPathCapabilityDeniesDriveRead(
env,
drive,
drivePath,
logicalAbs
) {
const on =
env.BARE_OS_PATH_CAPABILITY_ENFORCE_READ === '1' ||
env.BARE_OS_PATH_CAPABILITY_ENFORCE_READ === 'true'
if (!on || !drive || typeof drive.get !== 'function') return false
const root = String(env.BARE_OS_PATH_CAPABILITY_PREFIX || '/home/').trim()
if (!root.startsWith('/')) return false
const la = String(logicalAbs || '').replace(/\/+$/, '') || '/'
if (!la.startsWith(root)) return false
if (la.endsWith('.bare_xattr.json') || la.endsWith('.bare_acl')) return false
const p = String(drivePath || '').replace(/\/+$/, '') || '/'
const sidePath = p + '.bare_xattr.json'
let raw
try {
raw = await drive.get(sidePath, { follow: false })
} catch {
return true
}
if (!raw || !raw.byteLength) return true
let map
try {
map = JSON.parse(b4a.toString(raw))
} catch {
return true
}
if (!map || typeof map !== 'object') return true
const b64 = map['user.bareos.cap_v1']
if (typeof b64 !== 'string' || !b64.trim()) return true
let inner
try {
inner = JSON.parse(xattrB64ToUtf8(b64))
} catch {
return true
}
const vr = verifyPathCapabilityEnvelope(inner)
if (!vr.ok) return true
if (!la.startsWith(vr.payload.prefix)) return true
return false
}
@@ -82,6 +82,7 @@ function writeCStrToMemory(memory, ptr, outCap, text) {
* Also **`env.bare_os_wall_time_ms32`**: low 32 bits of wall time (`Date.now()`, signed i32 wrap) for bounded guest timing.
* With **`BARE_OS_WASM_KERNEL_MONOTONIC_MS=1`**, adds **`env.bare_os_monotonic_ms`**: floored monotonic milliseconds (`performance.now` when available), i32-shaped.
* With **`BARE_OS_WASM_KERNEL_HOSTNAME_IMPORT=1`** (requires syscall imports), adds **`env.bare_os_hostname_peek`**: writes session **`HOSTNAME`** (or **`bare-os`**) NUL-terminated into Wasm memory (**bounded**).
* With **`BARE_OS_WASM_KERNEL_CTX_API_PEEK=1`** (requires syscall imports), adds **`env.bare_os_ctx_api_version_peek`**: writes **`ctx.bareOsCtxApiVersion`** NUL-terminated (**bounded**).
*
* @param {Uint8Array} source
* @param {Record<string, unknown>} ctx
@@ -145,6 +146,10 @@ export async function bareOsWasmKernelInstantiate(source, ctx, opts = {}) {
wantSyscall &&
(shellEnv.BARE_OS_WASM_KERNEL_HOSTNAME_IMPORT === '1' ||
shellEnv.BARE_OS_WASM_KERNEL_HOSTNAME_IMPORT === 'true')
const wantCtxApiPeek =
wantSyscall &&
(shellEnv.BARE_OS_WASM_KERNEL_CTX_API_PEEK === '1' ||
shellEnv.BARE_OS_WASM_KERNEL_CTX_API_PEEK === 'true')
const pathconfFn = ctx && typeof ctx.bareOsPathconf === 'function'
? ctx.bareOsPathconf.bind(ctx)
: null
@@ -212,6 +217,16 @@ export async function bareOsWasmKernelInstantiate(source, ctx, opts = {}) {
return writeCStrToMemory(memory, Number(outPtr), Number(outCap), h)
}
}
if (wantCtxApiPeek) {
envImports.bare_os_ctx_api_version_peek = (outPtr, outCap) => {
const v = String(
ctx && ctx.bareOsCtxApiVersion != null
? ctx.bareOsCtxApiVersion
: ''
).slice(0, 64)
return writeCStrToMemory(memory, Number(outPtr), Number(outCap), v)
}
}
}
const importObject = {
env: envImports
@@ -239,7 +254,8 @@ export async function bareOsWasmKernelInstantiate(source, ctx, opts = {}) {
wasmPathconfImport: !!(wantSyscall && pathconfFn),
wasmWallClockMs32Import: !!wantSyscall,
wasmMonotonicMsImport: !!wantMonotonicMs,
wasmHostnamePeekImport: !!(wantSyscall && wantHostname)
wasmHostnamePeekImport: !!(wantSyscall && wantHostname),
wasmCtxApiVersionPeekImport: !!(wantSyscall && wantCtxApiPeek)
}
} catch (e) {
return { ok: false, reason: String(e && e.message ? e.message : e) }
@@ -831,6 +831,21 @@ export async function saveVaultToDrive(ctx) {
if (!id?.secretKey || id.state !== 'unlocked') {
throw new Error('Not logged in')
}
const env = /** @type {Record<string, string>} */ (ctx.env || {})
const snapHint =
env.BARE_OS_SAVEVAULT_PRESNAPSHOT_HINT === '1' ||
env.BARE_OS_SAVEVAULT_PRESNAPSHOT_HINT === 'true'
if (snapHint && typeof globalThis.process?.emit === 'function') {
try {
globalThis.process.emit('bare-os:vault-pre-save-snapshot-hint', {
atMs: Date.now(),
note:
'Host may create a corestore-snapshot (or equivalent) of the personal namespace before vault sealing.'
})
} catch {
/* ignore */
}
}
const vaultKey = vaultKeyFromSecret(id.secretKey)
const drive = ctx.personalDrive
@@ -875,7 +890,8 @@ export async function saveVaultToDrive(ctx) {
try {
await bareOsAppendVaultRotationCheckpoint(ctx, {
kind: 'vault_save',
fileCount
fileCount,
...(snapHint ? { preSnapshotHintEmitted: true } : {})
})
mirrorVaultCheckpointToAuditChain(ctx, {
kind: 'vault_save',
+14
View File
@@ -4,6 +4,7 @@ import { raceWithAbortAndTimeout } from './bare-os-abort.js'
import { bareOsIpcLogicalToActualFifoName } from './bare-os-ipc-namespace.js'
import { listBareInitdJournalUnits } from './bare-initd-journal.js'
import { bareOsVfsAclDeniesDriveOp } from './bare-os-vfs-acl-enforce.js'
import { bareOsVfsPathCapabilityDeniesDriveRead } from './bare-os-path-capability.js'
import {
extractBareOs,
identityNames,
@@ -5004,6 +5005,19 @@ export function createVfs(
return new Uint8Array(hit)
}
}
if (
drive === personalDrive &&
(await bareOsVfsPathCapabilityDeniesDriveRead(
env,
drive,
p,
absFollowed
))
) {
throw new Error(
'EACCES: path capability enforces deny read: ' + absFollowed
)
}
if (await bareOsVfsAclDeniesDriveOp(env, drive, p, 'read')) {
throw new Error('EACCES: ACL enforces deny read: ' + absFollowed)
}
+68 -2
View File
@@ -1495,9 +1495,10 @@ test('hrpc_allowlist_sketch proc lists stock bareOsHrpcRequest routes', async (t
{}
)
t.is(j.schema, 3)
t.is(j.routeTableSchemaVersion, 1)
t.is(j.routeTableSchemaVersion, 2)
t.ok(Array.isArray(j.stockRoutes))
t.ok(j.stockRoutes.includes('bare_os.replication_snapshot'))
t.ok(j.stockRoutes.includes('bare_os.search_local'))
})
test('pear_doctor_state proc JSON schema 2 parity fields', async (t) => {
@@ -1620,16 +1621,62 @@ test('disk.os searchLocal merges auxiliary drives and disk_os_hints RPC', async
t.ok(m2.some((p) => p.includes('x_only')))
const h = await bridge.execRpc('bare_os', 'disk_os_hints', [])
const hj = JSON.parse(h)
t.is(hj.schema, 2)
t.is(hj.schema, 3)
t.is(hj.auxiliaryDriveCount, 1)
t.ok(hj.protocolPackageVersion)
t.ok(typeof hj.protomuxChannelSchemaVersion === 'number')
t.is(hj.mirrorDriveHintV2?.schema, 2)
t.is(hj.httpDhtProxyHint?.routes, 1)
t.ok(hj.pathSearchLocal && hj.pathSearchLocal.schema === 1)
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('disk.os searchLocal uses path-manifest before drive listing', async (t) => {
const dir = testCorestoreDir('diskosmanifest')
const prevManifest = process.env.BARE_OS_DISK_OS_PATH_MANIFEST
const prevOnly = process.env.BARE_OS_DISK_OS_SEARCH_MANIFEST_ONLY
process.env.BARE_OS_DISK_OS_PATH_MANIFEST = '/etc/bare-os/path-manifest.json'
process.env.BARE_OS_DISK_OS_SEARCH_MANIFEST_ONLY = '1'
try {
const store = new Corestore(dir)
const drive = new Hyperdrive(store)
await drive.ready()
await drive.put(
'/etc/bare-os/path-manifest.json',
b4a.from(
JSON.stringify({
schema: 1,
paths: ['/vendor/only-on-manifest.txt']
})
)
)
const bridge = createBareOsDiskOsBridge({
drive,
auxiliaryDrives: [],
bareOsIpc: { list: () => [] },
ctxApiVersion: BARE_OS_CTX_API_VERSION,
systemRevision: null,
bootStartedMs: Date.now()
})
const m = await bridge.searchLocal('only-on')
t.ok(m.some((p) => p === '/vendor/only-on-manifest.txt'))
const h = await bridge.execRpc('bare_os', 'disk_os_hints', [])
const hj = JSON.parse(h)
t.is(hj.schema, 3)
t.is(hj.pathSearchLocal.manifestOnly, true)
t.ok(hj.pathSearchLocal.manifestMatchCount >= 1)
t.is(hj.pathSearchLocal.primaryScanMatchCount, 0)
await store.close()
} finally {
if (prevManifest === undefined) delete process.env.BARE_OS_DISK_OS_PATH_MANIFEST
else process.env.BARE_OS_DISK_OS_PATH_MANIFEST = prevManifest
if (prevOnly === undefined) delete process.env.BARE_OS_DISK_OS_SEARCH_MANIFEST_ONLY
else process.env.BARE_OS_DISK_OS_SEARCH_MANIFEST_ONLY = prevOnly
}
rmSync(dir, { recursive: true, force: true })
})
test('ls -l long listing uses session user and regular file mode', async (t) => {
const lsPath = path.join(__dirname, '../../kernel/bin/ls')
const lsSrc = await readFile(lsPath, 'utf8')
@@ -3016,6 +3063,16 @@ test('evaluateBareOsPeerAdmission BARE_OS_PEER_REQUIRE_CAPS_JSON', async (t) =>
)
})
test('evaluateBareOsPeerAdmission denylist overrides satisfied require_caps', async (t) => {
const env = {
BARE_OS_PEER_DENYLIST_HEX: 'dead',
BARE_OS_PEER_REQUIRE_CAPS_JSON: '["seed"]'
}
const r = evaluateBareOsPeerAdmission(env, 'dead', { caps: ['seed'] })
t.is(r.verdict, 'deny')
t.is(r.reason, 'peer_denylist')
})
test('bareOsFormatPeerAdmissionAuditEvent never embeds full peer keys', (t) => {
const full = 'a'.repeat(64)
const res = evaluateBareOsPeerAdmission({}, full)
@@ -6600,6 +6657,15 @@ test('coreutils gnu-gap batch: paste tac rev md5sum expr tsort numfmt truncate i
rmSync(dir, { recursive: true, force: true })
})
test('verifyPathCapabilityEnvelope rejects invalid envelopes', async (t) => {
const { verifyPathCapabilityEnvelope } = await import(
'./lib/bare-os-path-capability.js'
)
t.ok(!verifyPathCapabilityEnvelope(null).ok)
t.ok(!verifyPathCapabilityEnvelope({ schema: 2 }).ok)
t.ok(!verifyPathCapabilityEnvelope({ schema: 1, payload: {} }).ok)
})
test('verifyBootManifestEd25519 rejects invalid inputs', async (t) => {
const { verifyBootManifestEd25519 } =
await import('#bare-os-boot-manifest-sig')
@@ -92,8 +92,10 @@ export const COREUTILS_COMMANDS = [
'paste',
'patch',
'pathchk',
'pathcap-verify',
'pr',
'printenv',
'pkg-swarm-index',
'printf',
'procstat',
'ps',
@@ -5,7 +5,7 @@
"synopsis": [
"awk [OPTION]... [OPERAND]..."
],
"description": "Pattern-directed scanning and processing. Engine in lib/awk-engine.js; Issue 7inspired awk subset with documented bounds (includes next/nextfile control flow).",
"description": "Pattern-directed scanning and processing. Engine in lib/awk-engine.js; not full POSIX awk.",
"options": [],
"keywords": [
"awk",
@@ -5,7 +5,7 @@
"synopsis": [
"baretop [OPTION]... [OPERAND]..."
],
"description": "Full-screen session and operator dashboard (htop-inspired): metrics, logical process table from /proc/bare_os/process_table.json, /proc/bare_os mirrors, mem/load meters, eleven tabs, themes and filters. Requires a TTY. See packages/bare-os-coreutils/src/baretop.js for keys and BARE_TOP_* environment variables.",
"description": "Bare OS implementation of baretop. Full behavior is defined in packages/bare-os-coreutils/src/baretop.js.",
"options": [],
"keywords": [
"baretop",
@@ -5,7 +5,7 @@
"synopsis": [
"basenc [OPTION]... [OPERAND]..."
],
"description": "Encode or decode bytes using Base16 (--base16), Base32 (--base32, RFC-style), or Base64 (--base64). Full behavior is defined in packages/bare-os-coreutils/src/basenc.js.",
"description": "Bare OS implementation of basenc. Full behavior is defined in packages/bare-os-coreutils/src/basenc.js.",
"options": [],
"keywords": [
"basenc",
@@ -5,7 +5,7 @@
"synopsis": [
"btop [OPTION]... [OPERAND]..."
],
"description": "Same program as baretop(1); the /bin/btop bundle is identical to baretop. Full-screen session dashboard with logical process table, metrics, and operator /proc panels. See baretop(1) and packages/bare-os-coreutils/src/baretop.js.",
"description": "Bare OS implementation of btop. Full behavior is defined in packages/bare-os-coreutils/src/btop.js.",
"options": [],
"keywords": [
"btop",
@@ -2,13 +2,14 @@
"name": "expand",
"section": 1,
"title": "expand",
"synopsis": ["expand [-t LIST] [FILE]..."],
"description": "Convert tabs to spaces. One -t width uses uniform stops; a comma-separated increasing list uses POSIX-style tab stops (repeating interval from the last two stops).",
"options": [
{
"flag": "-t LIST",
"meaning": "Tab width or comma-separated tab stops (e.g. 8 or 1,4,8)."
}
"synopsis": [
"expand [OPTION]... [OPERAND]..."
],
"keywords": ["expand", "bare-os", "coreutils"]
"description": "Bare OS implementation of expand. Full behavior is defined in packages/bare-os-coreutils/src/expand.js.",
"options": [],
"keywords": [
"expand",
"bare-os",
"coreutils"
]
}
@@ -5,21 +5,8 @@
"synopsis": [
"hostname [OPTION]... [OPERAND]..."
],
"description": "Print the logical hostname from session env (HOSTNAME / COMPUTERNAME) or a stock default. When the host exposes ctx.bareOsSetSessionHostname and BARE_OS_HOSTNAME_SET=1, hostname -S / --set updates the session hostname (also recorded in /proc/bare_os/security_posture.json).",
"options": [
{
"flag": "-s, --short",
"meaning": "Print the host name up to the first dot (Issue 7-style short name)."
},
{
"flag": "-f, --fqdn",
"meaning": "Print the full string (default on Bare OS; included for script compatibility)."
},
{
"flag": "-S, --set",
"meaning": "Set the session logical hostname (requires BARE_OS_HOSTNAME_SET=1 and ctx.bareOsSetSessionHostname)."
}
],
"description": "Bare OS implementation of set or print hostname. Full behavior is defined in packages/bare-os-coreutils/src/hostname.js.",
"options": [],
"keywords": [
"hostname",
"bare-os",
@@ -29,14 +16,6 @@
{
"caption": "show host",
"code": "hostname"
},
{
"caption": "short name",
"code": "hostname -s"
},
{
"caption": "set session hostname (gated)",
"code": "hostname --set my-node"
}
]
}
@@ -2,9 +2,14 @@
"name": "link",
"section": 1,
"title": "link",
"synopsis": ["link FILE1 FILE2"],
"description": "Calls ctx.bareOsSyscall('link') with absolute paths resolved by the VFS. On stock Hyperdrive the booter implements link via file copy unless BARE_OS_VFS_STRICT_HARDLINK requests EOPNOTSUPP. Defined in packages/bare-os-coreutils/src/link.js.",
"synopsis": [
"link [OPTION]... [OPERAND]..."
],
"description": "Bare OS implementation of link. Full behavior is defined in packages/bare-os-coreutils/src/link.js.",
"options": [],
"keywords": ["link", "hardlink", "bare-os", "coreutils"],
"seeAlso": [{ "name": "ln", "section": 1 }, { "name": "symlink", "section": 7 }]
"keywords": [
"link",
"bare-os",
"coreutils"
]
}
@@ -5,18 +5,18 @@
"synopsis": [
"mkfifo [OPTION]... [OPERAND]..."
],
"description": "Creates an in-memory named channel for IPC under /run/bare-os/ipc/<name> when the booter exposes ctx.bareOsIpc. Paths must be under that prefix (e.g. /run/bare-os/ipc/demo). Not a kernel FIFO on Hyperdrive; use shell pipelines for stream composition between utilities.",
"description": "Creates in-memory FIFO endpoints under /run/bare-os/ipc/<name> (not Hyperdrive specials).",
"options": [],
"keywords": [
"mkfifo",
"bare-os",
"coreutils"
],
"bareOsNotes": "Optional BARE_OS_IPC_NAMESPACE prefixes the logical channel key. See handbook ch.9 (FIFOs, simulated IPC).",
"bareOsNotes": "Uses ctx.bareOsIpc.create; optional BARE_OS_IPC_NAMESPACE prefixes keys. BARE_OS_IPC_MAX_CHANNELS caps distinct channels; see metrics_live.ipcTelemetry.",
"examples": [
{
"caption": "create a named IPC channel",
"code": "mkfifo /run/bare-os/ipc/demo"
"caption": "stub",
"code": "# FIFOs not on Hyperdrive — use shell pipelines"
}
]
}
@@ -1,21 +1,15 @@
{
"name": "nice",
"section": 1,
"title": "invoke a utility with an altered scheduling priority (simulated)",
"title": "nice",
"synopsis": [
"nice [-n increment] utility [argument...]"
"nice [OPTION]... [OPERAND]..."
],
"description": "Bare OS implementation: records BARE_OS_SIMULATED_NICE on the session env and runs the utility via ctx.runBinCommand. The host JavaScript runtime does not change OS niceness; scripts use this for POSIX script compatibility.",
"description": "Bare OS implementation of nice. Full behavior is defined in packages/bare-os-coreutils/src/nice.js.",
"options": [],
"keywords": [
"nice",
"bare-os",
"coreutils"
],
"examples": [
{
"caption": "run with default increment",
"code": "nice make"
}
]
}
@@ -0,0 +1,15 @@
{
"name": "pathcap-verify",
"section": 1,
"title": "pathcap-verify",
"synopsis": [
"pathcap-verify [OPTION]... [OPERAND]..."
],
"description": "Bare OS implementation of pathcap-verify. Full behavior is defined in packages/bare-os-coreutils/src/pathcap-verify.js.",
"options": [],
"keywords": [
"pathcap-verify",
"bare-os",
"coreutils"
]
}
@@ -0,0 +1,15 @@
{
"name": "pkg-swarm-index",
"section": 1,
"title": "pkg-swarm-index",
"synopsis": [
"pkg-swarm-index [OPTION]... [OPERAND]..."
],
"description": "Bare OS implementation of pkg-swarm-index. Full behavior is defined in packages/bare-os-coreutils/src/pkg-swarm-index.js.",
"options": [],
"keywords": [
"pkg-swarm-index",
"bare-os",
"coreutils"
]
}
@@ -5,18 +5,14 @@
"synopsis": [
"systemctl list|list-units",
"systemctl status [UNIT] [--lines N]",
"systemctl show|cat UNIT",
"systemctl is-failed UNIT",
"systemctl reset-failed [UNIT]",
"systemctl logs UNIT [--lines N]",
"systemctl start|stop|restart UNIT",
"systemctl enable|disable UNIT",
"systemctl is-enabled UNIT",
"systemctl is-active UNIT",
"systemctl help",
"journalctl -u UNIT [--lines N|-n N]"
"journalctl -u UNIT [--lines N]"
],
"description": "Lists and manages session-scoped bare-initd units (kernel-logger, bare-cron, …). Implemented by packages/bare-os-booter/lib/systemctl-cli.js (kernel-runner); /bin stubs exist for PATH and man(1). Verbs match --help: show/cat dump unit fields; is-failed; reset-failed (logical no-op on stock initd); enable/disable toggle ~/.config/bare-os/initd/disabled.txt; is-enabled / is-active; logs and status accept --lines. journalctl supports -u, --lines, -n. The legacy name bare-initctl is still accepted as an alias.",
"description": "Lists and manages session-scoped bare-initd units (kernel-logger, bare-cron, …). Implemented by the booter (kernel-runner); /bin stubs exist for PATH and man(1). enable/disable toggle the personal-drive preset file ~/.config/bare-os/initd/disabled.txt for future boots in the same image. is-enabled reports enabled or disabled; is-active reports active vs inactive from runtime phase (exit 0 vs 3). Logs live under /var/log/bare-os/ when the unit defines a logPath. The legacy name bare-initctl is still accepted by the booter as an alias.",
"options": [
{
"flag": "--lines N",
@@ -34,7 +30,7 @@
"cron",
"systemd"
],
"bareOsNotes": "journalctl only supports -u UNIT and optional --lines / -n. Unknown systemd verbs are not implemented.",
"bareOsNotes": "journalctl supports -u UNIT and --lines / -n. Other systemd verbs are unavailable on Bare OS (exit 2).",
"seeAlso": [
{
"name": "crontab",
+2 -2
View File
@@ -9,7 +9,7 @@ async function run(ctx, argv) {
return
}
ctx.console.error(
'curl: ctx.bareOsRunCurlCli missing — upgrade booter / ctx API'
'curl: unavailable in this session (stock booter exposes ctx.bareOsRunCurlCli; upgrade bare-os-booter or run with a current ctx API)'
)
ctx.exitCode = 1
ctx.exitCode = 127
}
@@ -0,0 +1,74 @@
/**
* Verify a path-capability envelope (JSON) using ctx.bareOsVerifyPathCapabilityEnvelope.
* Usage: pathcap-verify FILE.json (or stdin JSON when FILE is -)
*/
async function run(ctx, argv) {
let path = ''
for (let i = 1; i < argv.length; i++) {
const a = argv[i]
if (a === '--help' || a === '-h') {
ctx.console.log(
'usage: pathcap-verify FILE.json\n pathcap-verify - (read envelope JSON from stdin)'
)
return
}
if (!a.startsWith('-')) {
path = a
break
}
ctx.console.error('pathcap-verify: unknown option ' + a)
ctx.exitCode = 1
return
}
if (!path) {
ctx.console.error(
'usage: pathcap-verify FILE.json\n pathcap-verify -'
)
ctx.exitCode = 1
return
}
if (typeof ctx.bareOsVerifyPathCapabilityEnvelope !== 'function') {
ctx.console.error(
'pathcap-verify: ctx.bareOsVerifyPathCapabilityEnvelope missing'
)
ctx.exitCode = 1
return
}
let text = ''
try {
if (path === '-') {
text = String(ctx.shellStdin || '')
} else {
const buf = await ctx.vfs.readFile(path)
if (!buf || !buf.byteLength) {
ctx.console.error('pathcap-verify: empty or missing: ' + path)
ctx.exitCode = 1
return
}
text = ctx.b4a.toString(buf)
}
} catch (e) {
ctx.console.error(
'pathcap-verify: read failed: ' + ((e && e.message) || String(e))
)
ctx.exitCode = 1
return
}
let env
try {
env = JSON.parse(text)
} catch {
ctx.console.error('pathcap-verify: invalid JSON')
ctx.exitCode = 1
return
}
const r = ctx.bareOsVerifyPathCapabilityEnvelope(env)
if (r.ok) {
ctx.console.log(
'ok prefix=' + r.payload.prefix + ' ops=' + r.payload.ops.join(',')
)
return
}
ctx.console.error('pathcap-verify: FAIL ' + r.reason)
ctx.exitCode = 1
}
@@ -0,0 +1,31 @@
/**
* pkg-swarm-index document Hyperbee/Hyperdrive package index pattern for P2P-first registries.
* Prints static guidance; real indexes are built with hyperbee + hyperswarm outside this utility.
*/
async function run(ctx, argv) {
const topic = String(
(ctx.env && ctx.env.BARE_OS_PKG_SWARM_TOPIC_HEX) || ''
).trim()
for (let i = 1; i < argv.length; i++) {
if (argv[i] === '--help' || argv[i] === '-h') {
ctx.console.log(`pkg-swarm-index — P2P package index notes for Bare OS
Set BARE_OS_PKG_SWARM_TOPIC_HEX (64 hex chars) to pin a swarm topic class for index peers.
Architecture (operators):
Writers publish signed records: name@version -> { driveKey, manifestHash } in a Hyperbee.
Readers join the hyperswarm topic, replicate the feed, verify Ed25519 signatures against boot policy.
See handbook § P2P-native package index (experimental) and kernel/etc/bare-os/hyperbee-index-hint.example.json.
${topic ? 'Current BARE_OS_PKG_SWARM_TOPIC_HEX prefix: ' + topic.slice(0, 16) + '…' : 'BARE_OS_PKG_SWARM_TOPIC_HEX is unset (documentation mode).'}
`)
return
}
}
ctx.console.log(
'pkg-swarm-index: P2P registry pattern — use `pkg-swarm-index --help`. ' +
(topic
? 'Topic pin active (' + topic.slice(0, 12) + '…).'
: 'No topic pin (set BARE_OS_PKG_SWARM_TOPIC_HEX).')
)
}
+2 -2
View File
@@ -8,7 +8,7 @@ async function run(ctx, argv) {
return
}
ctx.console.error(
'wget: ctx.bareOsRunWgetCli missing — upgrade booter / ctx API'
'wget: unavailable in this session (stock booter exposes ctx.bareOsRunWgetCli; upgrade bare-os-booter or run with a current ctx API)'
)
ctx.exitCode = 1
ctx.exitCode = 127
}
@@ -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.15'
export const BARE_OS_POSIX_PROFILE_VERSION = '1.0.16'
/** Short identifier for telemetry and `/proc` mirrors. */
export const BARE_OS_POSIX_PROFILE_ID = 'bare-os-posix-like'
+2 -2
View File
@@ -98,7 +98,7 @@ async function run(ctx, argv) {
return
}
ctx.console.error(
'curl: ctx.bareOsRunCurlCli missing — upgrade booter / ctx API'
'curl: unavailable in this session (stock booter exposes ctx.bareOsRunCurlCli; upgrade bare-os-booter or run with a current ctx API)'
)
ctx.exitCode = 1
ctx.exitCode = 127
}
+1 -1
View File
@@ -87,7 +87,7 @@ function bareOsEmitRaw(ctx, chunk) {
return false
}
var BARE_OS_HELP_BIN_SPACED = "arch awk baretop base32 base64 basename basenc btop bundlebee cat chgrp chmod chown cksum clear cmp comm cp crontab curl cut date dd df diff dir dircolors dirname du echo edit env exit expand expr factor false find fmt fold getconf getfacl git git-pear grep groups hdms head help hostid hostname hrpc iconv id install join journalctl jq kernel-boot-diff kernel-doctor kernel-explain kernel-fsck kernel-home-snapshot kernel-manifest-validate kernel-preflight kernel-triage kill link ln logger login logname logout ls man md5sum mkdir mkfifo mktemp mount mv nano nice nl nohup nproc numfmt od oidc-publish openssl openssl paste patch pathchk pear-runtime-matrix pr printenv printf procstat ps pwd readlink realpath rev rm rmdir savevault sed seq setfacl sh sha1sum sha256sum sha512sum shuf sidecar sleep sort split ssh-keygen ssh-keygen stat sum sync systemctl tac tail tar tar tee test theme time timeout touch tr true truncate tsort tty ulimit umount uname unexpand uniq unlink uptime users vdir wc wget which who whoami xargs xattr yes"
var BARE_OS_HELP_BIN_SPACED = "arch awk baretop base32 base64 basename basenc btop bundlebee cat chgrp chmod chown cksum clear cmp comm cp crontab curl cut date dd df diff dir dircolors dirname du echo edit env exit expand expr factor false find fmt fold getconf getfacl git git-pear grep groups hdms head help hostid hostname hrpc iconv id install join journalctl jq kernel-boot-diff kernel-doctor kernel-explain kernel-fsck kernel-home-snapshot kernel-manifest-validate kernel-preflight kernel-triage kill link ln logger login logname logout ls man md5sum mkdir mkfifo mktemp mount mv nano nice nl nohup nproc numfmt od oidc-publish openssl openssl paste patch pathcap-verify pathchk pear-runtime-matrix pkg-swarm-index pr printenv printf procstat ps pwd readlink realpath rev rm rmdir savevault sed seq setfacl sh sha1sum sha256sum sha512sum shuf sidecar sleep sort split ssh-keygen ssh-keygen stat sum sync systemctl tac tail tar tar tee test theme time timeout touch tr true truncate tsort tty ulimit umount uname unexpand uniq unlink uptime users vdir wc wget which who whoami xargs xattr yes"
async function run(ctx, argv) {
ctx.console.log(
'Bare OS — default user: guest | shell builtins: alias, barerc, cd, command, export, exit, login, logout, readonly, type, umask, unalias, unset, : | /bin: ' +
@@ -0,0 +1,163 @@
/* BARE_OS_BIN_API 1.0.0 — bump when staged /bin script semantics change (see developer guide). */
/** Shared helpers for drive-resident /bin scripts (prepended before each command). */
function bareStdin(ctx) {
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
}
/** @param {number} mode @param {'file' | 'directory' | 'symlink'} type */
function bareFormatModeString(mode, type) {
const typeChar = type === 'directory' ? 'd' : type === 'symlink' ? 'l' : '-'
const perm = mode & 0o777
const r = (bit) => (perm & bit ? 'r' : '-')
const w = (bit) => (perm & bit ? 'w' : '-')
const x = (bit) => (perm & bit ? 'x' : '-')
return (
typeChar +
r(0o400) +
w(0o200) +
x(0o100) +
r(0o040) +
w(0o020) +
x(0o010) +
r(0o004) +
w(0o002) +
x(0o001)
)
}
/** @param {number} mtimeMs @param {number} [nowMs] */
function bareFormatLsMtime(mtimeMs, nowMs) {
const now = nowMs != null ? nowMs : Date.now()
const d = new Date(mtimeMs)
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
]
const mon = months[d.getMonth()]
const day = String(d.getDate()).padStart(2, ' ')
const sixMo = 180 * 24 * 3600 * 1000
if (Math.abs(now - mtimeMs) > sixMo) {
const yr = String(d.getFullYear()).padStart(4, ' ')
return mon + ' ' + day + ' ' + yr
}
const hh = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
return mon + ' ' + day + ' ' + hh + ':' + mm
}
/** @param {number} size */
function barePosixBlocks(size) {
return Math.ceil(Number(size) / 512) || 0
}
/**
* Raw stdout for NUL/binary when **`process.stdout.write`** is missing.
* If **`ctx.bareOsBinWrite(Uint8Array|string)`** is set (tests / host), use it.
* @param {Record<string, unknown>} ctx
* @param {string | Uint8Array} chunk
* @returns {boolean}
*/
function bareOsEmitRaw(ctx, chunk) {
if (typeof ctx.bareOsBinWrite === 'function') {
const b4 = ctx.b4a
const u8 =
typeof chunk === 'string'
? b4 && typeof b4.from === 'function'
? b4.from(chunk)
: new TextEncoder().encode(chunk)
: chunk
ctx.bareOsBinWrite(u8 instanceof Uint8Array ? u8 : new Uint8Array(u8))
return true
}
const w = globalThis.process?.stdout?.write
if (typeof w === 'function') {
w.call(globalThis.process.stdout, chunk)
return true
}
return false
}
/**
* Verify a path-capability envelope (JSON) using ctx.bareOsVerifyPathCapabilityEnvelope.
* Usage: pathcap-verify FILE.json (or stdin JSON when FILE is -)
*/
async function run(ctx, argv) {
let path = ''
for (let i = 1; i < argv.length; i++) {
const a = argv[i]
if (a === '--help' || a === '-h') {
ctx.console.log(
'usage: pathcap-verify FILE.json\n pathcap-verify - (read envelope JSON from stdin)'
)
return
}
if (!a.startsWith('-')) {
path = a
break
}
ctx.console.error('pathcap-verify: unknown option ' + a)
ctx.exitCode = 1
return
}
if (!path) {
ctx.console.error(
'usage: pathcap-verify FILE.json\n pathcap-verify -'
)
ctx.exitCode = 1
return
}
if (typeof ctx.bareOsVerifyPathCapabilityEnvelope !== 'function') {
ctx.console.error(
'pathcap-verify: ctx.bareOsVerifyPathCapabilityEnvelope missing'
)
ctx.exitCode = 1
return
}
let text = ''
try {
if (path === '-') {
text = String(ctx.shellStdin || '')
} else {
const buf = await ctx.vfs.readFile(path)
if (!buf || !buf.byteLength) {
ctx.console.error('pathcap-verify: empty or missing: ' + path)
ctx.exitCode = 1
return
}
text = ctx.b4a.toString(buf)
}
} catch (e) {
ctx.console.error(
'pathcap-verify: read failed: ' + ((e && e.message) || String(e))
)
ctx.exitCode = 1
return
}
let env
try {
env = JSON.parse(text)
} catch {
ctx.console.error('pathcap-verify: invalid JSON')
ctx.exitCode = 1
return
}
const r = ctx.bareOsVerifyPathCapabilityEnvelope(env)
if (r.ok) {
ctx.console.log(
'ok prefix=' + r.payload.prefix + ' ops=' + r.payload.ops.join(',')
)
return
}
ctx.console.error('pathcap-verify: FAIL ' + r.reason)
ctx.exitCode = 1
}
@@ -0,0 +1,120 @@
/* BARE_OS_BIN_API 1.0.0 — bump when staged /bin script semantics change (see developer guide). */
/** Shared helpers for drive-resident /bin scripts (prepended before each command). */
function bareStdin(ctx) {
return typeof ctx.shellStdin === 'string' ? ctx.shellStdin : ''
}
/** @param {number} mode @param {'file' | 'directory' | 'symlink'} type */
function bareFormatModeString(mode, type) {
const typeChar = type === 'directory' ? 'd' : type === 'symlink' ? 'l' : '-'
const perm = mode & 0o777
const r = (bit) => (perm & bit ? 'r' : '-')
const w = (bit) => (perm & bit ? 'w' : '-')
const x = (bit) => (perm & bit ? 'x' : '-')
return (
typeChar +
r(0o400) +
w(0o200) +
x(0o100) +
r(0o040) +
w(0o020) +
x(0o010) +
r(0o004) +
w(0o002) +
x(0o001)
)
}
/** @param {number} mtimeMs @param {number} [nowMs] */
function bareFormatLsMtime(mtimeMs, nowMs) {
const now = nowMs != null ? nowMs : Date.now()
const d = new Date(mtimeMs)
const months = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
]
const mon = months[d.getMonth()]
const day = String(d.getDate()).padStart(2, ' ')
const sixMo = 180 * 24 * 3600 * 1000
if (Math.abs(now - mtimeMs) > sixMo) {
const yr = String(d.getFullYear()).padStart(4, ' ')
return mon + ' ' + day + ' ' + yr
}
const hh = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
return mon + ' ' + day + ' ' + hh + ':' + mm
}
/** @param {number} size */
function barePosixBlocks(size) {
return Math.ceil(Number(size) / 512) || 0
}
/**
* Raw stdout for NUL/binary when **`process.stdout.write`** is missing.
* If **`ctx.bareOsBinWrite(Uint8Array|string)`** is set (tests / host), use it.
* @param {Record<string, unknown>} ctx
* @param {string | Uint8Array} chunk
* @returns {boolean}
*/
function bareOsEmitRaw(ctx, chunk) {
if (typeof ctx.bareOsBinWrite === 'function') {
const b4 = ctx.b4a
const u8 =
typeof chunk === 'string'
? b4 && typeof b4.from === 'function'
? b4.from(chunk)
: new TextEncoder().encode(chunk)
: chunk
ctx.bareOsBinWrite(u8 instanceof Uint8Array ? u8 : new Uint8Array(u8))
return true
}
const w = globalThis.process?.stdout?.write
if (typeof w === 'function') {
w.call(globalThis.process.stdout, chunk)
return true
}
return false
}
/**
* pkg-swarm-index — document Hyperbee/Hyperdrive package index pattern for P2P-first registries.
* Prints static guidance; real indexes are built with hyperbee + hyperswarm outside this utility.
*/
async function run(ctx, argv) {
const topic = String(
(ctx.env && ctx.env.BARE_OS_PKG_SWARM_TOPIC_HEX) || ''
).trim()
for (let i = 1; i < argv.length; i++) {
if (argv[i] === '--help' || argv[i] === '-h') {
ctx.console.log(`pkg-swarm-index — P2P package index notes for Bare OS
Set BARE_OS_PKG_SWARM_TOPIC_HEX (64 hex chars) to pin a swarm topic class for index peers.
Architecture (operators):
• Writers publish signed records: name@version -> { driveKey, manifestHash } in a Hyperbee.
• Readers join the hyperswarm topic, replicate the feed, verify Ed25519 signatures against boot policy.
• See handbook § P2P-native package index (experimental) and kernel/etc/bare-os/hyperbee-index-hint.example.json.
${topic ? 'Current BARE_OS_PKG_SWARM_TOPIC_HEX prefix: ' + topic.slice(0, 16) + '…' : 'BARE_OS_PKG_SWARM_TOPIC_HEX is unset (documentation mode).'}
`)
return
}
}
ctx.console.log(
'pkg-swarm-index: P2P registry pattern — use `pkg-swarm-index --help`. ' +
(topic
? 'Topic pin active (' + topic.slice(0, 12) + '…).'
: 'No topic pin (set BARE_OS_PKG_SWARM_TOPIC_HEX).')
)
}
+2 -2
View File
@@ -97,7 +97,7 @@ async function run(ctx, argv) {
return
}
ctx.console.error(
'wget: ctx.bareOsRunWgetCli missing — upgrade booter / ctx API'
'wget: unavailable in this session (stock booter exposes ctx.bareOsRunWgetCli; upgrade bare-os-booter or run with a current ctx API)'
)
ctx.exitCode = 1
ctx.exitCode = 127
}
@@ -0,0 +1,8 @@
{
"schema": 1,
"note": "Optional sidecar next to a bundlebee-published extension: merge hints for kernel.ext.d strict verification (signer pins, drive key sketch). Non-secret metadata only.",
"extensionId": "example.p2p.extension",
"bundlebeeDriveKeyHexPrefix": "replace_with_discovery_or_id_prefix",
"signerPubkeysHex": ["replace_with_ed25519_public_key_64_hex"],
"publishedAtMs": 0
}
@@ -0,0 +1,11 @@
{
"schema": 1,
"note": "Sign bareOsPathCapabilityPayloadCanonicalUtf8(payload) with Ed25519; set pubkeyHex (64 hex) and signatureHex (128 hex). Inner payload fields: prefix (absolute path prefix), ops (include read or all), optional expMs (epoch ms). Store on a file as xattr user.bareos.cap_v1 = base64(JSON.stringify(this object)) via /bin/xattr -w. Enable enforcement: BARE_OS_PATH_CAPABILITY_ENFORCE_READ=1 and BARE_OS_PATH_CAPABILITY_PREFIX=/home/",
"pubkeyHex": "REPLACE_WITH_SIGNER_PUBLIC_KEY_64_HEX_CHARS",
"signatureHex": "REPLACE_WITH_SIGNATURE_OVER_CANONICAL_PAYLOAD_128_HEX_CHARS",
"payload": {
"expMs": 1893456000000,
"ops": ["read"],
"prefix": "/home/guest/"
}
}
@@ -0,0 +1,51 @@
{
"schema": 1,
"note": "Optional path list for disk.os searchLocal: substring matches are returned before (or instead of) full-drive listing. Maintainer: extend when adding Tier-1 /bin or boot-critical paths. Disable manifest use with BARE_OS_DISK_OS_PATH_MANIFEST= (empty) on the host.",
"paths": [
"/boot/init.js",
"/etc/os-release",
"/etc/motd",
"/etc/bare-os/rc",
"/etc/bare-os/path-manifest.json",
"/etc/bare-os/boot.policy.example.json",
"/lib/bare/README.md",
"/bin/sh",
"/bin/ls",
"/bin/cat",
"/bin/grep",
"/bin/sed",
"/bin/awk",
"/bin/curl",
"/bin/wget",
"/bin/man",
"/bin/login",
"/bin/logout",
"/bin/savevault",
"/bin/help",
"/bin/env",
"/bin/test",
"/bin/kill",
"/bin/ps",
"/bin/mkdir",
"/bin/cp",
"/bin/mv",
"/bin/rm",
"/bin/chmod",
"/bin/chown",
"/bin/tar",
"/bin/gzip",
"/bin/gunzip",
"/bin/find",
"/bin/xargs",
"/bin/sort",
"/bin/uniq",
"/bin/wc",
"/bin/head",
"/bin/tail",
"/bin/sleep",
"/bin/echo",
"/bin/printf",
"/bin/baretop",
"/bin/btop"
]
}
@@ -0,0 +1,7 @@
{
"schema": 1,
"note": "Golden-path alignment hint for operators: expected pear-runtime semver from local Holepunch clones (not enforced by stock booter). Compare with BARE_OS_PEAR_RUNTIME_VERSION / pear doctor.",
"expectedPearRuntimeSemver": "1.1.1",
"observedAt": "2026-04-05",
"clonePathHint": "~/dev/pearcli/holepunch-repos/holepunchto_repos/pear-runtime"
}
@@ -1,7 +1,7 @@
{
"schema": 2,
"profileId": "bare-os-posix-like",
"generatedAt": "2026-04-05T19:03:41.000Z",
"generatedAt": "2026-04-05T19:26:42.531Z",
"note": "Sparse POSIX Issue 7 coverage hints for /bin utilities. Omitted command names are not yet profiled here.",
"commandIndex": [
{
@@ -356,10 +356,18 @@
"name": "patch",
"tier": "tier1_bin"
},
{
"name": "pathcap-verify",
"tier": "tier1_bin"
},
{
"name": "pathchk",
"tier": "tier1_bin"
},
{
"name": "pkg-swarm-index",
"tier": "tier1_bin"
},
{
"name": "pr",
"tier": "tier1_bin"
@@ -0,0 +1,11 @@
{
"schema": 1,
"note": "Place approved JSON as /etc/bare-os/rc.proposals/enabled/<name>.json when BARE_OS_RC_PROPOSAL_MULTISIG_STRICT=1. pear.multisig.json must exist; each signaturesFrom entry must match a signer key in that file (case-insensitive); distinct count must be ≥ pear.quorum. Ed25519 / hyper-multisig verification of the proposal payload is a host or seeder step — the guest gate is policy alignment only.",
"proposalId": "example-2026-04-05",
"targetRcSnippet": "/etc/bare-os/rc.d/99-proposed.sh",
"payloadSha256Hex": "replace_with_sha256_of_script_bytes",
"signaturesFrom": [
"full_signer_public_key_hex_from_pear_multisig_json",
"second_signer_public_key_hex_from_pear_multisig_json"
]
}
@@ -1,6 +1,6 @@
{
"schemaVersion": 10,
"ctxApiVersion": "1.51.1",
"ctxApiVersion": "1.52.0",
"posixProfile": {
"id": "bare-os-posix-like",
"version": "1.0.15"
+269 -1
View File
@@ -2195,6 +2195,12 @@ async function runKernelExtDropins(ctx, opts = {}) {
* BARE_OS_RC_D_SKIP: comma-separated rc.d basenames to skip; a pattern ending with * skips
* names with that prefix (e.g. 10-* skips 10-foo).
*
* BARE_OS_RC_PROPOSAL_MULTISIG_STRICT=1: before **rc.d**, require **`/etc/bare-os/pear.multisig.json`**
* ( **`bareOsPearMultisigShapeOk`** ) and validate every **`/etc/bare-os/rc.proposals/enabled/*.json`**
* (**`schema`**: **1**, **`proposalId`**, **`targetRcSnippet`** under **`/etc/bare-os/rc.d/`**, **`signaturesFrom`**: distinct
* public keys each listed in **`pear.multisig.json`** **`signers`**, count **`quorum`**). Emits **`ctx.bareOsAuditLogAppend`**
* rows **`rc.proposal.multisig_ok`** / **`rc.proposal.multisig_fail`** when the hook exists.
*
* Use ctx.registerKernelShutdownHook(fn) for teardown before initd disposers.
*/
@@ -3223,6 +3229,246 @@ async function runBareOsKernelDir(ctx) {
return true
}
/**
* @param {Record<string, unknown>} ctx
* @param {Record<string, unknown>} entry
*/
function bareOsAppendRcProposalAudit(ctx, entry) {
try {
const fn = ctx.bareOsAuditLogAppend
if (typeof fn === 'function') fn(entry)
} catch {
/* ignore */
}
}
/**
* When **`BARE_OS_RC_PROPOSAL_MULTISIG_STRICT`**, every enabled proposal must cite enough
* **`pear.multisig.json`** signers (distinct keys). Cryptographic signature verification is host/seeder responsibility.
* @param {Record<string, unknown>} ctx
* @returns {Promise<boolean>}
*/
async function verifyRcProposalsMultisigStrict(ctx) {
const strict = ctx.env?.BARE_OS_RC_PROPOSAL_MULTISIG_STRICT
if (strict !== '1' && strict !== 'true') return true
const { drive, b4a } = ctx
let pearBuf
try {
pearBuf = await drive.get('/etc/bare-os/pear.multisig.json')
} catch {
pearBuf = null
}
if (!pearBuf) {
bootStructuredLog(
ctx,
'error',
'rc.proposal.multisig.pear_missing',
'[rc.proposals] BARE_OS_RC_PROPOSAL_MULTISIG_STRICT requires /etc/bare-os/pear.multisig.json'
)
bareOsAppendRcProposalAudit(ctx, {
type: 'rc.proposal.multisig_fail',
reason: 'pear_multisig_missing',
ts: Date.now()
})
return false
}
/** @type {unknown} */
let pearParsed
try {
pearParsed = JSON.parse(b4a.toString(pearBuf))
} catch (e) {
bootStructuredLog(
ctx,
'error',
'rc.proposal.multisig.pear_parse',
'[rc.proposals] pear.multisig.json: ' + ((e && e.message) || String(e))
)
bareOsAppendRcProposalAudit(ctx, {
type: 'rc.proposal.multisig_fail',
reason: 'pear_multisig_parse',
ts: Date.now()
})
return false
}
if (!bareOsPearMultisigShapeOk(pearParsed)) {
bootStructuredLog(
ctx,
'error',
'rc.proposal.multisig.pear_shape',
'[rc.proposals] pear.multisig.json must be { signers: string[], quorum: number } with 1 ≤ quorum ≤ signers.length'
)
bareOsAppendRcProposalAudit(ctx, {
type: 'rc.proposal.multisig_fail',
reason: 'pear_multisig_shape',
ts: Date.now()
})
return false
}
const pear = /** @type {{ signers: string[]; quorum: number }} */ (pearParsed)
const signerSet = new Set(pear.signers.map((s) => String(s).toLowerCase()))
const quorum = pear.quorum
/** @type {string[]} */
const proposalNames = []
try {
for await (const n of drive.readdir('/etc/bare-os/rc.proposals/enabled')) {
if (String(n).endsWith('.json')) proposalNames.push(String(n))
}
} catch {
return true
}
proposalNames.sort()
if (!proposalNames.length) return true
for (const fn of proposalNames) {
const p = `/etc/bare-os/rc.proposals/enabled/${fn}`
/** @type {unknown} */
let raw
try {
const buf = await drive.get(p)
if (!buf) {
bootStructuredLog(ctx, 'error', 'rc.proposal.missing', `[rc.proposals] empty: ${fn}`)
bareOsAppendRcProposalAudit(ctx, {
type: 'rc.proposal.multisig_fail',
reason: 'proposal_missing',
proposalFile: fn,
ts: Date.now()
})
return false
}
raw = JSON.parse(b4a.toString(buf))
} catch (e) {
bootStructuredLog(
ctx,
'error',
'rc.proposal.parse',
`[rc.proposals] ${fn}: ` + ((e && e.message) || String(e))
)
bareOsAppendRcProposalAudit(ctx, {
type: 'rc.proposal.multisig_fail',
reason: 'proposal_parse',
proposalFile: fn,
ts: Date.now()
})
return false
}
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
bootStructuredLog(ctx, 'error', 'rc.proposal.shape', `[rc.proposals] ${fn}: expected object`)
return false
}
const o = /** @type {Record<string, unknown>} */ (raw)
if (o.schema !== 1) {
bootStructuredLog(
ctx,
'error',
'rc.proposal.schema',
`[rc.proposals] ${fn}: schema must be 1`
)
bareOsAppendRcProposalAudit(ctx, {
type: 'rc.proposal.multisig_fail',
reason: 'proposal_schema',
proposalFile: fn,
ts: Date.now()
})
return false
}
const proposalId = String(o.proposalId || '').trim()
if (!proposalId) {
bootStructuredLog(ctx, 'error', 'rc.proposal.id', `[rc.proposals] ${fn}: proposalId required`)
bareOsAppendRcProposalAudit(ctx, {
type: 'rc.proposal.multisig_fail',
reason: 'proposal_id',
proposalFile: fn,
ts: Date.now()
})
return false
}
const targetRcSnippet = String(o.targetRcSnippet || '').trim()
if (!targetRcSnippet.startsWith('/etc/bare-os/rc.d/')) {
bootStructuredLog(
ctx,
'error',
'rc.proposal.target',
`[rc.proposals] ${fn}: targetRcSnippet must start with /etc/bare-os/rc.d/`
)
bareOsAppendRcProposalAudit(ctx, {
type: 'rc.proposal.multisig_fail',
reason: 'proposal_target',
proposalFile: fn,
proposalId,
ts: Date.now()
})
return false
}
const sigFrom = o.signaturesFrom
if (!Array.isArray(sigFrom)) {
bootStructuredLog(
ctx,
'error',
'rc.proposal.signaturesFrom',
`[rc.proposals] ${fn}: signaturesFrom must be an array of signer public keys`
)
bareOsAppendRcProposalAudit(ctx, {
type: 'rc.proposal.multisig_fail',
reason: 'signatures_from_shape',
proposalFile: fn,
proposalId,
ts: Date.now()
})
return false
}
/** @type {Set<string>} */
const approved = new Set()
for (const s of sigFrom) {
const k = String(s || '').toLowerCase().trim()
if (!k) continue
if (!signerSet.has(k)) {
bootStructuredLog(
ctx,
'error',
'rc.proposal.signer_unknown',
`[rc.proposals] ${fn}: signer not in pear.multisig.json`
)
bareOsAppendRcProposalAudit(ctx, {
type: 'rc.proposal.multisig_fail',
reason: 'signer_not_in_policy',
proposalFile: fn,
proposalId,
ts: Date.now()
})
return false
}
approved.add(k)
}
if (approved.size < quorum) {
bootStructuredLog(
ctx,
'error',
'rc.proposal.quorum',
`[rc.proposals] ${fn}: need ≥ pear.multisig quorum (${quorum}) distinct approved signers, got ${approved.size}`
)
bareOsAppendRcProposalAudit(ctx, {
type: 'rc.proposal.multisig_fail',
reason: 'quorum_not_met',
proposalFile: fn,
proposalId,
ts: Date.now()
})
return false
}
bareOsAppendRcProposalAudit(ctx, {
type: 'rc.proposal.multisig_ok',
proposalFile: fn,
proposalId,
targetRcSnippet,
signerCount: approved.size,
quorum,
ts: Date.now()
})
}
return true
}
/**
* Optional snippets under /etc/bare-os/rc.d/ executed in lexicographic order.
* @param {Record<string, unknown>} ctx
@@ -3230,6 +3476,7 @@ async function runBareOsKernelDir(ctx) {
*/
async function runBareOsRcDir(ctx) {
const { drive, b4a } = ctx
if (!(await verifyRcProposalsMultisigStrict(ctx))) return false
const skip = parseRcDSkipPatterns(ctx)
try {
/** @type {string[]} */
@@ -3978,6 +4225,26 @@ async function start(ctx) {
}
}
if (coldExceeded || stdlibExceeded) {
/** @type {{ kind: string, wallMs: number, limitMs: number }[]} */
const violations = []
if (coldExceeded && Number.isFinite(budget) && budget > 0) {
violations.push({
kind: 'cold',
wallMs: Date.now() - bootT0,
limitMs: budget
})
}
if (stdlibExceeded && Number.isFinite(sb) && sb > 0) {
const sw = Number.parseInt(
String(ctx.env?.BARE_OS_BOOT_BARE_STDLIB_RESOLUTION_MS || ''),
10
)
violations.push({
kind: 'bare_stdlib',
wallMs: Number.isFinite(sw) ? sw : 0,
limitMs: sb
})
}
await maybeAppendBootTransactionJournal(ctx, {
phase: 'boot.budget',
stage: 'boot.budget',
@@ -3985,9 +4252,10 @@ async function start(ctx) {
ms: 0,
ok: false,
bootBudgetViolation: true,
bootBudgetSchemaVersion: 1,
bootBudgetSchemaVersion: 2,
coldBudgetExceeded: coldExceeded,
bareStdlibBudgetExceeded: stdlibExceeded,
bootBudgetViolations: violations,
transactionState: BARE_OS_BOOT_TXN_STATE.STAGE_COMMITTED
})
}
@@ -2,9 +2,9 @@
"version": 1,
"bundles": [
{
"path": "/lib/bare/bundles/safetyCatch.js",
"path": "/lib/bare/bundles/hypercoreIdEncoding.js",
"keys": [
"safetyCatch"
"hypercoreIdEncoding"
]
},
{
@@ -14,9 +14,9 @@
]
},
{
"path": "/lib/bare/bundles/hypercoreIdEncoding.js",
"path": "/lib/bare/bundles/safetyCatch.js",
"keys": [
"hypercoreIdEncoding"
"safetyCatch"
]
},
{
@@ -37,6 +37,12 @@
"protomux"
]
},
{
"path": "/lib/bare/bundles/barePath.js",
"keys": [
"barePath"
]
},
{
"path": "/lib/bare/bundles/bareEvents.js",
"keys": [
@@ -49,18 +55,6 @@
"bareEncoding"
]
},
{
"path": "/lib/bare/bundles/barePath.js",
"keys": [
"barePath"
]
},
{
"path": "/lib/bare/bundles/bareAbort.js",
"keys": [
"bareAbort"
]
},
{
"path": "/lib/bare/bundles/bareAbortController.js",
"keys": [
@@ -68,9 +62,9 @@
]
},
{
"path": "/lib/bare/bundles/bareCrypto.js",
"path": "/lib/bare/bundles/bareAbort.js",
"keys": [
"bareCrypto"
"bareAbort"
]
},
{
@@ -85,6 +79,12 @@
"bareAddonResolve"
]
},
{
"path": "/lib/bare/bundles/bareCrypto.js",
"keys": [
"bareCrypto"
]
},
{
"path": "/lib/bare/bundles/bareAnsiEscapes.js",
"keys": [
@@ -109,24 +109,30 @@
"bareApk"
]
},
{
"path": "/lib/bare/bundles/bareAsyncHooks.js",
"keys": [
"bareAsyncHooks"
]
},
{
"path": "/lib/bare/bundles/bareAtomics.js",
"keys": [
"bareAtomics"
]
},
{
"path": "/lib/bare/bundles/bareAsyncHooks.js",
"keys": [
"bareAsyncHooks"
]
},
{
"path": "/lib/bare/bundles/bareAssert.js",
"keys": [
"bareAssert"
]
},
{
"path": "/lib/bare/bundles/bareBluetoothApple.js",
"keys": [
"bareBluetoothApple"
]
},
{
"path": "/lib/bare/bundles/bareBmp.js",
"keys": [
@@ -139,12 +145,6 @@
"bareBuffer"
]
},
{
"path": "/lib/bare/bundles/bareBundleCompile.js",
"keys": [
"bareBundleCompile"
]
},
{
"path": "/lib/bare/bundles/bareBundle.js",
"keys": [
@@ -152,15 +152,9 @@
]
},
{
"path": "/lib/bare/bundles/bareBluetoothApple.js",
"path": "/lib/bare/bundles/bareBundleCompile.js",
"keys": [
"bareBluetoothApple"
]
},
{
"path": "/lib/bare/bundles/bareBundleEvaluate.js",
"keys": [
"bareBundleEvaluate"
"bareBundleCompile"
]
},
{
@@ -170,15 +164,9 @@
]
},
{
"path": "/lib/bare/bundles/bareConsole.js",
"path": "/lib/bare/bundles/bareBundleEvaluate.js",
"keys": [
"bareConsole"
]
},
{
"path": "/lib/bare/bundles/bareDebugLog.js",
"keys": [
"bareDebugLog"
"bareBundleEvaluate"
]
},
{
@@ -188,9 +176,15 @@
]
},
{
"path": "/lib/bare/bundles/bareChannel.js",
"path": "/lib/bare/bundles/bareDebugLog.js",
"keys": [
"bareChannel"
"bareDebugLog"
]
},
{
"path": "/lib/bare/bundles/bareConsole.js",
"keys": [
"bareConsole"
]
},
{
@@ -199,12 +193,24 @@
"bareDaemon"
]
},
{
"path": "/lib/bare/bundles/bareChannel.js",
"keys": [
"bareChannel"
]
},
{
"path": "/lib/bare/bundles/bareDelta.js",
"keys": [
"bareDelta"
]
},
{
"path": "/lib/bare/bundles/bareCov.js",
"keys": [
"bareCov"
]
},
{
"path": "/lib/bare/bundles/bareDiagnosticsChannel.js",
"keys": [
@@ -235,12 +241,6 @@
"bareExif"
]
},
{
"path": "/lib/bare/bundles/bareCov.js",
"keys": [
"bareCov"
]
},
{
"path": "/lib/bare/bundles/bareFfmpeg.js",
"keys": [
@@ -259,24 +259,12 @@
"bareFormData"
]
},
{
"path": "/lib/bare/bundles/bareFormat.js",
"keys": [
"bareFormat"
]
},
{
"path": "/lib/bare/bundles/bareGif.js",
"keys": [
"bareGif"
]
},
{
"path": "/lib/bare/bundles/bareGtk.js",
"keys": [
"bareGtk"
]
},
{
"path": "/lib/bare/bundles/bareFileLogger.js",
"keys": [
@@ -284,9 +272,9 @@
]
},
{
"path": "/lib/bare/bundles/bareHrtime.js",
"path": "/lib/bare/bundles/bareFormat.js",
"keys": [
"bareHrtime"
"bareFormat"
]
},
{
@@ -301,6 +289,18 @@
"bareFs"
]
},
{
"path": "/lib/bare/bundles/bareHrtime.js",
"keys": [
"bareHrtime"
]
},
{
"path": "/lib/bare/bundles/bareGtk.js",
"keys": [
"bareGtk"
]
},
{
"path": "/lib/bare/bundles/bareHttpParser.js",
"keys": [
@@ -313,12 +313,6 @@
"bareIco"
]
},
{
"path": "/lib/bare/bundles/bareHttp1.js",
"keys": [
"bareHttp1"
]
},
{
"path": "/lib/bare/bundles/bareImageResample.js",
"keys": [
@@ -326,9 +320,9 @@
]
},
{
"path": "/lib/bare/bundles/bareHttps.js",
"path": "/lib/bare/bundles/bareHttp1.js",
"keys": [
"bareHttps"
"bareHttp1"
]
},
{
@@ -338,9 +332,9 @@
]
},
{
"path": "/lib/bare/bundles/bareJpeg.js",
"path": "/lib/bare/bundles/bareHttps.js",
"keys": [
"bareJpeg"
"bareHttps"
]
},
{
@@ -349,30 +343,30 @@
"bareIntl"
]
},
{
"path": "/lib/bare/bundles/bareJpeg.js",
"keys": [
"bareJpeg"
]
},
{
"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/bareLief.js",
"keys": [
"bareLief"
]
},
{
"path": "/lib/bare/bundles/bareLink.js",
"keys": [
@@ -380,9 +374,9 @@
]
},
{
"path": "/lib/bare/bundles/bareModuleLexer.js",
"path": "/lib/bare/bundles/bareLogger.js",
"keys": [
"bareModuleLexer"
"bareLogger"
]
},
{
@@ -397,30 +391,18 @@
"bareModule"
]
},
{
"path": "/lib/bare/bundles/bareModuleLexer.js",
"keys": [
"bareModuleLexer"
]
},
{
"path": "/lib/bare/bundles/bareModuleResolve.js",
"keys": [
"bareModuleResolve"
]
},
{
"path": "/lib/bare/bundles/bareNdk.js",
"keys": [
"bareNdk"
]
},
{
"path": "/lib/bare/bundles/bareNative.js",
"keys": [
"bareNative"
]
},
{
"path": "/lib/bare/bundles/bareNodeFetch.js",
"keys": [
"bareNodeFetch"
]
},
{
"path": "/lib/bare/bundles/bareModuleTraverse.js",
"keys": [
@@ -428,15 +410,27 @@
]
},
{
"path": "/lib/bare/bundles/bareOpen.js",
"path": "/lib/bare/bundles/bareMedia.js",
"keys": [
"bareOpen"
"bareMedia"
]
},
{
"path": "/lib/bare/bundles/bareOs.js",
"path": "/lib/bare/bundles/bareNdk.js",
"keys": [
"bareOs"
"bareNdk"
]
},
{
"path": "/lib/bare/bundles/bareDev.js",
"keys": [
"bareDev"
]
},
{
"path": "/lib/bare/bundles/bareNative.js",
"keys": [
"bareNative"
]
},
{
@@ -446,15 +440,21 @@
]
},
{
"path": "/lib/bare/bundles/bareMedia.js",
"path": "/lib/bare/bundles/bareNodeFetch.js",
"keys": [
"bareMedia"
"bareNodeFetch"
]
},
{
"path": "/lib/bare/bundles/barePerformance.js",
"path": "/lib/bare/bundles/bareOs.js",
"keys": [
"barePerformance"
"bareOs"
]
},
{
"path": "/lib/bare/bundles/bareOpen.js",
"keys": [
"bareOpen"
]
},
{
@@ -464,9 +464,9 @@
]
},
{
"path": "/lib/bare/bundles/barePackDrive.js",
"path": "/lib/bare/bundles/barePerformance.js",
"keys": [
"barePackDrive"
"barePerformance"
]
},
{
@@ -476,15 +476,15 @@
]
},
{
"path": "/lib/bare/bundles/barePipe.js",
"path": "/lib/bare/bundles/barePackDrive.js",
"keys": [
"barePipe"
"barePackDrive"
]
},
{
"path": "/lib/bare/bundles/bareDev.js",
"path": "/lib/bare/bundles/barePipe.js",
"keys": [
"bareDev"
"barePipe"
]
},
{
@@ -505,36 +505,36 @@
"barePrebuild"
]
},
{
"path": "/lib/bare/bundles/bareQueueMicrotask.js",
"keys": [
"bareQueueMicrotask"
]
},
{
"path": "/lib/bare/bundles/bareQuerystring.js",
"keys": [
"bareQuerystring"
]
},
{
"path": "/lib/bare/bundles/bareProcess.js",
"keys": [
"bareProcess"
]
},
{
"path": "/lib/bare/bundles/bareRealm.js",
"keys": [
"bareRealm"
]
},
{
"path": "/lib/bare/bundles/bareQueueMicrotask.js",
"keys": [
"bareQueueMicrotask"
]
},
{
"path": "/lib/bare/bundles/barePromClient.js",
"keys": [
"barePromClient"
]
},
{
"path": "/lib/bare/bundles/bareProcess.js",
"keys": [
"bareProcess"
]
},
{
"path": "/lib/bare/bundles/bareRuntime.js",
"keys": [
@@ -547,6 +547,18 @@
"bareSdl"
]
},
{
"path": "/lib/bare/bundles/bareRun.js",
"keys": [
"bareRun"
]
},
{
"path": "/lib/bare/bundles/bareRpc.js",
"keys": [
"bareRpc"
]
},
{
"path": "/lib/bare/bundles/bareRepl.js",
"keys": [
@@ -559,54 +571,42 @@
"bareSemver"
]
},
{
"path": "/lib/bare/bundles/bareRpc.js",
"keys": [
"bareRpc"
]
},
{
"path": "/lib/bare/bundles/bareSignals.js",
"keys": [
"bareSignals"
]
},
{
"path": "/lib/bare/bundles/bareSidecar.js",
"keys": [
"bareSidecar"
]
},
{
"path": "/lib/bare/bundles/bareRun.js",
"keys": [
"bareRun"
]
},
{
"path": "/lib/bare/bundles/bareStorage.js",
"keys": [
"bareStorage"
]
},
{
"path": "/lib/bare/bundles/bareSignals.js",
"keys": [
"bareSignals"
]
},
{
"path": "/lib/bare/bundles/bareStream.js",
"keys": [
"bareStream"
]
},
{
"path": "/lib/bare/bundles/bareStringDecoder.js",
"keys": [
"bareStringDecoder"
]
},
{
"path": "/lib/bare/bundles/bareStdio.js",
"keys": [
"bareStdio"
]
},
{
"path": "/lib/bare/bundles/bareStringDecoder.js",
"keys": [
"bareStringDecoder"
]
},
{
"path": "/lib/bare/bundles/bareSvg.js",
"keys": [
@@ -619,6 +619,12 @@
"bareTap"
]
},
{
"path": "/lib/bare/bundles/bareStructuredClone.js",
"keys": [
"bareStructuredClone"
]
},
{
"path": "/lib/bare/bundles/bareSystemLogger.js",
"keys": [
@@ -638,9 +644,9 @@
]
},
{
"path": "/lib/bare/bundles/bareTimers.js",
"path": "/lib/bare/bundles/bareThread.js",
"keys": [
"bareTimers"
"bareThread"
]
},
{
@@ -650,33 +656,9 @@
]
},
{
"path": "/lib/bare/bundles/bareTpl.js",
"path": "/lib/bare/bundles/bareTimers.js",
"keys": [
"bareTpl"
]
},
{
"path": "/lib/bare/bundles/bareStructuredClone.js",
"keys": [
"bareStructuredClone"
]
},
{
"path": "/lib/bare/bundles/bareThread.js",
"keys": [
"bareThread"
]
},
{
"path": "/lib/bare/bundles/bareTty.js",
"keys": [
"bareTty"
]
},
{
"path": "/lib/bare/bundles/bareType.js",
"keys": [
"bareType"
"bareTimers"
]
},
{
@@ -685,12 +667,36 @@
"bareTls"
]
},
{
"path": "/lib/bare/bundles/bareTpl.js",
"keys": [
"bareTpl"
]
},
{
"path": "/lib/bare/bundles/bareType.js",
"keys": [
"bareType"
]
},
{
"path": "/lib/bare/bundles/bareUiKit.js",
"keys": [
"bareUiKit"
]
},
{
"path": "/lib/bare/bundles/bareTty.js",
"keys": [
"bareTty"
]
},
{
"path": "/lib/bare/bundles/bareUnpack.js",
"keys": [
"bareUnpack"
]
},
{
"path": "/lib/bare/bundles/bareV8.js",
"keys": [
@@ -698,9 +704,9 @@
]
},
{
"path": "/lib/bare/bundles/bareUnpack.js",
"path": "/lib/bare/bundles/bareUnionBundle.js",
"keys": [
"bareUnpack"
"bareUnionBundle"
]
},
{
@@ -715,18 +721,18 @@
"bareWalkHandles"
]
},
{
"path": "/lib/bare/bundles/bareUnionBundle.js",
"keys": [
"bareUnionBundle"
]
},
{
"path": "/lib/bare/bundles/bareV8ToIstanbul.js",
"keys": [
"bareV8ToIstanbul"
]
},
{
"path": "/lib/bare/bundles/bareWebKit.js",
"keys": [
"bareWebKit"
]
},
{
"path": "/lib/bare/bundles/bareUtils.js",
"keys": [
@@ -739,24 +745,18 @@
"bareWebp"
]
},
{
"path": "/lib/bare/bundles/bareWhich.js",
"keys": [
"bareWhich"
]
},
{
"path": "/lib/bare/bundles/bareWebKit.js",
"keys": [
"bareWebKit"
]
},
{
"path": "/lib/bare/bundles/bareWebKitGtk.js",
"keys": [
"bareWebKitGtk"
]
},
{
"path": "/lib/bare/bundles/bareWhich.js",
"keys": [
"bareWhich"
]
},
{
"path": "/lib/bare/bundles/bareWinUi.js",
"keys": [
@@ -769,12 +769,6 @@
"bareXdiff"
]
},
{
"path": "/lib/bare/bundles/bareWorker.js",
"keys": [
"bareWorker"
]
},
{
"path": "/lib/bare/bundles/bareZlib.js",
"keys": [
@@ -787,6 +781,12 @@
"bareZmq"
]
},
{
"path": "/lib/bare/bundles/bareWorker.js",
"keys": [
"bareWorker"
]
},
{
"path": "/lib/bare/bundles/bareWs.js",
"keys": [
@@ -1595,8 +1595,8 @@
],
"bundleProvenance": {
"schemaVersion": 1,
"generatedAt": "2026-04-05T19:03:42.280Z",
"gitCommit": "39dbf0f1be135931add4414edfc66b593559be83",
"generatedAt": "2026-04-05T19:26:43.551Z",
"gitCommit": "169df862f7c3579c6eb28d62c3a6b05ab38ef0f5",
"nodeVersion": "v22.22.0",
"bundleTier": "all",
"normativeManifest": "packages/bare-os-booter/lib/bare-module-manifest.json",
@@ -1,6 +1,6 @@
{
"schema": 1,
"atMs": 1775415820999,
"atMs": 1775417202531,
"commands": [
"arch",
"awk",
@@ -90,7 +90,9 @@
"openssl",
"paste",
"patch",
"pathcap-verify",
"pathchk",
"pkg-swarm-index",
"pr",
"printenv",
"printf",
@@ -70,6 +70,12 @@
* BARE_OS_RC_D_SKIP: comma-separated rc.d basenames to skip; a pattern ending with * skips
* names with that prefix (e.g. 10-* skips 10-foo).
*
* BARE_OS_RC_PROPOSAL_MULTISIG_STRICT=1: before **rc.d**, require **`/etc/bare-os/pear.multisig.json`**
* ( **`bareOsPearMultisigShapeOk`** ) and validate every **`/etc/bare-os/rc.proposals/enabled/*.json`**
* (**`schema`**: **1**, **`proposalId`**, **`targetRcSnippet`** under **`/etc/bare-os/rc.d/`**, **`signaturesFrom`**: distinct
* public keys each listed in **`pear.multisig.json`** **`signers`**, count **`quorum`**). Emits **`ctx.bareOsAuditLogAppend`**
* rows **`rc.proposal.multisig_ok`** / **`rc.proposal.multisig_fail`** when the hook exists.
*
* Use ctx.registerKernelShutdownHook(fn) for teardown before initd disposers.
*/
@@ -1098,6 +1104,246 @@ async function runBareOsKernelDir(ctx) {
return true
}
/**
* @param {Record<string, unknown>} ctx
* @param {Record<string, unknown>} entry
*/
function bareOsAppendRcProposalAudit(ctx, entry) {
try {
const fn = ctx.bareOsAuditLogAppend
if (typeof fn === 'function') fn(entry)
} catch {
/* ignore */
}
}
/**
* When **`BARE_OS_RC_PROPOSAL_MULTISIG_STRICT`**, every enabled proposal must cite enough
* **`pear.multisig.json`** signers (distinct keys). Cryptographic signature verification is host/seeder responsibility.
* @param {Record<string, unknown>} ctx
* @returns {Promise<boolean>}
*/
async function verifyRcProposalsMultisigStrict(ctx) {
const strict = ctx.env?.BARE_OS_RC_PROPOSAL_MULTISIG_STRICT
if (strict !== '1' && strict !== 'true') return true
const { drive, b4a } = ctx
let pearBuf
try {
pearBuf = await drive.get('/etc/bare-os/pear.multisig.json')
} catch {
pearBuf = null
}
if (!pearBuf) {
bootStructuredLog(
ctx,
'error',
'rc.proposal.multisig.pear_missing',
'[rc.proposals] BARE_OS_RC_PROPOSAL_MULTISIG_STRICT requires /etc/bare-os/pear.multisig.json'
)
bareOsAppendRcProposalAudit(ctx, {
type: 'rc.proposal.multisig_fail',
reason: 'pear_multisig_missing',
ts: Date.now()
})
return false
}
/** @type {unknown} */
let pearParsed
try {
pearParsed = JSON.parse(b4a.toString(pearBuf))
} catch (e) {
bootStructuredLog(
ctx,
'error',
'rc.proposal.multisig.pear_parse',
'[rc.proposals] pear.multisig.json: ' + ((e && e.message) || String(e))
)
bareOsAppendRcProposalAudit(ctx, {
type: 'rc.proposal.multisig_fail',
reason: 'pear_multisig_parse',
ts: Date.now()
})
return false
}
if (!bareOsPearMultisigShapeOk(pearParsed)) {
bootStructuredLog(
ctx,
'error',
'rc.proposal.multisig.pear_shape',
'[rc.proposals] pear.multisig.json must be { signers: string[], quorum: number } with 1 ≤ quorum ≤ signers.length'
)
bareOsAppendRcProposalAudit(ctx, {
type: 'rc.proposal.multisig_fail',
reason: 'pear_multisig_shape',
ts: Date.now()
})
return false
}
const pear = /** @type {{ signers: string[]; quorum: number }} */ (pearParsed)
const signerSet = new Set(pear.signers.map((s) => String(s).toLowerCase()))
const quorum = pear.quorum
/** @type {string[]} */
const proposalNames = []
try {
for await (const n of drive.readdir('/etc/bare-os/rc.proposals/enabled')) {
if (String(n).endsWith('.json')) proposalNames.push(String(n))
}
} catch {
return true
}
proposalNames.sort()
if (!proposalNames.length) return true
for (const fn of proposalNames) {
const p = `/etc/bare-os/rc.proposals/enabled/${fn}`
/** @type {unknown} */
let raw
try {
const buf = await drive.get(p)
if (!buf) {
bootStructuredLog(ctx, 'error', 'rc.proposal.missing', `[rc.proposals] empty: ${fn}`)
bareOsAppendRcProposalAudit(ctx, {
type: 'rc.proposal.multisig_fail',
reason: 'proposal_missing',
proposalFile: fn,
ts: Date.now()
})
return false
}
raw = JSON.parse(b4a.toString(buf))
} catch (e) {
bootStructuredLog(
ctx,
'error',
'rc.proposal.parse',
`[rc.proposals] ${fn}: ` + ((e && e.message) || String(e))
)
bareOsAppendRcProposalAudit(ctx, {
type: 'rc.proposal.multisig_fail',
reason: 'proposal_parse',
proposalFile: fn,
ts: Date.now()
})
return false
}
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
bootStructuredLog(ctx, 'error', 'rc.proposal.shape', `[rc.proposals] ${fn}: expected object`)
return false
}
const o = /** @type {Record<string, unknown>} */ (raw)
if (o.schema !== 1) {
bootStructuredLog(
ctx,
'error',
'rc.proposal.schema',
`[rc.proposals] ${fn}: schema must be 1`
)
bareOsAppendRcProposalAudit(ctx, {
type: 'rc.proposal.multisig_fail',
reason: 'proposal_schema',
proposalFile: fn,
ts: Date.now()
})
return false
}
const proposalId = String(o.proposalId || '').trim()
if (!proposalId) {
bootStructuredLog(ctx, 'error', 'rc.proposal.id', `[rc.proposals] ${fn}: proposalId required`)
bareOsAppendRcProposalAudit(ctx, {
type: 'rc.proposal.multisig_fail',
reason: 'proposal_id',
proposalFile: fn,
ts: Date.now()
})
return false
}
const targetRcSnippet = String(o.targetRcSnippet || '').trim()
if (!targetRcSnippet.startsWith('/etc/bare-os/rc.d/')) {
bootStructuredLog(
ctx,
'error',
'rc.proposal.target',
`[rc.proposals] ${fn}: targetRcSnippet must start with /etc/bare-os/rc.d/`
)
bareOsAppendRcProposalAudit(ctx, {
type: 'rc.proposal.multisig_fail',
reason: 'proposal_target',
proposalFile: fn,
proposalId,
ts: Date.now()
})
return false
}
const sigFrom = o.signaturesFrom
if (!Array.isArray(sigFrom)) {
bootStructuredLog(
ctx,
'error',
'rc.proposal.signaturesFrom',
`[rc.proposals] ${fn}: signaturesFrom must be an array of signer public keys`
)
bareOsAppendRcProposalAudit(ctx, {
type: 'rc.proposal.multisig_fail',
reason: 'signatures_from_shape',
proposalFile: fn,
proposalId,
ts: Date.now()
})
return false
}
/** @type {Set<string>} */
const approved = new Set()
for (const s of sigFrom) {
const k = String(s || '').toLowerCase().trim()
if (!k) continue
if (!signerSet.has(k)) {
bootStructuredLog(
ctx,
'error',
'rc.proposal.signer_unknown',
`[rc.proposals] ${fn}: signer not in pear.multisig.json`
)
bareOsAppendRcProposalAudit(ctx, {
type: 'rc.proposal.multisig_fail',
reason: 'signer_not_in_policy',
proposalFile: fn,
proposalId,
ts: Date.now()
})
return false
}
approved.add(k)
}
if (approved.size < quorum) {
bootStructuredLog(
ctx,
'error',
'rc.proposal.quorum',
`[rc.proposals] ${fn}: need ≥ pear.multisig quorum (${quorum}) distinct approved signers, got ${approved.size}`
)
bareOsAppendRcProposalAudit(ctx, {
type: 'rc.proposal.multisig_fail',
reason: 'quorum_not_met',
proposalFile: fn,
proposalId,
ts: Date.now()
})
return false
}
bareOsAppendRcProposalAudit(ctx, {
type: 'rc.proposal.multisig_ok',
proposalFile: fn,
proposalId,
targetRcSnippet,
signerCount: approved.size,
quorum,
ts: Date.now()
})
}
return true
}
/**
* Optional snippets under /etc/bare-os/rc.d/ executed in lexicographic order.
* @param {Record<string, unknown>} ctx
@@ -1105,6 +1351,7 @@ async function runBareOsKernelDir(ctx) {
*/
async function runBareOsRcDir(ctx) {
const { drive, b4a } = ctx
if (!(await verifyRcProposalsMultisigStrict(ctx))) return false
const skip = parseRcDSkipPatterns(ctx)
try {
/** @type {string[]} */
@@ -1853,6 +2100,26 @@ async function start(ctx) {
}
}
if (coldExceeded || stdlibExceeded) {
/** @type {{ kind: string, wallMs: number, limitMs: number }[]} */
const violations = []
if (coldExceeded && Number.isFinite(budget) && budget > 0) {
violations.push({
kind: 'cold',
wallMs: Date.now() - bootT0,
limitMs: budget
})
}
if (stdlibExceeded && Number.isFinite(sb) && sb > 0) {
const sw = Number.parseInt(
String(ctx.env?.BARE_OS_BOOT_BARE_STDLIB_RESOLUTION_MS || ''),
10
)
violations.push({
kind: 'bare_stdlib',
wallMs: Number.isFinite(sw) ? sw : 0,
limitMs: sb
})
}
await maybeAppendBootTransactionJournal(ctx, {
phase: 'boot.budget',
stage: 'boot.budget',
@@ -1860,9 +2127,10 @@ async function start(ctx) {
ms: 0,
ok: false,
bootBudgetViolation: true,
bootBudgetSchemaVersion: 1,
bootBudgetSchemaVersion: 2,
coldBudgetExceeded: coldExceeded,
bareStdlibBudgetExceeded: stdlibExceeded,
bootBudgetViolations: violations,
transactionState: BARE_OS_BOOT_TXN_STATE.STAGE_COMMITTED
})
}
File diff suppressed because one or more lines are too long
+12
View File
@@ -168,6 +168,18 @@ Ensures **`boot.policy`** documents **`extensionSignerPinsV2`**…**`V5`** and *
Checks that **`packages/bare-os-booter/lib/bare-os-ctx-api.js`** documents a **`BARE_OS_CTX_API_VERSION`** that matches the contract surface (semver discipline) and that stock kernel feature words (**`BARE_OS_KERNEL_FEATURES_STOCK_WORD_*`**) in **`bare-os-protocol`** stay referenced from the booter (including **`kernelCapabilityWords`** / **`ctx`** maps for wire v2). Invoked from the root **`pretest`** hook.
## `verify-ctx-client-helper-sync.mjs`
**Usage:** `node scripts/verify-ctx-client-helper-sync.mjs`
Ensures **[`docs/reference/ctx-client-helper.generated.ts`](../docs/reference/ctx-client-helper.generated.ts)** mentions the current **`BARE_OS_CTX_API_VERSION`** after **`gen-ctx-client-helper.mjs`**. Root **`pretest`**.
## `verify-personal-drive-path-policy.mjs`
**Usage:** `node scripts/verify-personal-drive-path-policy.mjs`
Checks that canonical personal-drive path strings stay documented across the POSIX profile, handbook ch.45, and the environment appendix. Root **`pretest`**.
## `verify-kernel-capabilities-contract.mjs`
**Usage:** `node scripts/verify-kernel-capabilities-contract.mjs`
+7
View File
@@ -41,6 +41,13 @@ console.log(
'[release-checklist] verify-holepunch-clone-drift (BARE_OS_HOLEPUNCH_DRIFT_CHECK=0 to skip; empty repos[] is no-op)'
)
run('node', ['scripts/verify-holepunch-clone-drift.mjs'])
console.log('[release-checklist] verify-personal-drive-path-policy')
run('node', ['scripts/verify-personal-drive-path-policy.mjs'])
console.log('[release-checklist] verify-ctx-client-helper-sync')
run('node', ['scripts/verify-ctx-client-helper-sync.mjs'])
console.log(
'[release-checklist] Optional: BARE_OS_HOLEPUNCH_FRESHNESS_STRICT=1 with fresh `holepunchto_repos` clones — see docs/audit/holepunch-freshness-gate.json'
)
console.log(
'[release-checklist] If you edited kernel/lib/init/init-main.js or kernel/lib/boot/, run: npm run bundle:kernel && rsync -a --delete kernel/ packages/bare-os-seeder/kernel/ then re-run this script.'
)
+39
View File
@@ -0,0 +1,39 @@
#!/usr/bin/env node
/**
* CI: generated ctx client helper must list the current ctx API semver (same as bare-os-ctx.d.ts).
*/
import fs from 'node:fs'
import path from 'node:path'
import process from 'node:process'
import { fileURLToPath } from 'node:url'
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
function main() {
const apiPath = path.join(
root,
'packages/bare-os-booter/lib/bare-os-ctx-api.js'
)
const helperPath = path.join(
root,
'docs/reference/ctx-client-helper.generated.ts'
)
const apiSrc = fs.readFileSync(apiPath, 'utf8')
const m = apiSrc.match(/export const BARE_OS_CTX_API_VERSION = '([^']+)'/)
if (!m) {
console.error('verify-ctx-client-helper-sync: could not parse ctx API version')
process.exit(1)
}
const ver = m[1]
const helper = fs.readFileSync(helperPath, 'utf8')
if (!helper.includes(ver)) {
console.error(
'verify-ctx-client-helper-sync: run `node scripts/gen-ctx-client-helper.mjs` — helper missing',
ver
)
process.exit(1)
}
console.log('verify-ctx-client-helper-sync:', ver, 'OK')
}
main()
@@ -0,0 +1,45 @@
#!/usr/bin/env node
/**
* CI: canonical personal-drive special paths must stay documented.
*/
import fs from 'node:fs'
import path from 'node:path'
import process from 'node:process'
import { fileURLToPath } from 'node:url'
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
const paths = [
'/.bare/account',
'/.bare/vault/',
'/.bare-os/acct/',
'/.bare-os/migration/legacy-root-v1.json',
'vault-rotation-audit.ndjson'
]
const files = [
path.join(root, 'docs/architecture/POSIX_DECLARED_PROFILE.md'),
path.join(root, 'handbook/04-the-booter-runtime.md'),
path.join(root, 'handbook/05-identity-vault-and-hdms.md'),
path.join(root, 'docs/reference/environment-and-posix-appendix.md')
]
function main() {
const blob = files.map((f) => fs.readFileSync(f, 'utf8')).join('\n')
for (const p of paths) {
if (!blob.includes(p)) {
console.error(
'verify-personal-drive-path-policy: missing documentation for:',
p
)
process.exit(1)
}
}
console.log(
'verify-personal-drive-path-policy:',
paths.length,
'path tokens OK'
)
}
main()
@@ -46,6 +46,8 @@ npm run build -w bare-os-coreutils
If you changed **`kernel/`** but the seeder still ships an old tree, CIs **`verify-kernel-seeder-parity`** error means **`packages/bare-os-seeder/kernel/`** is out of sync with **`kernel/`**—copy or sync per maintainer workflow before release.
**Symptom: `curl` or `wget` prints “unavailable in this session” and exits 127.** The drive-resident scripts require **`ctx.bareOsRunCurlCli`** / **`ctx.bareOsRunWgetCli`**, which every stock **`bare-os-booter`** provides. Upgrade the booter package and align **`ctx` API** versions ([compatibility matrix](../docs/reference/compatibility-matrix.md)). Excluding **`curl`** or **`wget`** from **`BARE_OS_DELEGATE_ALLOW`** only skips the *host* delegate path; fetch still runs through those **`ctx`** hooks when the booter is current.
---
## Environment variables