- Extend bundle throw governance and guest boot trace; seeder parity hints
- fcntl F_GETFL/F_SETFL + posixPoll; bump POSIX profile to 1.0.3, syscalls example - bareOsSendSignal: negative target = logical pgid; process_table docs - IPC: max channel quota, telemetry; metrics_live.ipcTelemetry; BARE_OS_IPC_MAX_CHANNELS - VFS: bareOsClearWarmReadCaches; invalidate on batch bin/lib/bare puts; ctx helpers - Swarm: enforce BARE_OS_PEER_ALLOWLIST_HEX in shouldAttemptPeer - Replication JSON reader, boot-ready bareStdlib resolutionWallMs, vault audit mirror docs - sync-holepunch-clones.mjs report; kernel-extension resolver/test alignment; protomux tests - pathconf union/mirror; getconf tests; golden tests for test/expr/printf - docs: contract bump checklist, handbook/env appendix, package refs, protocol CHANGELOG
This commit is contained in:
@@ -16,7 +16,9 @@ This project is **experimental research software**. APIs described here follow t
|
||||
|
||||
**Architecture contract (boundaries):** [`docs/architecture/KERNEL_CONTRACT.md`](../docs/architecture/KERNEL_CONTRACT.md) — booter vs guest image, **`disk.os`** RPC, boot step IDs.
|
||||
|
||||
**Kernel source set:** [`docs/reference/kernel-extensions.md`](../docs/reference/kernel-extensions.md) — first-party paths (`kernel/init.js`, `kernel/lib/boot`, built `/bin`) vs vendored **`kernel/lib/bare/bundles`** (Holepunch IIFEs), CI allowlists, and seeder parity. Hand-authored code must stay free of incomplete markers; bundles are tracked separately until allowlists are empty.
|
||||
**Kernel source set:** [`docs/reference/kernel-extensions.md`](../docs/reference/kernel-extensions.md) — first-party paths (`kernel/init.js`, `kernel/lib/boot`, built `/bin`) vs vendored **`kernel/lib/bare/bundles`** (Holepunch IIFEs), CI allowlists, and seeder parity. Hand-authored code must stay free of incomplete markers; bundles are gated by **`verify-bundle-markers.mjs`**, **`verify-bundle-throws.mjs`** ( **`new Error` messages** with *not implemented* / abstract-base-class semantics — see [`docs/audit/bundle-throw-allowlist.json`](../docs/audit/bundle-throw-allowlist.json)), and **`sanitize-bare-bundles.mjs`**. Prefer shrinking the throw allowlist via upstream bundle refreshes over silent drift.
|
||||
|
||||
**Local Holepunch clones:** run **`node scripts/sync-holepunch-clones.mjs`** to emit **`docs/audit/holepunch-clone-sync-report.json`** comparing **`bare-module-manifest.json`** names to optional checkouts under **`BARE_OS_HOLEPUNCH_CLONES_ROOT`** (default **`~/dev/pearcli/holepunch-repos/holepunchto_repos`**).
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -74,6 +74,22 @@ If you must quote a banned substring for wire-compat or third-party context, add
|
||||
|
||||
---
|
||||
|
||||
## Kernel tree vs seeder mirror
|
||||
|
||||
**Canonical tree:** [`kernel/`](../kernel/) (hand-authored **`kernel/lib/init/init-main.js`**, **`kernel/lib/boot/*.js`**, **`kernel/etc/**`**, staged **`kernel/bin/**`**, **`kernel/lib/bare/**`**).
|
||||
|
||||
**Mirror:** [`packages/bare-os-seeder/kernel/`](../packages/bare-os-seeder/kernel/) must be **byte-identical** to **`kernel/`** (CI: [scripts/verify-kernel-seeder-parity.mjs](../scripts/verify-kernel-seeder-parity.mjs)).
|
||||
|
||||
Workflow after changing boot logic:
|
||||
|
||||
1. Edit **`kernel/lib/init/init-main.js`** (and optional **`kernel/lib/boot/*.js`**).
|
||||
2. Run **`node scripts/bundle-kernel-init.mjs`** to regenerate root **`kernel/init.js`**.
|
||||
3. Sync the mirror: **`rsync -a --delete kernel/ packages/bare-os-seeder/kernel/`** from the repo root.
|
||||
|
||||
Do not edit the seeder copy first; it will be overwritten by parity checks.
|
||||
|
||||
---
|
||||
|
||||
## Before you open a PR
|
||||
|
||||
```bash
|
||||
|
||||
@@ -118,6 +118,19 @@ How-to for code inside and around the image.
|
||||
| [Machine-readable data](data/README.md) | Roadmap JSON, terminology allowlist |
|
||||
| [Holepunch catalog](bare-holepunch-catalog.json) | Optional `ctx.bare` bundle metadata |
|
||||
|
||||
### Contract bump checklist
|
||||
|
||||
When you change **`BARE_OS_CTX_API_VERSION`**, **`/proc`** JSON schemas, **`BARE_OS_PROTOCOL_*`** / package semver, **`BARE_OS_POSIX_PROFILE_VERSION`**, or kernel capability words, update every dependent artifact in one pass:
|
||||
|
||||
1. [README.md](../README.md) and [DOCUMENTATION.md](../DOCUMENTATION.md) if the version string or install story changes.
|
||||
2. [Handbook](../handbook/README.md) chapters touched by behavior (especially ch.3–4, 6, 9).
|
||||
3. [Users manual](../users-manual/README.md) and [Developer guide](../developer-guide/README.md) for operator or extension workflows.
|
||||
4. [docs/reference/compatibility-matrix.md](reference/compatibility-matrix.md) and any affected topic pages under [docs/reference/](reference/README.md).
|
||||
5. Package changelogs: [packages/bare-os-booter/CHANGELOG.md](../packages/bare-os-booter/CHANGELOG.md), [packages/bare-os-protocol/CHANGELOG.md](../packages/bare-os-protocol/CHANGELOG.md) when applicable.
|
||||
6. [docs/schemas/](schemas/) and [docs/architecture/POSIX_DECLARED_PROFILE.md](architecture/POSIX_DECLARED_PROFILE.md) when proc, telemetry, or POSIX profiles move.
|
||||
7. [kernel/etc/bare-os/syscalls.example.json](../kernel/etc/bare-os/syscalls.example.json) and the [seeder mirror](../packages/bare-os-seeder/kernel/) after kernel contract edits; run **`node scripts/verify-kernel-seeder-parity.mjs`**.
|
||||
8. Regenerate **`docs/reference/ctx-client-helper.generated.ts`** via root **`pretest`** ( **`gen-ctx-client-helper.mjs`** ).
|
||||
|
||||
---
|
||||
|
||||
## Root stubs and Pear
|
||||
|
||||
@@ -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.1` |
|
||||
| `BARE_OS_POSIX_PROFILE_VERSION` | `1.0.3` |
|
||||
| `BARE_OS_POSIX_PROFILE_ID` | `bare-os-posix-like` |
|
||||
| `BARE_OS_POSIX_PROFILE_REFERENCE` | Open Group Issue 7 index URL |
|
||||
|
||||
@@ -18,7 +18,7 @@ This document is the **normative contract** for how closely the stock Bare OS ke
|
||||
|
||||
Bare OS does **not** expose a C ABI. The following **observability contracts** stand in for “system calls”:
|
||||
|
||||
- **`/proc/bare_os/syscalls.json`** — Declares **fd model**, **signal model**, **errno hints**, and **operation detail** (schema version in-file). **`ops`** lists invocable **`ctx.bareOsSyscall`** names; **`opsDetail`** also carries **POSIX.1 XSH** logical names (**`open`**, **`close`**, **`read`**, **`write`**, **`lseek`**, **`pipe`**, **`dup`**, **`dup2`**, **`fcntl`**) with **`posixAlignment`** (**`simulated`**, **`partial`**, **`ENOTSUP`**) and **`mapsTo`** for traceability. The **`posixXsh`** object summarizes those XSH names. Unsupported operations are listed with **ENOTSUP** / **ENOSYS**-style semantics where applicable.
|
||||
- **`/proc/bare_os/syscalls.json`** — Declares **fd model**, **signal model**, **errno hints**, and **operation detail** (schema version in-file). **`ops`** lists invocable **`ctx.bareOsSyscall`** names; **`opsDetail`** also carries **POSIX.1 XSH** logical names (**`open`**, **`close`**, **`read`**, **`write`**, **`lseek`**, **`pipe`**, **`dup`**, **`dup2`**, **`fcntl`**, **`poll`**) with **`posixAlignment`** (**`simulated`**, **`partial`**, **`ENOTSUP`**) and **`mapsTo`** for traceability. The **`posixXsh`** object summarizes those XSH names. **`fcntl`** is implemented as **`ctx.bareOsSyscall('fcntl', { fd, cmd, arg })`** with **`F_GETFL`** / **`F_SETFL`** (Linux-shaped **`O_APPEND`** **`0x400`**, **`O_NONBLOCK`** **`0x800`**). **`poll`** is approximated by **`ctx.bareOsPosixPoll`** / **`posixPoll`** on simulated pipe FDs. Unsupported operations are listed with **ENOTSUP** / **ENOSYS**-style semantics where applicable.
|
||||
- **`/proc/bare_os/process_table.json`** — **PGID** / **SID**-style fields, **signal routing**, and initd binding for the **logical** process table (not host PIDs).
|
||||
|
||||
**Explicit non-goals**
|
||||
@@ -53,11 +53,13 @@ Two logical drives (**system** + **personal**) form one namespace; **`.bareos_em
|
||||
|
||||
**Rename:** No atomic cross-drive **`rename`**; **`mv`** implements copy + remove where needed (handbook ch.9).
|
||||
|
||||
**`pathconf` / `getconf`:** Live **`getconf _PC_* /path`** delegates to **`ctx.bareOsPathconf`**. Union read prefixes (**`BARE_OS_VFS_UNION_PREFIXES`**) and read-only **mirror** paths (**`/mirror/…`**) adjust documented **`_PC_NO_TRUNC`** and **`_PC_CHOWN_RESTRICTED`** hints (not a certifiable pathconf implementation).
|
||||
|
||||
---
|
||||
|
||||
## 5. Signals and exit statuses
|
||||
|
||||
Signal names accepted by **`kill`** and shell builtins follow POSIX **names** where listed in proc JSON. **Exit codes** for pipelines and **`timeout`** follow documented stock behavior (see handbook and booter tests).
|
||||
Signal names accepted by **`kill`** and shell builtins follow POSIX **names** where listed in proc JSON. **`ctx.bareOsSendSignal`** and **`/bin/kill`** also accept a **negative** numeric operand as a logical **process group id** (**`pgid`**) for **shell background jobs** (active rows only; **`kill -0 -pgid`** checks existence). **Exit codes** for pipelines and **`timeout`** follow documented stock behavior (see handbook and booter tests).
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"generatedAt": "2026-04-05T02:38:13.083Z",
|
||||
"generatedAt": "2026-04-05T03:03:09.364Z",
|
||||
"normativeManifest": "packages/bare-os-booter/lib/bare-module-manifest.json",
|
||||
"buildTool": "packages/bare-os-bare-libs/build.mjs",
|
||||
"bundles": [
|
||||
|
||||
@@ -1,5 +1,21 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"description": "Vendored bundles that still contain throw new Error(\"not implemented\") until upstream removes them. Target: empty.",
|
||||
"entries": []
|
||||
"schemaVersion": 2,
|
||||
"description": "Vendored bundles that intentionally contain new Error(...) messages with 'not implemented' / abstract-base-class semantics from upstream Bare/Node-compat streams. Re-run after bare-os-bare-libs rebuild; trim when upstream removes throws.",
|
||||
"entries": [
|
||||
{
|
||||
"bundleFile": "bareDev.js",
|
||||
"rationale": "Duplex/transform stream base classes from upstream use _read/_write/_transform and abstract readByte/seek errors when subclasses omit hooks — standard Node stream pattern, not Bare OS stubs.",
|
||||
"triggerCondition": "Subclass Readable/Writable/Duplex without overriding _read/_write/_transform; RandomAccessReader subclasses without readByte/seek"
|
||||
},
|
||||
{
|
||||
"bundleFile": "bareMedia.js",
|
||||
"rationale": "Image pipeline defers ICO encoder path; throws only when ICO output is requested.",
|
||||
"triggerCondition": "ICO encoding write path when encoder not shipped in bundle"
|
||||
},
|
||||
{
|
||||
"bundleFile": "bareIco.js",
|
||||
"rationale": "ICO encode entrypoint matches bareMedia deferral; decode works, encode is unsupported until upstream implements.",
|
||||
"triggerCondition": "ICO encode API invoked"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ Single place to compare **protocol**, **booter `ctx` API**, **feature-bit docume
|
||||
|
||||
## Version artifacts
|
||||
|
||||
**`bareOsCtxApiVersion`** (current stock: **`1.34.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.
|
||||
**`bareOsCtxApiVersion`** (current stock: **`1.36.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).
|
||||
|
||||
|
||||
@@ -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.34.0' as const
|
||||
export const BARE_OS_CTX_API_CLIENT_VERSION = '1.36.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 = 141 as const
|
||||
export const BARE_OS_CTX_DTS_BAREOS_MEMBER_APPROX = 146 as const
|
||||
|
||||
/** Byte length of `bare-os-ctx.d.ts` when this file was generated. */
|
||||
export const BARE_OS_CTX_DTS_BYTES = 17146 as const
|
||||
export const BARE_OS_CTX_DTS_BYTES = 17869 as const
|
||||
|
||||
/** Pointers for external client generators (OpenAPI / JSON Schema follow-ups). */
|
||||
export const BARE_OS_CTX_SCHEMA_HINT = {
|
||||
|
||||
@@ -27,7 +27,7 @@ The list below is one **bullet per variable** in the form **name — component
|
||||
- `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
|
||||
- `BARE_OS_BOOT_TRACE` — Stock kernel (bundled **`/boot/init.js`** from [`kernel/lib/init/init-main.js`](../../kernel/lib/init/init-main.js) via [`scripts/bundle-kernel-init.mjs`](../../scripts/bundle-kernel-init.mjs)) — If `1` or `true`, log each boot phase duration on stderr as `[boot] phase: Nms`; if `json`, log `{"phase":"…","ms":n}` per phase; **`ndjson`** adds **`sessionId`** / **`ts`** (same shape as `ctx.bareOsEmitBootEvent`)
|
||||
- `BARE_OS_BOOT_TRACE` — Stock kernel (bundled **`/boot/init.js`** from [`kernel/lib/init/init-main.js`](../../kernel/lib/init/init-main.js) via [`scripts/bundle-kernel-init.mjs`](../../scripts/bundle-kernel-init.mjs)) — If `1` or `true`, log each boot phase duration on stderr as `[boot] phase: Nms`; if `json` / **`ndjson`**, log structured phase lines (**`bootTraceSchemaVersion` 2**, **`step`** / **`stage`** / **`phase`**) per completed stage; **`ndjson`** adds **`sessionId`** / **`ts`**. When `json` or **`ndjson`**, guest **`os-release`**, **`motd`**, and **`banner`** text also emit extra stderr lines with **`type":"bootOutput"`** and a capped **`textPreview`** (the live session output still uses **`ctx.console.log`** only).
|
||||
- `BARE_OS_BOOT_SAFE_MODE` — Stock kernel — **`1`** / **`true`** skips **`rc.d`**, **`kernel.ext.d`**, **`onboot`** after boot policy merge.
|
||||
- `BARE_OS_BOOT_TRANSACTION_JOURNAL` — Stock kernel — **`1`**, **`true`**, or **`ndjson`** appends phase records to **`/run/bare-os/boot-transaction.ndjson`** when **`ctx.vfs`** supports it.
|
||||
- `BARE_OS_BOOT_CHECKPOINT` — Stock kernel — **`1`** / **`true`** writes **`/run/bare-os/boot-checkpoint.json`** after each completed boot phase.
|
||||
@@ -50,6 +50,7 @@ The list below is one **bullet per variable** in the form **name — component
|
||||
- `BARE_OS_VFS_UNION_PREFIXES` — Booter / VFS — Comma-separated logical path prefixes where **`readFile`** may union system + overlay sources.
|
||||
- `BARE_OS_VFS_UNION_WRITE_DENY` — Booter / VFS — When **`1`** / **`true`**, block **`writeFile`** / **`unlink`** on paths under union read prefixes (read-only union overlay).
|
||||
- `BARE_OS_IPC_CHANNEL_MAX_BYTES` — Booter / IPC — Optional JSON map **`{"fifoName": maxBytes}`** (or legacy comma form) capping buffered bytes per FIFO name under **`/run/bare-os/ipc/`**.
|
||||
- `BARE_OS_IPC_MAX_CHANNELS` — Booter / IPC — Cap distinct FIFO channel keys (default **4096**); exceeding the quota makes **`mkfifo`** fail with **`bare-os ipc: channel quota exceeded`**. Telemetry appears under **`ipcTelemetry.telemetry`** in **`/proc/bare_os/metrics_live.json`**.
|
||||
- `BARE_OS_VFS_BIN_CACHE` — VFS — When **`1`** / **`true`**, LRU read cache for **`/bin`**; invalidated on **`vfs.watch`** when enabled.
|
||||
- `BARE_OS_VFS_BIN_CACHE_BLAKE2B` — VFS — When **`1`** / **`true`** with **`BARE_OS_VFS_BIN_CACHE`**, **`/bin`** cache entries are keyed by **BLAKE2b** content digests (via **`bare-crypto`**) for deduplication across paths within the LRU budget.
|
||||
- `BARE_OS_VFS_ENFORCE_ACL` — VFS — When **`1`** / **`true`**, **`PATH.bare_acl`** sidecars (same text as **`getfacl` / `setfacl`**) can deny reads/writes on the personal drive. Lines **`user::`**, **`user:UID:`**, **`group::`**, **`group:GID:`**, **`other::`**, and **`mask::`** are interpreted with Linux-style **mask** capping named users, named groups, and **`group::`**; **`user::`** and **`other::`** are not masked. Object owner/group default to **`UID` / `GID`**; override with **`BARE_OS_ACL_OBJECT_UID`** / **`BARE_OS_ACL_OBJECT_GID`** when inode metadata differs. See **`bare-os-vfs-acl-enforce.js`**.
|
||||
|
||||
@@ -15,6 +15,8 @@ Bare OS splits **hand-authored kernel code** from **vendored Holepunch runtime b
|
||||
|
||||
**Seeder parity:** [`kernel/`](../../kernel/) and [`packages/bare-os-seeder/kernel/`](../../packages/bare-os-seeder/kernel/) must be byte-identical ([`scripts/verify-kernel-seeder-parity.mjs`](../../scripts/verify-kernel-seeder-parity.mjs)). After editing `kernel/`, sync the seeder tree before commit.
|
||||
|
||||
**`kernel.ext.d` ordering:** The Kahn topological sort in [`kernel/lib/init/init-main.js`](../../kernel/lib/init/init-main.js) must stay aligned with [`packages/bare-os-booter/lib/kernel-extension-resolver.js`](../../packages/bare-os-booter/lib/kernel-extension-resolver.js) (**`topologicalOrderKernelExtensions`**); CI covers this via the Brittle test **`kernel extension topological order matches guest Kahn tie-break`** in **`packages/bare-os-booter/test.js`**.
|
||||
|
||||
## Governance (feature bits doc v14)
|
||||
|
||||
New capability bits are governed by [ADR 001 — Kernel feature bit governance](../../developer-guide/adr/001-kernel-feature-bits-governance.md). **`BARE_OS_KERNEL_FEATURE_BITS_DOC`** must bump when semantics change. Bits **28–30** on word 1 are assigned (see ADR). **Word 2 (`bits2`)** carries the extended seeding platform surface; always mask with **`>>> 0`**. Avoid `1 << 31` on word 1 in JS without `>>> 0` discipline.
|
||||
@@ -48,6 +50,7 @@ Seed RPC **`bare_os.capabilities`** includes **`protocolPackageVersion`**, optio
|
||||
- Command subst — `BARE_OS_SHELL_CMDSUBST=1` — Enable bounded `$(…)` in words (`BARE_OS_SHELL_CMDSUBST_MAX_BYTES`)
|
||||
- Union writes — `BARE_OS_VFS_UNION_WRITE_DENY` — Comma/colon list of path prefixes blocked for writes under union prefixes
|
||||
- IPC — `BARE_OS_IPC_CHANNEL_MAX_BYTES` — JSON map `{"fifoName": 65536}` per-channel backlog cap
|
||||
- IPC — `BARE_OS_IPC_MAX_CHANNELS` — Upper bound on distinct FIFO names (default 4096); telemetry in **`metrics_live.ipcTelemetry`**
|
||||
- Initd journal — `BARE_OS_INITD_JOURNAL_MAX_LINES` — Cap NDJSON lines per unit (default 400, max 20000)
|
||||
- Telemetry — `BARE_OS_TELEMETRY_NDJSON` — Logical path; append NDJSON mirror (`telemetrySchemaVersion` / `lifecycleSchemaVersion` **10**); optional **`sessionForkGeneration`**, **`bareModuleCryptoStagingProbeId`**, **`pearInspectLoggerTlsProbeId`**, **`hypercorePackHrpcLifecycleProbeId`**, **`bareModuleProbeClass`**, **`bareRpcProbeClass`**; optional correlation via **`BARE_OS_BOOT_ATTEMPT_ID`** → **`bootAttemptId`**
|
||||
- Telemetry OTel — `BARE_OS_TELEMETRY_OTEL_JSONL` — Logical path; append OTLP-inspired JSON lines (`otlSchemaVersion` **8**, optional **`scopeMetrics`** sketch; see [`otel-bare-os-jsonl.schema.json`](../schemas/otel-bare-os-jsonl.schema.json))
|
||||
|
||||
@@ -32,6 +32,10 @@ Former **DOCUMENTATION.md** §§12.1–12.9 (runtime, VFS, shell, identity). [Re
|
||||
|
||||
See also [Kernel subsystem map](../architecture/kernel-subsystems.md) and [ADR 0001](../adr/0001-kernel-subsystem-boundaries.md).
|
||||
|
||||
### 12.2b Corestore / swarm suspend–resume bridge
|
||||
|
||||
- **[`lib/corestore-host-lifecycle.js`](../../packages/bare-os-booter/lib/corestore-host-lifecycle.js)** — Registers one-shot hooks so guest **`ctx.bareOsRegisterSuspendHook`** / **`bareOsRegisterResumeHook`** call host **`corestore`** / **`hyperswarm`** **`suspend()`** / **`resume()`** when those methods exist (mobile sleep, Pear lifecycle). **`bareOsCorestoreSnapshotOperatorHint`** remains a documentation-only sketch (guest does not invoke **`corestore-snapshot`**).
|
||||
|
||||
### 12.3 [packages/bare-os-booter/index.js](../../packages/bare-os-booter/index.js)
|
||||
|
||||
**Imports** — Hyperswarm, Protomux, protocol, `./lib/swarm-disk.js`, `./lib/kernel-runner.js`, `./lib/bare-os-kernel-loader.js`, `./lib/bare-os-capability-registry.js`, `./lib/bare-os-lifecycle-manager.js`, `./lib/vfs.js`, `./lib/shell.js`, `./lib/paths.js`, `./lib/bare-os-ipc.js`, `./lib/bare-os-runtime-caps.js`, stdio/readline/repl/boot-splash helpers, `./lib/identity-session.js`.
|
||||
@@ -47,7 +51,7 @@ See also [Kernel subsystem map](../architecture/kernel-subsystems.md) and [ADR 0
|
||||
**`executeKernel(disk, store, swarm, initSource)`** (`store`/`swarm` unused but kept for signature symmetry / future use)
|
||||
|
||||
- Builds `shellEnv` with **guest** defaults: `USER`/`LOGNAME`=`guest`, `HOME`/`PWD`=`/home/guest`, `UID`/`GID`=`65534`, `BARE_OS_IDENTITY=guest`, `BARE_OS_EXIT_STATUS`=`0`, `BARE_OS_CTX_API_VERSION`, `PATH=/bin`, `SHELL`, `HOSTNAME`, `0`. When the host sets any of the keys listed under “host → session passthrough” in [Environment variables and POSIX appendix](environment-and-posix-appendix.md#14-environment-variables-complete-list), those values are copied into `shellEnv`. Sets **`BARE_OS_BOOT_PROFILE_RESOLVED`** from **`BARE_OS_BOOT_PROFILE`** or the first line of **`/etc/bare-os/profile`**, and **`BARE_OS_SESSION_ID`** (random UUID). Seeds **`/run/bare-os/boot.json`** fields **`imageDigest`**, **`pearChannel`**, **`pearRelease`** from **`BARE_OS_IMAGE_DIGEST`**, **`BARE_OS_PEAR_CHANNEL`** / **`PEAR_CHANNEL`**, **`BARE_OS_PEAR_RELEASE`** when present.
|
||||
- **`createBareOsIpc({ maxFifoBytes, perChannelMaxBytes?, ipcRpcToken?, enableFanout?, maxJsonRpcLineBytes? })`** ([`bare-os-ipc.js`](../../packages/bare-os-booter/lib/bare-os-ipc.js)) — FIFOs under **`/run/bare-os/ipc/<name>`**; optional per-name byte caps from **`BARE_OS_IPC_CHANNEL_MAX_BYTES`**; JSON-RPC with optional token and line cap; fan-out **`fanoutPublish`/`fanoutSubscribe`**; **`stats`** includes fan-out counts when caps expose **`features.ipcFanout`**.
|
||||
- **`createBareOsIpc({ maxFifoBytes, maxChannels?, perChannelMaxBytes?, ipcRpcToken?, enableFanout?, maxJsonRpcLineBytes? })`** ([`bare-os-ipc.js`](../../packages/bare-os-booter/lib/bare-os-ipc.js)) — FIFOs under **`/run/bare-os/ipc/<name>`**; optional per-name byte caps from **`BARE_OS_IPC_CHANNEL_MAX_BYTES`**; optional global channel cap **`BARE_OS_IPC_MAX_CHANNELS`**; JSON-RPC with optional token and line cap; fan-out **`fanoutPublish`/`fanoutSubscribe`**; **`stats`** includes fan-out counts, quota telemetry, and operator backpressure snapshots when caps expose **`features.ipcFanout`**.
|
||||
- **`createVfs(drive, personalDrive, shellEnv, vfsMountRef, vfsOptions)`** → `ctx.vfs` (same `env` object as `ctx.env`). **`vfsOptions`** supply **`procSnapshot`**, dynamic **`/proc/*`** and **`/sys/*`** text (quotas JSON, **`bare_os_features`** with **`bits`**–**`bits5`** when advertised, **`bare_os_net_summary`**, **`bare_os_host_os`**, **`bare_os_sync_window`**, **`bare_os_debug`**, net/disk stubs, session stats), **`bootProfileText`**, **`sessionText`**, **`initdRunText`**, **`bootReadyJsonText`**, mount map for **`/proc/mounts`**, **`bootStartedMs`** for **`/proc/uptime`**, etc. Exposes **`vfs.watch(logicalPath)`** for Hyperdrive-backed paths when **`BARE_OS_VFS_WATCH`** is not **`0`**.
|
||||
- **`applyGuestEnv(ctx)`** then **`ensureGuestHome(ctx)`** — normalizes `ctx.identity` and seeds `/.bare/` (and a guest marker) on the personal drive.
|
||||
- **`createReadLine()`** always resolves stdio first and returns `stdout` (may be `null`) alongside `readLine` so the kernel can write to the **same** stream as the REPL (including `bare-stdio` under Pear).
|
||||
|
||||
@@ -32,6 +32,8 @@ Re-exports from `./lib/seed-rpc-methods.js`: `BARE_OS_SEED_RPC_METHODS`, `BARE_O
|
||||
|
||||
Re-exports **`BARE_OS_KERNEL_FEATURES_STOCK_WORD_*`** stock masks and **`BARE_OS_FEATURE5_*`** … **`FEATURE11_*`** from `./lib/kernel-feature-bits.js` (alongside earlier words). Re-exports wire v2 helpers from **`./lib/kernel-capability-wire.js`** (**`kernelCapabilityWords`** shape, **`BARE_OS_KERNEL_CAPABILITY_WORD_KEYS`**, …). **`BARE_OS_PROTOCOL_PACKAGE_VERSION`** is published from `./lib/protocol-meta.js` for **`ctx.bareOsProtocolPackageVersion`** comparisons.
|
||||
|
||||
When Capability word **6** (replication operator surface) is advertised, the stock booter also exposes **`ctx.bareOsReadReplicationOperatorJson()`** as a typed convenience reader over the same JSON as **`/proc/bare_os/replication`** (feature-bit alignment is documented in the capability index).
|
||||
|
||||
### 10.3 [packages/bare-os-protocol/constants.js](../../packages/bare-os-protocol/constants.js)
|
||||
|
||||
- `PROTOCOL_NAME` — `'bare-os-v1'` — Protomux channel name
|
||||
|
||||
@@ -10,20 +10,20 @@
|
||||
"shell": {
|
||||
"grammar": "line_at_a_time_subset",
|
||||
"pipes": "simulated_capture",
|
||||
"job_control": "logical_jobs_only",
|
||||
"job_control": "logical_jobs_only; kill / bareOsSendSignal negative target = pgid on shellBackgroundJobs",
|
||||
"loop_control": "BARE_OS_SHELL_LOOP_CONTROL for break continue in while for"
|
||||
},
|
||||
"vfs": {
|
||||
"fifo": "in_memory_ipc_under_run",
|
||||
"union": "BARE_OS_VFS_UNION_PREFIXES",
|
||||
"system_ro_alias": "BARE_OS_VFS_SYSTEM_RO_ALIAS",
|
||||
"bin_cache": "BARE_OS_VFS_BIN_CACHE",
|
||||
"bin_cache": "BARE_OS_VFS_BIN_CACHE; warm cache clear via ctx.bareOsInvalidateWarmReadCaches / vfs.bareOsClearWarmReadCaches; bareOsVfsBatchWrite clears on bin/lib/bare puts",
|
||||
"lib_bare_cache": "BARE_OS_VFS_LIB_BARE_CACHE with bin cache"
|
||||
},
|
||||
"synthetic_proc": {
|
||||
"syscalls_json_schema": 4,
|
||||
"process_table_schema": 4,
|
||||
"paths": ["/proc/bare_os/syscalls.json", "/proc/bare_os/process_table.json"],
|
||||
"posix_xsh_surface": "open,close,read,write,lseek,pipe,dup,dup2,fcntl documented in syscalls.json opsDetail + posixXsh.namesCsv"
|
||||
"posix_xsh_surface": "open,close,read,write,lseek,pipe,dup,dup2,fcntl,poll in syscalls.json opsDetail + posixXsh.namesCsv; ctx.bareOsSyscall fcntl+posixPoll; ctx.bareOsPosixPoll"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,32 +8,34 @@
|
||||
"/proc/bare_os/syscalls.json"
|
||||
],
|
||||
"bareOsSyscallOps": [
|
||||
"readFile",
|
||||
"writeFile",
|
||||
"readdir",
|
||||
"mkdir",
|
||||
"stat",
|
||||
"unlink",
|
||||
"chmod",
|
||||
"access",
|
||||
"chdir",
|
||||
"getcwd",
|
||||
"readlink",
|
||||
"symlink",
|
||||
"chmod",
|
||||
"exists",
|
||||
"fcntl",
|
||||
"fdatasync",
|
||||
"fsync",
|
||||
"ftruncate",
|
||||
"getcwd",
|
||||
"kill",
|
||||
"link",
|
||||
"lstat",
|
||||
"rmdir",
|
||||
"mkdir",
|
||||
"mount",
|
||||
"pathconf",
|
||||
"umount",
|
||||
"kill",
|
||||
"posixPoll",
|
||||
"readFile",
|
||||
"readdir",
|
||||
"readlink",
|
||||
"rename",
|
||||
"link",
|
||||
"access",
|
||||
"utimes",
|
||||
"rmdir",
|
||||
"stat",
|
||||
"symlink",
|
||||
"truncate",
|
||||
"ftruncate",
|
||||
"fsync",
|
||||
"fdatasync"
|
||||
"umount",
|
||||
"unlink",
|
||||
"utimes",
|
||||
"writeFile"
|
||||
],
|
||||
"utilitiesRecent": [
|
||||
"dd",
|
||||
|
||||
@@ -46,7 +46,7 @@ The personal drive still persists: guest data is **not** anonymous to the drive
|
||||
|
||||
**What this means in practice:** the **passphrase** never sits on disk; the blob stores **salt + iterations + ciphertext**. Unlocking derives a key from the passphrase, decrypts the **signing secret**, and keeps derived session state in memory. **`login --new`** creates a new account; **`login`** decrypts an existing one. Legacy v1 blobs are rejected with a message to recreate.
|
||||
|
||||
**Vault (`savevault`, `logout --save`):** selected files are snapshotted into **`/.bare/vault/`** as **AEAD-protected / sealed** blobs at rest (path hashing + keys derived from the unlocked session material in the implementation). Vault ciphertext is only as good as your **passphrase**, **backups**, and **who can replicate** your personal drive.
|
||||
**Vault (`savevault`, `logout --save`):** selected files are snapshotted into **`/.bare/vault/`** as **AEAD-protected / sealed** blobs at rest (path hashing + keys derived from the unlocked session material in the implementation). Vault ciphertext is only as good as your **passphrase**, **backups**, and **who can replicate** your personal drive. NDJSON checkpoints still append under **`/.bare/vault-rotation-audit.ndjson`**; when **`ctx.bareOsAuditLogAppendBatch`** exists, the booter mirrors **`identity_unlock`** / **`vault_save`** rows into the host audit chain as **`identity.vault_audit_mirror`** for hash-chained accountability.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ The **prompt** (`[user@host:path] > `) is applied by the booter’s readline lay
|
||||
| **Primary goal** | CI / doctor preflight without side effects | Recover from a bad rollout by skipping selected stages |
|
||||
| **Key artifact** | stderr / trace only | **`/run/bare-os/boot-rollback.marker`** JSON merged into boot policy skips |
|
||||
- **`BARE_OS_REQUIRE_CTX_API_MIN`** / **`BARE_OS_BOOT_ABI_STRICT`** — Compare **`ctx.bareOsCtxApiVersion`** to a minimum semver when strict.
|
||||
- **`BARE_OS_BOOT_TRACE=ndjson`** — One JSON object per boot phase on stderr (**`type`**, **`phase`**, **`ms`**, **`sessionId`**, **`ts`**).
|
||||
- **`BARE_OS_BOOT_TRACE=ndjson`** (or **`json`**) — One JSON object per completed boot stage on stderr (**`bootTraceSchemaVersion`**: **2**, **`type":"boot"`**, **`step`** / **`stage`** / **`phase`**, **`ms`**, plus **`sessionId`** / **`ts`** for **`ndjson`**). Guest **`os-release`**, **`motd`**, and **`banner`** lines additionally emit **`type":"bootOutput"`** records with a preview of the text (**`textPreview`**); interactive output remains **`ctx.console.log`**.
|
||||
- **`BARE_OS_KERNEL_SELFTEST`** — After boot snippets, run a short trusted **`execLine`** checklist.
|
||||
- **`BARE_OS_SELFTEST_FORMAT=tap`** — Same self-test emits **TAP** lines on stderr (CI-friendly).
|
||||
- **`BARE_OS_BOOT_ALLOWLIST=1`** — Only run boot snippet lines whose first shell token is listed in **`/etc/bare-os/boot.allow`** (plus safe builtins); distributors can start from **`etc/bare-os/boot.allow.example`** on the system image. Pair with **`BARE_OS_BOOT_STRICT`** to exit on the first disallowed or failing line.
|
||||
|
||||
@@ -63,6 +63,7 @@ The authoritative script lives in root **[`package.json`](../package.json)** und
|
||||
- **`npm run build -w bare-os-coreutils`** — Produces **`/bin`** outputs and **`man.json`**. Failure usually means stale or broken utilities, TypeScript or syntax errors, or a bad build script.
|
||||
- **`npm run build -w bare-os-bare-libs`** — Builds **`ctx.bare`** bundles. Failure means esbuild errors or bad imports under **`kernel/lib/bare/`**.
|
||||
- **`node scripts/verify-kernel-seeder-parity.mjs`** — Ensures **`kernel/`** matches **`packages/bare-os-seeder/kernel/`**. Failure means the Pear seeder would ship the wrong tree; sync before release.
|
||||
- **`node scripts/sync-holepunch-clones.mjs`** — Optional maintainer report under **`docs/audit/holepunch-clone-sync-report.json`** (local **`holepunchto_repos`** vs **`bare-module-manifest.json`** names).
|
||||
- Additional verifiers (ctx API, capability contracts, roadmap wave rows, doc links, man coverage, compatibility matrix strings, Pear import rules, bare manifest smoke) run in the same **`pretest`** chain—read **`package.json`** for the full ordered list.
|
||||
|
||||
If any step fails, scroll the **`npm test`** output for the first non-zero exit; each script prints a specific error string.
|
||||
|
||||
@@ -42,9 +42,9 @@ We borrow **names and muscle memory** from POSIX because that makes scripts port
|
||||
## 1. Executive summary: what is _not_ POSIX here
|
||||
|
||||
- **Full POSIX `sh` grammar** — Bare uses a **line-at-a-time** shell: builtins plus **`/bin`**; lists with **`;`** (outside compound statements), **`&&`**, and **`|`** pipelines; bounded **`if`** … **`fi`**; bounded **`while`** / **`for`** (iteration cap **`BARE_OS_SHELL_LOOP_MAX`**, default **10000**); optional **`break`** / **`continue`** when **`BARE_OS_SHELL_LOOP_CONTROL=1`**; bounded **`case`** … **`esac`** (**`BARE_OS_SHELL_CASE_MAX_BRANCHES`**, default **32**). Optional bounded **`$(…)`** when **`BARE_OS_SHELL_CMDSUBST=1`** (see §3). There are **no subshells** and no full **`sh`** grammar.
|
||||
- **Processes, `fork`, pipes as OS primitives** — Pipelines are **simulated** by capturing **`console.log`** (and merged **`console.error`** when **`2>&1`** is used) into the next command. With **`BARE_OS_POSIX_FD_SIM=1`**, extensions may also use **`ctx.bareOsPosixFdSimPipe`** / **`bareOsPosixFdSimRead`** / **`bareOsPosixFdSimWrite`** for bounded in-memory pipe pairs surfaced as logical FDs in **`/proc/self/fd`** (not full kernel pipes).
|
||||
- **Processes, `fork`, pipes as OS primitives** — Pipelines are **simulated** by capturing **`console.log`** (and merged **`console.error`** when **`2>&1`** is used) into the next command. With **`BARE_OS_POSIX_FD_SIM=1`**, extensions may also use **`ctx.bareOsPosixFdSimPipe`** / **`bareOsPosixFdSimRead`** / **`bareOsPosixFdSimWrite`** for bounded in-memory pipe pairs surfaced as logical FDs in **`/proc/self/fd`** (not full kernel pipes). Logical FDs support **`ctx.bareOsSyscall('fcntl', { fd, cmd: 'F_GETFL' | 'F_SETFL', arg })`** (Linux-shaped **`O_APPEND`** **`0x400`**, **`O_NONBLOCK`** **`0x800`**) and readiness via **`ctx.bareOsPosixPoll`** / **`ctx.bareOsSyscall('posixPoll', { fds, timeoutMs })`** (see **`/proc/bare_os/syscalls.json`**).
|
||||
- **`chown` / `chgrp` / real UIDs across users** — **Single-session** identity; metadata carries **uid/gid** for display and checks, not a multi-user kernel.
|
||||
- **FIFOs, `mknod`, real devices** — No kernel FIFOs or **`mknod`**; **`mkfifo`** creates **in-memory** channels under **`/run/bare-os/ipc/<name>`** (see **`bare-os-ipc.js`**).
|
||||
- **FIFOs, `mknod`, real devices** — No kernel FIFOs or **`mknod`**; **`mkfifo`** creates **in-memory** channels under **`/run/bare-os/ipc/<name>`** (see **`bare-os-ipc.js`**). Optional **`BARE_OS_IPC_MAX_CHANNELS`** caps distinct channel keys; **`metrics_live.ipcTelemetry`** mirrors FIFO depth, fan-out counts, and quota counters.
|
||||
- **Full POSIX/GNU `xargs`** — Bounded implementation: **`ctx.runBinCommand`** only; **`-0`**, **`-n`** (and **`-nN`**), optional **`-P`** parallelism with effective cap **`min(requested, BARE_OS_XARGS_MAX_PROCS, 32)`** (default **8** when env unset; sequential when **`0`**); stdin, token, and invocation caps (see **`src/xargs.js`**).
|
||||
- **`getconf` / live `sysconf`** — Fixed name table plus **`-a`**; values are Bare constants, not host kernel queries (see **`src/getconf.js`**). **`getconf NAME /path`** delegates to **`ctx.bareOsPathconf`** for **`_PC_*`** variables ( **`_PC_CHOWN_RESTRICTED`** is **`0`** under **`/mirror/`** read-only aux mounts).
|
||||
- **Byte-identical `sed` / `awk` / `grep`** — Implementations are **JavaScript**; regex and edge cases differ from GNU or strict POSIX.
|
||||
@@ -90,7 +90,7 @@ When HDMS or the booter attaches extra Hyperdrives on **`disk.auxiliaryDrives`**
|
||||
|
||||
### 2.5.1 Read-through cache (`/bin`, optional `/lib/bare`)
|
||||
|
||||
With **`BARE_OS_VFS_BIN_CACHE=1`**, the booter keeps a bounded LRU of system-drive **`readFile`** results for **`/bin/*`**. Add **`BARE_OS_VFS_LIB_BARE_CACHE=1`** to extend the same warm path to **`/lib/bare/*`** (bundle resolution hot paths). Hit, miss, and per-prefix counters appear under **`warmReadCache`** in **`/proc/bare_os/metrics_live.json`** (and the coalesced **`ctx.bareOsReadProcMetricsLive()`** view) when the cache is enabled.
|
||||
With **`BARE_OS_VFS_BIN_CACHE=1`**, the booter keeps a bounded LRU of system-drive **`readFile`** results for **`/bin/*`**. Add **`BARE_OS_VFS_LIB_BARE_CACHE=1`** to extend the same warm path to **`/lib/bare/*`** (bundle resolution hot paths). Hit, miss, and per-prefix counters appear under **`warmReadCache`** in **`/proc/bare_os/metrics_live.json`** (and the coalesced **`ctx.bareOsReadProcMetricsLive()`** view) when the cache is enabled. After live replication or policy changes, call **`ctx.bareOsInvalidateWarmReadCaches(reason)`** (or rely on **`ctx.bareOsVfsBatchWrite`** when batch **`put`** paths touch **`bin/`** or **`lib/bare/`**) so operators do not read stale bytes from warm entries.
|
||||
|
||||
### 2.6 Copy and move
|
||||
|
||||
|
||||
+2
-2
@@ -139,10 +139,10 @@ const CONF = {
|
||||
_PC_2_SYMLINKS: '1',
|
||||
/** Comma-separated `ctx.bareOsSyscall` op names implemented in stock booter. */
|
||||
BARE_OS_SYSCALL_OPS:
|
||||
'readFile,writeFile,readdir,mkdir,stat,unlink,chmod,chdir,getcwd,readlink,symlink,exists,lstat,rmdir,mount,umount,kill,rename,link,access,utimes,truncate,ftruncate,fsync,fdatasync,pathconf',
|
||||
'access,chdir,chmod,exists,fcntl,fdatasync,fsync,ftruncate,getcwd,kill,link,lstat,mkdir,mount,pathconf,posixPoll,readFile,readdir,readlink,rename,rmdir,stat,symlink,truncate,umount,unlink,utimes,writeFile',
|
||||
/** POSIX.1 XSH-style names documented in `/proc/bare_os/syscalls.json` opsDetail (not separate ctx ops). */
|
||||
BARE_OS_POSIX_XSH_OPS:
|
||||
'open,close,read,write,lseek,pipe,dup,dup2,fcntl',
|
||||
'open,close,read,write,lseek,pipe,dup,dup2,fcntl,poll',
|
||||
/** Encodings accepted by `/bin/iconv` (subset; case-insensitive names). */
|
||||
BARE_OS_ICONV_ENCODINGS: 'UTF-8,ISO-8859-1,UTF-16LE,UTF-16BE',
|
||||
/** Synthetic process table JSON path (logical VFS). */
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"schemaVersion": 4,
|
||||
"ctxApiVersion": "1.34.0",
|
||||
"ctxApiVersion": "1.36.0",
|
||||
"posixProfile": {
|
||||
"id": "bare-os-posix-like",
|
||||
"version": "1.0.1"
|
||||
"version": "1.0.3"
|
||||
},
|
||||
"ops": ["readFile", "writeFile"],
|
||||
"opsDetail": [
|
||||
@@ -21,7 +21,7 @@
|
||||
"posixXsh": {
|
||||
"schema": 1,
|
||||
"note": "POSIX.1 XSH names in opsDetail with posixAlignment; not ctx.bareOsSyscall op strings.",
|
||||
"namesCsv": "open,close,read,write,lseek,pipe,dup,dup2,fcntl"
|
||||
"namesCsv": "open,close,read,write,lseek,pipe,dup,dup2,fcntl,poll"
|
||||
},
|
||||
"errnoHints": {
|
||||
"ENOENT": 2,
|
||||
|
||||
+74
-12
@@ -44,8 +44,8 @@ async function invokeCtxBootHooks(ctx, ev) {
|
||||
* BARE_OS_ONBOOT (newline-separated) or, if unset, every such line from /etc/bare-os/onboot
|
||||
* in file order (trusted). Then readLine yields EOF.
|
||||
*
|
||||
* BARE_OS_BOOT_TRACE=json logs one JSON object per boot stage on stderr: **`step`** (preferred), **`stage`**, legacy **`phase`** mirror, **`ms`**, **`bootTraceSchemaVersion`** (2).
|
||||
* BARE_OS_BOOT_TRACE=ndjson logs **`bootTraceSchemaVersion`**: **2** lines with **`type":"boot"`**, **`step`**, **`stage`**, **`phase`** (mirror), **`ms`**, **`sessionId`**, **`ts`**.
|
||||
* BARE_OS_BOOT_TRACE=json logs one JSON object per boot stage on stderr: **`step`** (preferred), **`stage`**, legacy **`phase`** mirror, **`ms`**, **`bootTraceSchemaVersion`** (2). Guest **`os-release`**, **`motd`**, and **`banner`** lines also emit **`type":"bootOutput"`** previews on stderr (stdout body still goes through **`ctx.console.log`** only).
|
||||
* BARE_OS_BOOT_TRACE=ndjson logs **`bootTraceSchemaVersion`**: **2** lines with **`type":"boot"`**, **`step`**, **`stage`**, **`phase`** (mirror), **`ms`**, **`sessionId`**, **`ts`**, plus **`type":"bootOutput"`** for guest-visible text previews.
|
||||
*
|
||||
* BARE_OS_BOOT_MINIMAL=1 or true: skip profile rc, rc, rc.d, rc.local, kernel.d (recovery shell).
|
||||
* BARE_OS_BOOT_SAFE_MODE=1 or true: after boot policy merge, skip rc.d, kernel.ext.d, onboot (lighter recovery).
|
||||
@@ -123,6 +123,62 @@ function isBootTraceNdjson(ctx) {
|
||||
return ctx.env && ctx.env.BARE_OS_BOOT_TRACE === 'ndjson'
|
||||
}
|
||||
|
||||
/**
|
||||
* Guest-visible boot text via **`ctx.console.log`** only (booter session / var-log mirror).
|
||||
* When **`BARE_OS_BOOT_TRACE`** is **`json`** or **`ndjson`**, append one stderr JSON line per emission (**`type":"bootOutput"`**, preview-capped) for CI and hosts.
|
||||
* @param {Record<string, unknown>} ctx
|
||||
* @param {string} step
|
||||
* @param {string} text
|
||||
*/
|
||||
function bootGuestOutLine(ctx, step, text) {
|
||||
const line = String(text)
|
||||
const out = ctx.console
|
||||
if (out && typeof out.log === 'function') out.log(line)
|
||||
else if (
|
||||
globalThis.console &&
|
||||
typeof globalThis.console.log === 'function'
|
||||
) {
|
||||
globalThis.console.log(line)
|
||||
}
|
||||
if (!wantBootTrace(ctx)) return
|
||||
const prev =
|
||||
isBootTraceNdjson(ctx) && line.length > 400
|
||||
? line.slice(0, 400) + '\u2026'
|
||||
: isBootTraceJson(ctx) && line.length > 200
|
||||
? line.slice(0, 200) + '\u2026'
|
||||
: line
|
||||
const errFn =
|
||||
out && typeof out.error === 'function'
|
||||
? out.error.bind(out)
|
||||
: globalThis.console && typeof globalThis.console.error === 'function'
|
||||
? globalThis.console.error.bind(globalThis.console)
|
||||
: null
|
||||
if (!errFn) return
|
||||
if (isBootTraceNdjson(ctx)) {
|
||||
errFn(
|
||||
JSON.stringify({
|
||||
type: 'bootOutput',
|
||||
bootTraceSchemaVersion: 2,
|
||||
step,
|
||||
stream: 'stdout',
|
||||
sessionId: String((ctx.env && ctx.env.BARE_OS_SESSION_ID) || ''),
|
||||
ts: Date.now(),
|
||||
textPreview: prev
|
||||
})
|
||||
)
|
||||
} else if (isBootTraceJson(ctx)) {
|
||||
errFn(
|
||||
JSON.stringify({
|
||||
bootTraceSchemaVersion: 2,
|
||||
type: 'bootOutput',
|
||||
step,
|
||||
stream: 'stdout',
|
||||
textPreview: prev
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Record<string, unknown>} ctx
|
||||
*/
|
||||
@@ -1792,12 +1848,14 @@ async function runRcLines(ctx, text) {
|
||||
* @param {Record<string, unknown>} ctx
|
||||
*/
|
||||
async function printOsRelease(ctx) {
|
||||
const { drive, b4a, console } = ctx
|
||||
const { drive, b4a } = ctx
|
||||
try {
|
||||
const rel = await drive.get('/etc/os-release')
|
||||
if (rel) console.log(b4a.toString(rel))
|
||||
if (rel) bootGuestOutLine(ctx, 'os-release', b4a.toString(rel))
|
||||
} catch (e) {
|
||||
console.error((e && e.message) || String(e))
|
||||
const c = ctx.console
|
||||
if (c && typeof c.error === 'function')
|
||||
c.error((e && e.message) || String(e))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1805,12 +1863,14 @@ async function printOsRelease(ctx) {
|
||||
* @param {Record<string, unknown>} ctx
|
||||
*/
|
||||
async function printMotd(ctx) {
|
||||
const { drive, b4a, console } = ctx
|
||||
const { drive, b4a } = ctx
|
||||
try {
|
||||
const motd = await drive.get('/etc/motd')
|
||||
if (motd) console.log(b4a.toString(motd).trimEnd())
|
||||
if (motd) bootGuestOutLine(ctx, 'motd', b4a.toString(motd).trimEnd())
|
||||
} catch (e) {
|
||||
console.error((e && e.message) || String(e))
|
||||
const c = ctx.console
|
||||
if (c && typeof c.error === 'function')
|
||||
c.error((e && e.message) || String(e))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2444,12 +2504,12 @@ async function runBareOsRcDir(ctx) {
|
||||
* @param {Record<string, unknown>} ctx
|
||||
*/
|
||||
async function printSessionBanner(ctx) {
|
||||
const { drive, b4a, console } = ctx
|
||||
const { drive, b4a } = ctx
|
||||
for (const p of ['/etc/bare-os/banner', '/etc/issue']) {
|
||||
try {
|
||||
const buf = await drive.get(p)
|
||||
if (buf) {
|
||||
console.log(b4a.toString(buf).trimEnd())
|
||||
bootGuestOutLine(ctx, 'banner', b4a.toString(buf).trimEnd())
|
||||
return
|
||||
}
|
||||
} catch {
|
||||
@@ -2459,12 +2519,14 @@ async function printSessionBanner(ctx) {
|
||||
const defaultBanner =
|
||||
'Bare operating system — guest session (login [--new] <passphrase> to unlock) | shell: cd, export, if/fi, && || ;, |, &, jobs/fg, <<<, exit | services: systemctl list-units, journalctl -u UNIT | try: help, ls /bin, crontab -l'
|
||||
if (ctx.bareOsSkipRepl) {
|
||||
console.log(
|
||||
bootGuestOutLine(
|
||||
ctx,
|
||||
'banner',
|
||||
'Bare operating system — non-interactive session (BARE_OS_SKIP_REPL).'
|
||||
)
|
||||
return
|
||||
}
|
||||
console.log(defaultBanner)
|
||||
bootGuestOutLine(ctx, 'banner', defaultBanner)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+216
-216
@@ -7,18 +7,18 @@
|
||||
"b4a"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/safetyCatch.js",
|
||||
"keys": [
|
||||
"safetyCatch"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/hypercoreIdEncoding.js",
|
||||
"keys": [
|
||||
"hypercoreIdEncoding"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/safetyCatch.js",
|
||||
"keys": [
|
||||
"safetyCatch"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/compactEncoding.js",
|
||||
"keys": [
|
||||
@@ -38,9 +38,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareEncoding.js",
|
||||
"path": "/lib/bare/bundles/barePath.js",
|
||||
"keys": [
|
||||
"bareEncoding"
|
||||
"barePath"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -50,9 +50,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/barePath.js",
|
||||
"path": "/lib/bare/bundles/bareEncoding.js",
|
||||
"keys": [
|
||||
"barePath"
|
||||
"bareEncoding"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -73,12 +73,6 @@
|
||||
"bareAnsiEscapes"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareAddonResolve.js",
|
||||
"keys": [
|
||||
"bareAddonResolve"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareReadline.js",
|
||||
"keys": [
|
||||
@@ -91,6 +85,18 @@
|
||||
"bareCrypto"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareAddonResolve.js",
|
||||
"keys": [
|
||||
"bareAddonResolve"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareAtomics.js",
|
||||
"keys": [
|
||||
"bareAtomics"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareAsyncHooks.js",
|
||||
"keys": [
|
||||
@@ -103,30 +109,24 @@
|
||||
"bareAppKit"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareAssert.js",
|
||||
"keys": [
|
||||
"bareAssert"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareApk.js",
|
||||
"keys": [
|
||||
"bareApk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareAtomics.js",
|
||||
"keys": [
|
||||
"bareAtomics"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/fetch.js",
|
||||
"keys": [
|
||||
"fetch"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareAssert.js",
|
||||
"keys": [
|
||||
"bareAssert"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareBmp.js",
|
||||
"keys": [
|
||||
@@ -145,6 +145,12 @@
|
||||
"bareBuffer"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareBundleEvaluate.js",
|
||||
"keys": [
|
||||
"bareBundleEvaluate"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareBundle.js",
|
||||
"keys": [
|
||||
@@ -157,48 +163,42 @@
|
||||
"bareBluetoothApple"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareBundleEvaluate.js",
|
||||
"keys": [
|
||||
"bareBundleEvaluate"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareBoot.js",
|
||||
"keys": [
|
||||
"bareBoot"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareConsole.js",
|
||||
"keys": [
|
||||
"bareConsole"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareBundleId.js",
|
||||
"keys": [
|
||||
"bareBundleId"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareConsole.js",
|
||||
"keys": [
|
||||
"bareConsole"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareDebugLog.js",
|
||||
"keys": [
|
||||
"bareDebugLog"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareDelta.js",
|
||||
"keys": [
|
||||
"bareDelta"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareChannel.js",
|
||||
"keys": [
|
||||
"bareChannel"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareDelta.js",
|
||||
"keys": [
|
||||
"bareDelta"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareDaemon.js",
|
||||
"keys": [
|
||||
@@ -229,6 +229,12 @@
|
||||
"bareExif"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareDgram.js",
|
||||
"keys": [
|
||||
"bareDgram"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareCov.js",
|
||||
"keys": [
|
||||
@@ -241,12 +247,6 @@
|
||||
"bareFfmpeg"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareDgram.js",
|
||||
"keys": [
|
||||
"bareDgram"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareFfmpegEncodings.js",
|
||||
"keys": [
|
||||
@@ -254,9 +254,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareFormData.js",
|
||||
"path": "/lib/bare/bundles/bareGif.js",
|
||||
"keys": [
|
||||
"bareFormData"
|
||||
"bareGif"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -266,9 +266,15 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareGif.js",
|
||||
"path": "/lib/bare/bundles/bareFormData.js",
|
||||
"keys": [
|
||||
"bareGif"
|
||||
"bareFormData"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareHrtime.js",
|
||||
"keys": [
|
||||
"bareHrtime"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -289,12 +295,6 @@
|
||||
"bareFileLogger"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareHrtime.js",
|
||||
"keys": [
|
||||
"bareHrtime"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareFs.js",
|
||||
"keys": [
|
||||
@@ -319,18 +319,18 @@
|
||||
"bareImageResample"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareInspect.js",
|
||||
"keys": [
|
||||
"bareInspect"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareHttp1.js",
|
||||
"keys": [
|
||||
"bareHttp1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareInspect.js",
|
||||
"keys": [
|
||||
"bareInspect"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareHttps.js",
|
||||
"keys": [
|
||||
@@ -355,6 +355,12 @@
|
||||
"bareIpc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareInspector.js",
|
||||
"keys": [
|
||||
"bareInspector"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareLief.js",
|
||||
"keys": [
|
||||
@@ -374,9 +380,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareInspector.js",
|
||||
"path": "/lib/bare/bundles/bareModule.js",
|
||||
"keys": [
|
||||
"bareInspector"
|
||||
"bareModule"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -385,60 +391,30 @@
|
||||
"bareMake"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareModuleResolve.js",
|
||||
"keys": [
|
||||
"bareModuleResolve"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareModuleLexer.js",
|
||||
"keys": [
|
||||
"bareModuleLexer"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareModule.js",
|
||||
"keys": [
|
||||
"bareModule"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareModuleTraverse.js",
|
||||
"keys": [
|
||||
"bareModuleTraverse"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareNdk.js",
|
||||
"keys": [
|
||||
"bareNdk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareModuleResolve.js",
|
||||
"keys": [
|
||||
"bareModuleResolve"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareNative.js",
|
||||
"keys": [
|
||||
"bareNative"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareNodeFetch.js",
|
||||
"keys": [
|
||||
"bareNodeFetch"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareOpen.js",
|
||||
"keys": [
|
||||
"bareOpen"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareOs.js",
|
||||
"keys": [
|
||||
"bareOs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareMedia.js",
|
||||
"keys": [
|
||||
@@ -446,15 +422,33 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareNet.js",
|
||||
"path": "/lib/bare/bundles/bareModuleTraverse.js",
|
||||
"keys": [
|
||||
"bareNet"
|
||||
"bareModuleTraverse"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/barePackDrive.js",
|
||||
"path": "/lib/bare/bundles/bareNodeFetch.js",
|
||||
"keys": [
|
||||
"barePackDrive"
|
||||
"bareNodeFetch"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareOs.js",
|
||||
"keys": [
|
||||
"bareOs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareOpen.js",
|
||||
"keys": [
|
||||
"bareOpen"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareNet.js",
|
||||
"keys": [
|
||||
"bareNet"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -470,9 +464,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/barePng.js",
|
||||
"path": "/lib/bare/bundles/barePackDrive.js",
|
||||
"keys": [
|
||||
"barePng"
|
||||
"barePackDrive"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -481,12 +475,30 @@
|
||||
"barePipe"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/barePng.js",
|
||||
"keys": [
|
||||
"barePng"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareNodeRuntime.js",
|
||||
"keys": [
|
||||
"bareNodeRuntime"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/barePrebuild.js",
|
||||
"keys": [
|
||||
"barePrebuild"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareDev.js",
|
||||
"keys": [
|
||||
"bareDev"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/barePunycode.js",
|
||||
"keys": [
|
||||
@@ -494,9 +506,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/barePrebuild.js",
|
||||
"path": "/lib/bare/bundles/bareProcess.js",
|
||||
"keys": [
|
||||
"barePrebuild"
|
||||
"bareProcess"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -517,108 +529,102 @@
|
||||
"bareRealm"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareDev.js",
|
||||
"keys": [
|
||||
"bareDev"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareProcess.js",
|
||||
"keys": [
|
||||
"bareProcess"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/barePromClient.js",
|
||||
"keys": [
|
||||
"barePromClient"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareRuntime.js",
|
||||
"keys": [
|
||||
"bareRuntime"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareRepl.js",
|
||||
"keys": [
|
||||
"bareRepl"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareSdl.js",
|
||||
"keys": [
|
||||
"bareSdl"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareSemver.js",
|
||||
"keys": [
|
||||
"bareSemver"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareRpc.js",
|
||||
"keys": [
|
||||
"bareRpc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareSignals.js",
|
||||
"keys": [
|
||||
"bareSignals"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareRun.js",
|
||||
"keys": [
|
||||
"bareRun"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareSemver.js",
|
||||
"keys": [
|
||||
"bareSemver"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareRepl.js",
|
||||
"keys": [
|
||||
"bareRepl"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/barePromClient.js",
|
||||
"keys": [
|
||||
"barePromClient"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareSignals.js",
|
||||
"keys": [
|
||||
"bareSignals"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareSidecar.js",
|
||||
"keys": [
|
||||
"bareSidecar"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareStorage.js",
|
||||
"keys": [
|
||||
"bareStorage"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareStream.js",
|
||||
"keys": [
|
||||
"bareStream"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareSvg.js",
|
||||
"keys": [
|
||||
"bareSvg"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareStdio.js",
|
||||
"keys": [
|
||||
"bareStdio"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareStringDecoder.js",
|
||||
"keys": [
|
||||
"bareStringDecoder"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareStream.js",
|
||||
"keys": [
|
||||
"bareStream"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareStorage.js",
|
||||
"keys": [
|
||||
"bareStorage"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareStructuredClone.js",
|
||||
"keys": [
|
||||
"bareStructuredClone"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareStdio.js",
|
||||
"keys": [
|
||||
"bareStdio"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareSvg.js",
|
||||
"keys": [
|
||||
"bareSvg"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareTiff.js",
|
||||
"keys": [
|
||||
"bareTiff"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareSystemLogger.js",
|
||||
"keys": [
|
||||
@@ -638,9 +644,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareTiff.js",
|
||||
"path": "/lib/bare/bundles/bareTpl.js",
|
||||
"keys": [
|
||||
"bareTiff"
|
||||
"bareTpl"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -661,30 +667,12 @@
|
||||
"bareTimers"
|
||||
]
|
||||
},
|
||||
{
|
||||
"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/bareUnpack.js",
|
||||
"keys": [
|
||||
"bareUnpack"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareTls.js",
|
||||
"keys": [
|
||||
@@ -697,6 +685,12 @@
|
||||
"bareTty"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareUnpack.js",
|
||||
"keys": [
|
||||
"bareUnpack"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareV8.js",
|
||||
"keys": [
|
||||
@@ -704,9 +698,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareUnionBundle.js",
|
||||
"path": "/lib/bare/bundles/bareUiKit.js",
|
||||
"keys": [
|
||||
"bareUnionBundle"
|
||||
"bareUiKit"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -715,6 +709,12 @@
|
||||
"bareWalkHandles"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareUnionBundle.js",
|
||||
"keys": [
|
||||
"bareUnionBundle"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareVm.js",
|
||||
"keys": [
|
||||
@@ -727,30 +727,30 @@
|
||||
"bareWebKit"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareWebp.js",
|
||||
"keys": [
|
||||
"bareWebp"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareWebKitGtk.js",
|
||||
"keys": [
|
||||
"bareWebKitGtk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareUtils.js",
|
||||
"keys": [
|
||||
"bareUtils"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareWebp.js",
|
||||
"keys": [
|
||||
"bareWebp"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareWhich.js",
|
||||
"keys": [
|
||||
"bareWhich"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareWebKitGtk.js",
|
||||
"keys": [
|
||||
"bareWebKitGtk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareWinUi.js",
|
||||
"keys": [
|
||||
@@ -769,18 +769,18 @@
|
||||
"bareV8ToIstanbul"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareZlib.js",
|
||||
"keys": [
|
||||
"bareZlib"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareWs.js",
|
||||
"keys": [
|
||||
"bareWs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareZlib.js",
|
||||
"keys": [
|
||||
"bareZlib"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareZmq.js",
|
||||
"keys": [
|
||||
@@ -1595,8 +1595,8 @@
|
||||
],
|
||||
"bundleProvenance": {
|
||||
"schemaVersion": 1,
|
||||
"generatedAt": "2026-04-05T02:38:13.070Z",
|
||||
"gitCommit": "8a6c1e7b0947b1a70fc837892c76d67222e44119",
|
||||
"generatedAt": "2026-04-05T03:03:09.352Z",
|
||||
"gitCommit": "985eadfda39fa8a847791ff5c7f8f40d4434d9ac",
|
||||
"nodeVersion": "v22.22.0",
|
||||
"bundleTier": "all",
|
||||
"normativeManifest": "packages/bare-os-booter/lib/bare-module-manifest.json",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"schema": 1,
|
||||
"atMs": 1775356692404,
|
||||
"atMs": 1775358188597,
|
||||
"commands": [
|
||||
"arch",
|
||||
"awk",
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
* BARE_OS_ONBOOT (newline-separated) or, if unset, every such line from /etc/bare-os/onboot
|
||||
* in file order (trusted). Then readLine yields EOF.
|
||||
*
|
||||
* BARE_OS_BOOT_TRACE=json logs one JSON object per boot stage on stderr: **`step`** (preferred), **`stage`**, legacy **`phase`** mirror, **`ms`**, **`bootTraceSchemaVersion`** (2).
|
||||
* BARE_OS_BOOT_TRACE=ndjson logs **`bootTraceSchemaVersion`**: **2** lines with **`type":"boot"`**, **`step`**, **`stage`**, **`phase`** (mirror), **`ms`**, **`sessionId`**, **`ts`**.
|
||||
* BARE_OS_BOOT_TRACE=json logs one JSON object per boot stage on stderr: **`step`** (preferred), **`stage`**, legacy **`phase`** mirror, **`ms`**, **`bootTraceSchemaVersion`** (2). Guest **`os-release`**, **`motd`**, and **`banner`** lines also emit **`type":"bootOutput"`** previews on stderr (stdout body still goes through **`ctx.console.log`** only).
|
||||
* BARE_OS_BOOT_TRACE=ndjson logs **`bootTraceSchemaVersion`**: **2** lines with **`type":"boot"`**, **`step`**, **`stage`**, **`phase`** (mirror), **`ms`**, **`sessionId`**, **`ts`**, plus **`type":"bootOutput"`** for guest-visible text previews.
|
||||
*
|
||||
* BARE_OS_BOOT_MINIMAL=1 or true: skip profile rc, rc, rc.d, rc.local, kernel.d (recovery shell).
|
||||
* BARE_OS_BOOT_SAFE_MODE=1 or true: after boot policy merge, skip rc.d, kernel.ext.d, onboot (lighter recovery).
|
||||
@@ -93,6 +93,62 @@ function isBootTraceNdjson(ctx) {
|
||||
return ctx.env && ctx.env.BARE_OS_BOOT_TRACE === 'ndjson'
|
||||
}
|
||||
|
||||
/**
|
||||
* Guest-visible boot text via **`ctx.console.log`** only (booter session / var-log mirror).
|
||||
* When **`BARE_OS_BOOT_TRACE`** is **`json`** or **`ndjson`**, append one stderr JSON line per emission (**`type":"bootOutput"`**, preview-capped) for CI and hosts.
|
||||
* @param {Record<string, unknown>} ctx
|
||||
* @param {string} step
|
||||
* @param {string} text
|
||||
*/
|
||||
function bootGuestOutLine(ctx, step, text) {
|
||||
const line = String(text)
|
||||
const out = ctx.console
|
||||
if (out && typeof out.log === 'function') out.log(line)
|
||||
else if (
|
||||
globalThis.console &&
|
||||
typeof globalThis.console.log === 'function'
|
||||
) {
|
||||
globalThis.console.log(line)
|
||||
}
|
||||
if (!wantBootTrace(ctx)) return
|
||||
const prev =
|
||||
isBootTraceNdjson(ctx) && line.length > 400
|
||||
? line.slice(0, 400) + '\u2026'
|
||||
: isBootTraceJson(ctx) && line.length > 200
|
||||
? line.slice(0, 200) + '\u2026'
|
||||
: line
|
||||
const errFn =
|
||||
out && typeof out.error === 'function'
|
||||
? out.error.bind(out)
|
||||
: globalThis.console && typeof globalThis.console.error === 'function'
|
||||
? globalThis.console.error.bind(globalThis.console)
|
||||
: null
|
||||
if (!errFn) return
|
||||
if (isBootTraceNdjson(ctx)) {
|
||||
errFn(
|
||||
JSON.stringify({
|
||||
type: 'bootOutput',
|
||||
bootTraceSchemaVersion: 2,
|
||||
step,
|
||||
stream: 'stdout',
|
||||
sessionId: String((ctx.env && ctx.env.BARE_OS_SESSION_ID) || ''),
|
||||
ts: Date.now(),
|
||||
textPreview: prev
|
||||
})
|
||||
)
|
||||
} else if (isBootTraceJson(ctx)) {
|
||||
errFn(
|
||||
JSON.stringify({
|
||||
bootTraceSchemaVersion: 2,
|
||||
type: 'bootOutput',
|
||||
step,
|
||||
stream: 'stdout',
|
||||
textPreview: prev
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Record<string, unknown>} ctx
|
||||
*/
|
||||
@@ -1762,12 +1818,14 @@ async function runRcLines(ctx, text) {
|
||||
* @param {Record<string, unknown>} ctx
|
||||
*/
|
||||
async function printOsRelease(ctx) {
|
||||
const { drive, b4a, console } = ctx
|
||||
const { drive, b4a } = ctx
|
||||
try {
|
||||
const rel = await drive.get('/etc/os-release')
|
||||
if (rel) console.log(b4a.toString(rel))
|
||||
if (rel) bootGuestOutLine(ctx, 'os-release', b4a.toString(rel))
|
||||
} catch (e) {
|
||||
console.error((e && e.message) || String(e))
|
||||
const c = ctx.console
|
||||
if (c && typeof c.error === 'function')
|
||||
c.error((e && e.message) || String(e))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1775,12 +1833,14 @@ async function printOsRelease(ctx) {
|
||||
* @param {Record<string, unknown>} ctx
|
||||
*/
|
||||
async function printMotd(ctx) {
|
||||
const { drive, b4a, console } = ctx
|
||||
const { drive, b4a } = ctx
|
||||
try {
|
||||
const motd = await drive.get('/etc/motd')
|
||||
if (motd) console.log(b4a.toString(motd).trimEnd())
|
||||
if (motd) bootGuestOutLine(ctx, 'motd', b4a.toString(motd).trimEnd())
|
||||
} catch (e) {
|
||||
console.error((e && e.message) || String(e))
|
||||
const c = ctx.console
|
||||
if (c && typeof c.error === 'function')
|
||||
c.error((e && e.message) || String(e))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2414,12 +2474,12 @@ async function runBareOsRcDir(ctx) {
|
||||
* @param {Record<string, unknown>} ctx
|
||||
*/
|
||||
async function printSessionBanner(ctx) {
|
||||
const { drive, b4a, console } = ctx
|
||||
const { drive, b4a } = ctx
|
||||
for (const p of ['/etc/bare-os/banner', '/etc/issue']) {
|
||||
try {
|
||||
const buf = await drive.get(p)
|
||||
if (buf) {
|
||||
console.log(b4a.toString(buf).trimEnd())
|
||||
bootGuestOutLine(ctx, 'banner', b4a.toString(buf).trimEnd())
|
||||
return
|
||||
}
|
||||
} catch {
|
||||
@@ -2429,12 +2489,14 @@ async function printSessionBanner(ctx) {
|
||||
const defaultBanner =
|
||||
'Bare operating system — guest session (login [--new] <passphrase> to unlock) | shell: cd, export, if/fi, && || ;, |, &, jobs/fg, <<<, exit | services: systemctl list-units, journalctl -u UNIT | try: help, ls /bin, crontab -l'
|
||||
if (ctx.bareOsSkipRepl) {
|
||||
console.log(
|
||||
bootGuestOutLine(
|
||||
ctx,
|
||||
'banner',
|
||||
'Bare operating system — non-interactive session (BARE_OS_SKIP_REPL).'
|
||||
)
|
||||
return
|
||||
}
|
||||
console.log(defaultBanner)
|
||||
bootGuestOutLine(ctx, 'banner', defaultBanner)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -15,6 +15,8 @@ Authoritative **version alignment** with protocol and telemetry schema numbers l
|
||||
|
||||
| Version | Booter (workspace) | Notes |
|
||||
| ------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| 1.36.0 | 0.1.0 | **`ctx.bareOsSendSignal`** negative target = synthetic **process group** (shell job **`pgid`**); **`ctx.bareOsReadReplicationOperatorJson`**, **`ctx.bareOsInvalidateWarmReadCaches`**, **`vfs.bareOsClearWarmReadCaches`**; **`bareOsVfsBatchWrite`** clears warm **`/bin`** / **`/lib/bare`** caches on matching puts; **`metrics_live.ipcTelemetry`** (FIFO quota / counters via **`BARE_OS_IPC_MAX_CHANNELS`**); swarm **`shouldAttemptPeer`** honors **`BARE_OS_PEER_ALLOWLIST_HEX`**; **`bareOsPathconf`** union/mirror **`_PC_NO_TRUNC`** / **`_PC_CHOWN_RESTRICTED`**; boot-ready **`subsystems.bareStdlib.resolutionWallMs`**. |
|
||||
| 1.35.0 | 0.1.0 | **`ctx.bareOsSyscall('fcntl', …)`** (**`F_GETFL`** / **`F_SETFL`**, **`O_APPEND`** **`0x400`**, **`O_NONBLOCK`** **`0x800`**) + **`bareOsLogicalFdFlags`**; **`ctx.bareOsPosixPoll`** / **`bareOsPosixPollProbe`** + **`bareOsSyscall('posixPoll', …)`**; **`bareOsPosixFdSimRead`** honors **`O_NONBLOCK`** (**`EAGAIN`** when empty); **`/proc/bare_os/syscalls.json`** **`posixXsh.namesCsv`** includes **`poll`**; **`getconf`** **`BARE_OS_SYSCALL_OPS`** / **`BARE_OS_POSIX_XSH_OPS`** updated; protocol **`BARE_OS_POSIX_PROFILE_VERSION` `1.0.2`**. |
|
||||
| 1.34.0 | 0.1.0 | **`ctx.bareOsPosixFdSimPipe`** / **`bareOsPosixFdSimRead`** / **`Write`** / **`Dup`** when **`BARE_OS_POSIX_FD_SIM`**; **`ctx.bareOsSigaction`** (**`IGNORE`** / **`DEFAULT`**) + **`process_table.sigactionSurface`**; expanded POSIX signal names; **`ctx.bareOsWasmKernelInstantiate`** (bounded **`Memory`** + timeout); **`/proc/bare_os/syscalls.json`** **`posixXsh`** + XSH **`opsDetail`** rows; **`bareOsPathconf`** **`_PC_*`** coverage; protocol **`BARE_OS_POSIX_PROFILE_VERSION` `1.0.1`**. |
|
||||
| 1.33.0 | 0.1.0 | **`ctx.bareOsListMirrorMounts`**, **`bareOsVaultAuditSummary`**, **`bareOsWasmKernelCompile`** ( **`BARE_OS_WASM_KERNEL`** ); **`/proc/bare_os/syscalls.json`** documents **`fdModel.posixLike`** mapping; **`process_table`** **`exitStatusModel`**; pipeline exit = last stage ( **`BARE_OS_SHELL_PIPEFAIL`** for bash-like early stop); script errors stop pipeline; **`corestore_snapshot`** operator proc id; Pear inspect + **`bareOsDiagnosticsEmit`**; IPC **`stats.ipcBackpressure`**. |
|
||||
| 1.32.1 | 0.1.0 | **`/proc/bare_os/process_table.json`** schema **4**: **`signalRouting`**, **`initdBinding`**, initd units as logical pids **5100+**; **`bareOsSendSignal`** accepts **4100–8999** (shell jobs / initd) without session exit. |
|
||||
|
||||
@@ -806,8 +806,17 @@ async function executeKernel(disk, store, swarm, initSource) {
|
||||
syncWindowProcState.active ? '1' : '0'
|
||||
|
||||
const ipcPerChannelMax = parseIpcChannelMaxBytesMap(shellEnv)
|
||||
const ipcMaxChannelsRaw = Number.parseInt(
|
||||
String(shellEnv.BARE_OS_IPC_MAX_CHANNELS || '').trim(),
|
||||
10
|
||||
)
|
||||
const ipcMaxChannelsResolved =
|
||||
Number.isFinite(ipcMaxChannelsRaw) && ipcMaxChannelsRaw > 0
|
||||
? Math.min(65536, ipcMaxChannelsRaw)
|
||||
: undefined
|
||||
const bareOsIpc = createBareOsIpc({
|
||||
maxFifoBytes: ipcMaxResolved,
|
||||
maxChannels: ipcMaxChannelsResolved,
|
||||
ipcRpcToken: ipcRpcTok,
|
||||
enableFanout: ipcFanoutOn,
|
||||
maxJsonRpcLineBytes: ipcJsonMaxResolved,
|
||||
@@ -2152,7 +2161,11 @@ async function executeKernel(disk, store, swarm, initSource) {
|
||||
ndjsonSchema: 'docs/schemas/telemetry-ndjson-record.schema.json',
|
||||
otelSchema: 'docs/schemas/otel-bare-os-jsonl.schema.json'
|
||||
},
|
||||
warmReadCache: warmReadCacheStatsRef.current
|
||||
warmReadCache: warmReadCacheStatsRef.current,
|
||||
ipcTelemetry:
|
||||
bareOsIpc && typeof bareOsIpc.stats === 'function'
|
||||
? bareOsIpc.stats()
|
||||
: null
|
||||
})}\n`
|
||||
}
|
||||
return metricsLiveCache.text
|
||||
@@ -2251,6 +2264,7 @@ async function executeKernel(disk, store, swarm, initSource) {
|
||||
|
||||
/** @type {Record<string, unknown>} */
|
||||
const bareLibrary = {}
|
||||
const bareCtxResolutionT0 = Date.now()
|
||||
if (bareOsBareModulesEnabled(shellEnv)) {
|
||||
await maybeMergeBareFromDrive(shellEnv, vfs, bareLibrary)
|
||||
if (bareOsBareHostImportsEnabled(shellEnv)) {
|
||||
@@ -2260,6 +2274,21 @@ async function executeKernel(disk, store, swarm, initSource) {
|
||||
if (lockWarn.length) bootReadyStateRef.bareModuleLockWarnings = lockWarn
|
||||
primeGlobalFetchFromBareLibrary(bareLibrary)
|
||||
}
|
||||
Object.assign(bootReadyStateRef.subsystems, {
|
||||
bareStdlib: bareOsBareModulesEnabled(shellEnv)
|
||||
? {
|
||||
ready: true,
|
||||
atMs: Date.now(),
|
||||
resolutionWallMs: Date.now() - bareCtxResolutionT0,
|
||||
exportCount: Object.keys(bareLibrary).length
|
||||
}
|
||||
: {
|
||||
ready: false,
|
||||
atMs: Date.now(),
|
||||
resolutionWallMs: 0,
|
||||
exportCount: 0
|
||||
}
|
||||
})
|
||||
|
||||
const hdmsController = new HdmsController()
|
||||
disk.hdmsController = hdmsController
|
||||
@@ -2373,6 +2402,46 @@ async function executeKernel(disk, store, swarm, initSource) {
|
||||
'PROF'
|
||||
])
|
||||
|
||||
/**
|
||||
* @param {Record<string, unknown>} ctx
|
||||
* @param {number} pid
|
||||
* @param {string} sig normalized signal short name (e.g. TERM)
|
||||
*/
|
||||
function deliverBareOsVirtualSignalToPid(ctx, pid, sig) {
|
||||
const extendedSynthetic =
|
||||
Number.isFinite(pid) && pid >= 4100 && pid < 9000
|
||||
if (sig === '0') {
|
||||
return { ok: true, delivered: false, exists: true }
|
||||
}
|
||||
if (
|
||||
ctx.bareOsLogicalSigaction &&
|
||||
ctx.bareOsLogicalSigaction[sig] === 'IGNORE'
|
||||
) {
|
||||
return {
|
||||
ok: true,
|
||||
delivered: false,
|
||||
ignored: true,
|
||||
atMs: Date.now()
|
||||
}
|
||||
}
|
||||
const atMs = Date.now()
|
||||
bareOsVirtualSignalState.set(pid, { signal: sig, atMs })
|
||||
if (typeof globalThis.process?.emit === 'function') {
|
||||
try {
|
||||
globalThis.process.emit('bare-os:signal', { pid, signal: sig, atMs })
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
const noSessionExit =
|
||||
extendedSynthetic || !BARE_OS_SESSION_EXIT_SIGNALS.has(sig)
|
||||
if (!noSessionExit) {
|
||||
const signum = BARE_OS_SIGNAL_EXIT[sig] || 15
|
||||
ctx.requestBooterExit(128 + signum)
|
||||
}
|
||||
return { ok: true, delivered: true, atMs }
|
||||
}
|
||||
|
||||
/**
|
||||
* Built-in hrpc bridge with strict validation and optional host event emission.
|
||||
* Hosts can still replace `ctx.bareOsHrpcRequest`, but the stock handler is functional.
|
||||
@@ -2487,6 +2556,8 @@ async function executeKernel(disk, store, swarm, initSource) {
|
||||
bareOsBootStartedMs: bootStartedMs,
|
||||
/** Logical open-file targets for `/proc/self/fd/*` beyond stdio (see `bareOsRegisterLogicalFd`). */
|
||||
bareOsLogicalFds: /** @type {Record<string, string>} */ ({}),
|
||||
/** Per-FD **`fcntl` F_SETFL** bits (**`O_APPEND`**, **`O_NONBLOCK`**) ORed with access mode from target; keys are decimal fd strings. */
|
||||
bareOsLogicalFdFlags: /** @type {Record<string, number>} */ ({}),
|
||||
/** In-memory POSIX pipe simulation (`BARE_OS_POSIX_FD_SIM`); see `bareOsPosixFdSimPipe`. */
|
||||
bareOsPosixFdSimState: {
|
||||
nextFd: 10,
|
||||
@@ -2962,7 +3033,7 @@ async function executeKernel(disk, store, swarm, initSource) {
|
||||
},
|
||||
/**
|
||||
* POSIX-like signal dispatch over synthetic process IDs.
|
||||
* Targets: numeric pid (**1–3** session stack, **4100+** shell jobs, **5100+** initd logical rows) or `kernel` / `booter` / `shell`.
|
||||
* Targets: numeric pid (**1–3** session stack, **4100+** shell jobs, **5100+** initd logical rows), **negative process group** (e.g. `-300` → all running shell jobs with `pgid` 300), or `kernel` / `booter` / `shell`.
|
||||
* Signals: HUP, INT, KILL, TERM, PIPE, CHLD, USR1, USR2; `PIPE`/`CHLD`/`USR*` update state only (no session exit).
|
||||
*/
|
||||
bareOsSendSignal(target, signal = 'TERM') {
|
||||
@@ -2972,11 +3043,80 @@ async function executeKernel(disk, store, swarm, initSource) {
|
||||
}
|
||||
const raw = String(target == null ? '' : target).trim()
|
||||
let pid = Number.parseInt(raw, 10)
|
||||
let pgidTarget = null
|
||||
if (Number.isFinite(pid) && pid < 0) {
|
||||
pgidTarget = -pid
|
||||
pid = NaN
|
||||
}
|
||||
if (!Number.isFinite(pid)) {
|
||||
if (pgidTarget == null) {
|
||||
if (raw === 'kernel') pid = 1
|
||||
else if (raw === 'booter') pid = 2
|
||||
else if (raw === 'shell') pid = 3
|
||||
}
|
||||
}
|
||||
if (pgidTarget != null) {
|
||||
const live = bareOsInteractiveCtxRef.ctx
|
||||
const list =
|
||||
live &&
|
||||
live.shellBackgroundJobs &&
|
||||
typeof live.shellBackgroundJobs === 'object' &&
|
||||
Array.isArray(live.shellBackgroundJobs.list)
|
||||
? live.shellBackgroundJobs.list
|
||||
: []
|
||||
const byPgid = list.filter(
|
||||
(j) =>
|
||||
j &&
|
||||
typeof j.pgid === 'number' &&
|
||||
Math.floor(j.pgid) === Math.floor(pgidTarget)
|
||||
)
|
||||
const active = byPgid.filter((j) => !j.done)
|
||||
if (sig === '0') {
|
||||
if (byPgid.length === 0) {
|
||||
throw new Error('bareOsSendSignal: unknown process group')
|
||||
}
|
||||
return {
|
||||
ok: true,
|
||||
pgid: pgidTarget,
|
||||
signal: sig,
|
||||
delivered: false,
|
||||
exists: true,
|
||||
jobCount: byPgid.length
|
||||
}
|
||||
}
|
||||
if (active.length === 0) {
|
||||
throw new Error('bareOsSendSignal: unknown process group')
|
||||
}
|
||||
if (
|
||||
this.bareOsLogicalSigaction &&
|
||||
this.bareOsLogicalSigaction[sig] === 'IGNORE'
|
||||
) {
|
||||
return {
|
||||
ok: true,
|
||||
pgid: pgidTarget,
|
||||
signal: sig,
|
||||
delivered: false,
|
||||
ignored: true,
|
||||
atMs: Date.now()
|
||||
}
|
||||
}
|
||||
const atMs = Date.now()
|
||||
/** @type {number[]} */
|
||||
const pids = []
|
||||
for (const j of active) {
|
||||
const jp = 4100 + j.id
|
||||
pids.push(jp)
|
||||
deliverBareOsVirtualSignalToPid(this, jp, sig)
|
||||
}
|
||||
return {
|
||||
ok: true,
|
||||
pgid: pgidTarget,
|
||||
signal: sig,
|
||||
delivered: true,
|
||||
atMs,
|
||||
pids
|
||||
}
|
||||
}
|
||||
const extendedSynthetic = Number.isFinite(pid) && pid >= 4100 && pid < 9000
|
||||
if (
|
||||
!Number.isFinite(pid) ||
|
||||
@@ -2988,37 +3128,24 @@ async function executeKernel(disk, store, swarm, initSource) {
|
||||
if (sig === '0') {
|
||||
return { ok: true, pid, signal: sig, delivered: false, exists: true }
|
||||
}
|
||||
if (
|
||||
this.bareOsLogicalSigaction &&
|
||||
this.bareOsLogicalSigaction[sig] === 'IGNORE'
|
||||
) {
|
||||
const r = deliverBareOsVirtualSignalToPid(this, pid, sig)
|
||||
if (r.ignored) {
|
||||
return {
|
||||
ok: true,
|
||||
pid,
|
||||
signal: sig,
|
||||
delivered: false,
|
||||
ignored: true,
|
||||
atMs: Date.now()
|
||||
atMs: r.atMs
|
||||
}
|
||||
}
|
||||
const atMs = Date.now()
|
||||
bareOsVirtualSignalState.set(pid, { signal: sig, atMs })
|
||||
if (typeof globalThis.process?.emit === 'function') {
|
||||
try {
|
||||
globalThis.process.emit('bare-os:signal', { pid, signal: sig, atMs })
|
||||
} catch {
|
||||
/* ignore */
|
||||
return {
|
||||
ok: true,
|
||||
pid,
|
||||
signal: sig,
|
||||
delivered: !!r.delivered,
|
||||
atMs: r.atMs
|
||||
}
|
||||
}
|
||||
const noSessionExit =
|
||||
extendedSynthetic ||
|
||||
sig === '0' ||
|
||||
!BARE_OS_SESSION_EXIT_SIGNALS.has(sig)
|
||||
if (!noSessionExit) {
|
||||
const signum = BARE_OS_SIGNAL_EXIT[sig] || 15
|
||||
this.requestBooterExit(128 + signum)
|
||||
}
|
||||
return { ok: true, pid, signal: sig, delivered: true, atMs }
|
||||
},
|
||||
/**
|
||||
* Try to spawn a host child via **`bare-subprocess`** when the addon is available (Bare hosts).
|
||||
@@ -3068,6 +3195,9 @@ async function executeKernel(disk, store, swarm, initSource) {
|
||||
if (this.bareOsLogicalFds && typeof this.bareOsLogicalFds === 'object') {
|
||||
delete this.bareOsLogicalFds[k]
|
||||
}
|
||||
if (this.bareOsLogicalFdFlags && typeof this.bareOsLogicalFdFlags === 'object') {
|
||||
delete this.bareOsLogicalFdFlags[k]
|
||||
}
|
||||
},
|
||||
bareOsPosixFdSimCapBytes() {
|
||||
const raw = String(shellEnv.BARE_OS_POSIX_FD_SIM_MAX_BYTES || '').trim()
|
||||
@@ -3177,7 +3307,12 @@ async function executeKernel(disk, store, swarm, initSource) {
|
||||
const pairId = m[1]
|
||||
const st = this.bareOsPosixFdSimState
|
||||
const q = st.queues[pairId]
|
||||
if (!q || q.length === 0) return { ok: true, data: '', eof: false }
|
||||
if (!q || q.length === 0) {
|
||||
const nonblock =
|
||||
(this.bareOsLogicalFdFlags[k] & 0x800) === 0x800
|
||||
if (nonblock) return { ok: false, reason: 'EAGAIN' }
|
||||
return { ok: true, data: '', eof: false }
|
||||
}
|
||||
let chunk = q.shift()
|
||||
if (!chunk) return { ok: true, data: '', eof: false }
|
||||
if (chunk.length > cap) {
|
||||
@@ -3189,6 +3324,83 @@ async function executeKernel(disk, store, swarm, initSource) {
|
||||
st.byteTotals[pairId] = Math.max(0, (st.byteTotals[pairId] || 0) - used)
|
||||
return { ok: true, data: chunk, eof: false }
|
||||
},
|
||||
/**
|
||||
* Non-blocking readiness probe for logical FDs (simulated pipes + stdout/stderr write).
|
||||
* @param {{ fds?: Array<{ fd: number, events?: string }>, timeoutMs?: number }} [spec]
|
||||
* @returns {{ ok: true, ready: Array<{ fd: number, revents: string }>, waitedMs: number, timedOut?: boolean }}
|
||||
*/
|
||||
bareOsPosixPollProbe(spec = {}) {
|
||||
const fds = Array.isArray(spec.fds) ? spec.fds : []
|
||||
/** @type {Array<{ fd: number, revents: string }>} */
|
||||
const ready = []
|
||||
for (const entry of fds.slice(0, 64)) {
|
||||
const fd = Number(entry && entry.fd)
|
||||
if (!Number.isFinite(fd) || fd < 0) continue
|
||||
const ev = String(entry.events || 'rw').toLowerCase()
|
||||
const wantR = ev.includes('r')
|
||||
const wantW = ev.includes('w')
|
||||
const k = String(fd >>> 0)
|
||||
const t = this.bareOsLogicalFds[k]
|
||||
const pipeR = /^posix-pipe:(\d+):r$/.exec(String(t || ''))
|
||||
const pipeW = /^posix-pipe:(\d+):w$/.exec(String(t || ''))
|
||||
if (wantR && pipeR) {
|
||||
const st = this.bareOsPosixFdSimState
|
||||
const q = st.queues[pipeR[1]]
|
||||
if (q && q.length > 0) ready.push({ fd, revents: 'r' })
|
||||
}
|
||||
if (wantW && pipeW) {
|
||||
const st = this.bareOsPosixFdSimState
|
||||
const pairId = pipeW[1]
|
||||
const prev = st.byteTotals[pairId] || 0
|
||||
const cap = this.bareOsPosixFdSimCapBytes()
|
||||
if (prev < cap) ready.push({ fd, revents: 'w' })
|
||||
}
|
||||
if (wantW && (k === '1' || k === '2')) {
|
||||
ready.push({ fd, revents: 'w' })
|
||||
}
|
||||
}
|
||||
return { ok: true, ready, waitedMs: 0 }
|
||||
},
|
||||
/**
|
||||
* Poll logical FDs with bounded wait (simulated **`poll(2)`** subset for **`BARE_OS_POSIX_FD_SIM`** pipes).
|
||||
* @param {{ fds?: Array<{ fd: number, events?: string }>, timeoutMs?: number }} [spec]
|
||||
*/
|
||||
async bareOsPosixPoll(spec = {}) {
|
||||
const maxWait = Math.min(
|
||||
60000,
|
||||
Math.max(0, Math.floor(Number(spec && spec.timeoutMs) || 0))
|
||||
)
|
||||
if (maxWait <= 0) {
|
||||
const once = this.bareOsPosixPollProbe(spec)
|
||||
return {
|
||||
ok: true,
|
||||
ready: once.ready,
|
||||
waitedMs: 0,
|
||||
timedOut: false
|
||||
}
|
||||
}
|
||||
const t0 = Date.now()
|
||||
const interval = 10
|
||||
while (Date.now() - t0 <= maxWait) {
|
||||
const once = this.bareOsPosixPollProbe(spec)
|
||||
if (once.ready.length) {
|
||||
return {
|
||||
ok: true,
|
||||
ready: once.ready,
|
||||
waitedMs: Date.now() - t0,
|
||||
timedOut: false
|
||||
}
|
||||
}
|
||||
if (Date.now() - t0 >= maxWait) break
|
||||
await new Promise((res) => setTimeout(res, interval))
|
||||
}
|
||||
return {
|
||||
ok: true,
|
||||
ready: [],
|
||||
waitedMs: Date.now() - t0,
|
||||
timedOut: true
|
||||
}
|
||||
},
|
||||
/** @type {Record<string, unknown>[]} */
|
||||
bareOsSnapshotHandles: [],
|
||||
/** @param {Record<string, unknown>} desc */
|
||||
@@ -3210,7 +3422,14 @@ async function executeKernel(disk, store, swarm, initSource) {
|
||||
*/
|
||||
bareOsPathconf(pathArg, name) {
|
||||
const k = String(name || '').trim()
|
||||
const p = String(pathArg || '/').trim() || '/'
|
||||
const p = vfs.resolveLogical(String(pathArg || '/').trim() || '/')
|
||||
const rawUnion = String(shellEnv.BARE_OS_VFS_UNION_PREFIXES || '')
|
||||
const underUnion = rawUnion
|
||||
.split(',')
|
||||
.map((s) => s.trim())
|
||||
.filter((x) => x.startsWith('/'))
|
||||
.some((pre) => p === pre || p.startsWith(pre + '/'))
|
||||
const underMirror = p === '/mirror' || p.startsWith('/mirror/')
|
||||
/** @type {Record<string, number>} */
|
||||
const table = {
|
||||
PATH_MAX: 4096,
|
||||
@@ -3229,7 +3448,11 @@ async function executeKernel(disk, store, swarm, initSource) {
|
||||
_POSIX_VERSION: 200809,
|
||||
_POSIX_THREAD_ATTR_STACKSIZE: 65536
|
||||
}
|
||||
if (k === '_PC_CHOWN_RESTRICTED' && p.startsWith('/mirror/')) {
|
||||
if (k === '_PC_CHOWN_RESTRICTED') {
|
||||
if (underMirror) return 0
|
||||
return 1
|
||||
}
|
||||
if (k === '_PC_NO_TRUNC' && (underMirror || underUnion)) {
|
||||
return 0
|
||||
}
|
||||
if (!k || table[k] === undefined) {
|
||||
@@ -3500,6 +3723,95 @@ async function executeKernel(disk, store, swarm, initSource) {
|
||||
note: 'Best-effort no-op unless the VFS exposes a flush primitive.'
|
||||
}
|
||||
}
|
||||
if (name === 'fcntl') {
|
||||
const O_ACCMODE = 3
|
||||
const O_APPEND = 0x400
|
||||
const O_NONBLOCK = 0x800
|
||||
const SETFL_MASK = O_APPEND | O_NONBLOCK
|
||||
const fd = Number(args.fd)
|
||||
if (!Number.isFinite(fd) || fd < 0 || fd > 65535) {
|
||||
throw new Error('bareOsSyscall: fcntl: invalid fd')
|
||||
}
|
||||
const k = String(fd >>> 0)
|
||||
const target = this.bareOsLogicalFds[k]
|
||||
const isStd = k === '0' || k === '1' || k === '2'
|
||||
if (!isStd && (target == null || target === '')) {
|
||||
const err = /** @type {Error & { code?: string }} */ (
|
||||
new Error('EBADF: fcntl')
|
||||
)
|
||||
err.code = 'EBADF'
|
||||
throw err
|
||||
}
|
||||
const baseAcc = () => {
|
||||
if (k === '0') return 0
|
||||
if (k === '1' || k === '2') return 2
|
||||
const s = String(target || '')
|
||||
if (/posix-pipe:\d+:r$/.test(s)) return 0
|
||||
if (/posix-pipe:\d+:w$/.test(s)) return 1
|
||||
return 2
|
||||
}
|
||||
let cmd = args.cmd
|
||||
if (typeof cmd === 'string') {
|
||||
const u = String(cmd).toUpperCase()
|
||||
if (u === 'F_GETFL' || u === 'GETFL') cmd = 3
|
||||
else if (u === 'F_SETFL' || u === 'SETFL') cmd = 4
|
||||
else {
|
||||
const err = /** @type {Error & { code?: string }} */ (
|
||||
new Error('ENOTSUP: fcntl cmd')
|
||||
)
|
||||
err.code = 'ENOTSUP'
|
||||
throw err
|
||||
}
|
||||
}
|
||||
cmd = Number(cmd)
|
||||
if (cmd === 3) {
|
||||
const acc = baseAcc() & O_ACCMODE
|
||||
const extra = (this.bareOsLogicalFdFlags[k] || 0) & SETFL_MASK
|
||||
return {
|
||||
ok: true,
|
||||
op: 'fcntl',
|
||||
cmd: 'F_GETFL',
|
||||
fd,
|
||||
flags: acc | extra
|
||||
}
|
||||
}
|
||||
if (cmd === 4) {
|
||||
if (!this.bareOsLogicalFdFlags || typeof this.bareOsLogicalFdFlags !== 'object') {
|
||||
this.bareOsLogicalFdFlags = {}
|
||||
}
|
||||
const arg = Number(args.arg ?? args.value ?? 0) >>> 0
|
||||
this.bareOsLogicalFdFlags[k] = arg & SETFL_MASK
|
||||
const acc = baseAcc() & O_ACCMODE
|
||||
const extra = this.bareOsLogicalFdFlags[k] & SETFL_MASK
|
||||
return {
|
||||
ok: true,
|
||||
op: 'fcntl',
|
||||
cmd: 'F_SETFL',
|
||||
fd,
|
||||
flags: acc | extra
|
||||
}
|
||||
}
|
||||
const err2 = /** @type {Error & { code?: string }} */ (
|
||||
new Error('ENOTSUP: fcntl cmd')
|
||||
)
|
||||
err2.code = 'ENOTSUP'
|
||||
throw err2
|
||||
}
|
||||
if (name === 'posixPoll') {
|
||||
const timeoutMs = Math.max(
|
||||
0,
|
||||
Math.min(60000, Math.floor(Number(args.timeoutMs) || 0))
|
||||
)
|
||||
if (timeoutMs > 0) {
|
||||
return this.bareOsPosixPoll({
|
||||
fds: Array.isArray(args.fds) ? args.fds : [],
|
||||
timeoutMs
|
||||
})
|
||||
}
|
||||
return this.bareOsPosixPollProbe({
|
||||
fds: Array.isArray(args.fds) ? args.fds : []
|
||||
})
|
||||
}
|
||||
if (name === 'kill') {
|
||||
return this.bareOsSendSignal(args.target, String(args.signal || 'TERM'))
|
||||
}
|
||||
@@ -3520,7 +3832,21 @@ async function executeKernel(disk, store, swarm, initSource) {
|
||||
* @param {{ path: string, buf: Uint8Array | ArrayBuffer, opts?: Record<string, unknown> }[]} puts
|
||||
*/
|
||||
async bareOsVfsBatchWrite(puts) {
|
||||
return bareOsVfsBatchPut(disk.drive, puts)
|
||||
await bareOsVfsBatchPut(disk.drive, puts)
|
||||
if (vfs?.bareOsClearWarmReadCaches && Array.isArray(puts)) {
|
||||
for (const row of puts) {
|
||||
const p = String(row?.path || '').replace(/^\/+/, '')
|
||||
if (
|
||||
p === 'bin' ||
|
||||
p.startsWith('bin/') ||
|
||||
p === 'lib/bare' ||
|
||||
p.startsWith('lib/bare/')
|
||||
) {
|
||||
vfs.bareOsClearWarmReadCaches()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Hyperdrive `diff` collector (API varies by stack version; see handbook).
|
||||
@@ -3662,6 +3988,33 @@ async function executeKernel(disk, store, swarm, initSource) {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Parse `/proc/bare_os/replication` (same JSON as the VFS pseudo file).
|
||||
* @returns {Promise<Record<string, unknown> | null>}
|
||||
*/
|
||||
async bareOsReadReplicationOperatorJson() {
|
||||
if (!vfs || typeof vfs.readFile !== 'function') return null
|
||||
try {
|
||||
const b = await vfs.readFile('/proc/bare_os/replication')
|
||||
if (!b) return null
|
||||
const t = b4a.toString(b, 'utf8').trim()
|
||||
if (!t) return null
|
||||
return /** @type {Record<string, unknown>} */ (JSON.parse(t))
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Invalidate `/bin` and `/lib/bare` warm read caches after replication or policy changes.
|
||||
* @param {string} [reason]
|
||||
*/
|
||||
bareOsInvalidateWarmReadCaches(reason) {
|
||||
if (vfs && typeof vfs.bareOsClearWarmReadCaches === 'function') {
|
||||
vfs.bareOsClearWarmReadCaches()
|
||||
}
|
||||
bareOsKernelMetricInc('vfs.warm_read_cache_invalidate_explicit')
|
||||
return { ok: true, reason: String(reason || '').slice(0, 256) }
|
||||
},
|
||||
/**
|
||||
* Batch-read `/proc/bare_os/*` mirrors for baretop(1). Keys must stay aligned with
|
||||
* `BARE_TOP_SNAPSHOT_PROC_ENTRIES` in packages/bare-os-coreutils/lib/baretop-snapshot.js.
|
||||
|
||||
@@ -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.34.0'
|
||||
export const BARE_OS_CTX_API_VERSION = '1.36.0'
|
||||
|
||||
+23
@@ -130,6 +130,10 @@ export interface BareOsKernelContext {
|
||||
bareOsIpc?: BareOsIpc
|
||||
bareOsGetResourceStatus(): BareOsResourceStatus
|
||||
bareOsReadProcMetricsLive(): Record<string, unknown>
|
||||
bareOsReadReplicationOperatorJson?(): Promise<Record<string, unknown> | null>
|
||||
bareOsInvalidateWarmReadCaches?(
|
||||
reason?: string
|
||||
): { ok: boolean; reason: string }
|
||||
/**
|
||||
* Optional batch `/proc/bare_os/*` read for baretop(1); keys match baretop-snapshot preamble.
|
||||
*/
|
||||
@@ -342,6 +346,8 @@ export interface BareOsKernelContext {
|
||||
}
|
||||
/** FD → logical VFS/IPC target; see `/proc/bare_os/syscalls.json` fdModel. */
|
||||
bareOsLogicalFds?: Record<string, string>
|
||||
/** `fcntl` F_SETFL bits (O_APPEND | O_NONBLOCK) per decimal fd key. */
|
||||
bareOsLogicalFdFlags?: Record<string, number>
|
||||
/** Shell `trap` COMMAND → SIGNAL map (logical; delivery is synthetic). */
|
||||
shellTrapHandlers?: Record<string, string>
|
||||
bareOsLogicalSigaction?: Record<string, string>
|
||||
@@ -373,6 +379,23 @@ export interface BareOsKernelContext {
|
||||
readFd: number,
|
||||
maxBytes?: number
|
||||
): { ok: boolean; data?: string; eof?: boolean; reason?: string }
|
||||
bareOsPosixPollProbe?(spec?: {
|
||||
fds?: Array<{ fd: number; events?: string }>
|
||||
timeoutMs?: number
|
||||
}): {
|
||||
ok: true
|
||||
ready: Array<{ fd: number; revents: string }>
|
||||
waitedMs: number
|
||||
}
|
||||
bareOsPosixPoll?(spec?: {
|
||||
fds?: Array<{ fd: number; events?: string }>
|
||||
timeoutMs?: number
|
||||
}): Promise<{
|
||||
ok: true
|
||||
ready: Array<{ fd: number; revents: string }>
|
||||
waitedMs: number
|
||||
timedOut?: boolean
|
||||
}>
|
||||
bareOsSigaction?(
|
||||
signal: string,
|
||||
mode: 'IGNORE' | 'DEFAULT' | 'IGN' | 'DEF'
|
||||
|
||||
@@ -136,7 +136,7 @@ function assertSafeIpcName(name) {
|
||||
const DEFAULT_JSON_RPC_MAX = 256 * 1024
|
||||
|
||||
/**
|
||||
* @param {{ maxFifoBytes?: number, perChannelMaxBytes?: Map<string, number>, ipcRpcToken?: string | null, enableFanout?: boolean, maxJsonRpcLineBytes?: number }} [opts]
|
||||
* @param {{ maxFifoBytes?: number, maxChannels?: number, perChannelMaxBytes?: Map<string, number>, ipcRpcToken?: string | null, enableFanout?: boolean, maxJsonRpcLineBytes?: number }} [opts]
|
||||
*/
|
||||
export function createBareOsIpc(opts = {}) {
|
||||
const maxFifoBytes =
|
||||
@@ -144,9 +144,22 @@ export function createBareOsIpc(opts = {}) {
|
||||
? Math.min(opts.maxFifoBytes, 16 * 1024 * 1024)
|
||||
: 1024 * 1024
|
||||
|
||||
const maxChannels =
|
||||
typeof opts.maxChannels === 'number' && opts.maxChannels > 0
|
||||
? Math.min(65536, Math.floor(opts.maxChannels))
|
||||
: 4096
|
||||
|
||||
const perChannelMax =
|
||||
opts.perChannelMaxBytes instanceof Map ? opts.perChannelMaxBytes : null
|
||||
|
||||
/** @type {{ fifoCreates: number, fifoCreateDeniedQuota: number, fifoPushDenied: number, mqFull: number }} */
|
||||
const telemetry = {
|
||||
fifoCreates: 0,
|
||||
fifoCreateDeniedQuota: 0,
|
||||
fifoPushDenied: 0,
|
||||
mqFull: 0
|
||||
}
|
||||
|
||||
const limitFor = (name) => {
|
||||
if (perChannelMax && perChannelMax.has(name)) {
|
||||
const n = perChannelMax.get(name)
|
||||
@@ -185,7 +198,14 @@ export function createBareOsIpc(opts = {}) {
|
||||
*/
|
||||
create(name, opts) {
|
||||
assertSafeIpcName(name)
|
||||
if (!channels.has(name)) channels.set(name, new FifoChannel())
|
||||
if (!channels.has(name)) {
|
||||
if (channels.size >= maxChannels) {
|
||||
telemetry.fifoCreateDeniedQuota++
|
||||
throw new Error('bare-os ipc: channel quota exceeded')
|
||||
}
|
||||
channels.set(name, new FifoChannel())
|
||||
telemetry.fifoCreates++
|
||||
}
|
||||
if (opts && opts.scope != null && String(opts.scope).trim()) {
|
||||
channelScopes.set(name, String(opts.scope).trim().slice(0, 64))
|
||||
}
|
||||
@@ -221,7 +241,14 @@ export function createBareOsIpc(opts = {}) {
|
||||
if (!Number.isFinite(n) || n < 0 || n > 0xffffffff) {
|
||||
throw new Error('invalid pgid')
|
||||
}
|
||||
if (!channels.has(channelName)) channels.set(channelName, new FifoChannel())
|
||||
if (!channels.has(channelName)) {
|
||||
if (channels.size >= maxChannels) {
|
||||
telemetry.fifoCreateDeniedQuota++
|
||||
throw new Error('bare-os ipc: channel quota exceeded')
|
||||
}
|
||||
channels.set(channelName, new FifoChannel())
|
||||
telemetry.fifoCreates++
|
||||
}
|
||||
channelScopes.set(channelName, 'pgid:' + String(Math.floor(n)))
|
||||
},
|
||||
|
||||
@@ -260,7 +287,18 @@ export function createBareOsIpc(opts = {}) {
|
||||
push(name, buf) {
|
||||
const ch = channels.get(name)
|
||||
if (!ch) throw new Error('no such fifo: ' + name)
|
||||
try {
|
||||
ch.push(buf, limitFor(name))
|
||||
} catch (e) {
|
||||
if (
|
||||
e &&
|
||||
typeof e === 'object' &&
|
||||
String(/** @type {Error} */ (e).message || '').includes('backlog')
|
||||
) {
|
||||
telemetry.fifoPushDenied++
|
||||
}
|
||||
throw e
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -412,6 +450,7 @@ export function createBareOsIpc(opts = {}) {
|
||||
throw new Error('bare-os ipc mq: message exceeds maxBytes')
|
||||
}
|
||||
if (q.msgs.length >= q.maxmsg) {
|
||||
telemetry.mqFull++
|
||||
throw new Error('bare-os ipc mq: queue full (maxmsg)')
|
||||
}
|
||||
if (q.curBytes + u8.byteLength > q.maxmsg * q.maxBytes) {
|
||||
@@ -467,6 +506,8 @@ export function createBareOsIpc(opts = {}) {
|
||||
}
|
||||
return {
|
||||
channelCount: channels.size,
|
||||
maxChannels,
|
||||
telemetry: { ...telemetry },
|
||||
queuedBytesTotal,
|
||||
fanoutTopicCount: fanouts.size,
|
||||
fanoutSubscribersTotal,
|
||||
|
||||
@@ -148,7 +148,8 @@ export function bareOsProcessTableSnapshot(opts = {}) {
|
||||
schema: 1,
|
||||
bareOsSendSignal:
|
||||
'Delivers to synthetic pids 1–3 (kernel, booter, shell), shell job rows (41xx), and initd logical rows (51xx) when keyed by pid; see bareOsSendSignal implementation.',
|
||||
killUtility: '/bin/kill maps names kernel|booter|shell and numeric pids to the same routing.',
|
||||
killUtility:
|
||||
'/bin/kill maps names kernel|booter|shell, numeric pids, and **negative pgid** (e.g. `kill -TERM -300` for logical group 300) to the same routing.',
|
||||
trap:
|
||||
'Shell builtins use ctx.shellTrapHandlers; signals are synthetic (no host kill).',
|
||||
sessionShellPgid: 3
|
||||
@@ -181,7 +182,7 @@ export function bareOsProcessTableSnapshot(opts = {}) {
|
||||
setpgidAnalog:
|
||||
'Background job start assigns pgid on shellBackgroundJobs rows where implemented.',
|
||||
killpgAnalog:
|
||||
'Virtual signal delivery consults shell job lists; not a full killpg(2) emulation.'
|
||||
'Negative numeric targets index **`shellBackgroundJobs`** by **`pgid`**; active jobs only (zombies excluded except **`kill -0`** existence checks). Not a full killpg(2) emulation.'
|
||||
},
|
||||
ipcProcessGroups:
|
||||
opts.ipcStats &&
|
||||
|
||||
@@ -4,8 +4,12 @@
|
||||
*
|
||||
* Tunable via optional env (read by caller): BARE_OS_SWARM_BAN_FAIL_THRESHOLD,
|
||||
* BARE_OS_SWARM_BAN_MS_INITIAL, BARE_OS_SWARM_BAN_MS_MAX, BARE_OS_SWARM_EWMA_ALPHA.
|
||||
* Optional **`BARE_OS_PEER_ALLOWLIST_HEX`** is enforced here so swarm scheduling aligns with
|
||||
* **`ctx.bareOsEvaluatePeerAdmission`** (deny before reconnect budget is spent).
|
||||
*/
|
||||
|
||||
import { evaluateBareOsPeerAdmission } from './bare-os-peer-admission.js'
|
||||
|
||||
const DEFAULT_FAILS_BEFORE_BAN = 4
|
||||
const DEFAULT_BAN_MS_INITIAL = 5000
|
||||
const DEFAULT_BAN_MS_MAX = 300000
|
||||
@@ -69,6 +73,7 @@ export class BareOsSwarmPeerPolicyEngine {
|
||||
*/
|
||||
constructor(swarm, opts = {}) {
|
||||
this.swarm = swarm
|
||||
this._env = opts.env && typeof opts.env === 'object' ? opts.env : null
|
||||
this._cfg = readSwarmPolicyEnv(opts.env)
|
||||
/** @type {Map<string, PeerState>} */
|
||||
this._peers = new Map()
|
||||
@@ -149,6 +154,10 @@ export class BareOsSwarmPeerPolicyEngine {
|
||||
*/
|
||||
shouldAttemptPeer(peerKey) {
|
||||
const k = this._key(peerKey)
|
||||
if (this._env) {
|
||||
const adm = evaluateBareOsPeerAdmission(this._env, k)
|
||||
if (adm.verdict === 'deny') return false
|
||||
}
|
||||
const st = this._peers.get(k)
|
||||
const now = Date.now()
|
||||
if (!st) return true
|
||||
|
||||
@@ -37,7 +37,9 @@ export const BARE_OS_SYSCALL_OPS_DETAIL = Object.freeze([
|
||||
{ name: 'ftruncate', category: 'fs', stability: 'experimental' },
|
||||
{ name: 'fsync', category: 'fs', stability: 'experimental' },
|
||||
{ name: 'fdatasync', category: 'fs', stability: 'experimental' },
|
||||
{ name: 'pathconf', category: 'fs', stability: 'experimental' }
|
||||
{ name: 'pathconf', category: 'fs', stability: 'experimental' },
|
||||
{ name: 'fcntl', category: 'fs', stability: 'stable' },
|
||||
{ name: 'posixPoll', category: 'fs', stability: 'experimental' }
|
||||
])
|
||||
|
||||
/**
|
||||
@@ -122,8 +124,23 @@ export const BARE_OS_POSIX_XSH_OPS_DETAIL = Object.freeze([
|
||||
category: 'fs',
|
||||
stability: 'stable',
|
||||
posixAlignment: 'partial',
|
||||
mapsTo: ['F_GETFL noop where honored', 'other cmds ENOTSUP'],
|
||||
errnoHint: 'ENOTSUP'
|
||||
mapsTo: [
|
||||
'ctx.bareOsSyscall("fcntl")',
|
||||
'F_GETFL',
|
||||
'F_SETFL',
|
||||
'O_APPEND',
|
||||
'O_NONBLOCK',
|
||||
'bareOsLogicalFdFlags'
|
||||
],
|
||||
errnoHint: 'EBADF'
|
||||
},
|
||||
{
|
||||
name: 'poll',
|
||||
category: 'fs',
|
||||
stability: 'experimental',
|
||||
posixAlignment: 'partial',
|
||||
mapsTo: ['ctx.bareOsPosixPoll', 'ctx.bareOsSyscall("posixPoll")'],
|
||||
errnoHint: 'EINVAL'
|
||||
}
|
||||
])
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ export function buildBareOsSyscallsProcJson(p) {
|
||||
posixXsh: {
|
||||
schema: 1,
|
||||
note: 'POSIX.1 XSH names in opsDetail with posixAlignment; not additional ctx.bareOsSyscall op strings.',
|
||||
namesCsv: 'open,close,read,write,lseek,pipe,dup,dup2,fcntl'
|
||||
namesCsv: 'open,close,read,write,lseek,pipe,dup,dup2,fcntl,poll'
|
||||
},
|
||||
errnoHints: bareOsErrnoTableForProc(),
|
||||
fdModel: {
|
||||
@@ -71,7 +71,22 @@ export function buildBareOsSyscallsProcJson(p) {
|
||||
partial: true
|
||||
},
|
||||
fcntl: {
|
||||
mapsTo: ['F_GETFL noop where honored; other cmds ENOTSUP at VFS layer'],
|
||||
mapsTo: [
|
||||
'ctx.bareOsSyscall("fcntl")',
|
||||
'F_GETFL',
|
||||
'F_SETFL',
|
||||
'O_APPEND 0x400',
|
||||
'O_NONBLOCK 0x800',
|
||||
'bareOsLogicalFdFlags per fd string key'
|
||||
],
|
||||
partial: true
|
||||
},
|
||||
poll: {
|
||||
mapsTo: [
|
||||
'ctx.bareOsPosixPoll',
|
||||
'ctx.bareOsPosixPollProbe',
|
||||
'ctx.bareOsSyscall("posixPoll")'
|
||||
],
|
||||
partial: true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,28 @@
|
||||
|
||||
import b4a from 'b4a'
|
||||
import { bareOsAppendVaultRotationCheckpoint } from './bare-os-vault-rotation-audit.js'
|
||||
|
||||
/**
|
||||
* Mirror vault NDJSON checkpoints into the tamper-evident host audit chain when the booter exposes it.
|
||||
* @param {Record<string, unknown>} ctx
|
||||
* @param {Record<string, unknown>} row
|
||||
*/
|
||||
function mirrorVaultCheckpointToAuditChain(ctx, row) {
|
||||
if (typeof ctx.bareOsAuditLogAppendBatch !== 'function') return
|
||||
try {
|
||||
ctx.bareOsAuditLogAppendBatch([
|
||||
{
|
||||
type: 'identity.vault_audit_mirror',
|
||||
schema: 1,
|
||||
vaultRotationAuditPath: '/.bare/vault-rotation-audit.ndjson',
|
||||
...row,
|
||||
atMs: Date.now()
|
||||
}
|
||||
])
|
||||
} catch {
|
||||
/* optional chain */
|
||||
}
|
||||
}
|
||||
import { loadBarerc } from './shell.js'
|
||||
import {
|
||||
ACCOUNT_PATH,
|
||||
@@ -200,6 +222,10 @@ export async function unlockIdentity(ctx, passphrase) {
|
||||
kind: 'identity_unlock',
|
||||
user: String(ctx.vfs?.env?.USER || '')
|
||||
})
|
||||
mirrorVaultCheckpointToAuditChain(ctx, {
|
||||
kind: 'identity_unlock',
|
||||
user: String(ctx.vfs?.env?.USER || '')
|
||||
})
|
||||
} catch {
|
||||
/* ignore audit failures */
|
||||
}
|
||||
@@ -422,6 +448,10 @@ export async function saveVaultToDrive(ctx) {
|
||||
kind: 'vault_save',
|
||||
fileCount
|
||||
})
|
||||
mirrorVaultCheckpointToAuditChain(ctx, {
|
||||
kind: 'vault_save',
|
||||
fileCount
|
||||
})
|
||||
} catch (e) {
|
||||
ctx.console?.error?.(
|
||||
'[bare-os] vault-save audit: ' + (e?.message || e)
|
||||
|
||||
@@ -4,6 +4,11 @@
|
||||
*
|
||||
* Enforcement at boot remains in `kernel/init.js` (`kernel.ext.d`); this module provides
|
||||
* shared deterministic resolution for booter tooling, tests, and strict preflight.
|
||||
*
|
||||
* **Keep in sync:** `topologicalOrderKernelExtensions` / `kernelExtensionDependencyDepth`
|
||||
* must match `topologicalOrderKernelExtEntries` / `kernelExtDependencyDepth` in
|
||||
* `kernel/lib/init/init-main.js` (same Kahn sort and depth walk). CI: booter test
|
||||
* `kernel extension topological order matches resolver module`.
|
||||
*/
|
||||
|
||||
/**
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
S_IFLNK,
|
||||
S_IFREG
|
||||
} from './vfs-posix-meta.js'
|
||||
import { bareOsKernelMetricInc } from './bare-os-kernel-metrics.js'
|
||||
|
||||
/**
|
||||
* Policy-oriented path class for VFS routing (system vs personal vs pseudo vs mount vs volatile).
|
||||
@@ -778,6 +779,23 @@ export function createVfs(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Drop `/bin` and `/lib/bare` warm read caches (replication / OTA safety). */
|
||||
function bareOsClearWarmReadCaches() {
|
||||
if (binReadCache) binReadCache.clear()
|
||||
if (binDigestCache) binDigestCache.clear()
|
||||
if (binPathToBlakeDigest) binPathToBlakeDigest.clear()
|
||||
if (binDigestRefcount) binDigestRefcount.clear()
|
||||
if (binBlake2bLruPaths) binBlake2bLruPaths.length = 0
|
||||
if (warmReadCacheStats) {
|
||||
warmReadCacheStats.hits = 0
|
||||
warmReadCacheStats.misses = 0
|
||||
warmReadCacheStats.binHits = 0
|
||||
warmReadCacheStats.libBareHits = 0
|
||||
}
|
||||
bareOsKernelMetricInc('vfs.warm_read_cache_clear')
|
||||
}
|
||||
|
||||
const sysClassNetLoText =
|
||||
typeof vfsOptions.sysClassNetLoText === 'function'
|
||||
? vfsOptions.sysClassNetLoText
|
||||
@@ -4366,6 +4384,8 @@ export function createVfs(
|
||||
|
||||
env,
|
||||
|
||||
bareOsClearWarmReadCaches,
|
||||
|
||||
async chdir(userPath) {
|
||||
const abs = resolveLogical(userPath)
|
||||
assertNotBootPolicyDenyVfs(abs, 'chdir')
|
||||
|
||||
@@ -18,6 +18,8 @@ import {
|
||||
} from './lib/vfs.js'
|
||||
import { buildBareOsHypercorePackHrpcLifecycleProcJson } from './lib/bare-os-proc-hypercore-pack-hrpc-lifecycle.js'
|
||||
import { createBareOsIpc } from './lib/bare-os-ipc.js'
|
||||
import { topologicalOrderKernelExtensions } from './lib/kernel-extension-resolver.js'
|
||||
import { createBareOsProtomuxAliasRegistry } from './lib/bare-os-protomux-alias-registry.js'
|
||||
import { bareOsHttpUrlAllowed } from './lib/bare-os-http-policy.js'
|
||||
import {
|
||||
bareOsParseBareAclText,
|
||||
@@ -483,13 +485,22 @@ test('stock kernel init.js BARE_OS_BOOT_TRACE=json emits boot trace JSON on stde
|
||||
}
|
||||
})
|
||||
.filter(Boolean)
|
||||
t.ok(phases.length >= 3)
|
||||
t.ok(phases.some((o) => o.phase === 'rc.local'))
|
||||
t.ok(phases.some((o) => o.step === 'rc.local'))
|
||||
t.ok(phases.some((o) => o.phase === 'kernel.d'))
|
||||
t.ok(phases.some((o) => o.step === 'kernel.d'))
|
||||
t.ok(phases.every((o) => o.bootTraceSchemaVersion === 2))
|
||||
t.ok(phases.every((o) => typeof o.ms === 'number'))
|
||||
const bootSteps = phases.filter(
|
||||
(o) => o && typeof o.ms === 'number' && o.type !== 'bootOutput'
|
||||
)
|
||||
t.ok(bootSteps.length >= 3)
|
||||
t.ok(bootSteps.some((o) => o.phase === 'rc.local'))
|
||||
t.ok(bootSteps.some((o) => o.step === 'rc.local'))
|
||||
t.ok(bootSteps.some((o) => o.phase === 'kernel.d'))
|
||||
t.ok(bootSteps.some((o) => o.step === 'kernel.d'))
|
||||
t.ok(bootSteps.every((o) => o.bootTraceSchemaVersion === 2))
|
||||
t.ok(bootSteps.every((o) => typeof o.ms === 'number'))
|
||||
t.ok(
|
||||
phases.some(
|
||||
(o) => o && o.type === 'bootOutput' && o.step === 'banner'
|
||||
),
|
||||
'bootOutput trace for guest-visible banner'
|
||||
)
|
||||
})
|
||||
|
||||
test('stock kernel BARE_OS_RC_D_SKIP skips matching rc.d snippets', async (t) => {
|
||||
@@ -840,9 +851,11 @@ test('buildBareOsSyscallsProcJson exposes posixLike fd mapping', async (t) => {
|
||||
t.ok(j.fdModel && j.fdModel.posixLike && j.fdModel.posixLike.pipe)
|
||||
t.ok(j.fdModel.posixLike.read && j.fdModel.posixLike.write)
|
||||
t.ok(j.fdModel.posixLike.fcntl)
|
||||
t.ok(j.fdModel.posixLike.poll)
|
||||
t.ok(j.posixXsh && j.posixXsh.schema === 1 && j.posixXsh.namesCsv.includes('open'))
|
||||
t.ok(j.posixXsh.namesCsv.includes('poll'))
|
||||
const xsh = j.opsDetail.filter((r) => r.posixAlignment)
|
||||
t.ok(xsh.length >= 9)
|
||||
t.ok(xsh.length >= 10)
|
||||
})
|
||||
|
||||
test('ls -l long listing uses session user and regular file mode', async (t) => {
|
||||
@@ -5427,6 +5440,46 @@ test('protomux and hyperswarm versions match repo lock contract', async (t) => {
|
||||
t.is(hs, snap.hyperswarm)
|
||||
})
|
||||
|
||||
test('kernel extension topological order matches guest Kahn tie-break', async (t) => {
|
||||
const entries = [
|
||||
{ file: 'b.json', extId: 'b', scripts: ['/x'], dependsOn: ['a'] },
|
||||
{ file: 'a.json', extId: 'a', scripts: ['/y'], dependsOn: [] }
|
||||
]
|
||||
const topo = topologicalOrderKernelExtensions(entries)
|
||||
t.ok(topo.ok)
|
||||
t.is(
|
||||
topo.ordered.map((e) => e.file).join(','),
|
||||
'a.json,b.json',
|
||||
'filename order breaks ties; dependsOn edge b→a'
|
||||
)
|
||||
})
|
||||
|
||||
test('protomux alias registry snapshot matches proc wire contract shape', async (t) => {
|
||||
const reg = createBareOsProtomuxAliasRegistry()
|
||||
reg.register('guestChannel', 'wireChannel')
|
||||
const rt = reg.snapshot()
|
||||
t.is(rt.schema, 2)
|
||||
t.is(rt.aliases.guestChannel, 'wireChannel')
|
||||
t.ok(Array.isArray(rt.changeLogTail))
|
||||
t.ok(rt.reverseIndex && typeof rt.reverseIndex === 'object')
|
||||
})
|
||||
|
||||
test('createBareOsIpc enforces maxChannels quota', async (t) => {
|
||||
const ipc = createBareOsIpc({ maxChannels: 2, maxFifoBytes: 4096 })
|
||||
ipc.create('a')
|
||||
ipc.create('b')
|
||||
let threw = false
|
||||
try {
|
||||
ipc.create('c')
|
||||
} catch {
|
||||
threw = true
|
||||
}
|
||||
t.ok(threw)
|
||||
const st = ipc.stats()
|
||||
t.is(st.maxChannels, 2)
|
||||
t.ok(st.telemetry.fifoCreateDeniedQuota >= 1)
|
||||
})
|
||||
|
||||
async function readBuiltBin(name) {
|
||||
const fs = await import('node:fs/promises')
|
||||
const p = path.join(__dirname, '../../kernel/bin', name)
|
||||
|
||||
@@ -6,6 +6,6 @@
|
||||
"description": "Build JS /bin utilities for bare-operating-system (concat + stage to kernel/)",
|
||||
"scripts": {
|
||||
"build": "node ./scripts/ensure-man-pages.mjs && node ./build.mjs",
|
||||
"test": "node ./test/help-bin-list.test.mjs && node ./test/edit-key-parse.test.mjs && node ./test/edit-teardown.test.mjs && node ./test/baretop-bundle.test.mjs && node ./test/baretop-fixture.test.mjs && node ./test/posix-test-int-compare.test.mjs && node ./test/posix-utils-edge.test.mjs && node ./test/awk-sed-posix-smoke.test.mjs && node ./test/xattr-acl-utils.test.mjs && node ./test/xcu-issue7-sweep.test.mjs"
|
||||
"test": "node ./test/help-bin-list.test.mjs && node ./test/edit-key-parse.test.mjs && node ./test/edit-teardown.test.mjs && node ./test/baretop-bundle.test.mjs && node ./test/baretop-fixture.test.mjs && node ./test/posix-test-int-compare.test.mjs && node ./test/posix-utils-edge.test.mjs && node ./test/posix-golden-issue7.test.mjs && node ./test/getconf-pathconf-union-mirror.test.mjs && node ./test/awk-sed-posix-smoke.test.mjs && node ./test/xattr-acl-utils.test.mjs && node ./test/xcu-issue7-sweep.test.mjs"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -396,8 +396,9 @@ EXTRA.getconf = {
|
||||
}
|
||||
EXTRA.mkfifo = {
|
||||
description:
|
||||
'FIFO special files are not implemented on Hyperdrive. The command reports failure.',
|
||||
bareOsNotes: 'Documented stub; no real pipes as kernel objects.'
|
||||
'Creates in-memory FIFO endpoints under /run/bare-os/ipc/<name> (not Hyperdrive specials).',
|
||||
bareOsNotes:
|
||||
'Uses ctx.bareOsIpc.create; optional BARE_OS_IPC_NAMESPACE prefixes keys. BARE_OS_IPC_MAX_CHANNELS caps distinct channels; see metrics_live.ipcTelemetry.'
|
||||
}
|
||||
EXTRA.chmod = {
|
||||
synopsis: [
|
||||
|
||||
@@ -50,10 +50,10 @@ const CONF = {
|
||||
_PC_2_SYMLINKS: '1',
|
||||
/** Comma-separated `ctx.bareOsSyscall` op names implemented in stock booter. */
|
||||
BARE_OS_SYSCALL_OPS:
|
||||
'readFile,writeFile,readdir,mkdir,stat,unlink,chmod,chdir,getcwd,readlink,symlink,exists,lstat,rmdir,mount,umount,kill,rename,link,access,utimes,truncate,ftruncate,fsync,fdatasync,pathconf',
|
||||
'access,chdir,chmod,exists,fcntl,fdatasync,fsync,ftruncate,getcwd,kill,link,lstat,mkdir,mount,pathconf,posixPoll,readFile,readdir,readlink,rename,rmdir,stat,symlink,truncate,umount,unlink,utimes,writeFile',
|
||||
/** POSIX.1 XSH-style names documented in `/proc/bare_os/syscalls.json` opsDetail (not separate ctx ops). */
|
||||
BARE_OS_POSIX_XSH_OPS:
|
||||
'open,close,read,write,lseek,pipe,dup,dup2,fcntl',
|
||||
'open,close,read,write,lseek,pipe,dup,dup2,fcntl,poll',
|
||||
/** Encodings accepted by `/bin/iconv` (subset; case-insensitive names). */
|
||||
BARE_OS_ICONV_ENCODINGS: 'UTF-8,ISO-8859-1,UTF-16LE,UTF-16BE',
|
||||
/** Synthetic process table JSON path (logical VFS). */
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* Live pathconf: union and read-only mirror paths affect _PC_NO_TRUNC / _PC_CHOWN_RESTRICTED.
|
||||
*/
|
||||
import { readFile } from 'node:fs/promises'
|
||||
import path from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import test from 'brittle'
|
||||
import b4a from 'b4a'
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||
|
||||
const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor
|
||||
|
||||
async function loadBin(name) {
|
||||
const runtime = await readFile(
|
||||
path.join(__dirname, '../lib/runtime.js'),
|
||||
'utf8'
|
||||
)
|
||||
const body = await readFile(
|
||||
path.join(__dirname, `../src/${name}.js`),
|
||||
'utf8'
|
||||
)
|
||||
return new AsyncFunction(
|
||||
'ctx',
|
||||
'argv',
|
||||
`${runtime}\n${body}\nif (typeof run === 'function') return await run(ctx, argv)\n`
|
||||
)
|
||||
}
|
||||
|
||||
function bareOsPathconfFromIndexLogic(shellEnv, vfs) {
|
||||
return (pathArg, name) => {
|
||||
const k = String(name || '').trim()
|
||||
const p = vfs.resolveLogical(String(pathArg || '/').trim() || '/')
|
||||
const rawUnion = String(shellEnv.BARE_OS_VFS_UNION_PREFIXES || '')
|
||||
const underUnion = rawUnion
|
||||
.split(',')
|
||||
.map((s) => s.trim())
|
||||
.filter((x) => x.startsWith('/'))
|
||||
.some((pre) => p === pre || p.startsWith(pre + '/'))
|
||||
const underMirror = p === '/mirror' || p.startsWith('/mirror/')
|
||||
const table = {
|
||||
_PC_NAME_MAX: 255,
|
||||
_PC_NO_TRUNC: 1,
|
||||
_PC_CHOWN_RESTRICTED: 1
|
||||
}
|
||||
if (k === '_PC_CHOWN_RESTRICTED') {
|
||||
if (underMirror) return 0
|
||||
return 1
|
||||
}
|
||||
if (k === '_PC_NO_TRUNC' && (underMirror || underUnion)) return 0
|
||||
if (table[k] === undefined) throw new Error('unknown: ' + k)
|
||||
return table[k]
|
||||
}
|
||||
}
|
||||
|
||||
test('getconf pathconf _PC_NO_TRUNC is 0 under union prefix', async (t) => {
|
||||
const run = await loadBin('getconf')
|
||||
const logs = []
|
||||
const shellEnv = {
|
||||
BARE_OS_VFS_UNION_PREFIXES: '/system'
|
||||
}
|
||||
const vfs = {
|
||||
resolveLogical(p) {
|
||||
return String(p || '/')
|
||||
}
|
||||
}
|
||||
const ctx = {
|
||||
b4a,
|
||||
exitCode: 0,
|
||||
env: shellEnv,
|
||||
vfs,
|
||||
bareOsPathconf: bareOsPathconfFromIndexLogic(shellEnv, vfs),
|
||||
console: {
|
||||
log(s) {
|
||||
logs.push(String(s))
|
||||
},
|
||||
error(s) {
|
||||
logs.push(String(s))
|
||||
}
|
||||
}
|
||||
}
|
||||
await run(ctx, ['getconf', '_PC_NO_TRUNC', '/system/bin/foo'])
|
||||
t.is(ctx.exitCode, 0)
|
||||
t.is(logs.pop(), '0')
|
||||
})
|
||||
|
||||
test('getconf pathconf _PC_CHOWN_RESTRICTED is 0 on mirror tree', async (t) => {
|
||||
const run = await loadBin('getconf')
|
||||
const logs = []
|
||||
const shellEnv = {}
|
||||
const vfs = {
|
||||
resolveLogical(p) {
|
||||
return String(p || '/')
|
||||
}
|
||||
}
|
||||
const ctx = {
|
||||
b4a,
|
||||
exitCode: 0,
|
||||
env: shellEnv,
|
||||
vfs,
|
||||
bareOsPathconf: bareOsPathconfFromIndexLogic(shellEnv, vfs),
|
||||
console: {
|
||||
log(s) {
|
||||
logs.push(String(s))
|
||||
},
|
||||
error(s) {
|
||||
logs.push(String(s))
|
||||
}
|
||||
}
|
||||
}
|
||||
await run(ctx, ['getconf', '_PC_CHOWN_RESTRICTED', '/mirror/aux0/x'])
|
||||
t.is(ctx.exitCode, 0)
|
||||
t.is(logs.pop(), '0')
|
||||
})
|
||||
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* Golden-style checks for high-traffic XCU utilities (bounded POSIX Issue 7 alignment).
|
||||
*/
|
||||
import { readFile } from 'node:fs/promises'
|
||||
import path from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import test from 'brittle'
|
||||
import b4a from 'b4a'
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||
|
||||
const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
*/
|
||||
async function loadBin(name) {
|
||||
const runtime = await readFile(
|
||||
path.join(__dirname, '../lib/runtime.js'),
|
||||
'utf8'
|
||||
)
|
||||
const body = await readFile(
|
||||
path.join(__dirname, `../src/${name}.js`),
|
||||
'utf8'
|
||||
)
|
||||
return new AsyncFunction(
|
||||
'ctx',
|
||||
'argv',
|
||||
`${runtime}\n${body}\nif (typeof run === 'function') return await run(ctx, argv)\n`
|
||||
)
|
||||
}
|
||||
|
||||
function createCtx() {
|
||||
const logs = []
|
||||
return {
|
||||
b4a,
|
||||
exitCode: 0,
|
||||
vfs: { async stat() { return null } },
|
||||
console: {
|
||||
log: (m) => logs.push(String(m)),
|
||||
error: (m) => logs.push(String(m))
|
||||
},
|
||||
_logs: logs
|
||||
}
|
||||
}
|
||||
|
||||
test('/bin/test: string equality and unary -z', async (t) => {
|
||||
const run = await loadBin('test')
|
||||
const c1 = createCtx()
|
||||
await run(c1, ['test', 'ab', '=', 'ab'])
|
||||
t.is(c1.exitCode, 0)
|
||||
const c2 = createCtx()
|
||||
await run(c2, ['test', 'ab', '!=', 'cd'])
|
||||
t.is(c2.exitCode, 0)
|
||||
const c3 = createCtx()
|
||||
await run(c3, ['test', '-z', ''])
|
||||
t.is(c3.exitCode, 0)
|
||||
})
|
||||
|
||||
test('/bin/expr: integer arithmetic and comparison', async (t) => {
|
||||
const run = await loadBin('expr')
|
||||
const c = createCtx()
|
||||
await run(c, ['expr', '3', '+', '4'])
|
||||
t.is(c.exitCode, 0)
|
||||
t.is(c._logs.join('\n'), '7')
|
||||
const c2 = createCtx()
|
||||
await run(c2, ['expr', '5', '>', '2'])
|
||||
t.is(c2.exitCode, 0)
|
||||
t.is(c2._logs.join('\n'), '1')
|
||||
})
|
||||
|
||||
test('/bin/printf: %d and literal percent', async (t) => {
|
||||
const run = await loadBin('printf')
|
||||
const c = createCtx()
|
||||
await run(c, ['printf', '%d%%\n', '42'])
|
||||
t.is(c.exitCode, 0)
|
||||
t.is(c._logs.join(''), '42%\n')
|
||||
})
|
||||
@@ -4,6 +4,7 @@ Cross-package **version alignment** (ctx API, feature-bits doc, lifecycle schema
|
||||
|
||||
## 0.9.0
|
||||
|
||||
- **`BARE_OS_POSIX_PROFILE_VERSION` `1.0.3`** — Documents **`pathconf`** union/mirror hints and logical **`kill`** process-group negatives in the declared profile (handbook + appendix).
|
||||
- **`lib/protomux-channel-schema.js`**: **`BARE_OS_PROTOMUX_CHANNEL_SCHEMA_VERSION`** handshake marker; **`BARE_OS_PROTOMUX_CORK_HINT_VERSION`** documents cork/uncork batching expectations; both re-exported from **`lib/channel.js`** for strict channel versioning.
|
||||
- **Breaking (wire):** **`bare_os.capabilities`** and related surfaces use **`kernelCapabilityWords`** (semantic camelCase keys) plus **`kernelCapabilityWireVersion: 2`** instead of top-level **`bits`** … **`bits11`**. Numeric masks and bit positions are unchanged; peers must upgrade together.
|
||||
- **`lib/kernel-capability-wire.js`**: **`BARE_OS_KERNEL_CAPABILITY_WIRE_VERSION`**, **`buildKernelCapabilityWordsObject`**, **`getKernelCapabilityWords`**, **`readKernelCapabilityWord`**, **`BARE_OS_KERNEL_CAPABILITY_WORD_KEYS`**.
|
||||
|
||||
@@ -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.1'
|
||||
export const BARE_OS_POSIX_PROFILE_VERSION = '1.0.3'
|
||||
|
||||
/** Short identifier for telemetry and `/proc` mirrors. */
|
||||
export const BARE_OS_POSIX_PROFILE_ID = 'bare-os-posix-like'
|
||||
|
||||
@@ -139,10 +139,10 @@ const CONF = {
|
||||
_PC_2_SYMLINKS: '1',
|
||||
/** Comma-separated `ctx.bareOsSyscall` op names implemented in stock booter. */
|
||||
BARE_OS_SYSCALL_OPS:
|
||||
'readFile,writeFile,readdir,mkdir,stat,unlink,chmod,chdir,getcwd,readlink,symlink,exists,lstat,rmdir,mount,umount,kill,rename,link,access,utimes,truncate,ftruncate,fsync,fdatasync,pathconf',
|
||||
'access,chdir,chmod,exists,fcntl,fdatasync,fsync,ftruncate,getcwd,kill,link,lstat,mkdir,mount,pathconf,posixPoll,readFile,readdir,readlink,rename,rmdir,stat,symlink,truncate,umount,unlink,utimes,writeFile',
|
||||
/** POSIX.1 XSH-style names documented in `/proc/bare_os/syscalls.json` opsDetail (not separate ctx ops). */
|
||||
BARE_OS_POSIX_XSH_OPS:
|
||||
'open,close,read,write,lseek,pipe,dup,dup2,fcntl',
|
||||
'open,close,read,write,lseek,pipe,dup,dup2,fcntl,poll',
|
||||
/** Encodings accepted by `/bin/iconv` (subset; case-insensitive names). */
|
||||
BARE_OS_ICONV_ENCODINGS: 'UTF-8,ISO-8859-1,UTF-16LE,UTF-16BE',
|
||||
/** Synthetic process table JSON path (logical VFS). */
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"schemaVersion": 4,
|
||||
"ctxApiVersion": "1.34.0",
|
||||
"ctxApiVersion": "1.36.0",
|
||||
"posixProfile": {
|
||||
"id": "bare-os-posix-like",
|
||||
"version": "1.0.1"
|
||||
"version": "1.0.3"
|
||||
},
|
||||
"ops": ["readFile", "writeFile"],
|
||||
"opsDetail": [
|
||||
@@ -21,7 +21,7 @@
|
||||
"posixXsh": {
|
||||
"schema": 1,
|
||||
"note": "POSIX.1 XSH names in opsDetail with posixAlignment; not ctx.bareOsSyscall op strings.",
|
||||
"namesCsv": "open,close,read,write,lseek,pipe,dup,dup2,fcntl"
|
||||
"namesCsv": "open,close,read,write,lseek,pipe,dup,dup2,fcntl,poll"
|
||||
},
|
||||
"errnoHints": {
|
||||
"ENOENT": 2,
|
||||
|
||||
@@ -44,8 +44,8 @@ async function invokeCtxBootHooks(ctx, ev) {
|
||||
* BARE_OS_ONBOOT (newline-separated) or, if unset, every such line from /etc/bare-os/onboot
|
||||
* in file order (trusted). Then readLine yields EOF.
|
||||
*
|
||||
* BARE_OS_BOOT_TRACE=json logs one JSON object per boot stage on stderr: **`step`** (preferred), **`stage`**, legacy **`phase`** mirror, **`ms`**, **`bootTraceSchemaVersion`** (2).
|
||||
* BARE_OS_BOOT_TRACE=ndjson logs **`bootTraceSchemaVersion`**: **2** lines with **`type":"boot"`**, **`step`**, **`stage`**, **`phase`** (mirror), **`ms`**, **`sessionId`**, **`ts`**.
|
||||
* BARE_OS_BOOT_TRACE=json logs one JSON object per boot stage on stderr: **`step`** (preferred), **`stage`**, legacy **`phase`** mirror, **`ms`**, **`bootTraceSchemaVersion`** (2). Guest **`os-release`**, **`motd`**, and **`banner`** lines also emit **`type":"bootOutput"`** previews on stderr (stdout body still goes through **`ctx.console.log`** only).
|
||||
* BARE_OS_BOOT_TRACE=ndjson logs **`bootTraceSchemaVersion`**: **2** lines with **`type":"boot"`**, **`step`**, **`stage`**, **`phase`** (mirror), **`ms`**, **`sessionId`**, **`ts`**, plus **`type":"bootOutput"`** for guest-visible text previews.
|
||||
*
|
||||
* BARE_OS_BOOT_MINIMAL=1 or true: skip profile rc, rc, rc.d, rc.local, kernel.d (recovery shell).
|
||||
* BARE_OS_BOOT_SAFE_MODE=1 or true: after boot policy merge, skip rc.d, kernel.ext.d, onboot (lighter recovery).
|
||||
@@ -123,6 +123,62 @@ function isBootTraceNdjson(ctx) {
|
||||
return ctx.env && ctx.env.BARE_OS_BOOT_TRACE === 'ndjson'
|
||||
}
|
||||
|
||||
/**
|
||||
* Guest-visible boot text via **`ctx.console.log`** only (booter session / var-log mirror).
|
||||
* When **`BARE_OS_BOOT_TRACE`** is **`json`** or **`ndjson`**, append one stderr JSON line per emission (**`type":"bootOutput"`**, preview-capped) for CI and hosts.
|
||||
* @param {Record<string, unknown>} ctx
|
||||
* @param {string} step
|
||||
* @param {string} text
|
||||
*/
|
||||
function bootGuestOutLine(ctx, step, text) {
|
||||
const line = String(text)
|
||||
const out = ctx.console
|
||||
if (out && typeof out.log === 'function') out.log(line)
|
||||
else if (
|
||||
globalThis.console &&
|
||||
typeof globalThis.console.log === 'function'
|
||||
) {
|
||||
globalThis.console.log(line)
|
||||
}
|
||||
if (!wantBootTrace(ctx)) return
|
||||
const prev =
|
||||
isBootTraceNdjson(ctx) && line.length > 400
|
||||
? line.slice(0, 400) + '\u2026'
|
||||
: isBootTraceJson(ctx) && line.length > 200
|
||||
? line.slice(0, 200) + '\u2026'
|
||||
: line
|
||||
const errFn =
|
||||
out && typeof out.error === 'function'
|
||||
? out.error.bind(out)
|
||||
: globalThis.console && typeof globalThis.console.error === 'function'
|
||||
? globalThis.console.error.bind(globalThis.console)
|
||||
: null
|
||||
if (!errFn) return
|
||||
if (isBootTraceNdjson(ctx)) {
|
||||
errFn(
|
||||
JSON.stringify({
|
||||
type: 'bootOutput',
|
||||
bootTraceSchemaVersion: 2,
|
||||
step,
|
||||
stream: 'stdout',
|
||||
sessionId: String((ctx.env && ctx.env.BARE_OS_SESSION_ID) || ''),
|
||||
ts: Date.now(),
|
||||
textPreview: prev
|
||||
})
|
||||
)
|
||||
} else if (isBootTraceJson(ctx)) {
|
||||
errFn(
|
||||
JSON.stringify({
|
||||
bootTraceSchemaVersion: 2,
|
||||
type: 'bootOutput',
|
||||
step,
|
||||
stream: 'stdout',
|
||||
textPreview: prev
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Record<string, unknown>} ctx
|
||||
*/
|
||||
@@ -1792,12 +1848,14 @@ async function runRcLines(ctx, text) {
|
||||
* @param {Record<string, unknown>} ctx
|
||||
*/
|
||||
async function printOsRelease(ctx) {
|
||||
const { drive, b4a, console } = ctx
|
||||
const { drive, b4a } = ctx
|
||||
try {
|
||||
const rel = await drive.get('/etc/os-release')
|
||||
if (rel) console.log(b4a.toString(rel))
|
||||
if (rel) bootGuestOutLine(ctx, 'os-release', b4a.toString(rel))
|
||||
} catch (e) {
|
||||
console.error((e && e.message) || String(e))
|
||||
const c = ctx.console
|
||||
if (c && typeof c.error === 'function')
|
||||
c.error((e && e.message) || String(e))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1805,12 +1863,14 @@ async function printOsRelease(ctx) {
|
||||
* @param {Record<string, unknown>} ctx
|
||||
*/
|
||||
async function printMotd(ctx) {
|
||||
const { drive, b4a, console } = ctx
|
||||
const { drive, b4a } = ctx
|
||||
try {
|
||||
const motd = await drive.get('/etc/motd')
|
||||
if (motd) console.log(b4a.toString(motd).trimEnd())
|
||||
if (motd) bootGuestOutLine(ctx, 'motd', b4a.toString(motd).trimEnd())
|
||||
} catch (e) {
|
||||
console.error((e && e.message) || String(e))
|
||||
const c = ctx.console
|
||||
if (c && typeof c.error === 'function')
|
||||
c.error((e && e.message) || String(e))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2444,12 +2504,12 @@ async function runBareOsRcDir(ctx) {
|
||||
* @param {Record<string, unknown>} ctx
|
||||
*/
|
||||
async function printSessionBanner(ctx) {
|
||||
const { drive, b4a, console } = ctx
|
||||
const { drive, b4a } = ctx
|
||||
for (const p of ['/etc/bare-os/banner', '/etc/issue']) {
|
||||
try {
|
||||
const buf = await drive.get(p)
|
||||
if (buf) {
|
||||
console.log(b4a.toString(buf).trimEnd())
|
||||
bootGuestOutLine(ctx, 'banner', b4a.toString(buf).trimEnd())
|
||||
return
|
||||
}
|
||||
} catch {
|
||||
@@ -2459,12 +2519,14 @@ async function printSessionBanner(ctx) {
|
||||
const defaultBanner =
|
||||
'Bare operating system — guest session (login [--new] <passphrase> to unlock) | shell: cd, export, if/fi, && || ;, |, &, jobs/fg, <<<, exit | services: systemctl list-units, journalctl -u UNIT | try: help, ls /bin, crontab -l'
|
||||
if (ctx.bareOsSkipRepl) {
|
||||
console.log(
|
||||
bootGuestOutLine(
|
||||
ctx,
|
||||
'banner',
|
||||
'Bare operating system — non-interactive session (BARE_OS_SKIP_REPL).'
|
||||
)
|
||||
return
|
||||
}
|
||||
console.log(defaultBanner)
|
||||
bootGuestOutLine(ctx, 'banner', defaultBanner)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,18 +7,18 @@
|
||||
"b4a"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/safetyCatch.js",
|
||||
"keys": [
|
||||
"safetyCatch"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/hypercoreIdEncoding.js",
|
||||
"keys": [
|
||||
"hypercoreIdEncoding"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/safetyCatch.js",
|
||||
"keys": [
|
||||
"safetyCatch"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/compactEncoding.js",
|
||||
"keys": [
|
||||
@@ -38,9 +38,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareEncoding.js",
|
||||
"path": "/lib/bare/bundles/barePath.js",
|
||||
"keys": [
|
||||
"bareEncoding"
|
||||
"barePath"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -50,9 +50,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/barePath.js",
|
||||
"path": "/lib/bare/bundles/bareEncoding.js",
|
||||
"keys": [
|
||||
"barePath"
|
||||
"bareEncoding"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -73,12 +73,6 @@
|
||||
"bareAnsiEscapes"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareAddonResolve.js",
|
||||
"keys": [
|
||||
"bareAddonResolve"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareReadline.js",
|
||||
"keys": [
|
||||
@@ -91,6 +85,18 @@
|
||||
"bareCrypto"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareAddonResolve.js",
|
||||
"keys": [
|
||||
"bareAddonResolve"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareAtomics.js",
|
||||
"keys": [
|
||||
"bareAtomics"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareAsyncHooks.js",
|
||||
"keys": [
|
||||
@@ -103,30 +109,24 @@
|
||||
"bareAppKit"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareAssert.js",
|
||||
"keys": [
|
||||
"bareAssert"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareApk.js",
|
||||
"keys": [
|
||||
"bareApk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareAtomics.js",
|
||||
"keys": [
|
||||
"bareAtomics"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/fetch.js",
|
||||
"keys": [
|
||||
"fetch"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareAssert.js",
|
||||
"keys": [
|
||||
"bareAssert"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareBmp.js",
|
||||
"keys": [
|
||||
@@ -145,6 +145,12 @@
|
||||
"bareBuffer"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareBundleEvaluate.js",
|
||||
"keys": [
|
||||
"bareBundleEvaluate"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareBundle.js",
|
||||
"keys": [
|
||||
@@ -157,48 +163,42 @@
|
||||
"bareBluetoothApple"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareBundleEvaluate.js",
|
||||
"keys": [
|
||||
"bareBundleEvaluate"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareBoot.js",
|
||||
"keys": [
|
||||
"bareBoot"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareConsole.js",
|
||||
"keys": [
|
||||
"bareConsole"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareBundleId.js",
|
||||
"keys": [
|
||||
"bareBundleId"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareConsole.js",
|
||||
"keys": [
|
||||
"bareConsole"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareDebugLog.js",
|
||||
"keys": [
|
||||
"bareDebugLog"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareDelta.js",
|
||||
"keys": [
|
||||
"bareDelta"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareChannel.js",
|
||||
"keys": [
|
||||
"bareChannel"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareDelta.js",
|
||||
"keys": [
|
||||
"bareDelta"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareDaemon.js",
|
||||
"keys": [
|
||||
@@ -229,6 +229,12 @@
|
||||
"bareExif"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareDgram.js",
|
||||
"keys": [
|
||||
"bareDgram"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareCov.js",
|
||||
"keys": [
|
||||
@@ -241,12 +247,6 @@
|
||||
"bareFfmpeg"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareDgram.js",
|
||||
"keys": [
|
||||
"bareDgram"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareFfmpegEncodings.js",
|
||||
"keys": [
|
||||
@@ -254,9 +254,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareFormData.js",
|
||||
"path": "/lib/bare/bundles/bareGif.js",
|
||||
"keys": [
|
||||
"bareFormData"
|
||||
"bareGif"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -266,9 +266,15 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareGif.js",
|
||||
"path": "/lib/bare/bundles/bareFormData.js",
|
||||
"keys": [
|
||||
"bareGif"
|
||||
"bareFormData"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareHrtime.js",
|
||||
"keys": [
|
||||
"bareHrtime"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -289,12 +295,6 @@
|
||||
"bareFileLogger"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareHrtime.js",
|
||||
"keys": [
|
||||
"bareHrtime"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareFs.js",
|
||||
"keys": [
|
||||
@@ -319,18 +319,18 @@
|
||||
"bareImageResample"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareInspect.js",
|
||||
"keys": [
|
||||
"bareInspect"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareHttp1.js",
|
||||
"keys": [
|
||||
"bareHttp1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareInspect.js",
|
||||
"keys": [
|
||||
"bareInspect"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareHttps.js",
|
||||
"keys": [
|
||||
@@ -355,6 +355,12 @@
|
||||
"bareIpc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareInspector.js",
|
||||
"keys": [
|
||||
"bareInspector"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareLief.js",
|
||||
"keys": [
|
||||
@@ -374,9 +380,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareInspector.js",
|
||||
"path": "/lib/bare/bundles/bareModule.js",
|
||||
"keys": [
|
||||
"bareInspector"
|
||||
"bareModule"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -385,60 +391,30 @@
|
||||
"bareMake"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareModuleResolve.js",
|
||||
"keys": [
|
||||
"bareModuleResolve"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareModuleLexer.js",
|
||||
"keys": [
|
||||
"bareModuleLexer"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareModule.js",
|
||||
"keys": [
|
||||
"bareModule"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareModuleTraverse.js",
|
||||
"keys": [
|
||||
"bareModuleTraverse"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareNdk.js",
|
||||
"keys": [
|
||||
"bareNdk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareModuleResolve.js",
|
||||
"keys": [
|
||||
"bareModuleResolve"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareNative.js",
|
||||
"keys": [
|
||||
"bareNative"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareNodeFetch.js",
|
||||
"keys": [
|
||||
"bareNodeFetch"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareOpen.js",
|
||||
"keys": [
|
||||
"bareOpen"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareOs.js",
|
||||
"keys": [
|
||||
"bareOs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareMedia.js",
|
||||
"keys": [
|
||||
@@ -446,15 +422,33 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareNet.js",
|
||||
"path": "/lib/bare/bundles/bareModuleTraverse.js",
|
||||
"keys": [
|
||||
"bareNet"
|
||||
"bareModuleTraverse"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/barePackDrive.js",
|
||||
"path": "/lib/bare/bundles/bareNodeFetch.js",
|
||||
"keys": [
|
||||
"barePackDrive"
|
||||
"bareNodeFetch"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareOs.js",
|
||||
"keys": [
|
||||
"bareOs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareOpen.js",
|
||||
"keys": [
|
||||
"bareOpen"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareNet.js",
|
||||
"keys": [
|
||||
"bareNet"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -470,9 +464,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/barePng.js",
|
||||
"path": "/lib/bare/bundles/barePackDrive.js",
|
||||
"keys": [
|
||||
"barePng"
|
||||
"barePackDrive"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -481,12 +475,30 @@
|
||||
"barePipe"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/barePng.js",
|
||||
"keys": [
|
||||
"barePng"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareNodeRuntime.js",
|
||||
"keys": [
|
||||
"bareNodeRuntime"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/barePrebuild.js",
|
||||
"keys": [
|
||||
"barePrebuild"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareDev.js",
|
||||
"keys": [
|
||||
"bareDev"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/barePunycode.js",
|
||||
"keys": [
|
||||
@@ -494,9 +506,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/barePrebuild.js",
|
||||
"path": "/lib/bare/bundles/bareProcess.js",
|
||||
"keys": [
|
||||
"barePrebuild"
|
||||
"bareProcess"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -517,108 +529,102 @@
|
||||
"bareRealm"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareDev.js",
|
||||
"keys": [
|
||||
"bareDev"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareProcess.js",
|
||||
"keys": [
|
||||
"bareProcess"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/barePromClient.js",
|
||||
"keys": [
|
||||
"barePromClient"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareRuntime.js",
|
||||
"keys": [
|
||||
"bareRuntime"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareRepl.js",
|
||||
"keys": [
|
||||
"bareRepl"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareSdl.js",
|
||||
"keys": [
|
||||
"bareSdl"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareSemver.js",
|
||||
"keys": [
|
||||
"bareSemver"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareRpc.js",
|
||||
"keys": [
|
||||
"bareRpc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareSignals.js",
|
||||
"keys": [
|
||||
"bareSignals"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareRun.js",
|
||||
"keys": [
|
||||
"bareRun"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareSemver.js",
|
||||
"keys": [
|
||||
"bareSemver"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareRepl.js",
|
||||
"keys": [
|
||||
"bareRepl"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/barePromClient.js",
|
||||
"keys": [
|
||||
"barePromClient"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareSignals.js",
|
||||
"keys": [
|
||||
"bareSignals"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareSidecar.js",
|
||||
"keys": [
|
||||
"bareSidecar"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareStorage.js",
|
||||
"keys": [
|
||||
"bareStorage"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareStream.js",
|
||||
"keys": [
|
||||
"bareStream"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareSvg.js",
|
||||
"keys": [
|
||||
"bareSvg"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareStdio.js",
|
||||
"keys": [
|
||||
"bareStdio"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareStringDecoder.js",
|
||||
"keys": [
|
||||
"bareStringDecoder"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareStream.js",
|
||||
"keys": [
|
||||
"bareStream"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareStorage.js",
|
||||
"keys": [
|
||||
"bareStorage"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareStructuredClone.js",
|
||||
"keys": [
|
||||
"bareStructuredClone"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareStdio.js",
|
||||
"keys": [
|
||||
"bareStdio"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareSvg.js",
|
||||
"keys": [
|
||||
"bareSvg"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareTiff.js",
|
||||
"keys": [
|
||||
"bareTiff"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareSystemLogger.js",
|
||||
"keys": [
|
||||
@@ -638,9 +644,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareTiff.js",
|
||||
"path": "/lib/bare/bundles/bareTpl.js",
|
||||
"keys": [
|
||||
"bareTiff"
|
||||
"bareTpl"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -661,30 +667,12 @@
|
||||
"bareTimers"
|
||||
]
|
||||
},
|
||||
{
|
||||
"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/bareUnpack.js",
|
||||
"keys": [
|
||||
"bareUnpack"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareTls.js",
|
||||
"keys": [
|
||||
@@ -697,6 +685,12 @@
|
||||
"bareTty"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareUnpack.js",
|
||||
"keys": [
|
||||
"bareUnpack"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareV8.js",
|
||||
"keys": [
|
||||
@@ -704,9 +698,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareUnionBundle.js",
|
||||
"path": "/lib/bare/bundles/bareUiKit.js",
|
||||
"keys": [
|
||||
"bareUnionBundle"
|
||||
"bareUiKit"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -715,6 +709,12 @@
|
||||
"bareWalkHandles"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareUnionBundle.js",
|
||||
"keys": [
|
||||
"bareUnionBundle"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareVm.js",
|
||||
"keys": [
|
||||
@@ -727,30 +727,30 @@
|
||||
"bareWebKit"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareWebp.js",
|
||||
"keys": [
|
||||
"bareWebp"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareWebKitGtk.js",
|
||||
"keys": [
|
||||
"bareWebKitGtk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareUtils.js",
|
||||
"keys": [
|
||||
"bareUtils"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareWebp.js",
|
||||
"keys": [
|
||||
"bareWebp"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareWhich.js",
|
||||
"keys": [
|
||||
"bareWhich"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareWebKitGtk.js",
|
||||
"keys": [
|
||||
"bareWebKitGtk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareWinUi.js",
|
||||
"keys": [
|
||||
@@ -769,18 +769,18 @@
|
||||
"bareV8ToIstanbul"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareZlib.js",
|
||||
"keys": [
|
||||
"bareZlib"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareWs.js",
|
||||
"keys": [
|
||||
"bareWs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareZlib.js",
|
||||
"keys": [
|
||||
"bareZlib"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/lib/bare/bundles/bareZmq.js",
|
||||
"keys": [
|
||||
@@ -1595,8 +1595,8 @@
|
||||
],
|
||||
"bundleProvenance": {
|
||||
"schemaVersion": 1,
|
||||
"generatedAt": "2026-04-05T02:38:13.070Z",
|
||||
"gitCommit": "8a6c1e7b0947b1a70fc837892c76d67222e44119",
|
||||
"generatedAt": "2026-04-05T03:03:09.352Z",
|
||||
"gitCommit": "985eadfda39fa8a847791ff5c7f8f40d4434d9ac",
|
||||
"nodeVersion": "v22.22.0",
|
||||
"bundleTier": "all",
|
||||
"normativeManifest": "packages/bare-os-booter/lib/bare-module-manifest.json",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"schema": 1,
|
||||
"atMs": 1775356692404,
|
||||
"atMs": 1775358188597,
|
||||
"commands": [
|
||||
"arch",
|
||||
"awk",
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
* BARE_OS_ONBOOT (newline-separated) or, if unset, every such line from /etc/bare-os/onboot
|
||||
* in file order (trusted). Then readLine yields EOF.
|
||||
*
|
||||
* BARE_OS_BOOT_TRACE=json logs one JSON object per boot stage on stderr: **`step`** (preferred), **`stage`**, legacy **`phase`** mirror, **`ms`**, **`bootTraceSchemaVersion`** (2).
|
||||
* BARE_OS_BOOT_TRACE=ndjson logs **`bootTraceSchemaVersion`**: **2** lines with **`type":"boot"`**, **`step`**, **`stage`**, **`phase`** (mirror), **`ms`**, **`sessionId`**, **`ts`**.
|
||||
* BARE_OS_BOOT_TRACE=json logs one JSON object per boot stage on stderr: **`step`** (preferred), **`stage`**, legacy **`phase`** mirror, **`ms`**, **`bootTraceSchemaVersion`** (2). Guest **`os-release`**, **`motd`**, and **`banner`** lines also emit **`type":"bootOutput"`** previews on stderr (stdout body still goes through **`ctx.console.log`** only).
|
||||
* BARE_OS_BOOT_TRACE=ndjson logs **`bootTraceSchemaVersion`**: **2** lines with **`type":"boot"`**, **`step`**, **`stage`**, **`phase`** (mirror), **`ms`**, **`sessionId`**, **`ts`**, plus **`type":"bootOutput"`** for guest-visible text previews.
|
||||
*
|
||||
* BARE_OS_BOOT_MINIMAL=1 or true: skip profile rc, rc, rc.d, rc.local, kernel.d (recovery shell).
|
||||
* BARE_OS_BOOT_SAFE_MODE=1 or true: after boot policy merge, skip rc.d, kernel.ext.d, onboot (lighter recovery).
|
||||
@@ -93,6 +93,62 @@ function isBootTraceNdjson(ctx) {
|
||||
return ctx.env && ctx.env.BARE_OS_BOOT_TRACE === 'ndjson'
|
||||
}
|
||||
|
||||
/**
|
||||
* Guest-visible boot text via **`ctx.console.log`** only (booter session / var-log mirror).
|
||||
* When **`BARE_OS_BOOT_TRACE`** is **`json`** or **`ndjson`**, append one stderr JSON line per emission (**`type":"bootOutput"`**, preview-capped) for CI and hosts.
|
||||
* @param {Record<string, unknown>} ctx
|
||||
* @param {string} step
|
||||
* @param {string} text
|
||||
*/
|
||||
function bootGuestOutLine(ctx, step, text) {
|
||||
const line = String(text)
|
||||
const out = ctx.console
|
||||
if (out && typeof out.log === 'function') out.log(line)
|
||||
else if (
|
||||
globalThis.console &&
|
||||
typeof globalThis.console.log === 'function'
|
||||
) {
|
||||
globalThis.console.log(line)
|
||||
}
|
||||
if (!wantBootTrace(ctx)) return
|
||||
const prev =
|
||||
isBootTraceNdjson(ctx) && line.length > 400
|
||||
? line.slice(0, 400) + '\u2026'
|
||||
: isBootTraceJson(ctx) && line.length > 200
|
||||
? line.slice(0, 200) + '\u2026'
|
||||
: line
|
||||
const errFn =
|
||||
out && typeof out.error === 'function'
|
||||
? out.error.bind(out)
|
||||
: globalThis.console && typeof globalThis.console.error === 'function'
|
||||
? globalThis.console.error.bind(globalThis.console)
|
||||
: null
|
||||
if (!errFn) return
|
||||
if (isBootTraceNdjson(ctx)) {
|
||||
errFn(
|
||||
JSON.stringify({
|
||||
type: 'bootOutput',
|
||||
bootTraceSchemaVersion: 2,
|
||||
step,
|
||||
stream: 'stdout',
|
||||
sessionId: String((ctx.env && ctx.env.BARE_OS_SESSION_ID) || ''),
|
||||
ts: Date.now(),
|
||||
textPreview: prev
|
||||
})
|
||||
)
|
||||
} else if (isBootTraceJson(ctx)) {
|
||||
errFn(
|
||||
JSON.stringify({
|
||||
bootTraceSchemaVersion: 2,
|
||||
type: 'bootOutput',
|
||||
step,
|
||||
stream: 'stdout',
|
||||
textPreview: prev
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Record<string, unknown>} ctx
|
||||
*/
|
||||
@@ -1762,12 +1818,14 @@ async function runRcLines(ctx, text) {
|
||||
* @param {Record<string, unknown>} ctx
|
||||
*/
|
||||
async function printOsRelease(ctx) {
|
||||
const { drive, b4a, console } = ctx
|
||||
const { drive, b4a } = ctx
|
||||
try {
|
||||
const rel = await drive.get('/etc/os-release')
|
||||
if (rel) console.log(b4a.toString(rel))
|
||||
if (rel) bootGuestOutLine(ctx, 'os-release', b4a.toString(rel))
|
||||
} catch (e) {
|
||||
console.error((e && e.message) || String(e))
|
||||
const c = ctx.console
|
||||
if (c && typeof c.error === 'function')
|
||||
c.error((e && e.message) || String(e))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1775,12 +1833,14 @@ async function printOsRelease(ctx) {
|
||||
* @param {Record<string, unknown>} ctx
|
||||
*/
|
||||
async function printMotd(ctx) {
|
||||
const { drive, b4a, console } = ctx
|
||||
const { drive, b4a } = ctx
|
||||
try {
|
||||
const motd = await drive.get('/etc/motd')
|
||||
if (motd) console.log(b4a.toString(motd).trimEnd())
|
||||
if (motd) bootGuestOutLine(ctx, 'motd', b4a.toString(motd).trimEnd())
|
||||
} catch (e) {
|
||||
console.error((e && e.message) || String(e))
|
||||
const c = ctx.console
|
||||
if (c && typeof c.error === 'function')
|
||||
c.error((e && e.message) || String(e))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2414,12 +2474,12 @@ async function runBareOsRcDir(ctx) {
|
||||
* @param {Record<string, unknown>} ctx
|
||||
*/
|
||||
async function printSessionBanner(ctx) {
|
||||
const { drive, b4a, console } = ctx
|
||||
const { drive, b4a } = ctx
|
||||
for (const p of ['/etc/bare-os/banner', '/etc/issue']) {
|
||||
try {
|
||||
const buf = await drive.get(p)
|
||||
if (buf) {
|
||||
console.log(b4a.toString(buf).trimEnd())
|
||||
bootGuestOutLine(ctx, 'banner', b4a.toString(buf).trimEnd())
|
||||
return
|
||||
}
|
||||
} catch {
|
||||
@@ -2429,12 +2489,14 @@ async function printSessionBanner(ctx) {
|
||||
const defaultBanner =
|
||||
'Bare operating system — guest session (login [--new] <passphrase> to unlock) | shell: cd, export, if/fi, && || ;, |, &, jobs/fg, <<<, exit | services: systemctl list-units, journalctl -u UNIT | try: help, ls /bin, crontab -l'
|
||||
if (ctx.bareOsSkipRepl) {
|
||||
console.log(
|
||||
bootGuestOutLine(
|
||||
ctx,
|
||||
'banner',
|
||||
'Bare operating system — non-interactive session (BARE_OS_SKIP_REPL).'
|
||||
)
|
||||
return
|
||||
}
|
||||
console.log(defaultBanner)
|
||||
bootGuestOutLine(ctx, 'banner', defaultBanner)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
File diff suppressed because one or more lines are too long
+7
-1
@@ -209,11 +209,17 @@ Asserts **[`docs/audit/bundle-health.json`](../docs/audit/bundle-health.json)**
|
||||
|
||||
Scans vendored **`kernel/lib/bare/bundles/*.js`** for **`TODO` / `FIXME` / `XXX` / `NOT_IMPLEMENTED` / `###TODO###`** unless the basename is listed in **[`docs/audit/bundle-marker-allowlist.json`](../docs/audit/bundle-marker-allowlist.json)**. Complements **`verify-runtime-no-incomplete-markers.mjs`** (hand-authored sources only). Root **`pretest`**.
|
||||
|
||||
## `sync-holepunch-clones.mjs`
|
||||
|
||||
**Usage:** `node scripts/sync-holepunch-clones.mjs`
|
||||
|
||||
Writes **[`docs/audit/holepunch-clone-sync-report.json`](../docs/audit/holepunch-clone-sync-report.json)** comparing **`packages/bare-os-booter/lib/bare-module-manifest.json`** entry names to optional local clones under **`BARE_OS_HOLEPUNCH_CLONES_ROOT`** (default **`$HOME/dev/pearcli/holepunch-repos/holepunchto_repos`**). Operator-maintained; not in CI.
|
||||
|
||||
## `verify-bundle-throws.mjs`
|
||||
|
||||
**Usage:** `node scripts/verify-bundle-throws.mjs`
|
||||
|
||||
Scans vendored bundles for **`throw new Error("not implemented")`** (case variants) unless listed in **[`docs/audit/bundle-throw-allowlist.json`](../docs/audit/bundle-throw-allowlist.json)**. Root **`pretest`**.
|
||||
Scans vendored **`kernel/lib/bare/bundles/*.js`** for **`new Error("…")`** string arguments whose message matches incomplete-implementation phrases (**`not implemented`**, **`not yet implemented`**, **`abstract method … not implemented`**), regardless of whether the call is **`throw`**, **`cb(…)`**, or **`emit("error", …)`**. Bundles listed in **[`docs/audit/bundle-throw-allowlist.json`](../docs/audit/bundle-throw-allowlist.json)** ( **`schemaVersion` 2** — per-file **`bundleFile`**, **`rationale`**, **`triggerCondition`**) are skipped. Rebuild bundles with **`npm run build -w bare-os-bare-libs`** before tightening the allowlist. Root **`pretest`**.
|
||||
|
||||
## `verify-kernel-program-doc.mjs`
|
||||
|
||||
|
||||
@@ -25,6 +25,9 @@ run('npm', ['test'])
|
||||
|
||||
console.log('[release-checklist] verify-kernel-seeder-parity')
|
||||
run('node', ['scripts/verify-kernel-seeder-parity.mjs'])
|
||||
console.log(
|
||||
'[release-checklist] If you edited kernel/lib/init/init-main.js or kernel/lib/boot/, run: node scripts/bundle-kernel-init.mjs && rsync -a --delete kernel/ packages/bare-os-seeder/kernel/ then re-run this script.'
|
||||
)
|
||||
|
||||
if (process.env.CHECK_DOC_LINKS === '1') {
|
||||
console.log('[release-checklist] doc link heuristic (CHECK_DOC_LINKS=1)')
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Compare local Holepunch org clones (optional) to workspace bundle sources.
|
||||
* Writes docs/audit/holepunch-clone-sync-report.json for operators.
|
||||
*
|
||||
* Env: BARE_OS_HOLEPUNCH_CLONES_ROOT — default ~/dev/pearcli/holepunch-repos/holepunchto_repos
|
||||
*/
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import process from 'node:process'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { execSync } from 'node:child_process'
|
||||
|
||||
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
|
||||
const manifestPath = path.join(
|
||||
root,
|
||||
'packages/bare-os-booter/lib/bare-module-manifest.json'
|
||||
)
|
||||
const outPath = path.join(root, 'docs/audit/holepunch-clone-sync-report.json')
|
||||
|
||||
const defaultClones = path.join(
|
||||
process.env.HOME || '',
|
||||
'dev/pearcli/holepunch-repos/holepunchto_repos'
|
||||
)
|
||||
const clonesRoot = String(
|
||||
process.env.BARE_OS_HOLEPUNCH_CLONES_ROOT || defaultClones
|
||||
).trim()
|
||||
|
||||
function gitHead(dir) {
|
||||
try {
|
||||
return execSync('git rev-parse HEAD', {
|
||||
cwd: dir,
|
||||
encoding: 'utf8',
|
||||
stdio: ['ignore', 'pipe', 'ignore']
|
||||
}).trim()
|
||||
} catch {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
function main() {
|
||||
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'))
|
||||
const entries = Array.isArray(manifest.entries) ? manifest.entries : []
|
||||
/** @type {string[]} */
|
||||
const names = []
|
||||
for (const e of entries) {
|
||||
const n =
|
||||
e && typeof e.name === 'string'
|
||||
? e.name.trim()
|
||||
: e && typeof e.packageName === 'string'
|
||||
? e.packageName.trim()
|
||||
: ''
|
||||
if (n) names.push(n)
|
||||
}
|
||||
const uniq = [...new Set(names)].sort()
|
||||
const rootExists = fs.existsSync(clonesRoot)
|
||||
const atMs = Date.now()
|
||||
/** @type {Record<string, unknown>[]} */
|
||||
const rows = []
|
||||
for (const name of uniq) {
|
||||
const cloneDir = path.join(clonesRoot, name)
|
||||
const exists = fs.existsSync(cloneDir)
|
||||
let pkgVersion = ''
|
||||
const pj = path.join(cloneDir, 'package.json')
|
||||
if (exists && fs.existsSync(pj)) {
|
||||
try {
|
||||
const p = JSON.parse(fs.readFileSync(pj, 'utf8'))
|
||||
pkgVersion = String(p.version || '').trim()
|
||||
} catch {
|
||||
pkgVersion = ''
|
||||
}
|
||||
}
|
||||
rows.push({
|
||||
name,
|
||||
clonePath: cloneDir,
|
||||
cloneExists: exists,
|
||||
packageJsonVersion: pkgVersion,
|
||||
gitHead: exists ? gitHead(cloneDir) : ''
|
||||
})
|
||||
}
|
||||
const report = {
|
||||
schema: 1,
|
||||
atMs,
|
||||
clonesRoot,
|
||||
clonesRootExists: rootExists,
|
||||
manifestPath: path.relative(root, manifestPath),
|
||||
entryCount: uniq.length,
|
||||
rows
|
||||
}
|
||||
fs.mkdirSync(path.dirname(outPath), { recursive: true })
|
||||
fs.writeFileSync(outPath, JSON.stringify(report, null, 2) + '\n')
|
||||
console.log(
|
||||
'sync-holepunch-clones: wrote',
|
||||
path.relative(root, outPath),
|
||||
'entries=',
|
||||
rows.length
|
||||
)
|
||||
}
|
||||
|
||||
main()
|
||||
@@ -1,7 +1,9 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* CI: flag vendored IIFE bundles that contain throw new Error("not implemented") style throws
|
||||
* (narrower than TODO markers; complements verify-bundle-markers.mjs).
|
||||
* CI: flag vendored IIFE bundles that contain Error throws with incomplete-implementation
|
||||
* messages (complements verify-bundle-markers.mjs). Allowlist entries carry rationale.
|
||||
*
|
||||
* Matches: throw new Error("…"), cb(new Error("…")), emit("error", new Error("…")), etc.
|
||||
*/
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
@@ -12,48 +14,80 @@ const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
|
||||
const bundlesDir = path.join(root, 'kernel/lib/bare/bundles')
|
||||
const allowPath = path.join(root, 'docs/audit/bundle-throw-allowlist.json')
|
||||
|
||||
const THROW_RE =
|
||||
/throw\s+new\s+Error\s*\(\s*['"](?:not\s+implemented|Not\s+implemented)['"]\s*\)/gi
|
||||
/** Message substring patterns that indicate stub / abstract upstream behavior. */
|
||||
const BAD_MESSAGE_RE =
|
||||
/not\s+yet\s+implemented|not\s+implemented|abstract\s+method[^'"]*not\s+implemented/i
|
||||
|
||||
/**
|
||||
* Extract string literals from new Error("…") or new Error('…') in source (handles \\ escapes).
|
||||
* @param {string} txt
|
||||
* @returns {Iterable<{ index: number, message: string, raw: string }>}
|
||||
*/
|
||||
function *iterNewErrorStringArgs(txt) {
|
||||
const re = /new\s+Error\s*\(\s*(['"])/g
|
||||
let m
|
||||
while ((m = re.exec(txt)) !== null) {
|
||||
const q = m[1]
|
||||
const start = m.index + m[0].length
|
||||
let i = start
|
||||
let out = ''
|
||||
while (i < txt.length) {
|
||||
const c = txt[i]
|
||||
if (c === q) {
|
||||
yield { index: m.index, message: out, raw: txt.slice(m.index, i + 1) }
|
||||
break
|
||||
}
|
||||
if (c === '\\' && i + 1 < txt.length) {
|
||||
out += txt[i + 1]
|
||||
i += 2
|
||||
continue
|
||||
}
|
||||
out += c
|
||||
i++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function loadAllowlist() {
|
||||
if (!fs.existsSync(allowPath)) return new Set()
|
||||
try {
|
||||
const j = JSON.parse(fs.readFileSync(allowPath, 'utf8'))
|
||||
const entries = Array.isArray(j.entries) ? j.entries : []
|
||||
/** @type {Set<string>} */
|
||||
const out = new Set()
|
||||
const allowedFiles = new Set()
|
||||
for (const e of entries) {
|
||||
const f = e && typeof e.bundleFile === 'string' ? e.bundleFile.trim() : ''
|
||||
if (f) out.add(f)
|
||||
if (f) allowedFiles.add(f)
|
||||
}
|
||||
return out
|
||||
return allowedFiles
|
||||
} catch {
|
||||
return new Set()
|
||||
}
|
||||
}
|
||||
|
||||
function main() {
|
||||
const allowed = loadAllowlist()
|
||||
const allowedFiles = loadAllowlist()
|
||||
/** @type {string[]} */
|
||||
const bad = []
|
||||
|
||||
for (const name of fs.readdirSync(bundlesDir)) {
|
||||
if (!name.endsWith('.js')) continue
|
||||
if (allowed.has(name)) continue
|
||||
if (allowedFiles.has(name)) continue
|
||||
const abs = path.join(bundlesDir, name)
|
||||
const txt = fs.readFileSync(abs, 'utf8')
|
||||
THROW_RE.lastIndex = 0
|
||||
const m = THROW_RE.exec(txt)
|
||||
if (m) {
|
||||
const line = txt.slice(0, m.index).split('\n').length
|
||||
bad.push(`${name}:${line}:${m[0].slice(0, 60)}…`)
|
||||
for (const { index, message } of iterNewErrorStringArgs(txt)) {
|
||||
if (!BAD_MESSAGE_RE.test(message)) continue
|
||||
const line = txt.slice(0, index).split('\n').length
|
||||
const preview = message.length > 72 ? message.slice(0, 72) + '…' : message
|
||||
bad.push(`${name}:${line}: ${preview}`)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (bad.length) {
|
||||
console.error(
|
||||
'verify-bundle-throws: disallowed "not implemented" throws in bundles (allowlist: docs/audit/bundle-throw-allowlist.json):'
|
||||
'verify-bundle-throws: disallowed incomplete-implementation Error messages in bundles.'
|
||||
)
|
||||
console.error('Allowlist (with rationale): docs/audit/bundle-throw-allowlist.json')
|
||||
for (const b of bad) console.error(' ', b)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
@@ -42,6 +42,9 @@ function main() {
|
||||
)
|
||||
if (onlyA.length) console.error('Only in kernel/:', onlyA.join(', '))
|
||||
if (onlyB.length) console.error('Only in seeder kernel/:', onlyB.join(', '))
|
||||
console.error(
|
||||
'Remediate from repo root: rsync -a --delete kernel/ packages/bare-os-seeder/kernel/'
|
||||
)
|
||||
process.exit(1)
|
||||
}
|
||||
/** @type {string[]} */
|
||||
@@ -58,6 +61,9 @@ function main() {
|
||||
'kernel/ vs packages/bare-os-seeder/kernel/: content differs for:',
|
||||
diffs.join(', ')
|
||||
)
|
||||
console.error(
|
||||
'Remediate: edit canonical kernel/ only, then: node scripts/bundle-kernel-init.mjs && rsync -a --delete kernel/ packages/bare-os-seeder/kernel/'
|
||||
)
|
||||
process.exit(1)
|
||||
}
|
||||
/** Staged /bin scripts must carry the BARE_OS_BIN_API pragma (see coreutils runtime). */
|
||||
|
||||
Reference in New Issue
Block a user