feat(booter): POSIX/P2P roadmap — syscalls v4, mirror VFS, seeder snapshot hints

- Bump ctx API to 1.31.0; extend /proc/bare_os/syscalls.json schema 4 (fdModel, signalModel)
- Add /mirror/aux* routing for auxiliary Hyperdrives; optional BLAKE2b-keyed /bin cache (BARE_OS_VFS_BIN_CACHE_BLAKE2B)
- Wire seeder snapshotHintsJson from BARE_OS_SEED_* env; add seeder unit tests
- Shell: trap -p; docs for jobs/bg/trap; sync schemas, examples, compatibility matrix, handbook ch.9
- Coreutils: Issue 7 man option rows for cp/mv/ln/find/grep/sed/awk/tar/test/true; xcu-issue7-sweep tests
- Docs: env appendix, vault-threat-model, node-vs-bare-host-matrix, test:bare, CHANGELOG 1.31.0
This commit is contained in:
Raven Scott
2026-04-04 22:00:01 -04:00
parent 57ba71899b
commit 92384990c5
60 changed files with 1587 additions and 529 deletions
+1
View File
@@ -16,6 +16,7 @@ Start at the **[documentation home](docs/README.md)** for role-based navigation
| Architecture narrative | [handbook/README.md](handbook/README.md) | | Architecture narrative | [handbook/README.md](handbook/README.md) |
| Extend `/bin`, `ctx`, Pear | [developer-guide/README.md](developer-guide/README.md) | | Extend `/bin`, `ctx`, Pear | [developer-guide/README.md](developer-guide/README.md) |
| File-level reference index | [docs/reference/README.md](docs/reference/README.md) | | File-level reference index | [docs/reference/README.md](docs/reference/README.md) |
| Node vs Bare test hosts | [docs/reference/node-vs-bare-host-matrix.md](docs/reference/node-vs-bare-host-matrix.md) |
| Kernel boundaries and boot steps | [docs/architecture/KERNEL_CONTRACT.md](docs/architecture/KERNEL_CONTRACT.md) | | Kernel boundaries and boot steps | [docs/architecture/KERNEL_CONTRACT.md](docs/architecture/KERNEL_CONTRACT.md) |
| Doc contribution guide | [docs/CONTRIBUTING-DOCS.md](docs/CONTRIBUTING-DOCS.md) | | Doc contribution guide | [docs/CONTRIBUTING-DOCS.md](docs/CONTRIBUTING-DOCS.md) |
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"schemaVersion": 1, "schemaVersion": 1,
"generatedAt": "2026-04-05T01:31:52.581Z", "generatedAt": "2026-04-05T01:58:32.353Z",
"normativeManifest": "packages/bare-os-booter/lib/bare-module-manifest.json", "normativeManifest": "packages/bare-os-booter/lib/bare-module-manifest.json",
"buildTool": "packages/bare-os-bare-libs/build.mjs", "buildTool": "packages/bare-os-bare-libs/build.mjs",
"bundles": [ "bundles": [
+1
View File
@@ -48,6 +48,7 @@ This directory holds the split **file-by-file inventory** that used to live in t
- **Bits, env, `/proc`, and `ctx` map** — [Kernel capabilities index](kernel-capabilities-index.md) - **Bits, env, `/proc`, and `ctx` map** — [Kernel capabilities index](kernel-capabilities-index.md)
- **`ctx` API versioning** — [ctx-api-versioning.md](ctx-api-versioning.md) - **`ctx` API versioning** — [ctx-api-versioning.md](ctx-api-versioning.md)
- **Version alignment** — [Compatibility matrix](compatibility-matrix.md) - **Version alignment** — [Compatibility matrix](compatibility-matrix.md)
- **Node vs Bare test hosts** — [node-vs-bare-host-matrix.md](node-vs-bare-host-matrix.md)
- **Legacy ↔ canonical names** — [Naming alias matrix](naming-alias-matrix.md) - **Legacy ↔ canonical names** — [Naming alias matrix](naming-alias-matrix.md)
- **Kernel program status tables** — [Feature roadmap status](feature-roadmap.md) - **Kernel program status tables** — [Feature roadmap status](feature-roadmap.md)
- **VFS path classes (policy sketch)** — [vfs-path-classes.md](vfs-path-classes.md) - **VFS path classes (policy sketch)** — [vfs-path-classes.md](vfs-path-classes.md)
+4 -3
View File
@@ -14,7 +14,7 @@ Single place to compare **protocol**, **booter `ctx` API**, **feature-bit docume
## Version artifacts ## Version artifacts
**`bareOsCtxApiVersion`** (current stock: **`1.30.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.31.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). **`BARE_OS_KERNEL_FEATURE_BITS_DOC`** (current stock: **`15`**) — Exported from [`packages/bare-os-protocol/lib/kernel-feature-bits.js`](../../packages/bare-os-protocol/lib/kernel-feature-bits.js). It versions the feature-bit documentation and related governance; keep it aligned with [`developer-guide/adr/001-kernel-feature-bits-governance.md`](../../developer-guide/adr/001-kernel-feature-bits-governance.md).
@@ -24,9 +24,9 @@ Single place to compare **protocol**, **booter `ctx` API**, **feature-bit docume
**Protocol package** — Workspace version in [`bare-os-protocol` `package.json`](../../packages/bare-os-protocol/package.json). Pair it with seeder and booter releases when you change wire behavior. **Protocol package** — Workspace version in [`bare-os-protocol` `package.json`](../../packages/bare-os-protocol/package.json). Pair it with seeder and booter releases when you change wire behavior.
**POSIX-like profile****`BARE_OS_POSIX_PROFILE_VERSION`** (**`1.0.0`**) and **`BARE_OS_POSIX_PROFILE_ID`** (**`bare-os-posix-like`**) in [`bare-os-protocol/lib/bare-os-posix-profile.js`](../../packages/bare-os-protocol/lib/bare-os-posix-profile.js). Surfaced under **`/proc/bare_os_features`** as **`capabilitySurface.posixProfile`** and in **`/proc/bare_os/syscalls.json`** (schema **3**). **POSIX-like profile****`BARE_OS_POSIX_PROFILE_VERSION`** (**`1.0.0`**) and **`BARE_OS_POSIX_PROFILE_ID`** (**`bare-os-posix-like`**) in [`bare-os-protocol/lib/bare-os-posix-profile.js`](../../packages/bare-os-protocol/lib/bare-os-posix-profile.js). Surfaced under **`/proc/bare_os_features`** as **`capabilitySurface.posixProfile`** and in **`/proc/bare_os/syscalls.json`** (schema **4**).
**`/proc/bare_os/syscalls.json`** — Schema **3** adds **`opsDetail`** (category + stability per op), full **`errnoHints`** table, and **`posixProfile`**. Example payload: [`kernel/etc/bare-os/syscalls.example.json`](../../kernel/etc/bare-os/syscalls.example.json); JSON Schema: [`docs/schemas/bare-os-syscalls.schema.json`](../schemas/bare-os-syscalls.schema.json). **`/proc/bare_os/syscalls.json`** — Schema **4** extends schema **3** with **`fdModel`** (logical FD table pointers) and **`signalModel`** (synthetic signal vocabulary). Schema **3** added **`opsDetail`**, **`errnoHints`**, and **`posixProfile`**. Example payload: [`kernel/etc/bare-os/syscalls.example.json`](../../kernel/etc/bare-os/syscalls.example.json); JSON Schema: [`docs/schemas/bare-os-syscalls.schema.json`](../schemas/bare-os-syscalls.schema.json).
--- ---
@@ -39,6 +39,7 @@ Single place to compare **protocol**, **booter `ctx` API**, **feature-bit docume
## See also ## See also
- [`node-vs-bare-host-matrix.md`](./node-vs-bare-host-matrix.md)
- [`kernel-extensions.md`](./kernel-extensions.md) - [`kernel-extensions.md`](./kernel-extensions.md)
- [`kernel-capabilities-index.md`](./kernel-capabilities-index.md) - [`kernel-capabilities-index.md`](./kernel-capabilities-index.md)
- [Handbook ch.11 — Kernel program](../../handbook/11-kernel-program-and-research.md) - [Handbook ch.11 — Kernel program](../../handbook/11-kernel-program-and-research.md)
@@ -1,16 +1,16 @@
// Auto-generated by scripts/gen-ctx-client-helper.mjs — do not edit by hand. // 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`. */ /** Canonical `ctx` API semver from `packages/bare-os-booter/lib/bare-os-ctx-api.js`. */
export const BARE_OS_CTX_API_CLIENT_VERSION = '1.30.0' as const export const BARE_OS_CTX_API_CLIENT_VERSION = '1.31.0' as const
/** Relative path to the hand-maintained TypeScript contract (schema source of truth). */ /** 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 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). */ /** Approximate count of `bareOs…` members in the DTS (diagnostic only; regenerate on contract edits). */
export const BARE_OS_CTX_DTS_BAREOS_MEMBER_APPROX = 122 as const export const BARE_OS_CTX_DTS_BAREOS_MEMBER_APPROX = 127 as const
/** Byte length of `bare-os-ctx.d.ts` when this file was generated. */ /** Byte length of `bare-os-ctx.d.ts` when this file was generated. */
export const BARE_OS_CTX_DTS_BYTES = 14640 as const export const BARE_OS_CTX_DTS_BYTES = 15264 as const
/** Pointers for external client generators (OpenAPI / JSON Schema follow-ups). */ /** Pointers for external client generators (OpenAPI / JSON Schema follow-ups). */
export const BARE_OS_CTX_SCHEMA_HINT = { export const BARE_OS_CTX_SCHEMA_HINT = {
@@ -16,6 +16,8 @@ Former **DOCUMENTATION.md** §14, §14a. [Reference index →](README.md)
The list below is one **bullet per variable** in the form **name — component — meaning**. Values shown as defaults are what the stock tree assumes when the variable is unset; always confirm in source when debugging a forked image. The list below is one **bullet per variable** in the form **name — component — meaning**. Values shown as defaults are what the stock tree assumes when the variable is unset; always confirm in source when debugging a forked image.
- `BARE_OS_KERNEL_ROOT` — Seeder — Absolute path to kernel tree (default: `repo/kernel`) - `BARE_OS_KERNEL_ROOT` — Seeder — Absolute path to kernel tree (default: `repo/kernel`)
- `BARE_OS_SEED_CORESTORE_SNAPSHOT_TAG` — Seeder — Optional tag string; when set (and **`BARE_OS_SEED_SNAPSHOT_HINTS_JSON`** is unset), the seeder builds **`snapshotHintsJson`** for **`bare_os.snapshot_hints`** so the booter can surface it under **`/proc/bare_os/snapshot_hints.json`** after RPC.
- `BARE_OS_SEED_SNAPSHOT_HINTS_JSON` — Seeder — Optional JSON string; when set, passed verbatim as **`snapshotHintsJson`** (wins over **`BARE_OS_SEED_CORESTORE_SNAPSHOT_TAG`**).
- `BARE_OS_HOST_DATA` — paths — Base directory for host state (default `~/.bare-os`; Corestore dirs live under `corestore/`) - `BARE_OS_HOST_DATA` — paths — Base directory for host state (default `~/.bare-os`; Corestore dirs live under `corestore/`)
- `BARE_OS_SEED_STORE` — Seeder — Corestore directory (default: `~/.bare-os/corestore/seeder`) - `BARE_OS_SEED_STORE` — Seeder — Corestore directory (default: `~/.bare-os/corestore/seeder`)
- `BARE_OS_BOOT_STORE` — Booter — Corestore for boot side (default: `~/.bare-os/corestore/booter`) - `BARE_OS_BOOT_STORE` — Booter — Corestore for boot side (default: `~/.bare-os/corestore/booter`)
@@ -41,12 +43,15 @@ The list below is one **bullet per variable** in the form **name — component
- `MANWIDTH``/bin/man` — Wrap width for manual text (default `72`; minimum `40`) - `MANWIDTH``/bin/man` — Wrap width for manual text (default `72`; minimum `40`)
- `NO_COLOR``/bin/man` — If set, disable ANSI bold for section headings on a TTY - `NO_COLOR``/bin/man` — If set, disable ANSI bold for section headings on a TTY
**Host → session passthrough** (booter copies into **`shellEnv`** when the host sets a non-empty value; canonical list in [`packages/bare-os-booter/index.js`](../../packages/bare-os-booter/index.js)): `BARE_OS_PIPELINE_MAX_STAGES`, `BARE_OS_PIPELINE_MAX_BYTES`, `BARE_OS_PIPELINE_MAX_LINES`, `BARE_OS_SHELL_STREAMING`, `BARE_OS_SHELL_STREAMING_MULT`, `BARE_OS_SHELL_CMDSUBST`, `BARE_OS_SHELL_CMDSUBST_MAX_BYTES`, `BARE_OS_SHELL_PARAM_EXPANSION`, `BARE_OS_SHELL_PARAM_EXPANSION_V2`, `BARE_OS_SHELL_PARAM_EXPANSION_V3`, `BARE_OS_ENV_DASH_S`, `BARE_OS_VFS_WATCH_PSEUDO`, `BARE_OS_VFS_WATCH_SWARM`, `BARE_OS_SHELL_LOCAL_DECLARE`, `BARE_OS_DIAGNOSTICS_SUBSCRIBE`, `BARE_OS_AUTOPASS_INVITE_URL`, `BARE_OS_BIN_WORKER_OFFLOAD`, `BARE_OS_BIN_WORKER_ALLOW`, `BARE_OS_BIN_WORKER_CPU_MS_MAX`, `BARE_OS_BOOT_PROFILE`, `BARE_OS_ONBOOT`, `BARE_OS_BOOT_STRICT`, `BARE_OS_RC_D_SKIP`, `BARE_OS_BOOT_MINIMAL`, `BARE_OS_BOOT_SKIP`, `BARE_OS_BOOT_SAFE_MODE`, `BARE_OS_BOOT_TRANSACTION_JOURNAL`, `BARE_OS_BOOT_CHECKPOINT`, `BARE_OS_BOOT_DRY_RUN`, `BARE_OS_BOOT_POLICY_PATH`, `BARE_OS_BOOT_ROLLBACK_APPLY`, `BARE_OS_BOOT_BUNDLE_DIGEST_HEX`, `BARE_OS_REQUIRE_CTX_API_MIN`, `BARE_OS_BOOT_ABI_STRICT`, `BARE_OS_LOADER_AUDIT`, `BARE_OS_DELEGATE_TRACE`, `BARE_OS_DNS_PROFILE`, `BARE_OS_BOOT_TRACE`, `BARE_OS_KERNEL_PROGRAM_PROC_JSON`, `BARE_OS_GIANT_PHASE_PROGRAM_JSON`, `BARE_OS_KERNEL_SELFTEST`, `BARE_OS_SELFTEST_FORMAT`, `BARE_OS_AUDIT`, `BARE_OS_AUDIT_JSON`, `BARE_OS_AUDIT_REDACT`, `BARE_OS_IMAGE_DIGEST`, `BARE_OS_EXEC_MAX_DEPTH`, `BARE_OS_EXEC_LINE_BUDGET_MS`, `BARE_OS_IPC_MAX_BYTES`, `BARE_OS_IPC_CHANNEL_MAX_BYTES`, `BARE_OS_IPC_RPC_TOKEN`, `BARE_OS_IPC_FANOUT`, `BARE_OS_IPC_JSON_MAX_BYTES`, `BARE_OS_IPC_NAMESPACE`, `BARE_OS_VFS_WATCH`, `BARE_OS_VFS_UNION_PREFIXES`, `BARE_OS_VFS_UNION_WRITE_DENY`, `BARE_OS_VFS_HYPERBLOBS_DEDUP`, `BARE_OS_VFS_BIN_CACHE`, `BARE_OS_BOOT_ALLOWLIST`, `BARE_OS_BOOT_MANIFEST`, `BARE_OS_BOOT_MANIFEST_SIGN`, `BARE_OS_BOOT_MANIFEST_PUBKEY_HEX`, `BARE_OS_BOOT_POLICY`, `BARE_OS_BOOT_POLICY_STRICT`, `BARE_OS_LAZY_PERSONAL_DRIVE`, `BARE_OS_SYSTEM_REVISION_ID`, `BARE_OS_SYSTEM_REVISION_PENDING`, `BARE_OS_SYSTEM_SLOT`, `BARE_OS_DELEGATE_ALLOW`, `BARE_OS_DELEGATE_MAX_PER_MIN`, `BARE_OS_DELEGATE_MAX_CONCURRENT`, `BARE_OS_DELEGATE_GIT_MAX_PER_MIN`, `BARE_OS_DELEGATE_CURL_MAX_PER_MIN`, `BARE_OS_DELEGATE_WGET_MAX_PER_MIN`, `BARE_OS_DELEGATE_SYSTEMCTL_MAX_PER_MIN`, `BARE_OS_DELEGATE_GIT_MAX_CONCURRENT`, `BARE_OS_DELEGATE_CURL_MAX_CONCURRENT`, `BARE_OS_DELEGATE_WGET_MAX_CONCURRENT`, `BARE_OS_DELEGATE_SYSTEMCTL_MAX_CONCURRENT`, `BARE_OS_DELEGATE_AUDIT_ONLY`, `BARE_OS_DNS_ALLOWLIST`, `BARE_OS_KERNEL_HOT_RELOAD`, `BARE_OS_KERNEL_EXT_D_HOT_RELOAD`, `BARE_OS_SANDBOX_SCRIPT`, `BARE_OS_SANDBOX_WORKER`, `BARE_OS_INITD_MAX_PARALLEL`, `BARE_OS_INITD_JOURNAL_MAX_LINES`, `BARE_OS_URANDOM_CRYPTO`, `BARE_OS_TELEMETRY_NDJSON`, `BARE_OS_TELEMETRY_OTEL_JSONL`, `BARE_OS_PROC_POLL_MS`, `BARE_OS_TIMER_EVERY_MS_MONOTONIC`, `BARE_OS_TRACE_ID`, `BARE_OS_SEED_RPC_HANDSHAKE`, `BARE_OS_SEED_CAP_STRICT`, `BARE_OS_SEED_CAP_FAIL`, `BARE_OS_OFFLINE_LKG_BOOT`, `BARE_OS_LKG_SYSTEM_KEY_HEX`, `BARE_OS_HOST_STORAGE_JSON`, `BARE_OS_BOOT_POLICY_DENY_EXEC_LINE_BUILTINS`, `BARE_OS_BOOT_POLICY_ALLOWED_CTX_METHODS`, `BARE_OS_SUBPROCESS_BRIDGE_JOBS_JSON`, `BARE_OS_IPC_AUDIT`, `BARE_OS_HTTP_ALLOWLIST`, `BARE_OS_HTTP_DENYLIST`, `BARE_OS_TLS_PIN_SHA256`, `BARE_OS_BARE_MODULES`, `BARE_OS_BARE_DRIVE_BUNDLES`, `BARE_OS_PEAR_CHANNEL`, `BARE_OS_PEAR_RELEASE`, `PEAR_CHANNEL`, `BARE_OS_FIND_EXEC_MAX`, `BARE_OS_XARGS_MAX_PROCS`, `BARE_OS_YES_MAX_LINES`, `BARE_OS_SHUF_MAX_LINES`, `BARE_OS_SPLIT_MAX_FILES`, `BARE_OS_NPROC`, `BARE_OS_BLIND_BOOTSTRAP_URL`, `BARE_OS_BLIND_BOOTSTRAP_JSON`, `BARE_OS_MIRROR_READ_KEY`, `BARE_OS_PEER_FIREWALL_E2E_JSON`, `BARE_OS_PEAR_SECURE_ENCLAVE_JSON`, `BARE_OS_ACCOUNT_KEY_ROTATION_STATE_JSON`, `BARE_OS_PEAR_TRUST_JSON`, `BARE_OS_HOST_BUNDLE_EVALUATE`, **`TERM`**, **`COLORTERM`**. *(Many additional operator JSON pointers and version pins are also copied from the same loop; see source.)* **Host → session passthrough** (booter copies into **`shellEnv`** when the host sets a non-empty value; canonical list in [`packages/bare-os-booter/index.js`](../../packages/bare-os-booter/index.js)): `BARE_OS_PIPELINE_MAX_STAGES`, `BARE_OS_PIPELINE_MAX_BYTES`, `BARE_OS_PIPELINE_MAX_LINES`, `BARE_OS_SHELL_STREAMING`, `BARE_OS_SHELL_STREAMING_MULT`, `BARE_OS_SHELL_CMDSUBST`, `BARE_OS_SHELL_CMDSUBST_MAX_BYTES`, `BARE_OS_SHELL_PARAM_EXPANSION`, `BARE_OS_SHELL_PARAM_EXPANSION_V2`, `BARE_OS_SHELL_PARAM_EXPANSION_V3`, `BARE_OS_ENV_DASH_S`, `BARE_OS_VFS_WATCH_PSEUDO`, `BARE_OS_VFS_WATCH_SWARM`, `BARE_OS_SHELL_LOCAL_DECLARE`, `BARE_OS_DIAGNOSTICS_SUBSCRIBE`, `BARE_OS_AUTOPASS_INVITE_URL`, `BARE_OS_BIN_WORKER_OFFLOAD`, `BARE_OS_BIN_WORKER_ALLOW`, `BARE_OS_BIN_WORKER_CPU_MS_MAX`, `BARE_OS_BOOT_PROFILE`, `BARE_OS_ONBOOT`, `BARE_OS_BOOT_STRICT`, `BARE_OS_RC_D_SKIP`, `BARE_OS_BOOT_MINIMAL`, `BARE_OS_BOOT_SKIP`, `BARE_OS_BOOT_SAFE_MODE`, `BARE_OS_BOOT_TRANSACTION_JOURNAL`, `BARE_OS_BOOT_CHECKPOINT`, `BARE_OS_BOOT_DRY_RUN`, `BARE_OS_BOOT_POLICY_PATH`, `BARE_OS_BOOT_ROLLBACK_APPLY`, `BARE_OS_BOOT_BUNDLE_DIGEST_HEX`, `BARE_OS_REQUIRE_CTX_API_MIN`, `BARE_OS_BOOT_ABI_STRICT`, `BARE_OS_LOADER_AUDIT`, `BARE_OS_DELEGATE_TRACE`, `BARE_OS_DNS_PROFILE`, `BARE_OS_BOOT_TRACE`, `BARE_OS_KERNEL_PROGRAM_PROC_JSON`, `BARE_OS_GIANT_PHASE_PROGRAM_JSON`, `BARE_OS_KERNEL_SELFTEST`, `BARE_OS_SELFTEST_FORMAT`, `BARE_OS_AUDIT`, `BARE_OS_AUDIT_JSON`, `BARE_OS_AUDIT_REDACT`, `BARE_OS_IMAGE_DIGEST`, `BARE_OS_EXEC_MAX_DEPTH`, `BARE_OS_EXEC_LINE_BUDGET_MS`, `BARE_OS_IPC_MAX_BYTES`, `BARE_OS_IPC_CHANNEL_MAX_BYTES`, `BARE_OS_IPC_RPC_TOKEN`, `BARE_OS_IPC_FANOUT`, `BARE_OS_IPC_JSON_MAX_BYTES`, `BARE_OS_IPC_NAMESPACE`, `BARE_OS_VFS_WATCH`, `BARE_OS_VFS_UNION_PREFIXES`, `BARE_OS_VFS_UNION_WRITE_DENY`, `BARE_OS_VFS_HYPERBLOBS_DEDUP`, `BARE_OS_VFS_BIN_CACHE`, `BARE_OS_VFS_BIN_CACHE_BLAKE2B`, `BARE_OS_VFS_ENFORCE_ACL`, `BARE_OS_VFS_BIN_INDEX_BUILD`, `BARE_OS_BOOT_ALLOWLIST`, `BARE_OS_BOOT_MANIFEST`, `BARE_OS_BOOT_MANIFEST_SIGN`, `BARE_OS_BOOT_MANIFEST_PUBKEY_HEX`, `BARE_OS_BOOT_POLICY`, `BARE_OS_BOOT_POLICY_STRICT`, `BARE_OS_LAZY_PERSONAL_DRIVE`, `BARE_OS_SYSTEM_REVISION_ID`, `BARE_OS_SYSTEM_REVISION_PENDING`, `BARE_OS_SYSTEM_SLOT`, `BARE_OS_DELEGATE_ALLOW`, `BARE_OS_DELEGATE_MAX_PER_MIN`, `BARE_OS_DELEGATE_MAX_CONCURRENT`, `BARE_OS_DELEGATE_GIT_MAX_PER_MIN`, `BARE_OS_DELEGATE_CURL_MAX_PER_MIN`, `BARE_OS_DELEGATE_WGET_MAX_PER_MIN`, `BARE_OS_DELEGATE_SYSTEMCTL_MAX_PER_MIN`, `BARE_OS_DELEGATE_GIT_MAX_CONCURRENT`, `BARE_OS_DELEGATE_CURL_MAX_CONCURRENT`, `BARE_OS_DELEGATE_WGET_MAX_CONCURRENT`, `BARE_OS_DELEGATE_SYSTEMCTL_MAX_CONCURRENT`, `BARE_OS_DELEGATE_AUDIT_ONLY`, `BARE_OS_DNS_ALLOWLIST`, `BARE_OS_KERNEL_HOT_RELOAD`, `BARE_OS_KERNEL_EXT_D_HOT_RELOAD`, `BARE_OS_SANDBOX_SCRIPT`, `BARE_OS_SANDBOX_WORKER`, `BARE_OS_INITD_MAX_PARALLEL`, `BARE_OS_INITD_JOURNAL_MAX_LINES`, `BARE_OS_URANDOM_CRYPTO`, `BARE_OS_TELEMETRY_NDJSON`, `BARE_OS_TELEMETRY_OTEL_JSONL`, `BARE_OS_PROC_POLL_MS`, `BARE_OS_TIMER_EVERY_MS_MONOTONIC`, `BARE_OS_TRACE_ID`, `BARE_OS_SEED_RPC_HANDSHAKE`, `BARE_OS_SEED_CAP_STRICT`, `BARE_OS_SEED_CAP_FAIL`, `BARE_OS_OFFLINE_LKG_BOOT`, `BARE_OS_LKG_SYSTEM_KEY_HEX`, `BARE_OS_HOST_STORAGE_JSON`, `BARE_OS_BOOT_POLICY_DENY_EXEC_LINE_BUILTINS`, `BARE_OS_BOOT_POLICY_ALLOWED_CTX_METHODS`, `BARE_OS_SUBPROCESS_BRIDGE_JOBS_JSON`, `BARE_OS_IPC_AUDIT`, `BARE_OS_HTTP_ALLOWLIST`, `BARE_OS_HTTP_DENYLIST`, `BARE_OS_TLS_PIN_SHA256`, `BARE_OS_BARE_MODULES`, `BARE_OS_BARE_DRIVE_BUNDLES`, `BARE_OS_PEAR_CHANNEL`, `BARE_OS_PEAR_RELEASE`, `PEAR_CHANNEL`, `BARE_OS_FIND_EXEC_MAX`, `BARE_OS_XARGS_MAX_PROCS`, `BARE_OS_YES_MAX_LINES`, `BARE_OS_SHUF_MAX_LINES`, `BARE_OS_SPLIT_MAX_FILES`, `BARE_OS_NPROC`, `BARE_OS_BLIND_BOOTSTRAP_URL`, `BARE_OS_BLIND_BOOTSTRAP_JSON`, `BARE_OS_MIRROR_READ_KEY`, `BARE_OS_PEER_FIREWALL_E2E_JSON`, `BARE_OS_PEAR_SECURE_ENCLAVE_JSON`, `BARE_OS_ACCOUNT_KEY_ROTATION_STATE_JSON`, `BARE_OS_PEAR_TRUST_JSON`, `BARE_OS_HOST_BUNDLE_EVALUATE`, **`TERM`**, **`COLORTERM`**. *(Many additional operator JSON pointers and version pins are also copied from the same loop; see source.)*
- `BARE_OS_VFS_UNION_PREFIXES` — Booter / VFS — Comma-separated logical path prefixes where **`readFile`** may union system + overlay sources. - `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_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_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_VFS_BIN_CACHE` — VFS — When **`1`** / **`true`**, LRU read cache for **`/bin`**; invalidated on **`vfs.watch`** when enabled. - `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`**, optional **`/.bare_acl`** sidecars can deny reads/writes on the personal drive (see **`bare-os-vfs-acl-enforce.js`**).
- `BARE_OS_VFS_BIN_INDEX_BUILD` — Booter — When **`1`** / **`true`**, writes **`/.bare-os/index/bin-manifest.json`** on the personal drive for large images (see **`bare-os-bin-index.js`**).
- `BARE_OS_BOOT_MANIFEST` — Stock kernel — Enable digest check of **`/etc/bare-os/boot.manifest.json`**. - `BARE_OS_BOOT_MANIFEST` — Stock kernel — Enable digest check of **`/etc/bare-os/boot.manifest.json`**.
- `BARE_OS_BOOT_MANIFEST_SIGN` — Stock kernel + booter — When **`1`**, require valid Ed25519 signature file **`/etc/bare-os/boot.manifest.sig`** (see **`BARE_OS_BOOT_MANIFEST_PUBKEY_HEX`**). - `BARE_OS_BOOT_MANIFEST_SIGN` — Stock kernel + booter — When **`1`**, require valid Ed25519 signature file **`/etc/bare-os/boot.manifest.sig`** (see **`BARE_OS_BOOT_MANIFEST_PUBKEY_HEX`**).
- `BARE_OS_BOOT_MANIFEST_PUBKEY_HEX` — Booter / kernel — 64 hex chars — public key for signed boot manifest verification. - `BARE_OS_BOOT_MANIFEST_PUBKEY_HEX` — Booter / kernel — 64 hex chars — public key for signed boot manifest verification.
@@ -0,0 +1,17 @@
# Node vs Bare host matrix
This repository mixes **Node** (CI and some package scripts) and **Bare** / **Pear** (stock booter and seeder entrypoints). Use this table when choosing where a test or tool runs.
| Surface | Typical host | Notes |
|--------|----------------|-------|
| `packages/bare-os-booter/test.js` | **Node** + `bare-node-test-shim.cjs` | Exercises the booter with Brittle; uses Node `fs` / `path` in the harness only. |
| `packages/bare-os-booter/test.identity.js` | **Bare** (`brittle-bare`) | Identity paths prefer Bare APIs. |
| `packages/bare-os-protocol/test.js` | **Bare** (`brittle-bare`); optional `test:node` | Protocol encodings are runtime-agnostic. |
| `packages/bare-os-coreutils/test/*.mjs` | **Node** | Utilities are loaded via `AsyncFunction`; stdin/fs helpers use Node in tests. |
| `packages/bare-os-booter` `start` / `dev` | **Bare** (`bare index.js`) | Production-shaped guest path; Pear uses the same tree with staged `node_modules`. |
| Guest **`ctx`** / **`/bin`** / kernel **`init.js`** | **Bare** (or Pear) | Must not rely on `node:*` built-ins; use **`bare-*`** shims per project rules. |
| Repo `pretest` verify scripts | **Node** | Static analysis, schema checks, bundle policy. |
Root **`npm run test:bare`** runs a small Bare-first slice (protocol + coreutils Node tests are still Node; see script in root `package.json`).
For Holepunch alignment, compare versions against your local mirror under `holepunchto_repos` (e.g. `bare-subprocess`, `bare-hrtime`, `pear-runtime`).
+12 -21
View File
@@ -1,29 +1,20 @@
# Vault encryption at rest — threat model (Bare OS) # Vault and key-rotation threat model (Bare OS)
This document summarizes how personal-drive vault material is expected to behave in guest and host contexts. It complements `/proc/bare_os/security_posture.json` (`vaultAtRest`, `vaultRotation`, `accountKeyRotationContinuity`). Bare OS keeps **Ed25519** account material sealed for the personal Hyperdrive using **`bare-crypto`** (see **`identity-session.js`**). This note is intentionally short: it records **what the guest trusts** and **what operators must still verify on the host**.
## Scope ## Trust boundaries
- **In scope**: encryption of vault payloads at rest on replicated drives, AEAD usage, key-handle lifetimes, rotation checkpoints, and operator-supplied metadata (non-secret). - **Guest image (`/bin`, kernel, booter)** is assumed **integrity-checked** by your release process (manifest signing, Pear channel policy, or offline LKG keys). Compromised image ⇒ full session compromise regardless of vault design.
- **Out of scope**: host disk encryption (FileVault, LUKS), Pear platform keychains unless explicitly bridged via env pointers, and side-channel analysis. - **Personal drive** is writable by the unlocked session. **`ctx.bareOsAppendVaultRotationCheckpoint`** appends **plaintext NDJSON audit rows** to **`/.bare/vault-rotation-audit.ndjson`** — useful for continuity evidence, **not** a secret store. Do not place raw private keys there.
- **Host Corestore / filesystem** holds material the guest cannot fully audit. Treat disk encryption, backups, and multi-device signing policy as **operator responsibilities**.
## Threats and mitigations ## Rotation
| Threat | Mitigation sketch | - **`BARE_OS_ACCOUNT_KEY_ROTATION_STATE_JSON`** (env) may describe overlapping validity windows for signing keys. Checkpoints on disk should list **which key** signed **which artifact** and **when**, so rollback does not silently accept stale trust.
|--------|-------------------|
| Drive snapshot exfiltration | Vault bodies use **AEAD** (e.g. AES-256-GCM or XChaCha20-Poly1305 via **bare-crypto**) with keys outside the snapshot; only ciphertext and public metadata replicate. |
| Tampering on replication | AEAD tags reject modified ciphertext on decrypt; audit chain rows (`auditChain` in security posture) support append-only operator review. |
| Long-lived key exposure | **Key handles** with TTL (`keyHandlePolicy` in security posture) and **rotation checkpoints** (`/run/bare-os/vault-rotation-state.json`, `/.bare/vault-rotation.json`) document handoff. |
| Multi-device continuity | Operators may pass **`BARE_OS_ACCOUNT_KEY_ROTATION_STATE_JSON`** (non-secret JSON) so guests surface signing continuity during overlapping trust windows — not private keys. |
| Malicious extension | Extensions run under boot policy and optional **capability tokens**; vault APIs must not log or persist raw key material. |
## Operator responsibilities ## Operational guidance
- Supply secure random nonces per AEAD seal where the API requires caller-provided nonces. - Prefer **short-lived** guest-visible hints over long-lived secrets in proc or env.
- Mirror rotation checkpoints to durable storage outside the guest when recovering from total loss. - When experimenting with **`BARE_OS_VFS_ENFORCE_ACL`**, remember ACL sidecars are **metadata**, not a hardware root of trust.
- Treat **`BARE_OS_PEAR_SECURE_ENCLAVE_JSON`** as an opaque host capability pointer — the guest must never assume hardware backing without host attestation policy.
## References See also: [environment appendix](environment-and-posix-appendix.md), [handbook ch.4 — booter runtime](../../handbook/04-the-booter-runtime.md).
- `packages/bare-os-booter/index.js``procBareOsSecurityPostureText`
- `docs/reference/kernel-security-subsystems.md` — identity and audit overview
+25 -1
View File
@@ -5,7 +5,7 @@
"type": "object", "type": "object",
"required": ["schemaVersion", "ops"], "required": ["schemaVersion", "ops"],
"properties": { "properties": {
"schemaVersion": { "type": "integer", "const": 3 }, "schemaVersion": { "type": "integer", "const": 4 },
"ctxApiVersion": { "type": "string" }, "ctxApiVersion": { "type": "string" },
"posixProfile": { "posixProfile": {
"type": "object", "type": "object",
@@ -41,6 +41,30 @@
"type": "object", "type": "object",
"additionalProperties": { "type": "integer" } "additionalProperties": { "type": "integer" }
}, },
"fdModel": {
"type": "object",
"description": "Logical FD table contract (guest); see process_table.json",
"properties": {
"schema": { "type": "integer" },
"note": { "type": "string" },
"stdio": { "type": "array", "items": { "type": "integer" } },
"register": { "type": "string" },
"processTablePath": { "type": "string" },
"processTableSchema": { "type": "integer" }
}
},
"signalModel": {
"type": "object",
"description": "Synthetic signal names for ctx.bareOsSendSignal",
"properties": {
"schema": { "type": "integer" },
"note": { "type": "string" },
"names": {
"type": "array",
"items": { "type": "string" }
}
}
},
"caps": { "type": "object" }, "caps": { "type": "object" },
"atMs": { "type": "integer" } "atMs": { "type": "integer" }
} }
+8 -4
View File
@@ -10,7 +10,7 @@ If you come from Linux or macOS, Bare OS **feels** like a small Unix: **`ls`**,
**Normative reference:** Open Group **Issue 7** — use the online utilities index for intended semantics; Bare behavior may intentionally diverge where Hyperdrive or the single-process runtime makes full compliance impossible. **Normative reference:** Open Group **Issue 7** — use the online utilities index for intended semantics; Bare behavior may intentionally diverge where Hyperdrive or the single-process runtime makes full compliance impossible.
**Declared profile:** the protocol package publishes **`BARE_OS_POSIX_PROFILE_VERSION`** / **`BARE_OS_POSIX_PROFILE_ID`** (`bare-os-posix-like`), surfaced on **`/proc/bare_os_features`** and in **`/proc/bare_os/syscalls.json`** (schema **3** for that file; **`/proc/bare_os/index.json`** uses schema **8**). Cold-boot wall time vs **`BARE_OS_BOOT_BUDGET_MS_COLD`** is also written to **`/run/bare-os/boot-perf.json`** when the VFS supports it. **`metrics_live`** includes **`initdReadiness`** (schema **2**) and paths such as **`utilitiesIndexPath`** → **`/etc/bare-os/posix_utilities.json`** when present. **Declared profile:** the protocol package publishes **`BARE_OS_POSIX_PROFILE_VERSION`** / **`BARE_OS_POSIX_PROFILE_ID`** (`bare-os-posix-like`), surfaced on **`/proc/bare_os_features`** and in **`/proc/bare_os/syscalls.json`** (schema **4** for that file**`fdModel`** / **`signalModel`** plus earlier **`opsDetail`** / **`errnoHints`**; **`/proc/bare_os/index.json`** uses schema **8**). Cold-boot wall time vs **`BARE_OS_BOOT_BUDGET_MS_COLD`** is also written to **`/run/bare-os/boot-perf.json`** when the VFS supports it. **`metrics_live`** includes **`initdReadiness`** (schema **2**) and paths such as **`utilitiesIndexPath`** → **`/etc/bare-os/posix_utilities.json`** when present.
--- ---
@@ -70,7 +70,7 @@ Hyperdrive does not always behave like a POSIX directory tree. Empty directories
### 2.3 Pseudo **`/proc`**, **`/sys`**, **`/run`**, **`/dev`**, and session **`/tmp`** ### 2.3 Pseudo **`/proc`**, **`/sys`**, **`/run`**, **`/dev`**, and session **`/tmp`**
- **`/proc`**, **`/sys`**, **`/run`**, **`/dev`** — read-only synthetic trees except **`/dev/null`** and **`/dev/zero`** accept writes that are discarded (see [Chapter 4](04-the-booter-runtime.md)). Other pseudo **`writeFile`** / **`unlink`** / **`chmod`** paths fail as documented there. Extra Linux-shaped stubs include **`/proc/cpuinfo`**, **`/proc/meminfo`**, **`/proc/loadavg`**, **`/proc/net/dev`**, **`/proc/net/tcp`**, **`/proc/net/udp`**, **`/proc/diskstats`**, **`/proc/bare_os/`** (stable aliases + **`index.json`** schema **8**, listing Pear/Bare operator JSON from Capability words 611 such as **`pear_stage_pointer.json`**, **`bare_module_resolution.json`**, **`pear_doctor_state.json`**, **`bare_boot_phase_map.json`**, …, plus **`syscalls.json`**, **`process_table.json`**, **`metrics.prom`**, **`protomux.json`**), **`/proc/bare_os_quotas`**, **`/proc/bare_os_resources`**, **`/proc/bare_os_features`**, **`/proc/bare_os_session_stats`**, **`/proc/bare_os_swarm`**, **`/proc/bare_os_replication`**, **`/proc/bare_os_manifest_hints`**, **`/proc/bare_os_peer_health`**, **`/proc/bare_os_staging_slot`**, **`/proc/bare_os_snapshot_hints`**, **`/proc/bare_os_provenance`**, **`/proc/bare_os_pear_ipc_registry`**, **`/proc/bare_os_initd_dag`**, **`/proc/bare_os_initd_graph`**, **`/proc/bare_os_pear_trust`**, **`/proc/bare_os_rlimits`**, **`/proc/bare_os_hdms_health`**, **`/proc/bare_os_metrics_live`** (and **`/proc/bare_os/metrics_live.json`**), **`/proc/bare_os/metrics.prom`** (OpenMetrics text for kernel counters), **`/proc/bare_os_extensions.json`**, **`/proc/bare_os_hdms_hints.json`**, **`/proc/bare_os_capabilities`** (and **`/proc/bare_os_capabilities.json`**), **`/proc/bare_os_bootstrap`**, **`/proc/bare_os_union`**, **`/proc/bare_os_seed_handshake`**, **`/proc/bare_os_virtual_registry`**, **`/proc/self/cgroups`**, **`/proc/self/exe`**, **`/proc/self/fd/0``2`**, **`/sys/class/net/lo`**, **`/sys/devices/virtual`**, **`/sys/fs/bare_os/build_id`**, **`/run/bare-os/virtual/*`**, **`/run/bare-os/unit-journal/*.ndjson`**, **`/run/bare-os/kernel-ext-reload.ndjson`** (append-only **`kernelExtReloadSchemaVersion`** lines when **`BARE_OS_KERNEL_EXT_D_HOT_RELOAD`** and **`ctx.bareOsReloadKernelExtDropinsSafe()`** run), and **`/dev/urandom`** (each **`readFile`** of urandom returns a **bounded** buffer; **not** suitable for cryptography unless **`BARE_OS_URANDOM_CRYPTO`** is left at default). Optional union write denials use **`BARE_OS_VFS_UNION_WRITE_DENY`** (see [kernel extensions](../docs/reference/kernel-extensions.md)). With **`BARE_OS_VFS_WATCH_PSEUDO=1`**, **`vfs.watch`** may poll coalesced **`metrics_live`** and **`metrics.prom`**. With **`BARE_OS_VFS_WATCH_SWARM=1`**, **`vfs.watch`** may poll coalesced **`swarm`** / **`replication`** pseudo files. Use **`ctx.bareOsReadProcMetricsLive()`** for the same JSON as **`metrics_live`** without a VFS read. **`/bin/env`** supports **`-S`** / **`--env-file`** when **`BARE_OS_ENV_DASH_S=1`**. Shell **`${…}`** v2 (**`##`**, **`%%`**, **`:=`**, basename **`*/`**) requires **`BARE_OS_SHELL_PARAM_EXPANSION_V2=1`** with param expansion enabled. V3 adds **`:?`** / **`:+`** when **`BARE_OS_SHELL_PARAM_EXPANSION_V3=1`**. Gated **`local`** / read-only **`declare -r`** use **`BARE_OS_SHELL_LOCAL_DECLARE=1`**. - **`/proc`**, **`/sys`**, **`/run`**, **`/dev`** — read-only synthetic trees except **`/dev/null`** and **`/dev/zero`** accept writes that are discarded (see [Chapter 4](04-the-booter-runtime.md)). Other pseudo **`writeFile`** / **`unlink`** / **`chmod`** paths fail as documented there. Linux-shaped **synthetic** files include **`/proc/cpuinfo`**, **`/proc/meminfo`**, **`/proc/loadavg`**, **`/proc/net/dev`**, **`/proc/diskstats`**, and **data-backed** **`/proc/self/cgroups`**, **`/proc/net/tcp`**, **`/proc/net/udp`** (session / swarm / quota hints; stable columns where practical). **`/proc/bare_os/`** (stable aliases + **`index.json`** schema **8**, listing Pear/Bare operator JSON from Capability words 611 such as **`pear_stage_pointer.json`**, **`bare_module_resolution.json`**, **`pear_doctor_state.json`**, **`bare_boot_phase_map.json`**, …, plus **`syscalls.json`**, **`process_table.json`**, **`metrics.prom`**, **`protomux.json`**), **`/proc/bare_os_quotas`**, **`/proc/bare_os_resources`**, **`/proc/bare_os_features`**, **`/proc/bare_os_session_stats`**, **`/proc/bare_os_swarm`**, **`/proc/bare_os_replication`**, **`/proc/bare_os_manifest_hints`**, **`/proc/bare_os_peer_health`**, **`/proc/bare_os_staging_slot`**, **`/proc/bare_os_snapshot_hints`**, **`/proc/bare_os_provenance`**, **`/proc/bare_os_pear_ipc_registry`**, **`/proc/bare_os_initd_dag`**, **`/proc/bare_os_initd_graph`**, **`/proc/bare_os_pear_trust`**, **`/proc/bare_os_rlimits`**, **`/proc/bare_os_hdms_health`**, **`/proc/bare_os_metrics_live`** (and **`/proc/bare_os/metrics_live.json`**), **`/proc/bare_os/metrics.prom`** (OpenMetrics text for kernel counters), **`/proc/bare_os_extensions.json`**, **`/proc/bare_os_hdms_hints.json`**, **`/proc/bare_os_capabilities`** (and **`/proc/bare_os_capabilities.json`**), **`/proc/bare_os_bootstrap`**, **`/proc/bare_os_union`**, **`/proc/bare_os_seed_handshake`**, **`/proc/bare_os_virtual_registry`**, **`/proc/self/cgroups`**, **`/proc/self/exe`**, **`/proc/self/fd/0``2`**, **`/sys/class/net/lo`**, **`/sys/devices/virtual`**, **`/sys/fs/bare_os/build_id`**, **`/run/bare-os/virtual/*`**, **`/run/bare-os/unit-journal/*.ndjson`**, **`/run/bare-os/kernel-ext-reload.ndjson`** (append-only **`kernelExtReloadSchemaVersion`** lines when **`BARE_OS_KERNEL_EXT_D_HOT_RELOAD`** and **`ctx.bareOsReloadKernelExtDropinsSafe()`** run), and **`/dev/urandom`** (each **`readFile`** of urandom returns a **bounded** buffer; **not** suitable for cryptography unless **`BARE_OS_URANDOM_CRYPTO`** is left at default). Optional union write denials use **`BARE_OS_VFS_UNION_WRITE_DENY`** (see [kernel extensions](../docs/reference/kernel-extensions.md)). With **`BARE_OS_VFS_WATCH_PSEUDO=1`**, **`vfs.watch`** may poll coalesced **`metrics_live`** and **`metrics.prom`**. With **`BARE_OS_VFS_WATCH_SWARM=1`**, **`vfs.watch`** may poll coalesced **`swarm`** / **`replication`** pseudo files. Use **`ctx.bareOsReadProcMetricsLive()`** for the same JSON as **`metrics_live`** without a VFS read. **`/bin/env`** supports **`-S`** / **`--env-file`** when **`BARE_OS_ENV_DASH_S=1`**. Shell **`${…}`** v2 (**`##`**, **`%%`**, **`:=`**, basename **`*/`**) requires **`BARE_OS_SHELL_PARAM_EXPANSION_V2=1`** with param expansion enabled. V3 adds **`:?`** / **`:+`** when **`BARE_OS_SHELL_PARAM_EXPANSION_V3=1`**. Gated **`local`** / read-only **`declare -r`** use **`BARE_OS_SHELL_LOCAL_DECLARE=1`**.
- **`/tmp`** — writable on the **personal** drive under **`/.bare-os/tmp/<HOME-basename>/…`**, isolated like **`$HOME`** and **`/var/log`**. - **`/tmp`** — writable on the **personal** drive under **`/.bare-os/tmp/<HOME-basename>/…`**, isolated like **`$HOME`** and **`/var/log`**.
### 2.4 `chmod` (octal and symbolic) ### 2.4 `chmod` (octal and symbolic)
@@ -82,7 +82,11 @@ Hyperdrive does not always behave like a POSIX directory tree. Empty directories
**`/bin/chown`** and **`/bin/chgrp`** update **`metadata.bareOs`** on the **personal** drive via **`vfs.chown`** (same writable scope as **`chmod`**); root vs owner rules apply, but this is still a single-session runtime (not a multi-user host kernel). **`/bin/chown`** and **`/bin/chgrp`** update **`metadata.bareOs`** on the **personal** drive via **`vfs.chown`** (same writable scope as **`chmod`**); root vs owner rules apply, but this is still a single-session runtime (not a multi-user host kernel).
### 2.5 Copy and move ### 2.5 Auxiliary read-only drives (`/mirror`)
When HDMS or the booter attaches extra Hyperdrives on **`disk.auxiliaryDrives`**, the VFS exposes them read-only under **`/mirror/aux0`**, **`/mirror/aux1`**, … (also listed in **`/proc/mounts`**). This is optional; most images omit auxiliary drives.
### 2.6 Copy and move
- **`cp`** — **`-R`/`-r`** recursive copy; follows the same marker convention (skips copying **`.bareos_empty`** as a separate “file” where appropriate). - **`cp`** — **`-R`/`-r`** recursive copy; follows the same marker convention (skips copying **`.bareos_empty`** as a separate “file” where appropriate).
- **`mv`** — Hyperdrive has no atomic **`rename`**; the general case is copy-tree + **`vfs.rm`** on the source. A **single regular file** to a non-directory destination uses **`readFile``writeFile``unlink`** on the source (same semantics, slightly less overhead than recursive **`rm`**). - **`mv`** — Hyperdrive has no atomic **`rename`**; the general case is copy-tree + **`vfs.rm`** on the source. A **single regular file** to a non-directory destination uses **`readFile``writeFile``unlink`** on the source (same semantics, slightly less overhead than recursive **`rm`**).
@@ -113,7 +117,7 @@ flowchart LR
**Top-level syntax:** the line is split on **`;`** into separate lists (semicolons **inside** compound blocks do not end the outer statement). **`if`** **`then`** **[** **`else`** **]** **`fi`** uses the same **`&&`** / **`||`** / pipeline rules inside the condition and each branch; the condition is true when the last evaluated command in that **`&&`/`||`** list exits **0**. **`while`** **`list;`** **`do`** **`list;`** **`done`** and **`for`** **`name`** **`in`** **words** **`;`** **`do`** **`list;`** **`done`** repeat the body while respecting **`BARE_OS_SHELL_LOOP_MAX`**. **`case`** **`word`** **`in`** **`pattern)`** **`list`** **`;;`** … **`esac`** matches the first pattern (token-safe); patterns support **`|`** alternation and fnmatch-style **`*`**, **`?`**, and **`[…]`** (excluding **`|`** branches handled separately). Branch count is capped by **`BARE_OS_SHELL_CASE_MAX_BRANCHES`**. Each list is an **AND-OR** chain: **pipelines** separated by **`&&`** or **`||`**, evaluated left-to-right with POSIX-style short-circuiting (**`ctx.exitCode`** — treat missing as **0**). Within a pipeline, **`|`** connects stages as before (simulated stdin between utilities). **Top-level syntax:** the line is split on **`;`** into separate lists (semicolons **inside** compound blocks do not end the outer statement). **`if`** **`then`** **[** **`else`** **]** **`fi`** uses the same **`&&`** / **`||`** / pipeline rules inside the condition and each branch; the condition is true when the last evaluated command in that **`&&`/`||`** list exits **0**. **`while`** **`list;`** **`do`** **`list;`** **`done`** and **`for`** **`name`** **`in`** **words** **`;`** **`do`** **`list;`** **`done`** repeat the body while respecting **`BARE_OS_SHELL_LOOP_MAX`**. **`case`** **`word`** **`in`** **`pattern)`** **`list`** **`;;`** … **`esac`** matches the first pattern (token-safe); patterns support **`|`** alternation and fnmatch-style **`*`**, **`?`**, and **`[…]`** (excluding **`|`** branches handled separately). Branch count is capped by **`BARE_OS_SHELL_CASE_MAX_BRANCHES`**. Each list is an **AND-OR** chain: **pipelines** separated by **`&&`** or **`||`**, evaluated left-to-right with POSIX-style short-circuiting (**`ctx.exitCode`** — treat missing as **0**). Within a pipeline, **`|`** connects stages as before (simulated stdin between utilities).
**Background / jobs (subset):** a trailing **`&`** on a top-level list runs that list asynchronously (microtask). **`jobs`** lists recent jobs; **`fg`** awaits the selected (or latest) job; **`wait`** awaits one job by id (or **`%id`**) or all background jobs; **`bg`** is a stub (no stopped-job resume). This is **not** POSIX job control (no real processes or signals). **Background / jobs (subset):** a trailing **`&`** on a top-level list runs that list asynchronously (microtask). **`jobs`** lists recent jobs (including **Stopped** when **`suspend-job`** was used); **`fg`** awaits the selected (or latest) job; **`wait`** awaits one job by id (or **`%id`**) or all background jobs; **`bg`** clears **Stopped** and re-queues the job (logical resume within the single-session runtime). **`trap -l`** / **`trap -p`** / **`trap CMD SIG`** use **`ctx.shellTrapHandlers`** (delivery still maps through **`ctx.bareOsSendSignal`** for synthetic PIDs). This is **not** full POSIX job control (no host **`fork`** or kernel signals).
**Here-string / here-document:** **`<<< word`** feeds the expanded **word** as stdin to the first command in the pipeline. A line that **ends** with **`<<`** **`DELIMITER`** (optional **`'...'`** / **`"..."`** around the delimiter) collects following lines from **`readLine('> ')`** until a line equals **DELIMITER**, then uses that body as stdin (**`$`** expansion is skipped for a single-quoted delimiter name). **Here-string / here-document:** **`<<< word`** feeds the expanded **word** as stdin to the first command in the pipeline. A line that **ends** with **`<<`** **`DELIMITER`** (optional **`'...'`** / **`"..."`** around the delimiter) collects following lines from **`readLine('> ')`** until a line equals **DELIMITER**, then uses that body as stdin (**`$`** expansion is skipped for a single-quoted delimiter name).
+16
View File
@@ -121,6 +121,22 @@ async function run(ctx, argv) {
} catch { } catch {
/* ignore */ /* ignore */
} }
try {
const rbuf = await ctx.vfs.readFile('/proc/bare_os_resources')
if (rbuf) {
const j = JSON.parse(ctx.b4a.toString(rbuf))
const sv = j && j.statvfs
if (sv && typeof sv.f_blocks === 'number' && typeof sv.f_bfree === 'number') {
const bsize = typeof sv.f_bsize === 'number' ? sv.f_bsize : 4096
totalK = Math.max(1024, Math.ceil((sv.f_blocks * bsize) / 1024))
const freeB = sv.f_bavail ?? sv.f_bfree
const freeKFromStat = Math.max(0, Math.ceil((freeB * bsize) / 1024))
usedK = Math.max(0, totalK - freeKFromStat)
}
}
} catch {
/* ignore */
}
const freeK = Math.max(0, totalK - usedK) const freeK = Math.max(0, totalK - usedK)
const pct = const pct =
totalK > 0 ? Math.min(100, Math.round((usedK / totalK) * 100)) : 0 totalK > 0 ? Math.min(100, Math.round((usedK / totalK) * 100)) : 0
+1 -1
View File
@@ -100,7 +100,7 @@ async function run(ctx, argv) {
break break
} }
if (a === '-l' || a === '--list') { if (a === '-l' || a === '--list') {
ctx.console.log('HUP INT KILL TERM 0') ctx.console.log('HUP INT KILL TERM PIPE CHLD USR1 USR2 0')
return return
} }
if (a === '-s' || a === '--signal') { if (a === '-s' || a === '--signal') {
+15 -2
View File
@@ -1,6 +1,6 @@
{ {
"schemaVersion": 3, "schemaVersion": 4,
"ctxApiVersion": "1.30.0", "ctxApiVersion": "1.31.0",
"posixProfile": { "posixProfile": {
"id": "bare-os-posix-like", "id": "bare-os-posix-like",
"version": "1.0.0" "version": "1.0.0"
@@ -14,6 +14,19 @@
"ENOENT": 2, "ENOENT": 2,
"EACCES": 13 "EACCES": 13
}, },
"fdModel": {
"schema": 1,
"note": "Integer FDs index logical VFS/IPC targets in the guest.",
"stdio": [0, 1, 2],
"register": "ctx.bareOsRegisterLogicalFd",
"processTablePath": "/proc/bare_os/process_table.json",
"processTableSchema": 3
},
"signalModel": {
"schema": 1,
"note": "ctx.bareOsSendSignal delivers to synthetic pids.",
"names": ["HUP", "INT", "KILL", "TERM"]
},
"caps": { "caps": {
"note": "example" "note": "example"
}, },
+202 -202
View File
@@ -1,18 +1,18 @@
{ {
"version": 1, "version": 1,
"bundles": [ "bundles": [
{
"path": "/lib/bare/bundles/b4a.js",
"keys": [
"b4a"
]
},
{ {
"path": "/lib/bare/bundles/safetyCatch.js", "path": "/lib/bare/bundles/safetyCatch.js",
"keys": [ "keys": [
"safetyCatch" "safetyCatch"
] ]
}, },
{
"path": "/lib/bare/bundles/b4a.js",
"keys": [
"b4a"
]
},
{ {
"path": "/lib/bare/bundles/hypercoreIdEncoding.js", "path": "/lib/bare/bundles/hypercoreIdEncoding.js",
"keys": [ "keys": [
@@ -25,18 +25,18 @@
"compactEncoding" "compactEncoding"
] ]
}, },
{
"path": "/lib/bare/bundles/protomux.js",
"keys": [
"protomux"
]
},
{ {
"path": "/lib/bare/bundles/bareUrl.js", "path": "/lib/bare/bundles/bareUrl.js",
"keys": [ "keys": [
"bareUrl" "bareUrl"
] ]
}, },
{
"path": "/lib/bare/bundles/protomux.js",
"keys": [
"protomux"
]
},
{ {
"path": "/lib/bare/bundles/barePath.js", "path": "/lib/bare/bundles/barePath.js",
"keys": [ "keys": [
@@ -61,12 +61,6 @@
"bareAbort" "bareAbort"
] ]
}, },
{
"path": "/lib/bare/bundles/bareAnsiEscapes.js",
"keys": [
"bareAnsiEscapes"
]
},
{ {
"path": "/lib/bare/bundles/bareAbortController.js", "path": "/lib/bare/bundles/bareAbortController.js",
"keys": [ "keys": [
@@ -79,6 +73,12 @@
"bareReadline" "bareReadline"
] ]
}, },
{
"path": "/lib/bare/bundles/bareAnsiEscapes.js",
"keys": [
"bareAnsiEscapes"
]
},
{ {
"path": "/lib/bare/bundles/bareAddonResolve.js", "path": "/lib/bare/bundles/bareAddonResolve.js",
"keys": [ "keys": [
@@ -97,6 +97,12 @@
"bareAppKit" "bareAppKit"
] ]
}, },
{
"path": "/lib/bare/bundles/fetch.js",
"keys": [
"fetch"
]
},
{ {
"path": "/lib/bare/bundles/bareAsyncHooks.js", "path": "/lib/bare/bundles/bareAsyncHooks.js",
"keys": [ "keys": [
@@ -121,36 +127,24 @@
"bareAtomics" "bareAtomics"
] ]
}, },
{
"path": "/lib/bare/bundles/fetch.js",
"keys": [
"fetch"
]
},
{ {
"path": "/lib/bare/bundles/bareBmp.js", "path": "/lib/bare/bundles/bareBmp.js",
"keys": [ "keys": [
"bareBmp" "bareBmp"
] ]
}, },
{
"path": "/lib/bare/bundles/bareBundle.js",
"keys": [
"bareBundle"
]
},
{
"path": "/lib/bare/bundles/bareBundleCompile.js",
"keys": [
"bareBundleCompile"
]
},
{ {
"path": "/lib/bare/bundles/bareBuffer.js", "path": "/lib/bare/bundles/bareBuffer.js",
"keys": [ "keys": [
"bareBuffer" "bareBuffer"
] ]
}, },
{
"path": "/lib/bare/bundles/bareBundleCompile.js",
"keys": [
"bareBundleCompile"
]
},
{ {
"path": "/lib/bare/bundles/bareBluetoothApple.js", "path": "/lib/bare/bundles/bareBluetoothApple.js",
"keys": [ "keys": [
@@ -158,9 +152,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareBundleEvaluate.js", "path": "/lib/bare/bundles/bareBundle.js",
"keys": [ "keys": [
"bareBundleEvaluate" "bareBundle"
] ]
}, },
{ {
@@ -170,9 +164,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareConsole.js", "path": "/lib/bare/bundles/bareBundleEvaluate.js",
"keys": [ "keys": [
"bareConsole" "bareBundleEvaluate"
] ]
}, },
{ {
@@ -188,9 +182,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareDelta.js", "path": "/lib/bare/bundles/bareConsole.js",
"keys": [ "keys": [
"bareDelta" "bareConsole"
] ]
}, },
{ {
@@ -206,15 +200,15 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareDiagnosticsChannel.js", "path": "/lib/bare/bundles/bareDelta.js",
"keys": [ "keys": [
"bareDiagnosticsChannel" "bareDelta"
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareDns.js", "path": "/lib/bare/bundles/bareDiagnosticsChannel.js",
"keys": [ "keys": [
"bareDns" "bareDiagnosticsChannel"
] ]
}, },
{ {
@@ -224,9 +218,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareExif.js", "path": "/lib/bare/bundles/bareDns.js",
"keys": [ "keys": [
"bareExif" "bareDns"
] ]
}, },
{ {
@@ -235,42 +229,36 @@
"bareDgram" "bareDgram"
] ]
}, },
{
"path": "/lib/bare/bundles/bareFfmpeg.js",
"keys": [
"bareFfmpeg"
]
},
{ {
"path": "/lib/bare/bundles/bareCov.js", "path": "/lib/bare/bundles/bareCov.js",
"keys": [ "keys": [
"bareCov" "bareCov"
] ]
}, },
{
"path": "/lib/bare/bundles/bareExif.js",
"keys": [
"bareExif"
]
},
{ {
"path": "/lib/bare/bundles/bareFfmpegEncodings.js", "path": "/lib/bare/bundles/bareFfmpegEncodings.js",
"keys": [ "keys": [
"bareFfmpegEncodings" "bareFfmpegEncodings"
] ]
}, },
{
"path": "/lib/bare/bundles/bareFfmpeg.js",
"keys": [
"bareFfmpeg"
]
},
{ {
"path": "/lib/bare/bundles/bareFormat.js", "path": "/lib/bare/bundles/bareFormat.js",
"keys": [ "keys": [
"bareFormat" "bareFormat"
] ]
}, },
{
"path": "/lib/bare/bundles/bareGif.js",
"keys": [
"bareGif"
]
},
{
"path": "/lib/bare/bundles/bareFormData.js",
"keys": [
"bareFormData"
]
},
{ {
"path": "/lib/bare/bundles/bareFileLogger.js", "path": "/lib/bare/bundles/bareFileLogger.js",
"keys": [ "keys": [
@@ -278,21 +266,15 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareFs.js", "path": "/lib/bare/bundles/bareFormData.js",
"keys": [ "keys": [
"bareFs" "bareFormData"
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareHttpParser.js", "path": "/lib/bare/bundles/bareGif.js",
"keys": [ "keys": [
"bareHttpParser" "bareGif"
]
},
{
"path": "/lib/bare/bundles/bareHeif.js",
"keys": [
"bareHeif"
] ]
}, },
{ {
@@ -301,12 +283,24 @@
"bareGtk" "bareGtk"
] ]
}, },
{
"path": "/lib/bare/bundles/bareHeif.js",
"keys": [
"bareHeif"
]
},
{ {
"path": "/lib/bare/bundles/bareHrtime.js", "path": "/lib/bare/bundles/bareHrtime.js",
"keys": [ "keys": [
"bareHrtime" "bareHrtime"
] ]
}, },
{
"path": "/lib/bare/bundles/bareHttpParser.js",
"keys": [
"bareHttpParser"
]
},
{ {
"path": "/lib/bare/bundles/bareIco.js", "path": "/lib/bare/bundles/bareIco.js",
"keys": [ "keys": [
@@ -320,9 +314,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareInspect.js", "path": "/lib/bare/bundles/bareFs.js",
"keys": [ "keys": [
"bareInspect" "bareFs"
] ]
}, },
{ {
@@ -332,21 +326,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareHttps.js", "path": "/lib/bare/bundles/bareInspect.js",
"keys": [ "keys": [
"bareHttps" "bareInspect"
]
},
{
"path": "/lib/bare/bundles/bareJpeg.js",
"keys": [
"bareJpeg"
]
},
{
"path": "/lib/bare/bundles/bareIpc.js",
"keys": [
"bareIpc"
] ]
}, },
{ {
@@ -356,15 +338,27 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareLogger.js", "path": "/lib/bare/bundles/bareJpeg.js",
"keys": [ "keys": [
"bareLogger" "bareJpeg"
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareLief.js", "path": "/lib/bare/bundles/bareHttps.js",
"keys": [ "keys": [
"bareLief" "bareHttps"
]
},
{
"path": "/lib/bare/bundles/bareIpc.js",
"keys": [
"bareIpc"
]
},
{
"path": "/lib/bare/bundles/bareLogger.js",
"keys": [
"bareLogger"
] ]
}, },
{ {
@@ -373,12 +367,6 @@
"bareInspector" "bareInspector"
] ]
}, },
{
"path": "/lib/bare/bundles/bareMake.js",
"keys": [
"bareMake"
]
},
{ {
"path": "/lib/bare/bundles/bareLink.js", "path": "/lib/bare/bundles/bareLink.js",
"keys": [ "keys": [
@@ -386,9 +374,15 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareModule.js", "path": "/lib/bare/bundles/bareLief.js",
"keys": [ "keys": [
"bareModule" "bareLief"
]
},
{
"path": "/lib/bare/bundles/bareMake.js",
"keys": [
"bareMake"
] ]
}, },
{ {
@@ -404,9 +398,15 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareModuleTraverse.js", "path": "/lib/bare/bundles/bareModule.js",
"keys": [ "keys": [
"bareModuleTraverse" "bareModule"
]
},
{
"path": "/lib/bare/bundles/bareDev.js",
"keys": [
"bareDev"
] ]
}, },
{ {
@@ -428,15 +428,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareDev.js", "path": "/lib/bare/bundles/bareModuleTraverse.js",
"keys": [ "keys": [
"bareDev" "bareModuleTraverse"
]
},
{
"path": "/lib/bare/bundles/bareMedia.js",
"keys": [
"bareMedia"
] ]
}, },
{ {
@@ -445,6 +439,12 @@
"bareOpen" "bareOpen"
] ]
}, },
{
"path": "/lib/bare/bundles/bareOs.js",
"keys": [
"bareOs"
]
},
{ {
"path": "/lib/bare/bundles/bareNet.js", "path": "/lib/bare/bundles/bareNet.js",
"keys": [ "keys": [
@@ -452,9 +452,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareOs.js", "path": "/lib/bare/bundles/bareMedia.js",
"keys": [ "keys": [
"bareOs" "bareMedia"
] ]
}, },
{ {
@@ -475,18 +475,18 @@
"barePack" "barePack"
] ]
}, },
{
"path": "/lib/bare/bundles/barePackDrive.js",
"keys": [
"barePackDrive"
]
},
{ {
"path": "/lib/bare/bundles/barePipe.js", "path": "/lib/bare/bundles/barePipe.js",
"keys": [ "keys": [
"barePipe" "barePipe"
] ]
}, },
{
"path": "/lib/bare/bundles/barePackDrive.js",
"keys": [
"barePackDrive"
]
},
{ {
"path": "/lib/bare/bundles/barePunycode.js", "path": "/lib/bare/bundles/barePunycode.js",
"keys": [ "keys": [
@@ -499,24 +499,18 @@
"barePrebuild" "barePrebuild"
] ]
}, },
{
"path": "/lib/bare/bundles/bareQuerystring.js",
"keys": [
"bareQuerystring"
]
},
{
"path": "/lib/bare/bundles/bareProcess.js",
"keys": [
"bareProcess"
]
},
{ {
"path": "/lib/bare/bundles/bareNodeRuntime.js", "path": "/lib/bare/bundles/bareNodeRuntime.js",
"keys": [ "keys": [
"bareNodeRuntime" "bareNodeRuntime"
] ]
}, },
{
"path": "/lib/bare/bundles/bareQuerystring.js",
"keys": [
"bareQuerystring"
]
},
{ {
"path": "/lib/bare/bundles/bareQueueMicrotask.js", "path": "/lib/bare/bundles/bareQueueMicrotask.js",
"keys": [ "keys": [
@@ -529,6 +523,12 @@
"bareRealm" "bareRealm"
] ]
}, },
{
"path": "/lib/bare/bundles/bareProcess.js",
"keys": [
"bareProcess"
]
},
{ {
"path": "/lib/bare/bundles/bareRuntime.js", "path": "/lib/bare/bundles/bareRuntime.js",
"keys": [ "keys": [
@@ -542,9 +542,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareSemver.js", "path": "/lib/bare/bundles/bareSdl.js",
"keys": [ "keys": [
"bareSemver" "bareSdl"
] ]
}, },
{ {
@@ -554,15 +554,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareSdl.js", "path": "/lib/bare/bundles/bareSemver.js",
"keys": [ "keys": [
"bareSdl" "bareSemver"
]
},
{
"path": "/lib/bare/bundles/bareSidecar.js",
"keys": [
"bareSidecar"
] ]
}, },
{ {
@@ -572,9 +566,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareRepl.js", "path": "/lib/bare/bundles/bareSidecar.js",
"keys": [ "keys": [
"bareRepl" "bareSidecar"
] ]
}, },
{ {
@@ -590,9 +584,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareStream.js", "path": "/lib/bare/bundles/bareRepl.js",
"keys": [ "keys": [
"bareStream" "bareRepl"
] ]
}, },
{ {
@@ -601,6 +595,12 @@
"bareStorage" "bareStorage"
] ]
}, },
{
"path": "/lib/bare/bundles/bareStream.js",
"keys": [
"bareStream"
]
},
{ {
"path": "/lib/bare/bundles/bareStdio.js", "path": "/lib/bare/bundles/bareStdio.js",
"keys": [ "keys": [
@@ -620,15 +620,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareSubprocess.js", "path": "/lib/bare/bundles/bareTiff.js",
"keys": [ "keys": [
"bareSubprocess" "bareTiff"
]
},
{
"path": "/lib/bare/bundles/bareSystemLogger.js",
"keys": [
"bareSystemLogger"
] ]
}, },
{ {
@@ -638,21 +632,15 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareTiff.js", "path": "/lib/bare/bundles/bareSystemLogger.js",
"keys": [ "keys": [
"bareTiff" "bareSystemLogger"
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareTimers.js", "path": "/lib/bare/bundles/bareSubprocess.js",
"keys": [ "keys": [
"bareTimers" "bareSubprocess"
]
},
{
"path": "/lib/bare/bundles/bareTpl.js",
"keys": [
"bareTpl"
] ]
}, },
{ {
@@ -661,6 +649,12 @@
"bareThread" "bareThread"
] ]
}, },
{
"path": "/lib/bare/bundles/bareTimers.js",
"keys": [
"bareTimers"
]
},
{ {
"path": "/lib/bare/bundles/bareTcp.js", "path": "/lib/bare/bundles/bareTcp.js",
"keys": [ "keys": [
@@ -668,9 +662,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareType.js", "path": "/lib/bare/bundles/bareTpl.js",
"keys": [ "keys": [
"bareType" "bareTpl"
] ]
}, },
{ {
@@ -679,12 +673,24 @@
"bareTls" "bareTls"
] ]
}, },
{
"path": "/lib/bare/bundles/bareType.js",
"keys": [
"bareType"
]
},
{ {
"path": "/lib/bare/bundles/bareUiKit.js", "path": "/lib/bare/bundles/bareUiKit.js",
"keys": [ "keys": [
"bareUiKit" "bareUiKit"
] ]
}, },
{
"path": "/lib/bare/bundles/bareTty.js",
"keys": [
"bareTty"
]
},
{ {
"path": "/lib/bare/bundles/bareUnpack.js", "path": "/lib/bare/bundles/bareUnpack.js",
"keys": [ "keys": [
@@ -697,24 +703,6 @@
"bareV8" "bareV8"
] ]
}, },
{
"path": "/lib/bare/bundles/bareTty.js",
"keys": [
"bareTty"
]
},
{
"path": "/lib/bare/bundles/bareWebKit.js",
"keys": [
"bareWebKit"
]
},
{
"path": "/lib/bare/bundles/bareVm.js",
"keys": [
"bareVm"
]
},
{ {
"path": "/lib/bare/bundles/bareUnionBundle.js", "path": "/lib/bare/bundles/bareUnionBundle.js",
"keys": [ "keys": [
@@ -727,6 +715,18 @@
"bareWalkHandles" "bareWalkHandles"
] ]
}, },
{
"path": "/lib/bare/bundles/bareVm.js",
"keys": [
"bareVm"
]
},
{
"path": "/lib/bare/bundles/bareWebKit.js",
"keys": [
"bareWebKit"
]
},
{ {
"path": "/lib/bare/bundles/bareWebp.js", "path": "/lib/bare/bundles/bareWebp.js",
"keys": [ "keys": [
@@ -734,15 +734,15 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareWhich.js", "path": "/lib/bare/bundles/bareWebKitGtk.js",
"keys": [ "keys": [
"bareWhich" "bareWebKitGtk"
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareV8ToIstanbul.js", "path": "/lib/bare/bundles/bareWhich.js",
"keys": [ "keys": [
"bareV8ToIstanbul" "bareWhich"
] ]
}, },
{ {
@@ -758,15 +758,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareWebKitGtk.js", "path": "/lib/bare/bundles/bareV8ToIstanbul.js",
"keys": [ "keys": [
"bareWebKitGtk" "bareV8ToIstanbul"
]
},
{
"path": "/lib/bare/bundles/bareXdiff.js",
"keys": [
"bareXdiff"
] ]
}, },
{ {
@@ -782,9 +776,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareWorker.js", "path": "/lib/bare/bundles/bareXdiff.js",
"keys": [ "keys": [
"bareWorker" "bareXdiff"
] ]
}, },
{ {
@@ -792,6 +786,12 @@
"keys": [ "keys": [
"bareWs" "bareWs"
] ]
},
{
"path": "/lib/bare/bundles/bareWorker.js",
"keys": [
"bareWorker"
]
} }
], ],
"bundleStats": { "bundleStats": {
@@ -1595,8 +1595,8 @@
], ],
"bundleProvenance": { "bundleProvenance": {
"schemaVersion": 1, "schemaVersion": 1,
"generatedAt": "2026-04-05T01:31:52.568Z", "generatedAt": "2026-04-05T01:58:32.340Z",
"gitCommit": "346bb71ffe4b7d059d4e16454dc9e4afff73224b", "gitCommit": "57ba71899bdfadc92caeb9eb8ede55d3484d9697",
"nodeVersion": "v22.22.0", "nodeVersion": "v22.22.0",
"bundleTier": "all", "bundleTier": "all",
"normativeManifest": "packages/bare-os-booter/lib/bare-module-manifest.json", "normativeManifest": "packages/bare-os-booter/lib/bare-module-manifest.json",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"schema": 1, "schema": 1,
"atMs": 1775352711830, "atMs": 1775354354273,
"commands": [ "commands": [
"arch", "arch",
"awk", "awk",
File diff suppressed because one or more lines are too long
+1
View File
@@ -17,6 +17,7 @@
"bundle:kernel": "node scripts/bundle-kernel-init.mjs", "bundle:kernel": "node scripts/bundle-kernel-init.mjs",
"pretest": "npm run build -w bare-os-coreutils && npm run build -w bare-os-bare-libs && npm run bundle:kernel && node scripts/gen-kernel-extensions-index.mjs && node scripts/verify-kernel-seeder-parity.mjs && node scripts/verify-banned-terminology.mjs && node scripts/verify-naming-alias-matrix.mjs && node scripts/verify-feature-roadmap-canonical.mjs && node scripts/verify-no-new-giant-phase-identifiers.mjs && node scripts/verify-runtime-no-incomplete-markers.mjs && node scripts/verify-bare-imports.mjs && node scripts/verify-booter-boot-steps-alignment.mjs && node scripts/gen-ctx-client-helper.mjs && node scripts/verify-extension-manifest-schema.mjs && node scripts/verify-ctx-api-feature-bits.mjs && node scripts/verify-kernel-capabilities-contract.mjs && node scripts/verify-kernel-capabilities-word-6.mjs && node scripts/verify-kernel-capabilities-word-7.mjs && node scripts/verify-kernel-capabilities-word-8.mjs && node scripts/verify-kernel-capabilities-word-9.mjs && node scripts/verify-kernel-capabilities-word-10.mjs && node scripts/verify-kernel-capabilities-word-11.mjs && node scripts/verify-ctx-dts.mjs && node scripts/validate-example-schemas.mjs && node scripts/verify-doc-links.mjs && node scripts/verify-man-coverage.mjs && node scripts/verify-compat-matrix.mjs && node scripts/verify-bundle-health.mjs && node scripts/verify-bundle-markers.mjs && node scripts/verify-bundle-throws.mjs && node scripts/verify-kernel-program-doc.mjs && node scripts/verify-kernel-program-proc-schema.mjs && node scripts/verify-kernel-program-roadmap-table.mjs && node scripts/verify-pear-no-static-node-import.mjs && npm run smoke:bare-manifest", "pretest": "npm run build -w bare-os-coreutils && npm run build -w bare-os-bare-libs && npm run bundle:kernel && node scripts/gen-kernel-extensions-index.mjs && node scripts/verify-kernel-seeder-parity.mjs && node scripts/verify-banned-terminology.mjs && node scripts/verify-naming-alias-matrix.mjs && node scripts/verify-feature-roadmap-canonical.mjs && node scripts/verify-no-new-giant-phase-identifiers.mjs && node scripts/verify-runtime-no-incomplete-markers.mjs && node scripts/verify-bare-imports.mjs && node scripts/verify-booter-boot-steps-alignment.mjs && node scripts/gen-ctx-client-helper.mjs && node scripts/verify-extension-manifest-schema.mjs && node scripts/verify-ctx-api-feature-bits.mjs && node scripts/verify-kernel-capabilities-contract.mjs && node scripts/verify-kernel-capabilities-word-6.mjs && node scripts/verify-kernel-capabilities-word-7.mjs && node scripts/verify-kernel-capabilities-word-8.mjs && node scripts/verify-kernel-capabilities-word-9.mjs && node scripts/verify-kernel-capabilities-word-10.mjs && node scripts/verify-kernel-capabilities-word-11.mjs && node scripts/verify-ctx-dts.mjs && node scripts/validate-example-schemas.mjs && node scripts/verify-doc-links.mjs && node scripts/verify-man-coverage.mjs && node scripts/verify-compat-matrix.mjs && node scripts/verify-bundle-health.mjs && node scripts/verify-bundle-markers.mjs && node scripts/verify-bundle-throws.mjs && node scripts/verify-kernel-program-doc.mjs && node scripts/verify-kernel-program-proc-schema.mjs && node scripts/verify-kernel-program-roadmap-table.mjs && node scripts/verify-pear-no-static-node-import.mjs && npm run smoke:bare-manifest",
"test": "npm run test --workspaces --if-present", "test": "npm run test --workspaces --if-present",
"test:bare": "npm run test -w bare-os-protocol && npm run test:bare -w bare-os-booter && npm run test -w bare-os-seeder",
"verify-kernel-seeder": "node scripts/verify-kernel-seeder-parity.mjs", "verify-kernel-seeder": "node scripts/verify-kernel-seeder-parity.mjs",
"release-checklist": "node scripts/release-checklist.mjs", "release-checklist": "node scripts/release-checklist.mjs",
"format": "prettier --write .", "format": "prettier --write .",
+2 -1
View File
@@ -4,7 +4,7 @@ Authoritative **version alignment** with protocol and telemetry schema numbers l
## Maintenance ## Maintenance
- **POSIX / proc**: **`/proc/bare_os/syscalls.json`** schema **3** (**`opsDetail`**, errno map via **`bare-os-posix-errno.js`**, **`posixProfile`**); **`/proc/bare_os_features`** includes **`capabilitySurface`** (feature words + POSIX profile summary); **`/proc/bare_os_swarm`** embeds **`timeSync`** sketch; **`/proc/bare_os/process_table.json`** schema **2** (session, rlimits, fd summary, job zombies). Protocol export **`BARE_OS_POSIX_PROFILE_*`**. - **POSIX / proc**: **`/proc/bare_os/syscalls.json`** schema **4** adds **`fdModel`** / **`signalModel`** (plus schema **3** **`opsDetail`**, errno map, **`posixProfile`**); **`/proc/bare_os_features`** includes **`capabilitySurface`**; **`/proc/bare_os_swarm`** embeds **`timeSync`** sketch; **`/proc/bare_os/process_table.json`** schema **3** (session, rlimits, logical FD rows, job **`stopped`**, **`signalModel`**). Protocol export **`BARE_OS_POSIX_PROFILE_*`**.
- **Vendored bundles**: post-esbuild **`scripts/sanitize-bare-bundles.mjs`** (from **`bare-os-bare-libs/build.mjs`**) strips CI-forbidden markers and replaces **`node:`** requires in **`bareDev.js`** with **`bare-*`** modules. - **Vendored bundles**: post-esbuild **`scripts/sanitize-bare-bundles.mjs`** (from **`bare-os-bare-libs/build.mjs`**) strips CI-forbidden markers and replaces **`node:`** requires in **`bareDev.js`** with **`bare-*`** modules.
- **Boot hooks (naming)**: canonical **`bareOsRegisterBootStepHook`**, **`bareOsInvokeBootStepHooks`**, **`bareOsEmitBareBootStepHint`**; legacy **`*BootPhase*`** methods remain thin wrappers (**no `bareOsCtxApiVersion` bump**). See [docs/reference/naming-alias-matrix.md](../../docs/reference/naming-alias-matrix.md). - **Boot hooks (naming)**: canonical **`bareOsRegisterBootStepHook`**, **`bareOsInvokeBootStepHooks`**, **`bareOsEmitBareBootStepHint`**; legacy **`*BootPhase*`** methods remain thin wrappers (**no `bareOsCtxApiVersion` bump**). See [docs/reference/naming-alias-matrix.md](../../docs/reference/naming-alias-matrix.md).
- **Pear / Bare**: [`lib/kernel-runner.js`](lib/kernel-runner.js) must not **static**-import **`node:module`** (Bares resolver cannot load **`node:`** builtins from a **`pear://`** bundle). Optional **`BARE_OS_BIN_WORKER_OFFLOAD`** now obtains **`createRequire`** via **dynamic** **`import('bare-module')`** when running outside Node. - **Pear / Bare**: [`lib/kernel-runner.js`](lib/kernel-runner.js) must not **static**-import **`node:module`** (Bares resolver cannot load **`node:`** builtins from a **`pear://`** bundle). Optional **`BARE_OS_BIN_WORKER_OFFLOAD`** now obtains **`createRequire`** via **dynamic** **`import('bare-module')`** when running outside Node.
@@ -14,6 +14,7 @@ Authoritative **version alignment** with protocol and telemetry schema numbers l
| Version | Booter (workspace) | Notes | | Version | Booter (workspace) | Notes |
| ------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | ------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| 1.31.0 | 0.1.0 | **`/proc/bare_os/syscalls.json`** schema **4** (**`fdModel`**, **`signalModel`**). **`ctx`**: **`bareOsRegisterLogicalFd`**, **`bareOsTrySpawnHostSubprocess`**, **`bareOsAppendVaultRotationCheckpoint`**, **`shellTrapHandlers`**. VFS: read-only **`/mirror/aux0`…** for **`disk.auxiliaryDrives`**; optional **`BARE_OS_VFS_BIN_CACHE_BLAKE2B`** (BLAKE2b-keyed **`/bin`** cache when **`bare-crypto`** loads). Passthrough env: **`BARE_OS_VFS_ENFORCE_ACL`**, **`BARE_OS_VFS_BIN_INDEX_BUILD`**. Seeder wires **`snapshotHintsJson`** from **`BARE_OS_SEED_SNAPSHOT_HINTS_JSON`** or **`BARE_OS_SEED_CORESTORE_SNAPSHOT_TAG`**. |
| 1.28.0 | 0.1.0 | **`ctx.bareOsRunSystemctlCli`**: drive-resident **`systemctl`** / **`journalctl`** call the same backend as host delegates when the booter exposes this method. **`disk.os`**: **`createBareOsDiskOsBridge`** — Hyperdrive path **`searchLocal`**, whitelisted **`bare_os.*`** **`execRpc`** (typed **`BareOsKernelError`** on unknown). Host delegates: **`warc`** (WARC 1.0 list/write-response), **`archive`** (ustar tar front-end); **`ssh-keygen`** passphrase → **`bareOsKeySchema: 2`** (PBKDF2 + ChaCha20-Poly1305). New **`lib/bare-os-boot-phases.js`**, **`lib/bare-os-errors.js`**. CI: **`verify-booter-boot-steps-alignment.mjs`**. Docs: **`docs/architecture/KERNEL_CONTRACT.md`**, **`docs/deployment/OTA_AND_BUNDLES.md`**, **`docs/audit/PLACEHOLDER_BASELINE.md`**, handbook §3 **`disk.os`**. | | 1.28.0 | 0.1.0 | **`ctx.bareOsRunSystemctlCli`**: drive-resident **`systemctl`** / **`journalctl`** call the same backend as host delegates when the booter exposes this method. **`disk.os`**: **`createBareOsDiskOsBridge`** — Hyperdrive path **`searchLocal`**, whitelisted **`bare_os.*`** **`execRpc`** (typed **`BareOsKernelError`** on unknown). Host delegates: **`warc`** (WARC 1.0 list/write-response), **`archive`** (ustar tar front-end); **`ssh-keygen`** passphrase → **`bareOsKeySchema: 2`** (PBKDF2 + ChaCha20-Poly1305). New **`lib/bare-os-boot-phases.js`**, **`lib/bare-os-errors.js`**. CI: **`verify-booter-boot-steps-alignment.mjs`**. Docs: **`docs/architecture/KERNEL_CONTRACT.md`**, **`docs/deployment/OTA_AND_BUNDLES.md`**, **`docs/audit/PLACEHOLDER_BASELINE.md`**, handbook §3 **`disk.os`**. |
| 1.25.0 | 0.1.0 | Kernel program proc: primary **`/proc/bare_os/kernel_program.json`** + proc id **`kernel_program`** (legacy **`giant_phase_program`** unchanged); JSON uses **`operatorSketches`** and **`program: bare-os-kernel-program`**; env **`BARE_OS_KERNEL_PROGRAM_PROC_JSON`** (legacy **`BARE_OS_GIANT_PHASE_PROGRAM_JSON`**); **`subsystems.kernel.programProc`** on **`bareOsPublishBootReady`** (replaces **`giantPhase`**). CI: **`verify-kernel-program-doc.mjs`**, **`verify-kernel-program-proc-schema.mjs`**, **`verify-kernel-program-roadmap-table.mjs`**. Module **`bare-os-giant-phase.js`** removed in favor of **`bare-os-kernel-program-proc.js`**. | | 1.25.0 | 0.1.0 | Kernel program proc: primary **`/proc/bare_os/kernel_program.json`** + proc id **`kernel_program`** (legacy **`giant_phase_program`** unchanged); JSON uses **`operatorSketches`** and **`program: bare-os-kernel-program`**; env **`BARE_OS_KERNEL_PROGRAM_PROC_JSON`** (legacy **`BARE_OS_GIANT_PHASE_PROGRAM_JSON`**); **`subsystems.kernel.programProc`** on **`bareOsPublishBootReady`** (replaces **`giantPhase`**). CI: **`verify-kernel-program-doc.mjs`**, **`verify-kernel-program-proc-schema.mjs`**, **`verify-kernel-program-roadmap-table.mjs`**. Module **`bare-os-giant-phase.js`** removed in favor of **`bare-os-kernel-program-proc.js`**. |
| 1.24.0 | 0.1.0 | **`bareOsEmitKernelEvent`** / **`bareOsSubscribeKernelEvent`**, **`bareOsAcquireKeyHandle`** sketch; **`/run/bare-os/services.json`** seed; optional **`BARE_OS_LOADER_AUDIT`** → **`loader-audit.ndjson`**; proc **schema 2** + operator env sketches (historically documented as giant-phase / **`gp2`**); runtime caps **`kernelEventSubscribe`**, **`keyBrokerHandleSketch`**. Stock kernel: boot stages, dry-run, policy fallbacks, ABI min, services registry consumers. | | 1.24.0 | 0.1.0 | **`bareOsEmitKernelEvent`** / **`bareOsSubscribeKernelEvent`**, **`bareOsAcquireKeyHandle`** sketch; **`/run/bare-os/services.json`** seed; optional **`BARE_OS_LOADER_AUDIT`** → **`loader-audit.ndjson`**; proc **schema 2** + operator env sketches (historically documented as giant-phase / **`gp2`**); runtime caps **`kernelEventSubscribe`**, **`keyBrokerHandleSketch`**. Stock kernel: boot stages, dry-run, policy fallbacks, ABI min, services registry consumers. |
+157 -17
View File
@@ -103,7 +103,8 @@ import {
unlockIdentity, unlockIdentity,
logoutIdentity, logoutIdentity,
saveVaultToDrive, saveVaultToDrive,
applyLoginKeys applyLoginKeys,
bareOsAppendVaultRotationCheckpoint
} from './lib/identity-session.js' } from './lib/identity-session.js'
import { HdmsController, runHdmsCli } from './lib/hdms-manager.js' import { HdmsController, runHdmsCli } from './lib/hdms-manager.js'
import { import {
@@ -180,6 +181,8 @@ import {
import { bareOsSwarmLifecycleSnapshot } from './lib/bare-os-swarm-lifecycle.js' import { bareOsSwarmLifecycleSnapshot } from './lib/bare-os-swarm-lifecycle.js'
import { bareOsReadHostProfile } from './lib/bare-os-host-profile.js' import { bareOsReadHostProfile } from './lib/bare-os-host-profile.js'
import { bareOsProcessTableSnapshot } from './lib/bare-os-process-table.js' import { bareOsProcessTableSnapshot } from './lib/bare-os-process-table.js'
import { bareOsStatvfsFromHyperdrive } from './lib/bare-os-statvfs-from-drive.js'
import { maybeBareOsBuildBinManifest } from './lib/bare-os-bin-index.js'
import { BARE_OS_ACCOUNT_CRYPTO_PROFILE_V2 } from './lib/identity-account.js' import { BARE_OS_ACCOUNT_CRYPTO_PROFILE_V2 } from './lib/identity-account.js'
import { evaluateBareOsPeerAdmission } from './lib/bare-os-peer-admission.js' import { evaluateBareOsPeerAdmission } from './lib/bare-os-peer-admission.js'
import { prioritizeReplicationPaths } from './lib/bare-os-replication-priority.js' import { prioritizeReplicationPaths } from './lib/bare-os-replication-priority.js'
@@ -516,6 +519,9 @@ async function executeKernel(disk, store, swarm, initSource) {
'BARE_OS_URANDOM_CRYPTO', 'BARE_OS_URANDOM_CRYPTO',
'BARE_OS_INITD_MAX_PARALLEL', 'BARE_OS_INITD_MAX_PARALLEL',
'BARE_OS_VFS_BIN_CACHE', 'BARE_OS_VFS_BIN_CACHE',
'BARE_OS_VFS_BIN_CACHE_BLAKE2B',
'BARE_OS_VFS_ENFORCE_ACL',
'BARE_OS_VFS_BIN_INDEX_BUILD',
'BARE_OS_SANDBOX_WORKER', 'BARE_OS_SANDBOX_WORKER',
'BARE_OS_BLIND_BOOTSTRAP_URL', 'BARE_OS_BLIND_BOOTSTRAP_URL',
'BARE_OS_BLIND_BOOTSTRAP_JSON', 'BARE_OS_BLIND_BOOTSTRAP_JSON',
@@ -664,6 +670,15 @@ async function executeKernel(disk, store, swarm, initSource) {
/** @type {Set<(ev: Record<string, unknown>) => void | Promise<void>>} */ /** @type {Set<(ev: Record<string, unknown>) => void | Promise<void>>} */
const eventBusSubs = new Set() const eventBusSubs = new Set()
const bootStartedMs = Date.now() const bootStartedMs = Date.now()
/** @type {null | (() => bigint)} */
let bootHrtimeNowNs = null
try {
const bh = await import('bare-hrtime')
const hr = bh?.hrtime
if (hr && typeof hr.bigint === 'function') bootHrtimeNowNs = () => hr.bigint()
} catch {
/* optional native clock */
}
/** @type {{ ready: boolean, phases: string[], stages: string[], steps?: string[], bootStepsFlat?: string[], booterPhases: string[], booterStages: string[], sessionId: string, completedAtMs: number | null, minimal: boolean, imageDigest: string, pearChannel: string, pearRelease: string, bootProvenance?: Record<string, unknown>, subsystems?: Record<string, unknown>, bareModuleLockWarnings?: string[] }} */ /** @type {{ ready: boolean, phases: string[], stages: string[], steps?: string[], bootStepsFlat?: string[], booterPhases: string[], booterStages: string[], sessionId: string, completedAtMs: number | null, minimal: boolean, imageDigest: string, pearChannel: string, pearRelease: string, bootProvenance?: Record<string, unknown>, subsystems?: Record<string, unknown>, bareModuleLockWarnings?: string[] }} */
const bootReadyStateRef = { const bootReadyStateRef = {
ready: false, ready: false,
@@ -687,7 +702,8 @@ async function executeKernel(disk, store, swarm, initSource) {
policyDigestSketch: String( policyDigestSketch: String(
shellEnv.BARE_OS_BOOT_POLICY_DIGEST_SKETCH || '' shellEnv.BARE_OS_BOOT_POLICY_DIGEST_SKETCH || ''
).trim(), ).trim(),
bootStartedMs bootStartedMs,
hrtimeAvailable: !!bootHrtimeNowNs
}, },
subsystems: {}, subsystems: {},
bareModuleLockWarnings: [] bareModuleLockWarnings: []
@@ -790,7 +806,10 @@ async function executeKernel(disk, store, swarm, initSource) {
ms: Date.now() - bootStartedMs, ms: Date.now() - bootStartedMs,
ts: Date.now(), ts: Date.now(),
sessionId: bareOsSessionId, sessionId: bareOsSessionId,
lifecycleSchemaVersion: BARE_OS_LIFECYCLE_SCHEMA_VERSION lifecycleSchemaVersion: BARE_OS_LIFECYCLE_SCHEMA_VERSION,
...(bootHrtimeNowNs
? { monotonicNs: String(bootHrtimeNowNs()) }
: {})
} }
for (const fn of bootEventSubs) { for (const fn of bootEventSubs) {
Promise.resolve(fn(ev)).catch(() => {}) Promise.resolve(fn(ev)).catch(() => {})
@@ -806,6 +825,19 @@ async function executeKernel(disk, store, swarm, initSource) {
/** @type {Record<string, unknown> | null} */ /** @type {Record<string, unknown> | null} */
ctx: null ctx: null
} }
function bareOsCollectLogicalFdRows() {
const c = bareOsInteractiveCtxRef.ctx
const o = c?.bareOsLogicalFds
if (!o || typeof o !== 'object') return []
return Object.keys(o)
.filter((k) => /^[0-9]+$/.test(k) && k !== '0' && k !== '1' && k !== '2')
.sort((a, b) => Number(a) - Number(b))
.map((fdNum) => ({
fd: Number(fdNum),
target: String(/** @type {Record<string, string>} */ (o)[fdNum] ?? '')
}))
}
function bareOsLiveShellJobsAndIpcStats() { function bareOsLiveShellJobsAndIpcStats() {
const live = bareOsInteractiveCtxRef.ctx const live = bareOsInteractiveCtxRef.ctx
const shellJobs = const shellJobs =
@@ -856,7 +888,9 @@ async function executeKernel(disk, store, swarm, initSource) {
CLOCK_BOOTTIME_RELATIVE_MS: CLOCK_BOOTTIME_RELATIVE_MS:
bootStartedMs != null ? wallMs - bootStartedMs : null, bootStartedMs != null ? wallMs - bootStartedMs : null,
CLOCK_MONOTONIC_NS_FROM_PERF: monotonicNsFromPerf, CLOCK_MONOTONIC_NS_FROM_PERF: monotonicNsFromPerf,
note: 'clock_gettime(CLOCK_REALTIME) analog; monotonic from performance.now where the host exposes it. Optional bare-hrtime can extend this surface on Bare.' CLOCK_MONOTONIC_NS_HRTIME:
bootHrtimeNowNs != null ? String(bootHrtimeNowNs()) : null,
note: 'clock_gettime(CLOCK_REALTIME) analog; monotonic from performance.now and optional bare-hrtime.bigint() when the addon loads.'
})}\n` })}\n`
}, },
getProcSelfExtraFds() { getProcSelfExtraFds() {
@@ -874,6 +908,28 @@ async function executeKernel(disk, store, swarm, initSource) {
target: String(/** @type {Record<string, string>} */ (o)[fdNum] ?? '') target: String(/** @type {Record<string, string>} */ (o)[fdNum] ?? '')
})) }))
}, },
getProcSyntheticLinuxCompat() {
const peers = disk.peers ? [...disk.peers] : []
return {
sessionId: bareOsSessionId,
swarmPeerCount: peers.length,
peerIds: peers.map((p) =>
String(
p.id ||
'0000000000000000000000000000000000000000000000000000000000000000'
)
)
}
},
getAuxiliaryMountLines() {
const aux = disk.auxiliaryDrives || []
return aux.map(
(_d, i) => `bare-os-aux${i} /mirror/aux${i} hyperdrive ro 0 0`
)
},
getAuxiliaryDrives() {
return disk.auxiliaryDrives || []
},
procBareOsDebugText() { procBareOsDebugText() {
if ( if (
shellEnv.BARE_OS_KERNEL_DEBUG !== '1' && shellEnv.BARE_OS_KERNEL_DEBUG !== '1' &&
@@ -1080,6 +1136,8 @@ async function executeKernel(disk, store, swarm, initSource) {
} else if (pwdLogical.startsWith('/var/')) { } else if (pwdLogical.startsWith('/var/')) {
volumeClass = 'system' volumeClass = 'system'
} }
const sysSv = bareOsStatvfsFromHyperdrive(disk.drive)
const perSv = bareOsStatvfsFromHyperdrive(disk.personalDrive)
return `${JSON.stringify({ return `${JSON.stringify({
ctxApiVersion: BARE_OS_CTX_API_VERSION, ctxApiVersion: BARE_OS_CTX_API_VERSION,
pipeline: pl, pipeline: pl,
@@ -1088,18 +1146,21 @@ async function executeKernel(disk, store, swarm, initSource) {
session: sessionStatsRef, session: sessionStatsRef,
peers: disk.peers?.size ?? 0, peers: disk.peers?.size ?? 0,
statvfs: { statvfs: {
f_bsize: 4096, schema: 2,
f_frsize: 4096, f_bsize: sysSv?.f_bsize ?? 4096,
f_blocks: null, f_frsize: sysSv?.f_frsize ?? 4096,
f_bfree: null, f_blocks: sysSv?.f_blocks ?? null,
f_bavail: null, f_bfree: sysSv?.f_bfree ?? null,
f_files: null, f_bavail: sysSv?.f_bavail ?? null,
f_ffree: null, f_files: sysSv?.f_files ?? null,
f_namemax: 255, f_ffree: sysSv?.f_ffree ?? null,
f_namemax: sysSv?.f_namemax ?? 255,
systemDrive: sysSv,
personalDrive: perSv,
pwd_logical: pwdLogical, pwd_logical: pwdLogical,
volume_class: volumeClass, volume_class: volumeClass,
f_basetype: 'hyperdrive', f_basetype: 'hyperdrive',
note: 'Symbolic statvfs sketch; block/file counts are null until drive-specific accounting exists. volume_class maps $PWD to the VFS routing bucket (not a POSIX fsid).' note: 'Best-effort Hyperdrive byte accounting via core/tree probes; null fields when the drive shape is unknown. volume_class maps $PWD to the VFS routing bucket.'
}, },
mountManager: { mountManager: {
schema: 1, schema: 1,
@@ -1183,6 +1244,18 @@ async function executeKernel(disk, store, swarm, initSource) {
shellEnv.BARE_OS_HTTP_RUNTIME_GUEST_ONLY !== 'false', shellEnv.BARE_OS_HTTP_RUNTIME_GUEST_ONLY !== 'false',
note: 'bare-http1 / bare-fetch load from guest bare library paths unless boot policy expands the allowlist.' note: 'bare-http1 / bare-fetch load from guest bare library paths unless boot policy expands the allowlist.'
}, },
pearRuntimeHint: {
schema: 1,
pearChannel: String(shellEnv.BARE_OS_PEAR_CHANNEL || '').trim() || undefined,
pearRelease: String(shellEnv.BARE_OS_PEAR_RELEASE || '').trim() || undefined,
pearRuntimeVersion: String(
shellEnv.BARE_OS_PEAR_RUNTIME_VERSION || ''
).trim() || undefined,
updaterChannel: String(
shellEnv.BARE_OS_PEAR_UPDATER_CHANNEL || ''
).trim() || undefined,
note: 'OTA / Pear stage hints for operators; compare pear-runtime-updater docs on Holepunch.'
},
posixShellExtendedProfile: { posixShellExtendedProfile: {
schema: 1, schema: 1,
envGate: 'BARE_OS_SH_EXTENDED_PROFILE', envGate: 'BARE_OS_SH_EXTENDED_PROFILE',
@@ -2023,7 +2096,8 @@ async function executeKernel(disk, store, swarm, initSource) {
bootStartedMs, bootStartedMs,
signalState: bareOsVirtualSignalState, signalState: bareOsVirtualSignalState,
shellJobs, shellJobs,
ipcStats ipcStats,
logicalFdRows: bareOsCollectLogicalFdRows()
}) })
})(), })(),
initdReadiness: bareInitdReadinessSnapshot(), initdReadiness: bareInitdReadinessSnapshot(),
@@ -2051,13 +2125,14 @@ async function executeKernel(disk, store, swarm, initSource) {
bootStartedMs, bootStartedMs,
signalState: bareOsVirtualSignalState, signalState: bareOsVirtualSignalState,
shellJobs, shellJobs,
ipcStats ipcStats,
logicalFdRows: bareOsCollectLogicalFdRows()
}) })
)}\n` )}\n`
}, },
procBareOsSyscallsText() { procBareOsSyscallsText() {
return `${JSON.stringify({ return `${JSON.stringify({
schemaVersion: 3, schemaVersion: 4,
ctxApiVersion: BARE_OS_CTX_API_VERSION, ctxApiVersion: BARE_OS_CTX_API_VERSION,
posixProfile: { posixProfile: {
id: BARE_OS_POSIX_PROFILE_ID, id: BARE_OS_POSIX_PROFILE_ID,
@@ -2067,6 +2142,19 @@ async function executeKernel(disk, store, swarm, initSource) {
ops: [...BARE_OS_STOCK_SYSCALL_OPS], ops: [...BARE_OS_STOCK_SYSCALL_OPS],
opsDetail: [...BARE_OS_SYSCALL_OPS_DETAIL], opsDetail: [...BARE_OS_SYSCALL_OPS_DETAIL],
errnoHints: bareOsErrnoTableForProc(), errnoHints: bareOsErrnoTableForProc(),
fdModel: {
schema: 1,
note: 'Integer FDs index logical VFS/IPC targets in the guest; not host kernel file descriptors.',
stdio: [0, 1, 2],
register: 'ctx.bareOsRegisterLogicalFd',
processTablePath: '/proc/bare_os/process_table.json',
processTableSchema: 3
},
signalModel: {
schema: 1,
note: 'ctx.bareOsSendSignal delivers to synthetic pids 13; see process_table.signalModel.sigpendingAnalog.',
names: ['HUP', 'INT', 'KILL', 'TERM', 'PIPE', 'CHLD', 'USR1', 'USR2']
},
caps: { caps: {
note: 'VFS path classes and boot policy may deny individual ops at runtime.' note: 'VFS path classes and boot policy may deny individual ops at runtime.'
}, },
@@ -2088,9 +2176,24 @@ async function executeKernel(disk, store, swarm, initSource) {
}, },
procBareOsProtomuxWireText() { procBareOsProtomuxWireText() {
const rt = protomuxAliasRegistry.snapshot() const rt = protomuxAliasRegistry.snapshot()
const peers = disk.peers ? [...disk.peers] : []
return `${JSON.stringify({ return `${JSON.stringify({
schemaVersion: 1, schemaVersion: 1,
ctxApiVersion: BARE_OS_CTX_API_VERSION, ctxApiVersion: BARE_OS_CTX_API_VERSION,
swarm: {
schema: 1,
peerCount: peers.length,
topicHex: b4a.toString(topicKey(), 'hex'),
protocol: 'bare-os-v1'
},
replication: {
schema: 1,
seedReplicationStatus:
disk.seedReplicationStatus &&
typeof disk.seedReplicationStatus === 'object'
? disk.seedReplicationStatus
: undefined
},
aliases: rt.aliases, aliases: rt.aliases,
changeLogTail: rt.changeLogTail, changeLogTail: rt.changeLogTail,
reverseIndex: rt.reverseIndex, reverseIndex: rt.reverseIndex,
@@ -2331,6 +2434,8 @@ async function executeKernel(disk, store, swarm, initSource) {
bareOsBootStartedMs: bootStartedMs, bareOsBootStartedMs: bootStartedMs,
/** Logical open-file targets for `/proc/self/fd/*` beyond stdio (see `bareOsRegisterLogicalFd`). */ /** Logical open-file targets for `/proc/self/fd/*` beyond stdio (see `bareOsRegisterLogicalFd`). */
bareOsLogicalFds: /** @type {Record<string, string>} */ ({}), bareOsLogicalFds: /** @type {Record<string, string>} */ ({}),
/** Shell `trap` COMMAND → SIGNAL map (logical job control; delivery is host-specific). */
shellTrapHandlers: Object.create(null),
/** True when `BARE_OS_SKIP_REPL=1` — stdin is non-interactive; `readLine` yields EOF immediately after boot. */ /** True when `BARE_OS_SKIP_REPL=1` — stdin is non-interactive; `readLine` yields EOF immediately after boot. */
bareOsSkipRepl: skipInteractive, bareOsSkipRepl: skipInteractive,
disk, disk,
@@ -2520,6 +2625,16 @@ async function executeKernel(disk, store, swarm, initSource) {
async saveVault() { async saveVault() {
await saveVaultToDrive(this) await saveVaultToDrive(this)
}, },
/**
* Append vault / key rotation audit metadata (plaintext NDJSON on the personal drive).
* @param {Record<string, unknown>} row
*/
async bareOsAppendVaultRotationCheckpoint(row) {
await bareOsAppendVaultRotationCheckpoint(
this,
row && typeof row === 'object' ? row : {}
)
},
/** Populated by `loadBarerc` / first `execShellLine` (default alias table). */ /** Populated by `loadBarerc` / first `execShellLine` (default alias table). */
shellAliases: undefined, shellAliases: undefined,
/** /**
@@ -2760,7 +2875,8 @@ async function executeKernel(disk, store, swarm, initSource) {
bootStartedMs, bootStartedMs,
signalState: bareOsVirtualSignalState, signalState: bareOsVirtualSignalState,
shellJobs, shellJobs,
ipcStats ipcStats,
logicalFdRows: bareOsCollectLogicalFdRows()
}) })
}, },
/** /**
@@ -2818,6 +2934,29 @@ async function executeKernel(disk, store, swarm, initSource) {
} }
return { ok: true, pid, signal: sig, delivered: true, atMs } 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).
* @param {string[]} argv
* @param {Record<string, unknown>} [opts]
*/
async bareOsTrySpawnHostSubprocess(argv, opts) {
const av = Array.isArray(argv) ? argv.map((x) => String(x)) : []
try {
const mod = await import('bare-subprocess')
const sp = mod && (mod.default || mod)
const spawn = sp && sp.spawn
if (typeof spawn !== 'function') {
return { ok: false, reason: 'bare-subprocess.spawn-unavailable' }
}
const child = spawn(av[0] || 'bare', av.slice(1), {
...(opts && typeof opts === 'object' ? opts : {}),
stdio: 'pipe'
})
return { ok: true, child }
} catch (e) {
return { ok: false, reason: (e && e.message) || String(e) }
}
},
/** /**
* @param {number} fd * @param {number} fd
* @param {string} target * @param {string} target
@@ -4625,6 +4764,7 @@ async function executeKernel(disk, store, swarm, initSource) {
await applyGuestEnv(ctx) await applyGuestEnv(ctx)
await ensureGuestHome(ctx) await ensureGuestHome(ctx)
await maybeBareOsBuildBinManifest(ctx)
const session = await createKernelReplSession({ const session = await createKernelReplSession({
stdin: sessionStdin, stdin: sessionStdin,
@@ -0,0 +1,44 @@
/**
* Optional `/bin` manifest on the personal drive for large images (`BARE_OS_VFS_BIN_INDEX_BUILD=1`).
*/
/**
* @param {Record<string, unknown>} ctx
*/
export async function maybeBareOsBuildBinManifest(ctx) {
const e = ctx.env
if (
e.BARE_OS_VFS_BIN_INDEX_BUILD !== '1' &&
e.BARE_OS_VFS_BIN_INDEX_BUILD !== 'true'
) {
return
}
const vfs = ctx.vfs
const drive = ctx.drive
const b4 = ctx.b4a
if (!vfs || !drive || typeof drive.readdir !== 'function' || !b4) return
/** @type {string[]} */
const names = []
try {
const stream = drive.readdir('/bin')
for await (const n of stream) names.push(String(n))
} catch {
return
}
names.sort()
const body =
JSON.stringify({
schema: 1,
atMs: Date.now(),
path: '/bin',
count: names.length,
names: names.slice(0, 8000)
}) + '\n'
try {
await vfs.writeFile('/.bare-os/index/bin-manifest.json', b4.from(body))
} catch (err) {
ctx.console?.error?.(
'[bare-os] bin index: ' + ((err && err.message) || String(err))
)
}
}
@@ -2,4 +2,4 @@
* Semantic version of the booter `ctx` contract for custom kernels. * Semantic version of the booter `ctx` contract for custom kernels.
* Bump when adding/removing/renaming documented `ctx` fields or changing behavior. * Bump when adding/removing/renaming documented `ctx` fields or changing behavior.
*/ */
export const BARE_OS_CTX_API_VERSION = '1.30.0' export const BARE_OS_CTX_API_VERSION = '1.31.0'
+16
View File
@@ -330,6 +330,22 @@ export interface BareOsKernelContext {
exists?: boolean exists?: boolean
atMs?: number atMs?: number
} }
/** FD → logical VFS/IPC target; see `/proc/bare_os/syscalls.json` fdModel. */
bareOsLogicalFds?: Record<string, string>
/** Shell `trap` COMMAND → SIGNAL map (logical; delivery is synthetic). */
shellTrapHandlers?: Record<string, string>
bareOsAppendVaultRotationCheckpoint?(
row: Record<string, unknown>
): Promise<void>
bareOsTrySpawnHostSubprocess?(
argv: string[],
opts?: Record<string, unknown>
): Promise<
| { ok: true; child: unknown }
| { ok: false; reason: string }
>
bareOsRegisterLogicalFd?(fd: number, target: string): void
bareOsUnregisterLogicalFd?(fd: number): void
bareOsPathconf?(path: string, name: string): number bareOsPathconf?(path: string, name: string): number
bareOsSyscall?( bareOsSyscall?(
op: string, op: string,
@@ -7,8 +7,9 @@
* sessionId?: string, * sessionId?: string,
* bootStartedMs?: number, * bootStartedMs?: number,
* signalState?: Map<number, { signal: string, atMs: number }>, * signalState?: Map<number, { signal: string, atMs: number }>,
* shellJobs?: Array<{ id?: number, done?: boolean, pgid?: number, sid?: number, label?: string }>, * shellJobs?: Array<{ id?: number, done?: boolean, stopped?: boolean, pgid?: number, sid?: number, label?: string }>,
* ipcStats?: { processGroups?: Record<string, unknown> } | null * ipcStats?: { processGroups?: Record<string, unknown> } | null,
* logicalFdRows?: Array<{ fd: number, target: string }>
* }} opts * }} opts
*/ */
export function bareOsProcessTableSnapshot(opts = {}) { export function bareOsProcessTableSnapshot(opts = {}) {
@@ -16,6 +17,11 @@ export function bareOsProcessTableSnapshot(opts = {}) {
const boot = typeof opts.bootStartedMs === 'number' ? opts.bootStartedMs : 0 const boot = typeof opts.bootStartedMs === 'number' ? opts.bootStartedMs : 0
const now = Date.now() const now = Date.now()
const sigState = opts.signalState instanceof Map ? opts.signalState : new Map() const sigState = opts.signalState instanceof Map ? opts.signalState : new Map()
const logicalRows = Array.isArray(opts.logicalFdRows) ? opts.logicalFdRows : []
const virtualSignalDeliveries = []
for (const [pid, v] of sigState.entries()) {
virtualSignalDeliveries.push({ pid, signal: v.signal, atMs: v.atMs })
}
const mk = (pid, ppid, name, startedAtMs) => { const mk = (pid, ppid, name, startedAtMs) => {
const sig = sigState.get(pid) const sig = sigState.get(pid)
return { return {
@@ -41,10 +47,11 @@ export function bareOsProcessTableSnapshot(opts = {}) {
pgid, pgid,
sid: session, sid: session,
name: 'bare-os-shell-job', name: 'bare-os-shell-job',
state: 'running', state: j.stopped ? 'stopped' : 'running',
startedAtMs: now, startedAtMs: now,
jobId: j.id, jobId: j.id,
label: typeof j.label === 'string' ? j.label.slice(0, 256) : undefined label: typeof j.label === 'string' ? j.label.slice(0, 256) : undefined,
stopped: !!j.stopped
} }
}) })
const zombieJobs = jobs.filter((j) => j && j.done === true && typeof j.id === 'number') const zombieJobs = jobs.filter((j) => j && j.done === true && typeof j.id === 'number')
@@ -67,10 +74,26 @@ export function bareOsProcessTableSnapshot(opts = {}) {
fds: [] fds: []
} }
}) })
const stdioTable = [
{ fd: 0, kind: 'stdio', target: '/dev/stdin' },
{ fd: 1, kind: 'stdio', target: '/dev/stdout' },
{ fd: 2, kind: 'stdio', target: '/dev/stderr' }
]
const extraFdTable = logicalRows.map((r) => ({
fd: r.fd | 0,
kind: 'logical',
target: String(r.target || '').slice(0, 1024)
}))
return { return {
schema: 2, schema: 3,
schemaVersion: 2, schemaVersion: 3,
note: 'Synthetic rows; Bare OS guests do not expose host OS processes.', note: 'Synthetic rows; Bare OS guests do not expose host OS processes.',
signalModel: {
schema: 1,
note: 'Virtual deliveries via ctx.bareOsSendSignal (not full sigaction/sigprocmask).',
sigpendingAnalog: virtualSignalDeliveries,
sigprocmaskAnalog: []
},
session: { session: {
sessionId: sid || undefined, sessionId: sid || undefined,
controllingTty: undefined, controllingTty: undefined,
@@ -97,12 +120,13 @@ export function bareOsProcessTableSnapshot(opts = {}) {
}, },
cwd: '/', cwd: '/',
fdSummary: { fdSummary: {
nextFd: 3, nextFd: 3 + extraFdTable.length,
reservedStdio: [0, 1, 2], reservedStdio: [0, 1, 2],
note: 'Additional FDs may appear when pseudo-files or IPC channels are opened.' note: 'Additional FDs may appear when pseudo-files or IPC channels are opened.'
}, },
jobStats: { jobStats: {
running: jobProcs.length, running: jobProcs.filter((j) => !j.stopped).length,
stopped: jobProcs.filter((j) => j.stopped).length,
zombie: zombieJobs.length zombie: zombieJobs.length
}, },
processes: [ processes: [
@@ -114,12 +138,14 @@ export function bareOsProcessTableSnapshot(opts = {}) {
sid: 1, sid: 1,
pgid: 3, pgid: 3,
cwd: '/', cwd: '/',
fds: [0, 1, 2] fds: [0, 1, 2],
fdTable: [...stdioTable, ...extraFdTable]
}, },
...jobProcs.map((p) => ({ ...jobProcs.map((p) => ({
...p, ...p,
cwd: typeof p.cwd === 'string' ? p.cwd : '/', cwd: typeof p.cwd === 'string' ? p.cwd : '/',
fds: Array.isArray(p.fds) ? p.fds : [0, 1, 2] fds: Array.isArray(p.fds) ? p.fds : [0, 1, 2],
fdTable: [...stdioTable]
})), })),
...zombieProcs ...zombieProcs
], ],
@@ -357,7 +357,7 @@ export function buildBareOsRuntimeCaps(shellEnv, extra = {}) {
vfsWatchPseudoMetrics: vfsWatchPseudoOn, vfsWatchPseudoMetrics: vfsWatchPseudoOn,
envDashSAndEnvFile: envDashSOn, envDashSAndEnvFile: envDashSOn,
shellParamExpansionV2: shellParamV2On, shellParamExpansionV2: shellParamV2On,
procCgroupsTcpStubs: true, procLinuxCompatCgroupsNet: true,
procKernelExtRegistry: true, procKernelExtRegistry: true,
initdIdleStop: true, initdIdleStop: true,
initdUnitsDFragments: true, initdUnitsDFragments: true,
@@ -0,0 +1,51 @@
/**
* Best-effort Hyperdrive statvfs-like counters for `/proc/bare_os_resources` and `df`.
* Hyperdrive versions differ; probes are defensive.
*/
import b4a from 'b4a'
/**
* @param {unknown} drive
* @returns {{ f_bsize: number, f_frsize: number, f_blocks: number, f_bfree: number, f_bavail: number, f_files: number | null, f_ffree: number | null, f_namemax: number, fsidPrefixHex?: string } | null}
*/
export function bareOsStatvfsFromHyperdrive(drive) {
if (!drive || typeof drive !== 'object') return null
const bsize = 4096
/** @type {number} */
let usedBytes = 0
try {
const c = /** @type {{ byteLength?: number, tree?: { byteLength?: number, length?: number } }} */ (
drive
).core
if (c && typeof c.byteLength === 'number') usedBytes = c.byteLength
else if (c && c.tree && typeof c.tree.byteLength === 'number')
usedBytes = c.tree.byteLength
else if (c && c.tree && typeof c.tree.length === 'number')
usedBytes = Math.max(usedBytes, (c.tree.length | 0) * 256)
} catch {
return null
}
if (!Number.isFinite(usedBytes) || usedBytes < 0) return null
const usedBlocks = Math.max(1, Math.ceil(usedBytes / bsize))
const headroom = Math.max(4096, usedBlocks)
const blocks = usedBlocks + headroom
const bfree = Math.max(0, blocks - usedBlocks)
let fsidPrefixHex
try {
const k = /** @type {{ key?: Uint8Array }} */ (drive).key
if (k && k.byteLength) fsidPrefixHex = b4a.toString(k, 'hex').slice(0, 16)
} catch {
/* optional */
}
return {
f_bsize: bsize,
f_frsize: bsize,
f_blocks: blocks,
f_bfree: bfree,
f_bavail: bfree,
f_files: null,
f_ffree: null,
f_namemax: 255,
fsidPrefixHex
}
}
@@ -8,7 +8,7 @@
/** @typedef {'fs' | 'proc' | 'ipc' | 'signal' | 'vfs_meta'} BareOsSyscallCategory */ /** @typedef {'fs' | 'proc' | 'ipc' | 'signal' | 'vfs_meta'} BareOsSyscallCategory */
/** /**
* Rich syscall table for `/proc/bare_os/syscalls.json` (schema v3+). * Rich syscall table for `/proc/bare_os/syscalls.json` (schema v3+; v4 adds fd/signal models in index.js).
* @type {ReadonlyArray<{ name: string, category: BareOsSyscallCategory, stability: BareOsSyscallStability }>} * @type {ReadonlyArray<{ name: string, category: BareOsSyscallCategory, stability: BareOsSyscallStability }>}
*/ */
export const BARE_OS_SYSCALL_OPS_DETAIL = Object.freeze([ export const BARE_OS_SYSCALL_OPS_DETAIL = Object.freeze([
@@ -0,0 +1,38 @@
/**
* Optional ACL enforcement using `PATH.bare_acl` sidecars (same layout as setfacl/getfacl).
*/
import b4a from 'b4a'
/**
* @param {Record<string, string>} env
* @param {import('hyperdrive').default} drive
* @param {string} drivePath absolute path on the Hyperdrive (no leading slash inconsistency callers pass the same `path` as `drive.get`)
* @param {'read' | 'write'} op
*/
export async function bareOsVfsAclDeniesDriveOp(env, drive, drivePath, op) {
const on =
env.BARE_OS_VFS_ENFORCE_ACL === '1' ||
env.BARE_OS_VFS_ENFORCE_ACL === 'true'
if (!on || !drive || typeof drive.get !== 'function') return false
const p = String(drivePath || '').replace(/\/+$/, '') || '/'
const sidePath = p + '.bare_acl'
let raw
try {
raw = await drive.get(sidePath, { follow: false })
} catch {
return false
}
if (!raw || !raw.byteLength) return false
const text = b4a.toString(raw)
const uid = String(env.UID || '')
const re = new RegExp(
'^user:' + uid.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + ':([rwx-]{3})',
'm'
)
const m = text.match(re)
if (!m) return false
const perm = m[1]
if (op === 'read' && !perm.includes('r')) return true
if (op === 'write' && !perm.includes('w')) return true
return false
}
@@ -408,3 +408,31 @@ export async function saveVaultToDrive(ctx) {
secureZero(vaultKey) secureZero(vaultKey)
ctx.console.log(`Vault saved (${Object.keys(indexFiles).length} files)`) ctx.console.log(`Vault saved (${Object.keys(indexFiles).length} files)`)
} }
/**
* Append a non-secret rotation / handoff checkpoint to the personal drive (plaintext NDJSON audit).
* Signing keys stay in the vault; this records operator metadata only.
* @param {Record<string, unknown>} ctx
* @param {Record<string, unknown>} row
*/
export async function bareOsAppendVaultRotationCheckpoint(ctx, row) {
const drive = ctx.personalDrive
if (!drive || typeof drive.put !== 'function') {
throw new Error('bareOsAppendVaultRotationCheckpoint: personal drive unavailable')
}
const line =
JSON.stringify({
schema: 1,
ts: Date.now(),
...row
}) + '\n'
const path = '/.bare/vault-rotation-audit.ndjson'
let prev = ''
try {
const b = await drive.get(path, { follow: false })
if (b) prev = b4a.toString(b)
} catch {
/* new */
}
await drive.put(path, b4a.from(prev + line))
}
+84 -8
View File
@@ -31,7 +31,9 @@ const SHELL_BUILTINS = new Set([
'jobs', 'jobs',
'fg', 'fg',
'bg', 'bg',
'wait' 'wait',
'suspend-job',
'trap'
]) ])
function isShellBuiltin(cmd) { function isShellBuiltin(cmd) {
@@ -1492,7 +1494,9 @@ async function execParsedPipeline(ctx, pipeline) {
origLog.call(ctx.console, '') origLog.call(ctx.console, '')
} else { } else {
for (const j of list) { for (const j of list) {
const st = j.done ? 'Done' : 'Running' let st = 'Running'
if (j.done) st = 'Done'
else if (j.stopped) st = 'Stopped'
const pg = const pg =
typeof j.pgid === 'number' && typeof j.sid === 'number' typeof j.pgid === 'number' && typeof j.sid === 'number'
? ` sid=${j.sid} pgid=${j.pgid}` ? ` sid=${j.sid} pgid=${j.pgid}`
@@ -1519,6 +1523,7 @@ async function execParsedPipeline(ctx, pipeline) {
origErr.call(ctx.console, 'fg: no such job') origErr.call(ctx.console, 'fg: no such job')
ctx.exitCode = 1 ctx.exitCode = 1
} else { } else {
if (j.stopped) j.stopped = false
try { try {
await j.promise await j.promise
} catch { } catch {
@@ -1527,11 +1532,82 @@ async function execParsedPipeline(ctx, pipeline) {
ctx.exitCode = 0 ctx.exitCode = 0
} }
} else if (name === 'bg') { } else if (name === 'bg') {
origErr.call( const list = ctx.shellBackgroundJobs?.list || []
ctx.console, const stopped = list.filter((j) => j && !j.done && j.stopped)
'bg: job resume is not supported (no stopped jobs)' if (!stopped.length) {
) origErr.call(ctx.console, 'bg: no stopped jobs')
ctx.exitCode = 1 ctx.exitCode = 1
} else {
let targets = stopped
const arg = argv[1]
if (arg) {
const raw = arg.startsWith('%') ? arg.slice(1) : arg
const jid = Number.parseInt(raw, 10)
if (Number.isFinite(jid)) {
targets = stopped.filter((j) => j.id === jid)
}
}
if (!targets.length) {
origErr.call(ctx.console, 'bg: no stopped jobs')
ctx.exitCode = 1
} else {
for (const j of targets) j.stopped = false
ctx.exitCode = 0
}
}
} else if (name === 'suspend-job') {
const list = ctx.shellBackgroundJobs?.list || []
let candidates = list.filter((j) => j && !j.done && !j.stopped)
const arg = argv[1]
if (arg) {
const raw = arg.startsWith('%') ? arg.slice(1) : arg
const jid = Number.parseInt(raw, 10)
if (Number.isFinite(jid)) {
candidates = list.filter((j) => j && !j.done && j.id === jid)
}
}
const j = candidates.length ? candidates[candidates.length - 1] : null
if (!j) {
origErr.call(ctx.console, 'suspend-job: no such job')
ctx.exitCode = 1
} else {
j.stopped = true
ctx.exitCode = 0
}
} else if (name === 'trap') {
if (argv[1] === '-l' || argv[1] === '--list') {
ctx.console.log('HUP INT KILL TERM PIPE CHLD USR1 USR2 EXIT')
} else if (argv[1] === '-p') {
const h =
ctx.shellTrapHandlers && typeof ctx.shellTrapHandlers === 'object'
? ctx.shellTrapHandlers
: {}
for (const k of Object.keys(h)) {
ctx.console.log(
`trap -- '${String(
/** @type {Record<string, string>} */ (h)[k]
).replace(/'/g, `'\\''`)}' ${k}`
)
}
} else if (argv.length < 3) {
origErr.call(ctx.console, 'trap: usage: trap COMMAND SIGNAL')
ctx.exitCode = 1
} else {
if (!ctx.shellTrapHandlers || typeof ctx.shellTrapHandlers !== 'object')
ctx.shellTrapHandlers = Object.create(null)
const cmd = argv[1]
const sig = String(argv[2] || '')
.replace(/^SIG/i, '')
.toUpperCase()
if (cmd === '-' || cmd === '') {
delete /** @type {Record<string, string>} */ (ctx.shellTrapHandlers)[
sig
]
} else {
/** @type {Record<string, string>} */ (ctx.shellTrapHandlers)[sig] =
cmd
}
}
} else if (name === 'wait') { } else if (name === 'wait') {
const list = ctx.shellBackgroundJobs?.list || [] const list = ctx.shellBackgroundJobs?.list || []
const arg = argv[1] const arg = argv[1]
@@ -1737,8 +1813,8 @@ function scheduleBackgroundShell(ctx, toks) {
syncBareOsExitStatusEnv(childCtx) syncBareOsExitStatusEnv(childCtx)
return 'ok' return 'ok'
})() })()
/** @type {{ id: number, label: string, promise: Promise<string>, done: boolean, pgid: number, sid: number }} */ /** @type {{ id: number, label: string, promise: Promise<string>, done: boolean, stopped: boolean, pgid: number, sid: number }} */
const entry = { id, label, promise, done: false, pgid, sid } const entry = { id, label, promise, done: false, stopped: false, pgid, sid }
ctx.shellBackgroundJobs.list.push(entry) ctx.shellBackgroundJobs.list.push(entry)
promise.finally(() => { promise.finally(() => {
entry.done = true entry.done = true
@@ -339,9 +339,11 @@ export function isVirtualMountPoint(abs) {
abs === '/' || abs === '/' ||
abs === '/home' || abs === '/home' ||
abs === '/mnt' || abs === '/mnt' ||
abs === '/mirror' ||
abs === '/var' || abs === '/var' ||
abs === '/home/' || abs === '/home/' ||
abs === '/mnt/' || abs === '/mnt/' ||
abs === '/mirror/' ||
abs === '/var/' || abs === '/var/' ||
n === '/proc' || n === '/proc' ||
n === '/proc/self' || n === '/proc/self' ||
+310 -17
View File
@@ -3,6 +3,7 @@ import b4a from 'b4a'
import { raceWithAbortAndTimeout } from './bare-os-abort.js' import { raceWithAbortAndTimeout } from './bare-os-abort.js'
import { bareOsIpcLogicalToActualFifoName } from './bare-os-ipc-namespace.js' import { bareOsIpcLogicalToActualFifoName } from './bare-os-ipc-namespace.js'
import { listBareInitdJournalUnits } from './bare-initd-journal.js' import { listBareInitdJournalUnits } from './bare-initd-journal.js'
import { bareOsVfsAclDeniesDriveOp } from './bare-os-vfs-acl-enforce.js'
import { import {
extractBareOs, extractBareOs,
identityNames, identityNames,
@@ -385,7 +386,10 @@ export const BARE_OS_PROC_FILE_TO_ID_HYPERCORE_PACK_HRPC_LIFECYCLE = Object.free
* sysClassNetLoText?: () => string, * sysClassNetLoText?: () => string,
* hostProcStatsRef?: { stats?: unknown } | null, * hostProcStatsRef?: { stats?: unknown } | null,
* ipcFifoLogicalToActual?: (logicalName: string) => string, * ipcFifoLogicalToActual?: (logicalName: string) => string,
* getProcSelfExtraFds?: () => { fdNum: string, target: string }[] * getProcSelfExtraFds?: () => { fdNum: string, target: string }[],
* getProcSyntheticLinuxCompat?: () => { sessionId: string, swarmPeerCount: number, peerIds: string[] } | null | undefined,
* getAuxiliaryMountLines?: () => (string | null | undefined)[],
* getAuxiliaryDrives?: () => unknown[] | null | undefined
* }} [vfsOptions] * }} [vfsOptions]
*/ */
export function createVfs( export function createVfs(
@@ -640,6 +644,18 @@ export function createVfs(
typeof vfsOptions.getVirtualReaders === 'function' typeof vfsOptions.getVirtualReaders === 'function'
? vfsOptions.getVirtualReaders ? vfsOptions.getVirtualReaders
: null : null
const getProcSyntheticLinuxCompat =
typeof vfsOptions.getProcSyntheticLinuxCompat === 'function'
? vfsOptions.getProcSyntheticLinuxCompat
: null
const getAuxiliaryMountLines =
typeof vfsOptions.getAuxiliaryMountLines === 'function'
? vfsOptions.getAuxiliaryMountLines
: null
const getAuxiliaryDrives =
typeof vfsOptions.getAuxiliaryDrives === 'function'
? vfsOptions.getAuxiliaryDrives
: null
const unionReadPrefixes = Array.isArray(vfsOptions.unionReadPrefixes) const unionReadPrefixes = Array.isArray(vfsOptions.unionReadPrefixes)
? vfsOptions.unionReadPrefixes.filter( ? vfsOptions.unionReadPrefixes.filter(
(s) => typeof s === 'string' && s.startsWith('/') (s) => typeof s === 'string' && s.startsWith('/')
@@ -687,9 +703,42 @@ export function createVfs(
const binCacheEnabled = const binCacheEnabled =
env.BARE_OS_VFS_BIN_CACHE === '1' || env.BARE_OS_VFS_BIN_CACHE === 'true' env.BARE_OS_VFS_BIN_CACHE === '1' || env.BARE_OS_VFS_BIN_CACHE === 'true'
/** @type {Map<string, Uint8Array>} */ const binCacheBlake2b =
const binReadCache = binCacheEnabled ? new Map() : null binCacheEnabled &&
(env.BARE_OS_VFS_BIN_CACHE_BLAKE2B === '1' ||
env.BARE_OS_VFS_BIN_CACHE_BLAKE2B === 'true')
/** @type {Map<string, Uint8Array> | null} */
const binReadCache = binCacheEnabled && !binCacheBlake2b ? new Map() : null
/** @type {Map<string, Uint8Array> | null} digest hex → bytes */
const binDigestCache = binCacheBlake2b ? new Map() : null
/** @type {Map<string, string> | null} logical path → digest */
const binPathToBlakeDigest = binCacheBlake2b ? new Map() : null
/** @type {Map<string, number> | null} digest refcount */
const binDigestRefcount = binCacheBlake2b ? new Map() : null
/** @type {string[] | null} */
const binBlake2bLruPaths = binCacheBlake2b ? [] : null
const BIN_READ_CACHE_MAX = 64 const BIN_READ_CACHE_MAX = 64
function touchBinBlake2bLru(p) {
if (!binBlake2bLruPaths || !binPathToBlakeDigest) return
const i = binBlake2bLruPaths.indexOf(p)
if (i >= 0) binBlake2bLruPaths.splice(i, 1)
binBlake2bLruPaths.push(p)
while (binBlake2bLruPaths.length > BIN_READ_CACHE_MAX) {
const victim = binBlake2bLruPaths.shift()
if (!victim) continue
const hex = binPathToBlakeDigest.get(victim)
if (!hex) continue
binPathToBlakeDigest.delete(victim)
const n = (binDigestRefcount.get(hex) || 1) - 1
if (n <= 0) {
binDigestRefcount.delete(hex)
binDigestCache.delete(hex)
} else {
binDigestRefcount.set(hex, n)
}
}
}
const sysClassNetLoText = const sysClassNetLoText =
typeof vfsOptions.sysClassNetLoText === 'function' typeof vfsOptions.sysClassNetLoText === 'function'
? vfsOptions.sysClassNetLoText ? vfsOptions.sysClassNetLoText
@@ -921,16 +970,114 @@ export function createVfs(
return '/bin/sh (bare-os pseudo inode; no backing host file)\n' return '/bin/sh (bare-os pseudo inode; no backing host file)\n'
} }
function pseudoSelfCgroupsText() { function bareOsSanitizeCgroupSegment(s) {
return '0::/bare-os.scope\n' return String(s || 'unknown')
.replace(/[^a-zA-Z0-9._-]/g, '_')
.slice(0, 128)
} }
/** @returns {{ sessionId: string, swarmPeerCount: number, peerIds: string[] }} */
function readProcSyntheticLinuxCompat() {
let sessionId = String(env.BARE_OS_SESSION_ID || env.BARE_OS_TRACE_ID || '')
.trim()
let swarmPeerCount = 0
let peerIds = []
try {
const h = getProcSyntheticLinuxCompat ? getProcSyntheticLinuxCompat() : null
if (h && typeof h === 'object') {
if (h.sessionId != null && String(h.sessionId).trim())
sessionId = String(h.sessionId).trim()
const n = Number(h.swarmPeerCount)
if (Number.isFinite(n) && n >= 0) swarmPeerCount = Math.min(4096, n | 0)
if (Array.isArray(h.peerIds))
peerIds = h.peerIds.map((x) => String(x || '')).filter(Boolean)
}
} catch {
/* ignore */
}
if (!sessionId) sessionId = 'session'
return { sessionId, swarmPeerCount, peerIds }
}
/**
* Linux cgroup v2 single-column lines, scoped to the guest session (not host cgroups).
*/
function pseudoSelfCgroupsText() {
const { sessionId } = readProcSyntheticLinuxCompat()
const sid = bareOsSanitizeCgroupSegment(sessionId)
return [
'# bare_os synthetic cgroup v2 view (guest session + swarm scope; not the host cgroup hierarchy)',
'0::/bare-os.scope',
`0::/bare-os.session/${sid}/init`,
`0::/bare-os.session/${sid}/swarm`,
''
].join('\n')
}
/** IPv4:port as in /proc/net/tcp (little-endian quad hex + port hex). */
function bareOsProcNetIpv4PortHex(ip, port) {
const parts = String(ip || '0.0.0.0').split('.')
const a = Number(parts[0]) || 0
const b = Number(parts[1]) || 0
const c = Number(parts[2]) || 0
const d = Number(parts[3]) || 0
const addr = (a | (b << 8) | (c << 16) | (d << 24)) >>> 0
const p = port & 0xffff
return (
addr.toString(16).padStart(8, '0') +
':' +
p.toString(16).padStart(4, '0')
)
}
/**
* Logical Hyperswarm / replication streams as Linux-shaped /proc/net/tcp rows (inode encodes slot).
*/
function pseudoNetTcpText() { function pseudoNetTcpText() {
return 'sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n' const { swarmPeerCount, peerIds } = readProcSyntheticLinuxCompat()
const nPeers = Math.max(swarmPeerCount, peerIds.length)
const lines = [
' sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode',
'# bare_os: logical swarm/replication endpoints (not host TCP); st 0A=LISTEN 01=ESTABLISHED'
]
lines.push(
` 0: ${bareOsProcNetIpv4PortHex('127.0.0.1', 57800)} 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 800000`
)
let inode = 800001
for (let i = 0; i < nPeers; i++) {
const id = peerIds[i] || '0'.repeat(64)
const h = id.slice(0, 8) || '00000000'
const v = Number.parseInt(h, 16) || 0
const o1 = v & 255
const o2 = (v >>> 8) & 255
const o3 = (v >>> 16) & 255
const o4 = (v >>> 24) & 255
const remIp = `${o1}.${o2}.${o3}.${o4}`
lines.push(
` ${i + 1}: ${bareOsProcNetIpv4PortHex('127.0.0.1', 58000 + i)} ${bareOsProcNetIpv4PortHex(remIp, 57900 + i)} 01 00000000:00000000 00:00000000 00000000 0 0 ${inode}`
)
inode++
}
lines.push('')
return lines.join('\n')
} }
function pseudoNetUdpText() { function pseudoNetUdpText() {
return 'sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode ref\n' const { swarmPeerCount } = readProcSyntheticLinuxCompat()
const lines = [
' sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode ref',
'# bare_os: logical UDP-shaped discovery/datagram hints (not host UDP)'
]
lines.push(
` 0: ${bareOsProcNetIpv4PortHex('0.0.0.0', 49721)} 00000000:0000 07 00000000:00000000 00:00000000 00000000 0 0 810000 2`
)
if (swarmPeerCount > 0) {
lines.push(
` 1: ${bareOsProcNetIpv4PortHex('127.0.0.1', 49722)} 00000000:0000 07 00000000:00000000 00:00000000 00000000 0 0 810001 2`
)
}
lines.push('')
return lines.join('\n')
} }
/** /**
@@ -1099,7 +1246,7 @@ export function createVfs(
if (f === 'bare_os_syscalls') { if (f === 'bare_os_syscalls') {
const t = procBareOsSyscallsText const t = procBareOsSyscallsText
? procBareOsSyscallsText() ? procBareOsSyscallsText()
: '{"schemaVersion":3,"ops":[],"errnoHints":{}}\n' : '{"schemaVersion":4,"ops":[],"errnoHints":{},"fdModel":{"schema":1},"signalModel":{"schema":1,"names":[]}}\n'
return utf8Encode(t) return utf8Encode(t)
} }
if (f === 'bare_os_metrics_prom') { if (f === 'bare_os_metrics_prom') {
@@ -2939,9 +3086,56 @@ export function createVfs(
const rw = ent.writable ? 'rw' : 'ro' const rw = ent.writable ? 'rw' : 'ro'
lines.push(`bare-os-${label} /mnt/${label} hyperdrive ${rw} 0 0`) lines.push(`bare-os-${label} /mnt/${label} hyperdrive ${rw} 0 0`)
} }
try {
const extra = getAuxiliaryMountLines ? getAuxiliaryMountLines() : []
if (Array.isArray(extra)) {
for (const row of extra) {
if (typeof row === 'string' && row.trim()) lines.push(row.trim())
}
}
} catch {
/* ignore */
}
return lines.join('\n') + '\n' return lines.join('\n') + '\n'
} }
/**
* Optional read-only auxiliary Hyperdrives under `/mirror/aux0`, `/mirror/aux1`,
* @returns {null | Record<string, unknown>}
*/
function routeMirror(absPath) {
const aux = getAuxiliaryDrives ? getAuxiliaryDrives() : null
if (!Array.isArray(aux) || aux.length === 0) return null
if (absPath === '/mirror' || absPath === '/mirror/') {
return { virtualMirrorRoot: true }
}
if (!absPath.startsWith('/mirror/')) return null
const rest = absPath.slice('/mirror/'.length)
const slash = rest.indexOf('/')
const seg = slash === -1 ? rest : rest.slice(0, slash)
const tail = slash === -1 ? '' : rest.slice(slash + 1)
if (!seg) return { virtualMirrorRoot: true }
const m = /^aux(\d+)$/.exec(seg)
if (!m) {
return { drive: systemDrive, path: absPath }
}
const idx = Number(m[1])
const drive = /** @type {unknown} */ (aux[idx])
if (
!drive ||
typeof (/** @type {{ get?: unknown }} */ (drive).get) !== 'function'
) {
return { drive: systemDrive, path: absPath }
}
const sub = tail ? '/' + tail.replace(/^\/+/, '') : '/'
const p = unixPathResolve('/', sub)
return {
drive: /** @type {import('hyperdrive').default} */ (drive),
path: p,
mntReadOnly: true
}
}
/** /**
* @returns {null | Record<string, unknown>} * @returns {null | Record<string, unknown>}
*/ */
@@ -2970,6 +3164,8 @@ export function createVfs(
* Virtual /home lists only the active session dir; /home/<active>/ is the personal drive. * Virtual /home lists only the active session dir; /home/<active>/ is the personal drive.
*/ */
function route(absPath) { function route(absPath) {
const mirR = routeMirror(absPath)
if (mirR) return mirR
const mntR = routeMnt(absPath) const mntR = routeMnt(absPath)
if (mntR) return mntR if (mntR) return mntR
@@ -3099,6 +3295,7 @@ export function createVfs(
if ( if (
r.virtualHomeDir || r.virtualHomeDir ||
r.virtualMntRoot || r.virtualMntRoot ||
r.virtualMirrorRoot ||
r.virtualVarRoot || r.virtualVarRoot ||
r.virtualPseudo r.virtualPseudo
) { ) {
@@ -3171,6 +3368,9 @@ export function createVfs(
} }
async function lstatFromAbs(abs) { async function lstatFromAbs(abs) {
if (abs === '/mirror' || abs === '/mirror/') {
return { ...synthesizeStat(abs, false, env, 'directory'), path: abs }
}
if (abs === '/mnt' || abs === '/mnt/') { if (abs === '/mnt' || abs === '/mnt/') {
return { ...synthesizeStat(abs, false, env, 'directory'), path: abs } return { ...synthesizeStat(abs, false, env, 'directory'), path: abs }
} }
@@ -3182,7 +3382,7 @@ export function createVfs(
return { ...synthesizeStat(abs, false, env, 'directory'), path: abs } return { ...synthesizeStat(abs, false, env, 'directory'), path: abs }
} }
const r = route(abs) const r = route(abs)
if (r.virtualMntRoot) { if (r.virtualMntRoot || r.virtualMirrorRoot) {
return { ...synthesizeStat(abs, false, env, 'directory'), path: abs } return { ...synthesizeStat(abs, false, env, 'directory'), path: abs }
} }
if (r.virtualVarRoot) { if (r.virtualVarRoot) {
@@ -3278,6 +3478,8 @@ export function createVfs(
if ( if (
abs === '/mnt' || abs === '/mnt' ||
abs === '/mnt/' || abs === '/mnt/' ||
abs === '/mirror' ||
abs === '/mirror/' ||
abs === '/home' || abs === '/home' ||
abs === '/' || abs === '/' ||
normVar === '/var' || normVar === '/var' ||
@@ -3290,6 +3492,7 @@ export function createVfs(
const r0 = route(abs) const r0 = route(abs)
if ( if (
r0.virtualMntRoot || r0.virtualMntRoot ||
r0.virtualMirrorRoot ||
r0.virtualVarRoot || r0.virtualVarRoot ||
r0.virtualPseudo || r0.virtualPseudo ||
r0.virtualSnapshotRoot || r0.virtualSnapshotRoot ||
@@ -3310,6 +3513,7 @@ export function createVfs(
cur === '/' || cur === '/' ||
cur === '/home' || cur === '/home' ||
cur === '/mnt' || cur === '/mnt' ||
cur === '/mirror' ||
curNorm === '/var' || curNorm === '/var' ||
curNorm === '/var/log' || curNorm === '/var/log' ||
curNorm === '/tmp' curNorm === '/tmp'
@@ -3317,7 +3521,12 @@ export function createVfs(
return cur return cur
} }
const r = route(cur) const r = route(cur)
if (r.virtualMntRoot || r.virtualVarRoot || r.virtualPseudo) { if (
r.virtualMntRoot ||
r.virtualMirrorRoot ||
r.virtualVarRoot ||
r.virtualPseudo
) {
return cur return cur
} }
const { drive, path: p } = r const { drive, path: p } = r
@@ -3861,6 +4070,12 @@ export function createVfs(
} }
} }
const r = route(abs) const r = route(abs)
if (r.virtualMirrorRoot) {
const aux = getAuxiliaryDrives ? getAuxiliaryDrives() : []
return Array.isArray(aux)
? aux.map((_, i) => 'aux' + i).sort()
: []
}
if (r.virtualMntRoot) { if (r.virtualMntRoot) {
return [...getMntMap().keys()].sort() return [...getMntMap().keys()].sort()
} }
@@ -3879,6 +4094,10 @@ export function createVfs(
if (abs === '/' && !names.includes('mnt')) { if (abs === '/' && !names.includes('mnt')) {
names.push('mnt') names.push('mnt')
} }
if (abs === '/' && !names.includes('mirror')) {
const aux = getAuxiliaryDrives ? getAuxiliaryDrives() : []
if (Array.isArray(aux) && aux.length) names.push('mirror')
}
if (abs === '/' && !names.includes('var')) { if (abs === '/' && !names.includes('var')) {
names.push('var') names.push('var')
} }
@@ -3920,6 +4139,7 @@ export function createVfs(
if ( if (
r.virtualHomeDir || r.virtualHomeDir ||
r.virtualMntRoot || r.virtualMntRoot ||
r.virtualMirrorRoot ||
r.virtualVarRoot || r.virtualVarRoot ||
r.virtualPseudo r.virtualPseudo
) { ) {
@@ -3957,6 +4177,7 @@ export function createVfs(
if ( if (
r.virtualHomeDir || r.virtualHomeDir ||
r.virtualMntRoot || r.virtualMntRoot ||
r.virtualMirrorRoot ||
r.virtualVarRoot || r.virtualVarRoot ||
r.virtualPseudo r.virtualPseudo
) { ) {
@@ -4020,6 +4241,7 @@ export function createVfs(
if ( if (
r.virtualHomeDir || r.virtualHomeDir ||
r.virtualMntRoot || r.virtualMntRoot ||
r.virtualMirrorRoot ||
r.virtualVarRoot || r.virtualVarRoot ||
r.virtualPseudo r.virtualPseudo
) { ) {
@@ -4039,6 +4261,9 @@ export function createVfs(
if (isHyperdriveRootPath(p)) { if (isHyperdriveRootPath(p)) {
throw new Error('Cannot write directory: ' + abs) throw new Error('Cannot write directory: ' + abs)
} }
if (await bareOsVfsAclDeniesDriveOp(env, drive, p, 'write')) {
throw new Error('EACCES: ACL enforces deny write: ' + abs)
}
const existing = await entryOn(drive, p, { follow: false }) const existing = await entryOn(drive, p, { follow: false })
const hadBlob = !!existing?.value?.blob const hadBlob = !!existing?.value?.blob
if (hadBlob) await assertTraverseTo(abs, 'write') if (hadBlob) await assertTraverseTo(abs, 'write')
@@ -4137,12 +4362,22 @@ export function createVfs(
} }
return null return null
} }
if (r.virtualHomeDir || r.virtualMntRoot || r.virtualVarRoot) if (
r.virtualHomeDir ||
r.virtualMntRoot ||
r.virtualMirrorRoot ||
r.virtualVarRoot
)
return null return null
await assertTraverseTo(abs, 'read') await assertTraverseTo(abs, 'read')
const absFollowed = await resolveSymlinksOnLogicalAbs(abs) const absFollowed = await resolveSymlinksOnLogicalAbs(abs)
const r2 = route(absFollowed) const r2 = route(absFollowed)
if (r2.virtualHomeDir || r2.virtualMntRoot || r2.virtualVarRoot) if (
r2.virtualHomeDir ||
r2.virtualMntRoot ||
r2.virtualMirrorRoot ||
r2.virtualVarRoot
)
return null return null
if (r2.virtualPseudo) { if (r2.virtualPseudo) {
await assertTraverseTo(absFollowed, 'read') await assertTraverseTo(absFollowed, 'read')
@@ -4208,6 +4443,13 @@ export function createVfs(
} }
} }
} }
if (binDigestCache && drive === systemDrive && absFollowed.startsWith('/bin/')) {
const dh = binPathToBlakeDigest.get(absFollowed)
if (dh) {
const hit = binDigestCache.get(dh)
if (hit) return new Uint8Array(hit)
}
}
if ( if (
binReadCache && binReadCache &&
drive === systemDrive && drive === systemDrive &&
@@ -4216,19 +4458,58 @@ export function createVfs(
const hit = binReadCache.get(absFollowed) const hit = binReadCache.get(absFollowed)
if (hit) return new Uint8Array(hit) if (hit) return new Uint8Array(hit)
} }
if (await bareOsVfsAclDeniesDriveOp(env, drive, p, 'read')) {
throw new Error('EACCES: ACL enforces deny read: ' + absFollowed)
}
const got = await drive.get(p, { follow: false }) const got = await drive.get(p, { follow: false })
if (got && drive === systemDrive && absFollowed.startsWith('/bin/')) {
if ( if (
binReadCache && binDigestCache &&
drive === systemDrive && binPathToBlakeDigest &&
absFollowed.startsWith('/bin/') && binDigestRefcount &&
got binBlake2bLruPaths
) { ) {
try {
const { createHash: ch } = await import('bare-crypto')
if (typeof ch !== 'function') throw new Error('no createHash')
const u8 = got instanceof Uint8Array ? got : new Uint8Array(got)
const h = ch('blake2b')
h.update(u8)
const dig = String(h.digest('hex'))
const oldHex = binPathToBlakeDigest.get(absFollowed)
if (oldHex === dig) {
touchBinBlake2bLru(absFollowed)
} else {
if (oldHex) {
const n0 = (binDigestRefcount.get(oldHex) || 1) - 1
if (n0 <= 0) {
binDigestRefcount.delete(oldHex)
binDigestCache.delete(oldHex)
} else {
binDigestRefcount.set(oldHex, n0)
}
}
binPathToBlakeDigest.set(absFollowed, dig)
if (!binDigestCache.has(dig)) {
binDigestCache.set(dig, new Uint8Array(u8))
}
binDigestRefcount.set(
dig,
(binDigestRefcount.get(dig) || 0) + 1
)
touchBinBlake2bLru(absFollowed)
}
} catch {
/* ignore blake2b cache failures */
}
} else if (binReadCache) {
if (binReadCache.size >= BIN_READ_CACHE_MAX) { if (binReadCache.size >= BIN_READ_CACHE_MAX) {
const first = binReadCache.keys().next().value const first = binReadCache.keys().next().value
binReadCache.delete(first) binReadCache.delete(first)
} }
binReadCache.set(absFollowed, new Uint8Array(got)) binReadCache.set(absFollowed, new Uint8Array(got))
} }
}
return got return got
})(), })(),
abortOpts, abortOpts,
@@ -4281,6 +4562,7 @@ export function createVfs(
if ( if (
r.virtualHomeDir || r.virtualHomeDir ||
r.virtualMntRoot || r.virtualMntRoot ||
r.virtualMirrorRoot ||
r.virtualVarRoot || r.virtualVarRoot ||
r.virtualPseudo r.virtualPseudo
) { ) {
@@ -4328,7 +4610,13 @@ export function createVfs(
if (r.node === 'enoent') return false if (r.node === 'enoent') return false
return true return true
} }
if (r.virtualHomeDir || r.virtualMntRoot || r.virtualVarRoot) return true if (
r.virtualHomeDir ||
r.virtualMntRoot ||
r.virtualMirrorRoot ||
r.virtualVarRoot
)
return true
const { drive, path: p } = r const { drive, path: p } = r
if (isHyperdriveRootPath(p)) return true if (isHyperdriveRootPath(p)) return true
return drive.exists(p) return drive.exists(p)
@@ -4360,6 +4648,7 @@ export function createVfs(
if ( if (
r.virtualHomeDir || r.virtualHomeDir ||
r.virtualMntRoot || r.virtualMntRoot ||
r.virtualMirrorRoot ||
r.virtualVarRoot || r.virtualVarRoot ||
r.virtualPseudo r.virtualPseudo
) { ) {
@@ -4430,6 +4719,7 @@ export function createVfs(
if ( if (
r.virtualHomeDir || r.virtualHomeDir ||
r.virtualMntRoot || r.virtualMntRoot ||
r.virtualMirrorRoot ||
r.virtualVarRoot || r.virtualVarRoot ||
r.virtualPseudo r.virtualPseudo
) { ) {
@@ -4590,6 +4880,7 @@ export function createVfs(
if ( if (
r.virtualHomeDir || r.virtualHomeDir ||
r.virtualMntRoot || r.virtualMntRoot ||
r.virtualMirrorRoot ||
r.virtualVarRoot || r.virtualVarRoot ||
r.virtualPseudo r.virtualPseudo
) { ) {
@@ -4637,6 +4928,7 @@ export function createVfs(
if ( if (
r.virtualHomeDir || r.virtualHomeDir ||
r.virtualMntRoot || r.virtualMntRoot ||
r.virtualMirrorRoot ||
r.virtualVarRoot || r.virtualVarRoot ||
r.virtualPseudo r.virtualPseudo
) { ) {
@@ -4766,6 +5058,7 @@ export function createVfs(
r.virtualHomeDir || r.virtualHomeDir ||
r.virtualVarRoot || r.virtualVarRoot ||
r.virtualMntRoot || r.virtualMntRoot ||
r.virtualMirrorRoot ||
r.virtualSnapshotRoot || r.virtualSnapshotRoot ||
r.virtualSnapshotSystem r.virtualSnapshotSystem
) { ) {
+2 -1
View File
@@ -8,7 +8,8 @@
"start": "node ../../scripts/ensure-pear-node-modules.mjs packages/bare-os-booter && bare index.js", "start": "node ../../scripts/ensure-pear-node-modules.mjs packages/bare-os-booter && bare index.js",
"dev": "node ../../scripts/ensure-pear-node-modules.mjs packages/bare-os-booter && bare index.js", "dev": "node ../../scripts/ensure-pear-node-modules.mjs packages/bare-os-booter && bare index.js",
"pear:dev": "node ../../scripts/ensure-pear-node-modules.mjs packages/bare-os-booter && pear run --dev .", "pear:dev": "node ../../scripts/ensure-pear-node-modules.mjs packages/bare-os-booter && pear run --dev .",
"test": "../../node_modules/.bin/brittle-bare test.identity.js && node --require ./scripts/bare-node-test-shim.cjs ../../node_modules/.bin/brittle-node test.js" "test": "../../node_modules/.bin/brittle-bare test.identity.js && node --require ./scripts/bare-node-test-shim.cjs ../../node_modules/.bin/brittle-node test.js",
"test:bare": "../../node_modules/.bin/brittle-bare test.identity.js"
}, },
"dependencies": { "dependencies": {
"autopass": "^3.4.0", "autopass": "^3.4.0",
+38
View File
@@ -793,6 +793,42 @@ test('vfs lstat personal file includes mode uid user mtime', async (t) => {
rmSync(dir, { recursive: true, force: true }) rmSync(dir, { recursive: true, force: true })
}) })
test('vfs syscalls.json uses schema 4 when provider wired', async (t) => {
const dir = testCorestoreDir('syscallsproc')
const store = new Corestore(dir)
const sys = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('scp'))
await sys.ready()
await personal.ready()
const sample = {
schemaVersion: 4,
ctxApiVersion: BARE_OS_CTX_API_VERSION,
ops: ['readFile'],
errnoHints: { ENOENT: 2 },
fdModel: { schema: 1, stdio: [0, 1, 2] },
signalModel: { schema: 1, names: ['INT'] },
atMs: 0
}
const vfs = createVfs(
sys,
personal,
{ HOME: '/home/g', PWD: '/', PATH: '/bin' },
null,
{
procBareOsSyscallsText() {
return `${JSON.stringify(sample)}\n`
}
}
)
const buf = await vfs.readFile('/proc/bare_os/syscalls.json')
const j = JSON.parse(b4a.toString(buf))
t.is(j.schemaVersion, 4)
t.ok(j.fdModel && j.fdModel.schema === 1)
t.ok(j.signalModel && Array.isArray(j.signalModel.names))
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('ls -l long listing uses session user and regular file mode', async (t) => { test('ls -l long listing uses session user and regular file mode', async (t) => {
const lsPath = path.join(__dirname, '../../kernel/bin/ls') const lsPath = path.join(__dirname, '../../kernel/bin/ls')
const lsSrc = await readFile(lsPath, 'utf8') const lsSrc = await readFile(lsPath, 'utf8')
@@ -5212,6 +5248,8 @@ test('process table snapshot includes processGroups metadata', async (t) => {
t.ok(s.processGroups) t.ok(s.processGroups)
t.is(s.processGroups.schema, 1) t.is(s.processGroups.schema, 1)
t.ok(String(s.processGroups.killpgAnalog).includes('killpg')) t.ok(String(s.processGroups.killpgAnalog).includes('killpg'))
t.is(s.schemaVersion, 3)
t.ok(s.signalModel && Array.isArray(s.signalModel.sigpendingAnalog))
}) })
async function readBuiltBin(name) { async function readBuiltBin(name) {
@@ -6,7 +6,10 @@
"awk [OPTION]... [OPERAND]..." "awk [OPTION]... [OPERAND]..."
], ],
"description": "Pattern-directed scanning and processing. Engine in lib/awk-engine.js; not full POSIX awk.", "description": "Pattern-directed scanning and processing. Engine in lib/awk-engine.js; not full POSIX awk.",
"options": [], "options": [
{ "flag": "-F sep", "meaning": "[Issue 7 awk] Field separator." },
{ "flag": "-v var=value", "meaning": "[Issue 7 awk] Assign variable before script runs." }
],
"keywords": [ "keywords": [
"awk", "awk",
"pattern", "pattern",
+10 -1
View File
@@ -6,12 +6,21 @@
"cp [OPTION]... [OPERAND]..." "cp [OPTION]... [OPERAND]..."
], ],
"description": "Bare OS implementation of copy files. Full behavior is defined in packages/bare-os-coreutils/src/cp.js.", "description": "Bare OS implementation of copy files. Full behavior is defined in packages/bare-os-coreutils/src/cp.js.",
"options": [], "options": [
{ "flag": "-R, -r, --recursive", "meaning": "[Issue 7 cp] Recursive directory copy; skips .bareos_empty marker files in tree walks." },
{ "flag": "-L, --dereference", "meaning": "[Issue 7 cp] Follow symlinks to copy referent file or directory." },
{ "flag": "-P, --no-dereference", "meaning": "[Issue 7 cp] Copy symlink itself (default)." },
{ "flag": "-u, --update", "meaning": "[Issue 7 cp] Skip copy when destination mtime >= source mtime." },
{ "flag": "-v, --verbose", "meaning": "[Issue 7 cp] Log each copy." },
{ "flag": "-p, --preserve", "meaning": "[Issue 7 cp] Preserve timestamps via mtimeMs/ctimeMs on writeFile when supported." },
{ "flag": "--preserve=timestamps|time|all", "meaning": "[Issue 7 cp] Subset: timestamps|time|all enables preserveTime only." }
],
"keywords": [ "keywords": [
"cp", "cp",
"bare-os", "bare-os",
"coreutils" "coreutils"
], ],
"bareOsNotes": "Issue 7 option matrix above is for traceability; behavior is bounded by the Hyperdrive VFS (no hard links, no xattr copy).",
"examples": [ "examples": [
{ {
"caption": "copy file", "caption": "copy file",
@@ -6,7 +6,11 @@
"find [PATH...] [EXPRESSION]" "find [PATH...] [EXPRESSION]"
], ],
"description": "Walks directories and applies expressions (-name, -type, -print, -maxdepth, logical -and/-or/-not).", "description": "Walks directories and applies expressions (-name, -type, -print, -maxdepth, logical -and/-or/-not).",
"options": [], "options": [
{ "flag": "-name", "meaning": "[Issue 7 find] Glob pattern on basename." },
{ "flag": "-type", "meaning": "[Issue 7 find] Filter f, d, or l." },
{ "flag": "-maxdepth", "meaning": "[Issue 7 find] Limit walk depth." }
],
"keywords": [ "keywords": [
"find", "find",
"directory", "directory",
@@ -6,6 +6,7 @@
"grep [-E|-F] [-i] [-v] [-n] [-c] [-l] [-q] [-s] [-H|-h] [-e pat] ... [-f file] ... [pattern] [file...]" "grep [-E|-F] [-i] [-v] [-n] [-c] [-l] [-q] [-s] [-H|-h] [-e pat] ... [-f file] ... [pattern] [file...]"
], ],
"description": "Searches input or files for lines matching a pattern. Uses JavaScript RegExp unless -F (fixed string). Not bit-identical to GNU grep.", "description": "Searches input or files for lines matching a pattern. Uses JavaScript RegExp unless -F (fixed string). Not bit-identical to GNU grep.",
"bareOsNotes": "POSIX Issue 7 option names are listed below; regex semantics follow JavaScript RegExp unless -F.",
"options": [ "options": [
{ {
"flag": "-E", "flag": "-E",
+5 -1
View File
@@ -6,7 +6,11 @@
"ln [OPTION]... [OPERAND]..." "ln [OPTION]... [OPERAND]..."
], ],
"description": "Bare OS implementation of link files. Full behavior is defined in packages/bare-os-coreutils/src/ln.js.", "description": "Bare OS implementation of link files. Full behavior is defined in packages/bare-os-coreutils/src/ln.js.",
"options": [], "options": [
{ "flag": "-s, --symbolic", "meaning": "[Issue 7 ln] Create symbolic link." },
{ "flag": "-f, --force", "meaning": "[Issue 7 ln] Replace existing destination when supported." },
{ "flag": "-r, --relative", "meaning": "[Issue 7 ln] Store relative symlink target when requested." }
],
"keywords": [ "keywords": [
"ln", "ln",
"bare-os", "bare-os",
+5 -1
View File
@@ -6,7 +6,11 @@
"mv [OPTION]... [OPERAND]..." "mv [OPTION]... [OPERAND]..."
], ],
"description": "Bare OS implementation of move or rename files. Full behavior is defined in packages/bare-os-coreutils/src/mv.js.", "description": "Bare OS implementation of move or rename files. Full behavior is defined in packages/bare-os-coreutils/src/mv.js.",
"options": [], "options": [
{ "flag": "-L, --dereference", "meaning": "[Issue 7 mv] Follow symlinks when moving file content." },
{ "flag": "-P, --no-dereference", "meaning": "[Issue 7 mv] Move symlink node (default for symlinks)." }
],
"bareOsNotes": "Directories always move recursively (copy+delete); see src/mv.js.",
"keywords": [ "keywords": [
"mv", "mv",
"bare-os", "bare-os",
@@ -6,7 +6,11 @@
"sed [OPTION]... [OPERAND]..." "sed [OPTION]... [OPERAND]..."
], ],
"description": "Stream editor with a subset of POSIX sed. Large engine is vendored in lib/sed-engine.js.", "description": "Stream editor with a subset of POSIX sed. Large engine is vendored in lib/sed-engine.js.",
"options": [], "options": [
{ "flag": "-n", "meaning": "[Issue 7 sed] Suppress default print when script uses p selectively." },
{ "flag": "-e script", "meaning": "[Issue 7 sed] Append edit script." },
{ "flag": "-f file", "meaning": "[Issue 7 sed] Read script from file." }
],
"keywords": [ "keywords": [
"sed", "sed",
"stream", "stream",
@@ -6,7 +6,11 @@
"tar [OPTION]... [OPERAND]..." "tar [OPTION]... [OPERAND]..."
], ],
"description": "Bare OS implementation of tar. Full behavior is defined in packages/bare-os-coreutils/src/tar.js. Extended attributes for unpacked files may be mirrored manually using sibling PATH.bare_xattr.json sidecars (see xattr(1)).", "description": "Bare OS implementation of tar. Full behavior is defined in packages/bare-os-coreutils/src/tar.js. Extended attributes for unpacked files may be mirrored manually using sibling PATH.bare_xattr.json sidecars (see xattr(1)).",
"options": [], "options": [
{ "flag": "-c, -x, -t", "meaning": "[Issue 7 tar] Create, extract, and list modes as implemented in src/tar.js (bounded archive subset)." },
{ "flag": "-f", "meaning": "[Issue 7 tar] Archive file path operand." }
],
"bareOsNotes": "Not full GNU tar; see src/tar.js for supported members and limits.",
"keywords": [ "keywords": [
"tar", "tar",
"bare-os", "bare-os",
@@ -6,7 +6,11 @@
"test [OPTION]... [OPERAND]..." "test [OPTION]... [OPERAND]..."
], ],
"description": "Bare OS implementation of evaluate a condition. Full behavior is defined in packages/bare-os-coreutils/src/test.js.", "description": "Bare OS implementation of evaluate a condition. Full behavior is defined in packages/bare-os-coreutils/src/test.js.",
"options": [], "options": [
{ "flag": "-e, -a", "meaning": "[Issue 7 test] Path exists." },
{ "flag": "-f, -d", "meaning": "[Issue 7 test] File or directory." },
{ "flag": "-eq, -ne, …", "meaning": "[Issue 7 test] Integer compares when both operands are decimal." }
],
"keywords": [ "keywords": [
"test", "test",
"bare-os", "bare-os",
@@ -6,7 +6,9 @@
"true [OPTION]... [OPERAND]..." "true [OPTION]... [OPERAND]..."
], ],
"description": "Bare OS implementation of return true value. Full behavior is defined in packages/bare-os-coreutils/src/true.js.", "description": "Bare OS implementation of return true value. Full behavior is defined in packages/bare-os-coreutils/src/true.js.",
"options": [], "options": [
{ "flag": "(none required)", "meaning": "[Issue 7 true] Exits 0; ignores operands." }
],
"keywords": [ "keywords": [
"true", "true",
"bare-os", "bare-os",
+1 -1
View File
@@ -6,6 +6,6 @@
"description": "Build JS /bin utilities for bare-operating-system (concat + stage to kernel/)", "description": "Build JS /bin utilities for bare-operating-system (concat + stage to kernel/)",
"scripts": { "scripts": {
"build": "node ./scripts/ensure-man-pages.mjs && node ./build.mjs", "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/awk-sed-posix-smoke.test.mjs && node ./test/xattr-acl-utils.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/awk-sed-posix-smoke.test.mjs && node ./test/xattr-acl-utils.test.mjs && node ./test/xcu-issue7-sweep.test.mjs"
} }
} }
@@ -11,6 +11,7 @@ import { COREUTILS_COMMANDS, MAN_EXTRA_PAGES } from '../lib/commands.mjs'
const __dirname = dirname(fileURLToPath(import.meta.url)) const __dirname = dirname(fileURLToPath(import.meta.url))
const pagesDir = join(__dirname, '../man/pages') const pagesDir = join(__dirname, '../man/pages')
/** Commands whose man pages are explicitly marked stub/unsupported (keep empty in production). */
const STUB = new Set() const STUB = new Set()
const POSIX_TITLE = { const POSIX_TITLE = {
+16
View File
@@ -32,6 +32,22 @@ async function run(ctx, argv) {
} catch { } catch {
/* ignore */ /* ignore */
} }
try {
const rbuf = await ctx.vfs.readFile('/proc/bare_os_resources')
if (rbuf) {
const j = JSON.parse(ctx.b4a.toString(rbuf))
const sv = j && j.statvfs
if (sv && typeof sv.f_blocks === 'number' && typeof sv.f_bfree === 'number') {
const bsize = typeof sv.f_bsize === 'number' ? sv.f_bsize : 4096
totalK = Math.max(1024, Math.ceil((sv.f_blocks * bsize) / 1024))
const freeB = sv.f_bavail ?? sv.f_bfree
const freeKFromStat = Math.max(0, Math.ceil((freeB * bsize) / 1024))
usedK = Math.max(0, totalK - freeKFromStat)
}
}
} catch {
/* ignore */
}
const freeK = Math.max(0, totalK - usedK) const freeK = Math.max(0, totalK - usedK)
const pct = const pct =
totalK > 0 ? Math.min(100, Math.round((usedK / totalK) * 100)) : 0 totalK > 0 ? Math.min(100, Math.round((usedK / totalK) * 100)) : 0
+1 -1
View File
@@ -11,7 +11,7 @@ async function run(ctx, argv) {
break break
} }
if (a === '-l' || a === '--list') { if (a === '-l' || a === '--list') {
ctx.console.log('HUP INT KILL TERM 0') ctx.console.log('HUP INT KILL TERM PIPE CHLD USR1 USR2 0')
return return
} }
if (a === '-s' || a === '--signal') { if (a === '-s' || a === '--signal') {
@@ -0,0 +1,92 @@
/**
* Brittle edge checks for high-traffic utilities (Issue 7 traceability).
*/
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`
)
}
test('cp -u skips newer destination', async (t) => {
const run = await loadBin('cp')
const store = {
files: {
'/src': {
stat: { mode: 0o644, type: 'file', mtimeMs: 100 },
body: b4a.from('a')
},
'/dst': {
stat: { mode: 0o644, type: 'file', mtimeMs: 200 },
body: b4a.from('b')
}
}
}
const ctx = {
b4a,
exitCode: 0,
console: { log: () => {}, error: () => {} },
vfs: {
async lstat(p) {
const e = store.files[p]
return e ? e.stat : null
},
async readFile(p) {
const e = store.files[p]
return e ? e.body : null
},
async writeFile() {
throw new Error('should not write when -u skips')
}
}
}
await run(ctx, ['cp', '-u', '/src', '/dst'])
t.is(ctx.exitCode, 0)
t.is(b4a.toString(store.files['/dst'].body), 'b')
})
test('test -e is true for existing file', async (t) => {
const run = await loadBin('test')
let code = 1
const ctx = {
exitCode: 0,
console: { log: () => {}, error: () => {} },
vfs: {
async stat() {
return { type: 'file' }
}
}
}
await run(ctx, ['test', '-e', '/x'])
code = ctx.exitCode || 0
t.is(code, 0)
})
test('true exits 0', async (t) => {
const run = await loadBin('true')
const ctx = { exitCode: 1, console: { log: () => {}, error: () => {} } }
await run(ctx, ['true'])
t.is(ctx.exitCode, 0)
})
@@ -46,7 +46,7 @@ export const BARE_OS_FEATURE2_CRON_REBOOT_JITTER = 1 << 10
/** Word2 bit 11: Boot-ready `subsystems.initdActiveUnits` alignment. */ /** Word2 bit 11: Boot-ready `subsystems.initdActiveUnits` alignment. */
export const BARE_OS_FEATURE2_INITD_BOOT_READY_UNITS = 1 << 11 export const BARE_OS_FEATURE2_INITD_BOOT_READY_UNITS = 1 << 11
/** Word2 bit 12: `/proc/self/cgroups` and `/proc/net/tcp` stubs. */ /** Word2 bit 12: `/proc/self/cgroups` and `/proc/net/tcp` synthetic Linux-compat procfs (session + swarmbacked rows). */
export const BARE_OS_FEATURE2_PROC_CGROUPS_TCP = 1 << 12 export const BARE_OS_FEATURE2_PROC_CGROUPS_TCP = 1 << 12
/** Word2 bit 13: Bounded `vfs.watch` on allowlisted pseudo paths. */ /** Word2 bit 13: Bounded `vfs.watch` on allowlisted pseudo paths. */
@@ -97,7 +97,7 @@ export const BARE_OS_FEATURE2_INITD_SUSPEND_RESUME_ORDER = 1 << 27
/** Word2 bit 28: Timer `OnInactiveSec=` / `Persistent=` in `.timer` files. */ /** Word2 bit 28: Timer `OnInactiveSec=` / `Persistent=` in `.timer` files. */
export const BARE_OS_FEATURE2_TIMER_ON_INACTIVE = 1 << 28 export const BARE_OS_FEATURE2_TIMER_ON_INACTIVE = 1 << 28
/** Word2 bit 29: `/proc/net/udp` stub (with meminfo already present). */ /** Word2 bit 29: `/proc/net/udp` synthetic Linux-compat view (logical discovery/datagram hints). */
export const BARE_OS_FEATURE2_PROC_NET_UDP = 1 << 29 export const BARE_OS_FEATURE2_PROC_NET_UDP = 1 << 29
/** Word2 bit 30: Bounded `vfs.watch` on `/proc/bare_os_swarm` when enabled. */ /** Word2 bit 30: Bounded `vfs.watch` on `/proc/bare_os_swarm` when enabled. */
@@ -445,7 +445,7 @@ export const BARE_OS_FEATURE9_BOOT_POLICY_SURFACE = 1 << 4
export const BARE_OS_FEATURE9_KERNEL_EXT_REGISTRY_V6 = 1 << 5 export const BARE_OS_FEATURE9_KERNEL_EXT_REGISTRY_V6 = 1 << 5
/** Word9 bit 6: Pear/Bare bridge word 9 (stage hint, bare module probe, …). */ /** Word9 bit 6: Pear/Bare bridge word 9 (stage hint, bare module probe, …). */
export const BARE_OS_FEATURE9_PEAR_BARE_BRIDGE_EXTENDED = 1 << 6 export const BARE_OS_FEATURE9_PEAR_BARE_BRIDGE_EXTENDED = 1 << 6
/** Word9 bit 7: VFS/POSIX/shell word 9 (statfs sketch, tar hardlink, crypto stub). */ /** Word9 bit 7: VFS/POSIX/shell word 9 (Hyperdrive-backed statvfs in resources, tar hardlink, crypto staging proc). */
export const BARE_OS_FEATURE9_VFS_POSIX_EXTENSIONS = 1 << 7 export const BARE_OS_FEATURE9_VFS_POSIX_EXTENSIONS = 1 << 7
/** Word9 bit 8: Initd / subprocess / limits word 9. */ /** Word9 bit 8: Initd / subprocess / limits word 9. */
export const BARE_OS_FEATURE9_INITD_SUBPROCESS = 1 << 8 export const BARE_OS_FEATURE9_INITD_SUBPROCESS = 1 << 8
@@ -491,7 +491,7 @@ export const BARE_OS_FEATURE10_BOOT_POLICY_SURFACE = 1 << 4
export const BARE_OS_FEATURE10_KERNEL_EXT_REGISTRY_V7 = 1 << 5 export const BARE_OS_FEATURE10_KERNEL_EXT_REGISTRY_V7 = 1 << 5
/** Word10 bit 6: Pear/Bare bridge word 10 (doctor hint, bare logger tap, …). */ /** Word10 bit 6: Pear/Bare bridge word 10 (doctor hint, bare logger tap, …). */
export const BARE_OS_FEATURE10_PEAR_BARE_BRIDGE_EXTENDED = 1 << 6 export const BARE_OS_FEATURE10_PEAR_BARE_BRIDGE_EXTENDED = 1 << 6
/** Word10 bit 7: VFS/POSIX/shell word 10 (ssh-keygen stub, xargs -P, tar xattr). */ /** Word10 bit 7: VFS/POSIX/shell word 10 (ssh-keygen delegate, xargs -P, tar xattr). */
export const BARE_OS_FEATURE10_VFS_POSIX_EXTENSIONS = 1 << 7 export const BARE_OS_FEATURE10_VFS_POSIX_EXTENSIONS = 1 << 7
/** Word10 bit 8: Initd / subprocess / git worktree policy word 10. */ /** Word10 bit 8: Initd / subprocess / git worktree policy word 10. */
export const BARE_OS_FEATURE10_INITD_SUBPROCESS = 1 << 8 export const BARE_OS_FEATURE10_INITD_SUBPROCESS = 1 << 8
+3 -1
View File
@@ -19,6 +19,7 @@ import {
defaultSeedCorestorePath defaultSeedCorestorePath
} from './lib/paths.js' } from './lib/paths.js'
import { logPearMultisigKernelHint } from './lib/pear-multisig-hint.js' import { logPearMultisigKernelHint } from './lib/pear-multisig-hint.js'
import { buildSeederSnapshotHintsJson } from './lib/build-seeder-snapshot-hints-json.js'
const _pkg = packageRootDir(import.meta.url) const _pkg = packageRootDir(import.meta.url)
@@ -459,7 +460,8 @@ async function main() {
readyGuardV5Json: String(hostEnv?.BARE_OS_SEED_READY_GUARD_V5_JSON ?? ''), readyGuardV5Json: String(hostEnv?.BARE_OS_SEED_READY_GUARD_V5_JSON ?? ''),
mirrorDriveCompactionV6Json: String( mirrorDriveCompactionV6Json: String(
hostEnv?.BARE_OS_SEED_MIRROR_DRIVE_COMPACTION_V6_JSON ?? '' hostEnv?.BARE_OS_SEED_MIRROR_DRIVE_COMPACTION_V6_JSON ?? ''
) ),
snapshotHintsJson: buildSeederSnapshotHintsJson(hostEnv)
} }
const swarm = new Hyperswarm() const swarm = new Hyperswarm()
+16
View File
@@ -121,6 +121,22 @@ async function run(ctx, argv) {
} catch { } catch {
/* ignore */ /* ignore */
} }
try {
const rbuf = await ctx.vfs.readFile('/proc/bare_os_resources')
if (rbuf) {
const j = JSON.parse(ctx.b4a.toString(rbuf))
const sv = j && j.statvfs
if (sv && typeof sv.f_blocks === 'number' && typeof sv.f_bfree === 'number') {
const bsize = typeof sv.f_bsize === 'number' ? sv.f_bsize : 4096
totalK = Math.max(1024, Math.ceil((sv.f_blocks * bsize) / 1024))
const freeB = sv.f_bavail ?? sv.f_bfree
const freeKFromStat = Math.max(0, Math.ceil((freeB * bsize) / 1024))
usedK = Math.max(0, totalK - freeKFromStat)
}
}
} catch {
/* ignore */
}
const freeK = Math.max(0, totalK - usedK) const freeK = Math.max(0, totalK - usedK)
const pct = const pct =
totalK > 0 ? Math.min(100, Math.round((usedK / totalK) * 100)) : 0 totalK > 0 ? Math.min(100, Math.round((usedK / totalK) * 100)) : 0
+1 -1
View File
@@ -100,7 +100,7 @@ async function run(ctx, argv) {
break break
} }
if (a === '-l' || a === '--list') { if (a === '-l' || a === '--list') {
ctx.console.log('HUP INT KILL TERM 0') ctx.console.log('HUP INT KILL TERM PIPE CHLD USR1 USR2 0')
return return
} }
if (a === '-s' || a === '--signal') { if (a === '-s' || a === '--signal') {
@@ -1,6 +1,6 @@
{ {
"schemaVersion": 3, "schemaVersion": 4,
"ctxApiVersion": "1.30.0", "ctxApiVersion": "1.31.0",
"posixProfile": { "posixProfile": {
"id": "bare-os-posix-like", "id": "bare-os-posix-like",
"version": "1.0.0" "version": "1.0.0"
@@ -14,6 +14,19 @@
"ENOENT": 2, "ENOENT": 2,
"EACCES": 13 "EACCES": 13
}, },
"fdModel": {
"schema": 1,
"note": "Integer FDs index logical VFS/IPC targets in the guest.",
"stdio": [0, 1, 2],
"register": "ctx.bareOsRegisterLogicalFd",
"processTablePath": "/proc/bare_os/process_table.json",
"processTableSchema": 3
},
"signalModel": {
"schema": 1,
"note": "ctx.bareOsSendSignal delivers to synthetic pids.",
"names": ["HUP", "INT", "KILL", "TERM"]
},
"caps": { "caps": {
"note": "example" "note": "example"
}, },
@@ -1,18 +1,18 @@
{ {
"version": 1, "version": 1,
"bundles": [ "bundles": [
{
"path": "/lib/bare/bundles/b4a.js",
"keys": [
"b4a"
]
},
{ {
"path": "/lib/bare/bundles/safetyCatch.js", "path": "/lib/bare/bundles/safetyCatch.js",
"keys": [ "keys": [
"safetyCatch" "safetyCatch"
] ]
}, },
{
"path": "/lib/bare/bundles/b4a.js",
"keys": [
"b4a"
]
},
{ {
"path": "/lib/bare/bundles/hypercoreIdEncoding.js", "path": "/lib/bare/bundles/hypercoreIdEncoding.js",
"keys": [ "keys": [
@@ -25,18 +25,18 @@
"compactEncoding" "compactEncoding"
] ]
}, },
{
"path": "/lib/bare/bundles/protomux.js",
"keys": [
"protomux"
]
},
{ {
"path": "/lib/bare/bundles/bareUrl.js", "path": "/lib/bare/bundles/bareUrl.js",
"keys": [ "keys": [
"bareUrl" "bareUrl"
] ]
}, },
{
"path": "/lib/bare/bundles/protomux.js",
"keys": [
"protomux"
]
},
{ {
"path": "/lib/bare/bundles/barePath.js", "path": "/lib/bare/bundles/barePath.js",
"keys": [ "keys": [
@@ -61,12 +61,6 @@
"bareAbort" "bareAbort"
] ]
}, },
{
"path": "/lib/bare/bundles/bareAnsiEscapes.js",
"keys": [
"bareAnsiEscapes"
]
},
{ {
"path": "/lib/bare/bundles/bareAbortController.js", "path": "/lib/bare/bundles/bareAbortController.js",
"keys": [ "keys": [
@@ -79,6 +73,12 @@
"bareReadline" "bareReadline"
] ]
}, },
{
"path": "/lib/bare/bundles/bareAnsiEscapes.js",
"keys": [
"bareAnsiEscapes"
]
},
{ {
"path": "/lib/bare/bundles/bareAddonResolve.js", "path": "/lib/bare/bundles/bareAddonResolve.js",
"keys": [ "keys": [
@@ -97,6 +97,12 @@
"bareAppKit" "bareAppKit"
] ]
}, },
{
"path": "/lib/bare/bundles/fetch.js",
"keys": [
"fetch"
]
},
{ {
"path": "/lib/bare/bundles/bareAsyncHooks.js", "path": "/lib/bare/bundles/bareAsyncHooks.js",
"keys": [ "keys": [
@@ -121,36 +127,24 @@
"bareAtomics" "bareAtomics"
] ]
}, },
{
"path": "/lib/bare/bundles/fetch.js",
"keys": [
"fetch"
]
},
{ {
"path": "/lib/bare/bundles/bareBmp.js", "path": "/lib/bare/bundles/bareBmp.js",
"keys": [ "keys": [
"bareBmp" "bareBmp"
] ]
}, },
{
"path": "/lib/bare/bundles/bareBundle.js",
"keys": [
"bareBundle"
]
},
{
"path": "/lib/bare/bundles/bareBundleCompile.js",
"keys": [
"bareBundleCompile"
]
},
{ {
"path": "/lib/bare/bundles/bareBuffer.js", "path": "/lib/bare/bundles/bareBuffer.js",
"keys": [ "keys": [
"bareBuffer" "bareBuffer"
] ]
}, },
{
"path": "/lib/bare/bundles/bareBundleCompile.js",
"keys": [
"bareBundleCompile"
]
},
{ {
"path": "/lib/bare/bundles/bareBluetoothApple.js", "path": "/lib/bare/bundles/bareBluetoothApple.js",
"keys": [ "keys": [
@@ -158,9 +152,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareBundleEvaluate.js", "path": "/lib/bare/bundles/bareBundle.js",
"keys": [ "keys": [
"bareBundleEvaluate" "bareBundle"
] ]
}, },
{ {
@@ -170,9 +164,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareConsole.js", "path": "/lib/bare/bundles/bareBundleEvaluate.js",
"keys": [ "keys": [
"bareConsole" "bareBundleEvaluate"
] ]
}, },
{ {
@@ -188,9 +182,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareDelta.js", "path": "/lib/bare/bundles/bareConsole.js",
"keys": [ "keys": [
"bareDelta" "bareConsole"
] ]
}, },
{ {
@@ -206,15 +200,15 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareDiagnosticsChannel.js", "path": "/lib/bare/bundles/bareDelta.js",
"keys": [ "keys": [
"bareDiagnosticsChannel" "bareDelta"
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareDns.js", "path": "/lib/bare/bundles/bareDiagnosticsChannel.js",
"keys": [ "keys": [
"bareDns" "bareDiagnosticsChannel"
] ]
}, },
{ {
@@ -224,9 +218,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareExif.js", "path": "/lib/bare/bundles/bareDns.js",
"keys": [ "keys": [
"bareExif" "bareDns"
] ]
}, },
{ {
@@ -235,42 +229,36 @@
"bareDgram" "bareDgram"
] ]
}, },
{
"path": "/lib/bare/bundles/bareFfmpeg.js",
"keys": [
"bareFfmpeg"
]
},
{ {
"path": "/lib/bare/bundles/bareCov.js", "path": "/lib/bare/bundles/bareCov.js",
"keys": [ "keys": [
"bareCov" "bareCov"
] ]
}, },
{
"path": "/lib/bare/bundles/bareExif.js",
"keys": [
"bareExif"
]
},
{ {
"path": "/lib/bare/bundles/bareFfmpegEncodings.js", "path": "/lib/bare/bundles/bareFfmpegEncodings.js",
"keys": [ "keys": [
"bareFfmpegEncodings" "bareFfmpegEncodings"
] ]
}, },
{
"path": "/lib/bare/bundles/bareFfmpeg.js",
"keys": [
"bareFfmpeg"
]
},
{ {
"path": "/lib/bare/bundles/bareFormat.js", "path": "/lib/bare/bundles/bareFormat.js",
"keys": [ "keys": [
"bareFormat" "bareFormat"
] ]
}, },
{
"path": "/lib/bare/bundles/bareGif.js",
"keys": [
"bareGif"
]
},
{
"path": "/lib/bare/bundles/bareFormData.js",
"keys": [
"bareFormData"
]
},
{ {
"path": "/lib/bare/bundles/bareFileLogger.js", "path": "/lib/bare/bundles/bareFileLogger.js",
"keys": [ "keys": [
@@ -278,21 +266,15 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareFs.js", "path": "/lib/bare/bundles/bareFormData.js",
"keys": [ "keys": [
"bareFs" "bareFormData"
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareHttpParser.js", "path": "/lib/bare/bundles/bareGif.js",
"keys": [ "keys": [
"bareHttpParser" "bareGif"
]
},
{
"path": "/lib/bare/bundles/bareHeif.js",
"keys": [
"bareHeif"
] ]
}, },
{ {
@@ -301,12 +283,24 @@
"bareGtk" "bareGtk"
] ]
}, },
{
"path": "/lib/bare/bundles/bareHeif.js",
"keys": [
"bareHeif"
]
},
{ {
"path": "/lib/bare/bundles/bareHrtime.js", "path": "/lib/bare/bundles/bareHrtime.js",
"keys": [ "keys": [
"bareHrtime" "bareHrtime"
] ]
}, },
{
"path": "/lib/bare/bundles/bareHttpParser.js",
"keys": [
"bareHttpParser"
]
},
{ {
"path": "/lib/bare/bundles/bareIco.js", "path": "/lib/bare/bundles/bareIco.js",
"keys": [ "keys": [
@@ -320,9 +314,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareInspect.js", "path": "/lib/bare/bundles/bareFs.js",
"keys": [ "keys": [
"bareInspect" "bareFs"
] ]
}, },
{ {
@@ -332,21 +326,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareHttps.js", "path": "/lib/bare/bundles/bareInspect.js",
"keys": [ "keys": [
"bareHttps" "bareInspect"
]
},
{
"path": "/lib/bare/bundles/bareJpeg.js",
"keys": [
"bareJpeg"
]
},
{
"path": "/lib/bare/bundles/bareIpc.js",
"keys": [
"bareIpc"
] ]
}, },
{ {
@@ -356,15 +338,27 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareLogger.js", "path": "/lib/bare/bundles/bareJpeg.js",
"keys": [ "keys": [
"bareLogger" "bareJpeg"
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareLief.js", "path": "/lib/bare/bundles/bareHttps.js",
"keys": [ "keys": [
"bareLief" "bareHttps"
]
},
{
"path": "/lib/bare/bundles/bareIpc.js",
"keys": [
"bareIpc"
]
},
{
"path": "/lib/bare/bundles/bareLogger.js",
"keys": [
"bareLogger"
] ]
}, },
{ {
@@ -373,12 +367,6 @@
"bareInspector" "bareInspector"
] ]
}, },
{
"path": "/lib/bare/bundles/bareMake.js",
"keys": [
"bareMake"
]
},
{ {
"path": "/lib/bare/bundles/bareLink.js", "path": "/lib/bare/bundles/bareLink.js",
"keys": [ "keys": [
@@ -386,9 +374,15 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareModule.js", "path": "/lib/bare/bundles/bareLief.js",
"keys": [ "keys": [
"bareModule" "bareLief"
]
},
{
"path": "/lib/bare/bundles/bareMake.js",
"keys": [
"bareMake"
] ]
}, },
{ {
@@ -404,9 +398,15 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareModuleTraverse.js", "path": "/lib/bare/bundles/bareModule.js",
"keys": [ "keys": [
"bareModuleTraverse" "bareModule"
]
},
{
"path": "/lib/bare/bundles/bareDev.js",
"keys": [
"bareDev"
] ]
}, },
{ {
@@ -428,15 +428,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareDev.js", "path": "/lib/bare/bundles/bareModuleTraverse.js",
"keys": [ "keys": [
"bareDev" "bareModuleTraverse"
]
},
{
"path": "/lib/bare/bundles/bareMedia.js",
"keys": [
"bareMedia"
] ]
}, },
{ {
@@ -445,6 +439,12 @@
"bareOpen" "bareOpen"
] ]
}, },
{
"path": "/lib/bare/bundles/bareOs.js",
"keys": [
"bareOs"
]
},
{ {
"path": "/lib/bare/bundles/bareNet.js", "path": "/lib/bare/bundles/bareNet.js",
"keys": [ "keys": [
@@ -452,9 +452,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareOs.js", "path": "/lib/bare/bundles/bareMedia.js",
"keys": [ "keys": [
"bareOs" "bareMedia"
] ]
}, },
{ {
@@ -475,18 +475,18 @@
"barePack" "barePack"
] ]
}, },
{
"path": "/lib/bare/bundles/barePackDrive.js",
"keys": [
"barePackDrive"
]
},
{ {
"path": "/lib/bare/bundles/barePipe.js", "path": "/lib/bare/bundles/barePipe.js",
"keys": [ "keys": [
"barePipe" "barePipe"
] ]
}, },
{
"path": "/lib/bare/bundles/barePackDrive.js",
"keys": [
"barePackDrive"
]
},
{ {
"path": "/lib/bare/bundles/barePunycode.js", "path": "/lib/bare/bundles/barePunycode.js",
"keys": [ "keys": [
@@ -499,24 +499,18 @@
"barePrebuild" "barePrebuild"
] ]
}, },
{
"path": "/lib/bare/bundles/bareQuerystring.js",
"keys": [
"bareQuerystring"
]
},
{
"path": "/lib/bare/bundles/bareProcess.js",
"keys": [
"bareProcess"
]
},
{ {
"path": "/lib/bare/bundles/bareNodeRuntime.js", "path": "/lib/bare/bundles/bareNodeRuntime.js",
"keys": [ "keys": [
"bareNodeRuntime" "bareNodeRuntime"
] ]
}, },
{
"path": "/lib/bare/bundles/bareQuerystring.js",
"keys": [
"bareQuerystring"
]
},
{ {
"path": "/lib/bare/bundles/bareQueueMicrotask.js", "path": "/lib/bare/bundles/bareQueueMicrotask.js",
"keys": [ "keys": [
@@ -529,6 +523,12 @@
"bareRealm" "bareRealm"
] ]
}, },
{
"path": "/lib/bare/bundles/bareProcess.js",
"keys": [
"bareProcess"
]
},
{ {
"path": "/lib/bare/bundles/bareRuntime.js", "path": "/lib/bare/bundles/bareRuntime.js",
"keys": [ "keys": [
@@ -542,9 +542,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareSemver.js", "path": "/lib/bare/bundles/bareSdl.js",
"keys": [ "keys": [
"bareSemver" "bareSdl"
] ]
}, },
{ {
@@ -554,15 +554,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareSdl.js", "path": "/lib/bare/bundles/bareSemver.js",
"keys": [ "keys": [
"bareSdl" "bareSemver"
]
},
{
"path": "/lib/bare/bundles/bareSidecar.js",
"keys": [
"bareSidecar"
] ]
}, },
{ {
@@ -572,9 +566,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareRepl.js", "path": "/lib/bare/bundles/bareSidecar.js",
"keys": [ "keys": [
"bareRepl" "bareSidecar"
] ]
}, },
{ {
@@ -590,9 +584,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareStream.js", "path": "/lib/bare/bundles/bareRepl.js",
"keys": [ "keys": [
"bareStream" "bareRepl"
] ]
}, },
{ {
@@ -601,6 +595,12 @@
"bareStorage" "bareStorage"
] ]
}, },
{
"path": "/lib/bare/bundles/bareStream.js",
"keys": [
"bareStream"
]
},
{ {
"path": "/lib/bare/bundles/bareStdio.js", "path": "/lib/bare/bundles/bareStdio.js",
"keys": [ "keys": [
@@ -620,15 +620,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareSubprocess.js", "path": "/lib/bare/bundles/bareTiff.js",
"keys": [ "keys": [
"bareSubprocess" "bareTiff"
]
},
{
"path": "/lib/bare/bundles/bareSystemLogger.js",
"keys": [
"bareSystemLogger"
] ]
}, },
{ {
@@ -638,21 +632,15 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareTiff.js", "path": "/lib/bare/bundles/bareSystemLogger.js",
"keys": [ "keys": [
"bareTiff" "bareSystemLogger"
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareTimers.js", "path": "/lib/bare/bundles/bareSubprocess.js",
"keys": [ "keys": [
"bareTimers" "bareSubprocess"
]
},
{
"path": "/lib/bare/bundles/bareTpl.js",
"keys": [
"bareTpl"
] ]
}, },
{ {
@@ -661,6 +649,12 @@
"bareThread" "bareThread"
] ]
}, },
{
"path": "/lib/bare/bundles/bareTimers.js",
"keys": [
"bareTimers"
]
},
{ {
"path": "/lib/bare/bundles/bareTcp.js", "path": "/lib/bare/bundles/bareTcp.js",
"keys": [ "keys": [
@@ -668,9 +662,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareType.js", "path": "/lib/bare/bundles/bareTpl.js",
"keys": [ "keys": [
"bareType" "bareTpl"
] ]
}, },
{ {
@@ -679,12 +673,24 @@
"bareTls" "bareTls"
] ]
}, },
{
"path": "/lib/bare/bundles/bareType.js",
"keys": [
"bareType"
]
},
{ {
"path": "/lib/bare/bundles/bareUiKit.js", "path": "/lib/bare/bundles/bareUiKit.js",
"keys": [ "keys": [
"bareUiKit" "bareUiKit"
] ]
}, },
{
"path": "/lib/bare/bundles/bareTty.js",
"keys": [
"bareTty"
]
},
{ {
"path": "/lib/bare/bundles/bareUnpack.js", "path": "/lib/bare/bundles/bareUnpack.js",
"keys": [ "keys": [
@@ -697,24 +703,6 @@
"bareV8" "bareV8"
] ]
}, },
{
"path": "/lib/bare/bundles/bareTty.js",
"keys": [
"bareTty"
]
},
{
"path": "/lib/bare/bundles/bareWebKit.js",
"keys": [
"bareWebKit"
]
},
{
"path": "/lib/bare/bundles/bareVm.js",
"keys": [
"bareVm"
]
},
{ {
"path": "/lib/bare/bundles/bareUnionBundle.js", "path": "/lib/bare/bundles/bareUnionBundle.js",
"keys": [ "keys": [
@@ -727,6 +715,18 @@
"bareWalkHandles" "bareWalkHandles"
] ]
}, },
{
"path": "/lib/bare/bundles/bareVm.js",
"keys": [
"bareVm"
]
},
{
"path": "/lib/bare/bundles/bareWebKit.js",
"keys": [
"bareWebKit"
]
},
{ {
"path": "/lib/bare/bundles/bareWebp.js", "path": "/lib/bare/bundles/bareWebp.js",
"keys": [ "keys": [
@@ -734,15 +734,15 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareWhich.js", "path": "/lib/bare/bundles/bareWebKitGtk.js",
"keys": [ "keys": [
"bareWhich" "bareWebKitGtk"
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareV8ToIstanbul.js", "path": "/lib/bare/bundles/bareWhich.js",
"keys": [ "keys": [
"bareV8ToIstanbul" "bareWhich"
] ]
}, },
{ {
@@ -758,15 +758,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareWebKitGtk.js", "path": "/lib/bare/bundles/bareV8ToIstanbul.js",
"keys": [ "keys": [
"bareWebKitGtk" "bareV8ToIstanbul"
]
},
{
"path": "/lib/bare/bundles/bareXdiff.js",
"keys": [
"bareXdiff"
] ]
}, },
{ {
@@ -782,9 +776,9 @@
] ]
}, },
{ {
"path": "/lib/bare/bundles/bareWorker.js", "path": "/lib/bare/bundles/bareXdiff.js",
"keys": [ "keys": [
"bareWorker" "bareXdiff"
] ]
}, },
{ {
@@ -792,6 +786,12 @@
"keys": [ "keys": [
"bareWs" "bareWs"
] ]
},
{
"path": "/lib/bare/bundles/bareWorker.js",
"keys": [
"bareWorker"
]
} }
], ],
"bundleStats": { "bundleStats": {
@@ -1595,8 +1595,8 @@
], ],
"bundleProvenance": { "bundleProvenance": {
"schemaVersion": 1, "schemaVersion": 1,
"generatedAt": "2026-04-05T01:31:52.568Z", "generatedAt": "2026-04-05T01:58:32.340Z",
"gitCommit": "346bb71ffe4b7d059d4e16454dc9e4afff73224b", "gitCommit": "57ba71899bdfadc92caeb9eb8ede55d3484d9697",
"nodeVersion": "v22.22.0", "nodeVersion": "v22.22.0",
"bundleTier": "all", "bundleTier": "all",
"normativeManifest": "packages/bare-os-booter/lib/bare-module-manifest.json", "normativeManifest": "packages/bare-os-booter/lib/bare-module-manifest.json",
@@ -1,6 +1,6 @@
{ {
"schema": 1, "schema": 1,
"atMs": 1775352711830, "atMs": 1775354354273,
"commands": [ "commands": [
"arch", "arch",
"awk", "awk",
File diff suppressed because one or more lines are too long
@@ -0,0 +1,17 @@
/**
* Build `snapshotHintsJson` for `setupSeedChannel` (bare_os.snapshot_hints RPC booter proc).
* @param {Record<string, string | undefined> | null | undefined} hostEnv
* @returns {string}
*/
export function buildSeederSnapshotHintsJson(hostEnv) {
const e = hostEnv && typeof hostEnv === 'object' ? hostEnv : {}
const explicit = String(e.BARE_OS_SEED_SNAPSHOT_HINTS_JSON ?? '').trim()
if (explicit) return explicit
const tag = String(e.BARE_OS_SEED_CORESTORE_SNAPSHOT_TAG ?? '').trim()
if (!tag) return ''
return JSON.stringify({
schema: 1,
corestoreSnapshotTag: tag,
source: 'BARE_OS_SEED_CORESTORE_SNAPSHOT_TAG'
})
}
+2 -1
View File
@@ -7,7 +7,8 @@
"scripts": { "scripts": {
"start": "node ../../scripts/ensure-pear-node-modules.mjs packages/bare-os-seeder && bare index.js", "start": "node ../../scripts/ensure-pear-node-modules.mjs packages/bare-os-seeder && bare index.js",
"dev": "node ../../scripts/ensure-pear-node-modules.mjs packages/bare-os-seeder && bare index.js", "dev": "node ../../scripts/ensure-pear-node-modules.mjs packages/bare-os-seeder && bare index.js",
"pear:dev": "node ../../scripts/ensure-pear-node-modules.mjs packages/bare-os-seeder && pear run --dev ." "pear:dev": "node ../../scripts/ensure-pear-node-modules.mjs packages/bare-os-seeder && pear run --dev .",
"test": "node --test test/*.test.mjs"
}, },
"dependencies": { "dependencies": {
"bare-subprocess": "^5.2.3", "bare-subprocess": "^5.2.3",
@@ -0,0 +1,29 @@
/**
* @import { test } from 'node:test'
*/
import assert from 'node:assert'
import test from 'node:test'
import { buildSeederSnapshotHintsJson } from '../lib/build-seeder-snapshot-hints-json.js'
test('empty env yields empty snapshotHintsJson', () => {
assert.strictEqual(buildSeederSnapshotHintsJson({}), '')
})
test('BARE_OS_SEED_CORESTORE_SNAPSHOT_TAG becomes hints payload', () => {
const j = buildSeederSnapshotHintsJson({
BARE_OS_SEED_CORESTORE_SNAPSHOT_TAG: 'release-42'
})
const o = JSON.parse(j)
assert.strictEqual(o.schema, 1)
assert.strictEqual(o.corestoreSnapshotTag, 'release-42')
assert.strictEqual(o.source, 'BARE_OS_SEED_CORESTORE_SNAPSHOT_TAG')
})
test('BARE_OS_SEED_SNAPSHOT_HINTS_JSON wins over tag', () => {
const want = '{"schema":9,"slots":["a"]}'
const j = buildSeederSnapshotHintsJson({
BARE_OS_SEED_SNAPSHOT_HINTS_JSON: want,
BARE_OS_SEED_CORESTORE_SNAPSHOT_TAG: 'ignored'
})
assert.strictEqual(j, want)
})
+4
View File
@@ -259,6 +259,10 @@ Validates representative JSON under **`kernel/etc/bare-os/`** (e.g. **`boot.poli
Maintainer helper for release/staging workflows (see script header for intended use). Maintainer helper for release/staging workflows (see script header for intended use).
## Root `npm run test:bare`
Runs **`bare-os-protocol`** tests, **`bare-os-booter`** **`test:bare`** (**`brittle-bare`** identity slice), and **`bare-os-seeder`** **`node --test`** helpers. Full workspace coverage remains **`npm test`** (includes **`brittle-node`** booter harness + coreutils).
## See also ## See also
- Root [README.md](../README.md) — Pear + workspaces note - Root [README.md](../README.md) — Pear + workspaces note