Updates
This commit is contained in:
+11
-2
@@ -30,6 +30,8 @@ pear run pear://0.4955.1ycyj65xxgn6c5cirhtnm8aokkrpe9dzibriszyfkmfggc3opjzy
|
|||||||
pear run pear://sodbfkpk16rwowq5b9eigdgi57xh9ofh98mj6sg5rhhnykt1haho
|
pear run pear://sodbfkpk16rwowq5b9eigdgi57xh9ofh98mj6sg5rhhnykt1haho
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The booter entrypoint avoids **static** **`node:…`** imports (Bare cannot resolve them). Hashing uses **`bare-crypto`** (`createHash`); **`fs`** / **`path`** / **`url`** go through **`package.json` `imports`** maps to **`bare-fs`**, **`bare-path`**, **`bare-url`**. Dynamic **`import('node:readline')`** / **`import('node:console')`** stay in try/catch fallbacks only. Signed boot manifest verification uses **`#bare-os-boot-manifest-sig`**: under the **`bare`** condition Pear loads **`bare-os-boot-manifest-sig.bare.js`** plus a small **`.cjs`** bridge (no **`node:module`**); Node / **`brittle-node`** resolves **`default`** to **`bare-os-boot-manifest-sig.node.js`** with lazy **`require`** + **`createRequire`**.
|
||||||
|
|
||||||
## Re-staging after code changes
|
## Re-staging after code changes
|
||||||
|
|
||||||
From the package directory:
|
From the package directory:
|
||||||
@@ -48,15 +50,20 @@ pear release bare-os-booter
|
|||||||
|
|
||||||
## Booter host env (Pear / shell)
|
## Booter host env (Pear / shell)
|
||||||
|
|
||||||
The booter copies many **`BARE_OS_*`** keys into the session (see [developer-guide/02-the-context-object.md](developer-guide/02-the-context-object.md)). Notable additions for Pear hosts wiring **OTA** or **HTTP**:
|
The booter copies many **`BARE_OS_*`** keys into the session (see [developer-guide/02-the-context-object.md](developer-guide/02-the-context-object.md) and [docs/reference/environment-and-posix-appendix.md](docs/reference/environment-and-posix-appendix.md)). Notable additions for Pear hosts wiring **OTA** or **HTTP**:
|
||||||
|
|
||||||
- **`BARE_OS_PEAR_CHANNEL`**, **`BARE_OS_PEAR_RELEASE`**, **`PEAR_CHANNEL`** — mirrored into **`/run/bare-os/boot.json`**; use with **`ctx.bareOsRequestPearReload()`** hints and **`pear-runtime-updater`** on the host.
|
- **`BARE_OS_PEAR_CHANNEL`**, **`BARE_OS_PEAR_RELEASE`**, **`PEAR_CHANNEL`** — mirrored into **`/run/bare-os/boot.json`**; pair with **`ctx.bareOsRequestPearReload(opts?)`** (async **`{ requested, hint, env }`**). With **`{ persistRequest: true }`**, the booter writes **`~/.bare-os/pear-reload.request`** and may emit **`process.emit('bare-os:pear-reload', …)`** on Node so a **`pear-runtime-updater`**-style host can swap the runtime without blocking the guest.
|
||||||
- **`BARE_OS_HTTP_ALLOWLIST`**, **`BARE_OS_HTTP_DENYLIST`** — restrict delegated **`curl`/`wget`** when global **`fetch`** exists.
|
- **`BARE_OS_HTTP_ALLOWLIST`**, **`BARE_OS_HTTP_DENYLIST`** — restrict delegated **`curl`/`wget`** when global **`fetch`** exists.
|
||||||
- **`BARE_OS_TLS_PIN_SHA256`** — forwarded as **`init.bareOsCurlTls.pinnedSha256`** for host **`httpFetch`** implementations that implement pinning.
|
- **`BARE_OS_TLS_PIN_SHA256`** — forwarded as **`init.bareOsCurlTls.pinnedSha256`** for host **`httpFetch`** implementations that implement pinning.
|
||||||
- **`BARE_OS_BARE_MODULES`**, **`BARE_OS_BARE_DRIVE_BUNDLES`** — control **`ctx.bare`** (host dynamic imports + optional trusted bundles from **`/lib/bare/`** on the system drive). See **`man 7 bare-os-ctx-bare`** (after seeding) and [developer-guide/12-bare-modules-and-pear-ecosystem.md](developer-guide/12-bare-modules-and-pear-ecosystem.md).
|
- **`BARE_OS_BARE_MODULES`**, **`BARE_OS_BARE_DRIVE_BUNDLES`** — control **`ctx.bare`** (host dynamic imports + optional trusted bundles from **`/lib/bare/`** on the system drive). See **`man 7 bare-os-ctx-bare`** (after seeding) and [developer-guide/12-bare-modules-and-pear-ecosystem.md](developer-guide/12-bare-modules-and-pear-ecosystem.md).
|
||||||
|
- **`ctx.bareOsRequestMirror({ key?, label? })`**, **`ctx.bareOsExportPersonalSnapshot({ label? })`**, **`ctx.bareOsPearIpcEmit(channel, payload)`** — policy-gated host bridges (return hints / boolean success); on Node the booter emits **`bare-os:mirror-request`**, **`bare-os:export-personal-snapshot`**, and **`bare-os:pear-ipc`** for the embedding app to attach real mirror keys, run **corestore-snapshot**-style export, or forward **pear-ipc**-shaped payloads to the host UI.
|
||||||
|
|
||||||
Re-seed **`/lib/bare`** after changing the manifest or bundle list: **`npm run build -w bare-os-bare-libs`** (also runs from the seeder when started via `node` / `file:` URL).
|
Re-seed **`/lib/bare`** after changing the manifest or bundle list: **`npm run build -w bare-os-bare-libs`** (also runs from the seeder when started via `node` / `file:` URL).
|
||||||
|
|
||||||
|
### pear-ipc alignment (embedded Pear)
|
||||||
|
|
||||||
|
Guest code should treat **`ctx.bareOsPearIpcEmit`** as a **thin forwarder**: the host decides how messages map to **[pear-ipc](https://github.com/holepunchto/pear-ipc)** (or your channel bridge). Use a small, versioned **`payload`** shape per **`channel`** (e.g. **`{ v: 1, type: '…', … }`**) and document both sides in your app; Bare OS does not ship a second IPC stack inside the image for this—duplex **`bareOsIpc`** remains the in-session FIFO/RPC surface (see **`duplexJsonRoundTrip`** in [developer-guide/02-the-context-object.md](developer-guide/02-the-context-object.md)).
|
||||||
|
|
||||||
## Pear / `import.meta.url` (fixed in tree)
|
## Pear / `import.meta.url` (fixed in tree)
|
||||||
|
|
||||||
Under `pear run`, `import.meta.url` is `pear://…`. Node-style `fileURLToPath(import.meta.url)` goes through `bare-url` and throws `INVALID_URL_SCHEME`. This repo follows the same idea as [pear-rti](https://github.com/holepunchto/pear-rti) (`holepunchto_repos/pear-rti/index.js`): use `global.Pear.constructor.RTI.mount`, then `Pear.config.swapDir`, then `process.cwd()`.
|
Under `pear run`, `import.meta.url` is `pear://…`. Node-style `fileURLToPath(import.meta.url)` goes through `bare-url` and throws `INVALID_URL_SCHEME`. This repo follows the same idea as [pear-rti](https://github.com/holepunchto/pear-rti) (`holepunchto_repos/pear-rti/index.js`): use `global.Pear.constructor.RTI.mount`, then `Pear.config.swapDir`, then `process.cwd()`.
|
||||||
@@ -72,6 +79,8 @@ Kernel for Pear is vendored at `packages/bare-os-seeder/kernel/` (sync from repo
|
|||||||
|
|
||||||
Hoisted deps live at the repo root. For `pear stage` to include runtime modules, use a symlink `packages/bare-os-seeder/node_modules` → `../../node_modules` and a **real directory copy** of `packages/bare-os-protocol` at `node_modules/bare-os-protocol` (workspace symlinks outside the package are not bundled correctly). Re-stage after dependency or path helper changes.
|
Hoisted deps live at the repo root. For `pear stage` to include runtime modules, use a symlink `packages/bare-os-seeder/node_modules` → `../../node_modules` and a **real directory copy** of `packages/bare-os-protocol` at `node_modules/bare-os-protocol` (workspace symlinks outside the package are not bundled correctly). Re-stage after dependency or path helper changes.
|
||||||
|
|
||||||
|
**Bare/Pear runtime:** seeder sources must not import **`node:…`** specifiers (e.g. **`node:child_process`** is unresolved in Bare). Use **`bare-subprocess`** for sync spawn (see [`packages/bare-os-seeder/lib/pear-multisig-hint.js`](packages/bare-os-seeder/lib/pear-multisig-hint.js) and **`BARE_OS_HYPER_MULTISIG_VERIFY`**) and prefer **`import … from 'url'`** over **`node:url`** so `package.json` **`imports`** maps resolve to **`bare-url`**.
|
||||||
|
|
||||||
## `pear run` vs `pear release`
|
## `pear run` vs `pear release`
|
||||||
|
|
||||||
After `pear stage`, run `pear release <channel>` so `pear run pear://<key>` resolves to the new length.
|
After `pear stage`, run `pear release <channel>` so `pear run pear://<key>` resolves to the new length.
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ The following are set on `ctx` before the kernel starts (unless noted as overwri
|
|||||||
|
|
||||||
| Field | Role |
|
| Field | Role |
|
||||||
| -------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| -------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| **`bareOsCtxApiVersion`** | String semver for the documented **`ctx`** contract (e.g. **`1.7.1`**). Bump in [`bare-os-ctx-api.js`](../packages/bare-os-booter/lib/bare-os-ctx-api.js) when you make breaking changes to stable fields. |
|
| **`bareOsCtxApiVersion`** | String semver for the documented **`ctx`** contract (e.g. **`1.9.0`**). Bump in [`bare-os-ctx-api.js`](../packages/bare-os-booter/lib/bare-os-ctx-api.js) when you make breaking changes to stable fields. |
|
||||||
| **`bareOsRuntimeCaps`** | **Frozen** snapshot from [`bare-os-runtime-caps.js`](../packages/bare-os-booter/lib/bare-os-runtime-caps.js): **`ctxApiVersion`**, simulated **pipeline** limits, **`pseudoFsPaths`**, and **`features`** (including **`jobControl`**, **`shellHereString`**, **`bootReadyPseudoFs`**, **`vfsChown`**, **`auditLog`**, **`initdRequiresWants`**, **`seederRpcExtended`**, **`bareCtxModules`**, **`bareDriveBundles`**, …). |
|
| **`bareOsRuntimeCaps`** | **Frozen** snapshot from [`bare-os-runtime-caps.js`](../packages/bare-os-booter/lib/bare-os-runtime-caps.js): **`ctxApiVersion`**, simulated **pipeline** limits, **`pseudoFsPaths`**, and **`features`** (including **`jobControl`**, **`shellHereString`**, **`bootReadyPseudoFs`**, **`vfsChown`**, **`auditLog`**, **`initdRequiresWants`**, **`seederRpcExtended`**, **`bareCtxModules`**, **`bareDriveBundles`**, …). |
|
||||||
| **`bareOsPublishBootReady(patch)`** | Kernel-only: merge **`patch`** into the session boot-ready state exposed as **`/run/bare-os/boot.json`** and **`/run/bare-os/ready`**. The booter pre-seeds **`imageDigest`**, **`pearChannel`**, **`pearRelease`** from host env when set (see **`BARE_OS_IMAGE_DIGEST`**, **`BARE_OS_PEAR_CHANNEL`**, **`BARE_OS_PEAR_RELEASE`**, **`PEAR_CHANNEL`**). |
|
| **`bareOsPublishBootReady(patch)`** | Kernel-only: merge **`patch`** into the session boot-ready state exposed as **`/run/bare-os/boot.json`** and **`/run/bare-os/ready`**. The booter pre-seeds **`imageDigest`**, **`pearChannel`**, **`pearRelease`** from host env when set (see **`BARE_OS_IMAGE_DIGEST`**, **`BARE_OS_PEAR_CHANNEL`**, **`BARE_OS_PEAR_RELEASE`**, **`PEAR_CHANNEL`**). |
|
||||||
| **`bareOsSessionStats`** | Mutable **`{ execLineCount, pipelineBytesTotal }`** mirrored in **`/proc/bare_os_session_stats`**. |
|
| **`bareOsSessionStats`** | Mutable **`{ execLineCount, pipelineBytesTotal }`** mirrored in **`/proc/bare_os_session_stats`**. |
|
||||||
@@ -58,12 +58,17 @@ The following are set on `ctx` before the kernel starts (unless noted as overwri
|
|||||||
| **`bareOsRegisterBootPhaseHook(phase, fn)`** / **`bareOsInvokeBootPhaseHooks(ev)`** | Hooks around stock **`kernel/init.js`** phases; **`ev`** includes **`phase`**, **`when`** (`before` / `after`), **`label`**. **`phase`** may be `*` or `before:rc` style. |
|
| **`bareOsRegisterBootPhaseHook(phase, fn)`** / **`bareOsInvokeBootPhaseHooks(ev)`** | Hooks around stock **`kernel/init.js`** phases; **`ev`** includes **`phase`**, **`when`** (`before` / `after`), **`label`**. **`phase`** may be `*` or `before:rc` style. |
|
||||||
| **`bareOsInvalidateVirtualFile(name)`** / **`bareOsUpdateVirtualFileMeta(name, patch)`** | Virtual files under **`/run/bare-os/virtual/`**; **`bareOsRegisterVirtualFile`** accepts optional **`{ etag }`** third argument or **`{ read }`** object. |
|
| **`bareOsInvalidateVirtualFile(name)`** / **`bareOsUpdateVirtualFileMeta(name, patch)`** | Virtual files under **`/run/bare-os/virtual/`**; **`bareOsRegisterVirtualFile`** accepts optional **`{ etag }`** third argument or **`{ read }`** object. |
|
||||||
| **`bareOsRequestPearReload(opts?)`** | **`async`** — returns **`{ requested, hint, env }`**; with **`{ persistRequest: true }`** writes **`~/.bare-os/pear-reload.request`** and may **`process.emit('bare-os:pear-reload', …)`** on Node. |
|
| **`bareOsRequestPearReload(opts?)`** | **`async`** — returns **`{ requested, hint, env }`**; with **`{ persistRequest: true }`** writes **`~/.bare-os/pear-reload.request`** and may **`process.emit('bare-os:pear-reload', …)`** on Node. |
|
||||||
|
| **`bareOsVerifyBootManifestSignature(manifestBytes, signatureBytes, publicKeyHex?)`** | Ed25519 verify helper used when **`BARE_OS_BOOT_MANIFEST_SIGN=1`**; public key from arg or **`BARE_OS_BOOT_MANIFEST_PUBKEY_HEX`**. |
|
||||||
|
| **`bareOsRequestMirror(opts?)`** / **`bareOsExportPersonalSnapshot(opts?)`** | **`async`** host bridges returning **`{ ok, hint }`**; on Node emit **`bare-os:mirror-request`** / **`bare-os:export-personal-snapshot`**. |
|
||||||
|
| **`bareOsPearIpcEmit(channel, payload)`** | **`boolean`** — forwards structured **`payload`** to the host when registered (**`bare-os:pear-ipc`** on Node). Align channel names with your **[pear-ipc](https://github.com/holepunchto/pear-ipc)** consumer. |
|
||||||
| **`bareOsHostStats`** _(optional)_ | When the **`bare-os`** npm module loads on the host, a **frozen** snapshot: **`hostname`**, **`loadavg`**, **`cpus`**, **`networkInterfaces`**. |
|
| **`bareOsHostStats`** _(optional)_ | When the **`bare-os`** npm module loads on the host, a **frozen** snapshot: **`hostname`**, **`loadavg`**, **`cpus`**, **`networkInterfaces`**. |
|
||||||
| **`httpFetch`** _(optional)_ | When Node/global **`fetch`** exists, the booter sets a **`fetch`** compatible function with optional **HTTP allow/deny** policy (**`BARE_OS_HTTP_ALLOWLIST`**, **`BARE_OS_HTTP_DENYLIST`**) and audit hooks when **`BARE_OS_AUDIT`** is on. |
|
| **`httpFetch`** _(optional)_ | When Node/global **`fetch`** exists, the booter sets a **`fetch`** compatible function with optional **HTTP allow/deny** policy (**`BARE_OS_HTTP_ALLOWLIST`**, **`BARE_OS_HTTP_DENYLIST`**) and audit hooks when **`BARE_OS_AUDIT`** is on. |
|
||||||
|
|
||||||
Kernel boot composition lives on the **system image** (`/boot/init.js`, `/etc/bare-os/rc`, `/etc/bare-os/rc.d/`, optional **`/etc/bare-os/rc.local`**, optional **`/etc/bare-os/kernel.d/`** (same digit-prefix rules as **`rc.d`**), optional **`/etc/bare-os/profile`** / **`rc.profile.*`**, **`/etc/bare-os/onboot`**), not on `ctx`—extend the image or hooks like **`registerKernelShutdownHook`** rather than adding boot fields to the context object. The booter seeds **`ctx.env`** from the host for **`BARE_OS_PIPELINE_*`**, **`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_TRACE`**, **`BARE_OS_KERNEL_SELFTEST`**, **`BARE_OS_SELFTEST_FORMAT`**, **`BARE_OS_AUDIT`**, **`BARE_OS_AUDIT_JSON`**, **`BARE_OS_AUDIT_REDACT`**, **`BARE_OS_BOOT_ALLOWLIST`**, **`BARE_OS_EXEC_MAX_DEPTH`**, **`BARE_OS_IPC_MAX_BYTES`**, **`BARE_OS_IPC_RPC_TOKEN`**, **`BARE_OS_IPC_FANOUT`** (set to **`0`** to disable fan-out), **`BARE_OS_IPC_JSON_MAX_BYTES`**, **`BARE_OS_HTTP_ALLOWLIST`**, **`BARE_OS_HTTP_DENYLIST`**, **`BARE_OS_TLS_PIN_SHA256`** (comma-separated SHA-256 hints for delegated TLS; host **`httpFetch`** may honor **`init.bareOsCurlTls.pinnedSha256`**), **`BARE_OS_VFS_WATCH`**, **`BARE_OS_IMAGE_DIGEST`**, **`BARE_OS_PEAR_CHANNEL`**, **`BARE_OS_PEAR_RELEASE`**, **`BARE_OS_BARE_MODULES`**, **`BARE_OS_BARE_DRIVE_BUNDLES`**, **`BARE_OS_FIND_EXEC_MAX`**, **`BARE_OS_YES_MAX_LINES`**, **`BARE_OS_SHUF_MAX_LINES`**, **`BARE_OS_SPLIT_MAX_FILES`**, **`BARE_OS_NPROC`**, **`TERM`**, **`COLORTERM`**, and **`PEAR_CHANNEL`** when set; always sets **`BARE_OS_BOOT_PROFILE_RESOLVED`** and **`BARE_OS_SESSION_ID`**. When **`ctx.httpFetch`** handles **`curl`**, check optional **`init.bareOsCurlTls`** (`insecure`, **`caPem`**, **`pinnedSha256`**) for **`--cacert`** / **`-k`** semantics.
|
Kernel boot composition lives on the **system image** (`/boot/init.js`, `/etc/bare-os/rc`, `/etc/bare-os/rc.d/`, optional **`/etc/bare-os/rc.local`**, optional **`/etc/bare-os/kernel.d/`** (same digit-prefix rules as **`rc.d`**), optional **`/etc/bare-os/profile`** / **`rc.profile.*`**, **`/etc/bare-os/onboot`**), not on `ctx`—extend the image or hooks like **`registerKernelShutdownHook`** rather than adding boot fields to the context object. The booter seeds **`ctx.env`** from the host for **`BARE_OS_PIPELINE_*`**, boot profile / audit / IPC / HTTP policy keys, **`BARE_OS_VFS_WATCH`**, **`BARE_OS_VFS_UNION_PREFIXES`**, **`BARE_OS_VFS_BIN_CACHE`**, **`BARE_OS_IMAGE_DIGEST`**, Pear channel fields, **`BARE_OS_BOOT_MANIFEST`**, **`BARE_OS_BOOT_MANIFEST_SIGN`**, **`BARE_OS_BOOT_MANIFEST_PUBKEY_HEX`**, **`BARE_OS_SANDBOX_SCRIPT`**, **`BARE_OS_SANDBOX_WORKER`**, **`BARE_OS_INITD_MAX_PARALLEL`**, **`BARE_OS_URANDOM_CRYPTO`**, **`BARE_OS_BLIND_BOOTSTRAP_URL`**, **`BARE_OS_BLIND_BOOTSTRAP_JSON`**, **`BARE_OS_MIRROR_READ_KEY`**, **`BARE_OS_FIND_EXEC_MAX`**, **`BARE_OS_YES_MAX_LINES`**, **`BARE_OS_SHUF_MAX_LINES`**, **`BARE_OS_SPLIT_MAX_FILES`**, **`BARE_OS_NPROC`**, **`TERM`**, **`COLORTERM`**, **`PEAR_CHANNEL`**, and the rest of the passthrough table in [environment appendix §14](../docs/reference/environment-and-posix-appendix.md#14-environment-variables-complete-list); always sets **`BARE_OS_BOOT_PROFILE_RESOLVED`** and **`BARE_OS_SESSION_ID`**. When **`ctx.httpFetch`** handles **`curl`**, check optional **`init.bareOsCurlTls`** (`insecure`, **`caPem`**, **`pinnedSha256`**) for **`--cacert`** / **`-k`** semantics.
|
||||||
|
|
||||||
After [`createVfs`](../packages/bare-os-booter/lib/vfs.js), **`ctx.vfs.watch(logicalPath)`** returns a Hyperdrive watcher when **`BARE_OS_VFS_WATCH`** is not disabled. **`ctx.bareOsIpc`** exposes FIFO **`push`/`take`**, optional JSON-RPC **`pushJson`/`takeJson`** (max line size, optional RPC token), **`fanoutPublish`/`fanoutSubscribe`**, and **`stats`** (see [`bare-os-ipc.js`](../packages/bare-os-booter/lib/bare-os-ipc.js)).
|
After [`createVfs`](../packages/bare-os-booter/lib/vfs.js), **`ctx.vfs.watch(logicalPath)`** returns a Hyperdrive watcher when **`BARE_OS_VFS_WATCH`** is not disabled. **`ctx.bareOsIpc`** exposes FIFO **`push`/`take`**, optional JSON-RPC **`pushJson`/`takeJson`** (max line size, optional RPC token), **`fanoutPublish`/`fanoutSubscribe`**, **`createDuplexBridge`**, **`duplexJsonRoundTrip`** (one JSON request / one JSON reply over a duplex side — useful for unit-to-unit or guest–helper protocols without pulling **`bare-rpc`** into **`/bin`**), and **`stats`** (see [`bare-os-ipc.js`](../packages/bare-os-booter/lib/bare-os-ipc.js)).
|
||||||
|
|
||||||
|
**Initd / long-running services:** prefer **`duplexJsonRoundTrip`** or **`pushJson`/`takeJson`** for structured messages with byte limits already enforced by IPC options. A dedicated **`bare-rpc`** dependency is optional on the host or in **`ctx.bare`** if you need richer framing; the stock image documents the FIFO-level building blocks only.
|
||||||
|
|
||||||
After [`createKernelReplSession`](../packages/bare-os-booter/lib/repl-session.js) returns:
|
After [`createKernelReplSession`](../packages/bare-os-booter/lib/repl-session.js) returns:
|
||||||
|
|
||||||
|
|||||||
@@ -12,13 +12,15 @@ The **system** Hyperdrive is the **OS image**. You normally obtain it by **repli
|
|||||||
|
|
||||||
**Boot snippet tightening:** with **`BARE_OS_BOOT_ALLOWLIST=1`**, the stock kernel only runs lines from trusted **`rc`**, **`rc.d`**, **`rc.local`**, **`kernel.d`**, and **`onboot`** whose first shell token appears in **`/etc/bare-os/boot.allow`** (plus builtins such as **`export`**, **`cd`**, **`:`**). Pair with **`BARE_OS_BOOT_STRICT=1`** to exit the session on the first disallowed or failing line.
|
**Boot snippet tightening:** with **`BARE_OS_BOOT_ALLOWLIST=1`**, the stock kernel only runs lines from trusted **`rc`**, **`rc.d`**, **`rc.local`**, **`kernel.d`**, and **`onboot`** whose first shell token appears in **`/etc/bare-os/boot.allow`** (plus builtins such as **`export`**, **`cd`**, **`:`**). Pair with **`BARE_OS_BOOT_STRICT=1`** to exit the session on the first disallowed or failing line.
|
||||||
|
|
||||||
|
**Boot manifest integrity:** **`BARE_OS_BOOT_MANIFEST=1`** makes the stock kernel compare **`/etc/bare-os/boot.manifest.json`** to an expected digest (**`ctx.bareOsBootFileSha256Hex`**). **`BARE_OS_BOOT_MANIFEST_SIGN=1`** adds **Ed25519** verification of the **raw manifest bytes** against **`/etc/bare-os/boot.manifest.sig`**, using **`ctx.bareOsVerifyBootManifestSignature`** and **`BARE_OS_BOOT_MANIFEST_PUBKEY_HEX`** (64 hex chars). Rotation is operational: ship a new public key in host env and a matching signature file on the system image. This does **not** replace image trust—you still choose peers and seed sources carefully.
|
||||||
|
|
||||||
**Audit:** **`BARE_OS_AUDIT=1`** logs **`execLine`** activity to **`/var/log/bare-os/audit.log`**. **`BARE_OS_AUDIT_JSON=1`** uses JSON lines; **`BARE_OS_AUDIT_REDACT=1`** (or **`true`**) masks common secret-like **`VAR=value`** patterns; **`BARE_OS_AUDIT_REDACT=TOKEN,PASSWORD`** masks named keys.
|
**Audit:** **`BARE_OS_AUDIT=1`** logs **`execLine`** activity to **`/var/log/bare-os/audit.log`**. **`BARE_OS_AUDIT_JSON=1`** uses JSON lines; **`BARE_OS_AUDIT_REDACT=1`** (or **`true`**) masks common secret-like **`VAR=value`** patterns; **`BARE_OS_AUDIT_REDACT=TOKEN,PASSWORD`** masks named keys.
|
||||||
|
|
||||||
**Delegated HTTP:** when the booter sets **`ctx.httpFetch`** from the host **`fetch`**, **`BARE_OS_HTTP_ALLOWLIST`** and **`BARE_OS_HTTP_DENYLIST`** restrict **`http`/`https`** URLs for **`curl`/`wget`** (host-pattern globs). Failed checks throw before the request; with audit on, allow/deny outcomes can be logged.
|
**Delegated HTTP:** when the booter sets **`ctx.httpFetch`** from the host **`fetch`**, **`BARE_OS_HTTP_ALLOWLIST`** and **`BARE_OS_HTTP_DENYLIST`** restrict **`http`/`https`** URLs for **`curl`/`wget`** (host-pattern globs). Failed checks throw before the request; with audit on, allow/deny outcomes can be logged.
|
||||||
|
|
||||||
**IPC JSON-RPC:** when **`BARE_OS_IPC_RPC_TOKEN`** is set, **`pushJson`** payloads must include matching **`bareOsIpcToken`** or the push throws. Line size is capped (**`BARE_OS_IPC_JSON_MAX_BYTES`**, default 256 KiB).
|
**IPC JSON-RPC:** when **`BARE_OS_IPC_RPC_TOKEN`** is set, **`pushJson`** payloads must include matching **`bareOsIpcToken`** or the push throws. Line size is capped (**`BARE_OS_IPC_JSON_MAX_BYTES`**, default 256 KiB).
|
||||||
|
|
||||||
**Sandboxed scripts:** **`ctx.bareOsSandboxRunScript(source, argv?, opts?)`** runs in-image JS with a **restricted `ctx`**: writes are limited to the personal namespace (same routing rules as `isPersonalRoute`), and identity / vault / virtual-file registration hooks are disabled. Disable entirely with **`BARE_OS_SANDBOX_SCRIPT=0`**. This is still **not** a hardware isolate—treat it as a trust reducer, not a security boundary.
|
**Sandboxed scripts:** **`ctx.bareOsSandboxRunScript(source, argv?, opts?)`** runs in-image JS with a **restricted `ctx`**: writes are limited to the personal namespace (same routing rules as `isPersonalRoute`), and identity / vault / virtual-file registration hooks are disabled. Disable entirely with **`BARE_OS_SANDBOX_SCRIPT=0`**. **`BARE_OS_SANDBOX_WORKER=1`** records intent for a future worker-style isolate; the default path still runs **inline** in the session. This is still **not** a hardware isolate—treat it as a trust reducer, not a security boundary.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,27 @@ Former **DOCUMENTATION.md** §14, §14a. [Reference index →](README.md)
|
|||||||
| `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): `BARE_OS_PIPELINE_MAX_STAGES`, `BARE_OS_PIPELINE_MAX_BYTES`, `BARE_OS_PIPELINE_MAX_LINES`, `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_TRACE`, `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_IPC_MAX_BYTES`, `BARE_OS_VFS_WATCH`, `BARE_OS_BOOT_ALLOWLIST`, `BARE_OS_PEAR_CHANNEL`, `BARE_OS_PEAR_RELEASE`, `PEAR_CHANNEL`, **`BARE_OS_FIND_EXEC_MAX`** (cap for **`find -exec`/`-ok`**), **`BARE_OS_YES_MAX_LINES`**, **`BARE_OS_SHUF_MAX_LINES`**, **`BARE_OS_SPLIT_MAX_FILES`**, **`BARE_OS_NPROC`** (override for **`/bin/nproc`**), **`TERM`**, **`COLORTERM`** (terminal capability hints for colorized tools).
|
**Host → session passthrough** (booter copies into **`shellEnv`** when the host sets a non-empty value): `BARE_OS_PIPELINE_MAX_STAGES`, `BARE_OS_PIPELINE_MAX_BYTES`, `BARE_OS_PIPELINE_MAX_LINES`, `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_TRACE`, `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_IPC_MAX_BYTES`, `BARE_OS_IPC_RPC_TOKEN`, `BARE_OS_IPC_FANOUT`, `BARE_OS_IPC_JSON_MAX_BYTES`, `BARE_OS_VFS_WATCH`, `BARE_OS_VFS_UNION_PREFIXES`, `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_SANDBOX_SCRIPT`, `BARE_OS_SANDBOX_WORKER`, `BARE_OS_INITD_MAX_PARALLEL`, `BARE_OS_URANDOM_CRYPTO`, `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_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`, **`TERM`**, **`COLORTERM`**.
|
||||||
|
|
||||||
|
| Variable | Used by | Meaning |
|
||||||
|
| -------- | ------- | ------- |
|
||||||
|
| `BARE_OS_VFS_UNION_PREFIXES` | Booter / VFS | Comma-separated logical path prefixes where **`readFile`** may union system + overlay sources. |
|
||||||
|
| `BARE_OS_VFS_BIN_CACHE` | VFS | When **`1`** / **`true`**, LRU read cache for **`/bin`**; invalidated on **`vfs.watch`** when enabled. |
|
||||||
|
| `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_PUBKEY_HEX` | Booter / kernel | 64 hex chars — public key for signed boot manifest verification. |
|
||||||
|
| `BARE_OS_SANDBOX_SCRIPT` | `ctx.bareOsSandboxRunScript` | Set **`0`** / **`false`** to disable sandbox script helper. |
|
||||||
|
| `BARE_OS_SANDBOX_WORKER` | Booter | When **`1`**, logs intent to use worker-style isolation when a host entrypoint exists (inline fallback today). |
|
||||||
|
| `BARE_OS_INITD_MAX_PARALLEL` | bare-initd | Max concurrent unit **starts** per DAG level (integer ≥ **1**; default **1**). |
|
||||||
|
| `BARE_OS_URANDOM_CRYPTO` | VFS **`/dev/urandom`** | Set **`0`** to use non-crypto PRNG for synthetic urandom (default prefers crypto where available). |
|
||||||
|
| `BARE_OS_BLIND_BOOTSTRAP_URL` | `/proc/bare_os_bootstrap` | Non-empty URL string echoed into the proc JSON for host/operator wiring (guest does not fetch it). |
|
||||||
|
| `BARE_OS_BLIND_BOOTSTRAP_JSON` | `/proc/bare_os_bootstrap` | Inline JSON string parsed and exposed under **`blindBootstrap`** (invalid JSON yields an error object in the proc file). |
|
||||||
|
| `BARE_OS_MIRROR_READ_KEY` | Replication proc / mirror hint | Optional mirror drive key string included in replication snapshot for operators. |
|
||||||
|
| `BARE_OS_SHELL_LOOP_MAX` | Shell | Max iterations for **`while`** / **`for`** (default **10000**). |
|
||||||
|
| `BARE_OS_SHELL_CASE_MAX_BRANCHES` | Shell | Max **`case`** branches (default **32**). |
|
||||||
|
| `BARE_OS_GREP_FILTER_MAX` | **`grep -r`** | Max **`--include` / `--exclude` / `--exclude-dir`** glob patterns (default **32**). |
|
||||||
|
| `BARE_OS_SED_NULL_MAX_RECORDS` | **`sed -z`** | Max NUL-separated records per run (default **100000**). |
|
||||||
|
| `BARE_OS_HYPER_MULTISIG_VERIFY` | Seeder | When set, runs **`hyper-multisig verify`** via **[`bare-subprocess`](https://github.com/holepunchto/bare-subprocess)** under Pear/Bare (dependency of **`bare-os-seeder`**), or **`child_process`** on Node. Requires **`hyper-multisig`** on **`PATH`**. |
|
||||||
|
|
||||||
**Session env (set by booter, not user configuration):** `USER`, `LOGNAME`, `HOME`, `PWD`, `UID`, `GID`, `GROUP`, `BARE_OS_IDENTITY` (`guest` or `unlocked`), `BARE_OS_CTX_API_VERSION`, `BARE_OS_SESSION_ID`, `BARE_OS_BOOT_PROFILE_RESOLVED`, and when unlocked `BARE_OS_PUBLIC_KEY` (hex Ed25519 public key).
|
**Session env (set by booter, not user configuration):** `USER`, `LOGNAME`, `HOME`, `PWD`, `UID`, `GID`, `GROUP`, `BARE_OS_IDENTITY` (`guest` or `unlocked`), `BARE_OS_CTX_API_VERSION`, `BARE_OS_SESSION_ID`, `BARE_OS_BOOT_PROFILE_RESOLVED`, and when unlocked `BARE_OS_PUBLIC_KEY` (hex Ed25519 public key).
|
||||||
|
|
||||||
@@ -40,9 +60,9 @@ Former **DOCUMENTATION.md** §14, §14a. [Reference index →](README.md)
|
|||||||
| Area | Status |
|
| Area | Status |
|
||||||
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| **VFS** | Two-drive unified paths; **`$HOME`** maps to the personal Hyperdrive; writable mounts under **`/mnt`** when HDMS allows. **`mkdir`/`rmdir`**, **`chmod`** (octal + symbolic subset), **`symlink`/`readlink`**, **`stat`/`lstat`**, **`rm`** recursive, **`watch()`** on Hyperdrive paths (optional host **`BARE_OS_VFS_WATCH=0`** to disable). Synthetic **`/proc`**, **`/sys`**, **`/run`**, **`/dev`** for introspection (quotas JSON, boot JSON, initd snapshot, etc.). Empty dirs use **`.bareos_empty`** (same idea as `git-fs-adapter`). |
|
| **VFS** | Two-drive unified paths; **`$HOME`** maps to the personal Hyperdrive; writable mounts under **`/mnt`** when HDMS allows. **`mkdir`/`rmdir`**, **`chmod`** (octal + symbolic subset), **`symlink`/`readlink`**, **`stat`/`lstat`**, **`rm`** recursive, **`watch()`** on Hyperdrive paths (optional host **`BARE_OS_VFS_WATCH=0`** to disable). Synthetic **`/proc`**, **`/sys`**, **`/run`**, **`/dev`** for introspection (quotas JSON, boot JSON, initd snapshot, etc.). Empty dirs use **`.bareos_empty`** (same idea as `git-fs-adapter`). |
|
||||||
| **Shell** | Pipelines (pipe between commands), list separator **`;`**, short-circuit logical-**AND** / logical-**OR** between commands, redirects **`>`** / **`>>`** / **`<`**, quoting, **`$VAR`** / **`${VAR}`**, **`$?`** / **`${?}`** (from **`BARE_OS_EXIT_STATUS`**), builtins: **`alias`**, **`unalias`**, **`cd`**, **`export`**, **`unset`**, **`readonly`**, **`umask`**, **`:`**, **`command`**, **`type`**, **`login`**, **`logout`**, **`exit`**. Branching uses **`ctx.exitCode`**. Bounded pipeline capture; optional boot-snippet allowlist (**`BARE_OS_BOOT_ALLOWLIST`** + **`/etc/bare-os/boot.allow`**). No full POSIX **`sh`** grammar. |
|
| **Shell** | Pipelines, **`;`**, **`&&`** / **`||`**, redirects, quoting, **`$VAR`**, **`$?`**, builtins including bounded **`if`**, **`while`**, **`for`**, **`case`** (caps **`BARE_OS_SHELL_LOOP_MAX`**, **`BARE_OS_SHELL_CASE_MAX_BRANCHES`**). Branching uses **`ctx.exitCode`**. Bounded pipeline capture (**`BARE_OS_PIPELINE_*`**, **`ctx.bareOsRuntimeCaps`**); optional boot-snippet allowlist (**`BARE_OS_BOOT_ALLOWLIST`** + **`/etc/bare-os/boot.allow`**). No subshells or full POSIX **`sh`**. |
|
||||||
| **Ownership** | Display and permission checks use **`UID`/`GID`** and mode bits; **`chown`/`chgrp`** update **`metadata.bareOs`** on the **personal** writable tree (not a multi-user host kernel). |
|
| **Ownership** | Display and permission checks use **`UID`/`GID`** and mode bits; **`chown`/`chgrp`** update **`metadata.bareOs`** on the **personal** writable tree (not a multi-user host kernel). |
|
||||||
| **Utilities** | Tier-1 JS **`/bin`** (**~111** commands; see [§12.10](package-bare-os-coreutils-and-ci.md#1210-package-bare-os-coreutils)): text tools include **`paste`**, **`split`**, **`tac`**, **`rev`**, **`expand`**, **`unexpand`**, **`fold`**, **`fmt`**, **`comm`**, **`join`**, **`pr`**, **`yes`** (line-capped via **`BARE_OS_YES_MAX_LINES`** / **`getconf`**), **`shuf`** (capped via **`BARE_OS_SHUF_MAX_LINES`**), **`tsort`**, **`factor`**, **`expr`** (integer-focused subset), **`numfmt`** (**`--to=iec`** / **`--to=si`**). Checksums: **`md5sum`** (bundled MD5), **`sha1sum`**, **`sha256sum`**, **`sha512sum`** (Web Crypto where available), **`sum`**, **`base32`**, **`basenc`** (**`--base16`**). Files: **`truncate`**, **`unlink`**, **`install`**, **`df`** (synthetic Hyperdrive row; **`-h`** human sizes), **`sync`** (no-op). Session stubs: **`arch`**, **`groups`**, **`hostid`**, **`nproc`**, **`uptime`**, **`users`**, **`who`**. Plus earlier parity: **`man`**, **`sed`**, **`awk`**, **`cp`** (**`-u`/`-v`/`-p`**), **`mv`**, **`find`** (**`-regex`**, **`-exec`/`-ok`**, **`BARE_OS_FIND_EXEC_MAX`**), **`mktemp`**, **`git-pear`**, **`cksum`**, **`getconf`** (includes pipeline / cap names + **`-a`**), **`xargs`**, **`dircolors`**, **`theme`**, **`ls`**, **`uniq`**, **`realpath`**, **`base64`**, **`rm`** **`-d`**, **`stat`** **`%F`**. **`dir`** / **`vdir`** call **`ls`**. Large **`sed`/`awk`** are not byte-identical to GNU on all inputs. **`mkfifo`** → **`/run/bare-os/ipc/`**. Online help: **`/share/man/man.json`** and **`man`**. |
|
| **Utilities** | Tier-1 JS **`/bin`** (**~111** commands; see [§12.10](package-bare-os-coreutils-and-ci.md#1210-package-bare-os-coreutils)): text tools include **`paste`**, **`split`**, **`tac`**, **`rev`**, **`expand`**, **`unexpand`**, **`fold`**, **`fmt`**, **`comm`**, **`join`**, **`pr`**, **`yes`** (line-capped via **`BARE_OS_YES_MAX_LINES`** / **`getconf`**), **`shuf`** (capped via **`BARE_OS_SHUF_MAX_LINES`**), **`tsort`**, **`factor`**, **`expr`** (integer-focused subset), **`numfmt`** (**`--to=iec`** / **`--to=si`**). Checksums: **`md5sum`** (bundled MD5), **`sha1sum`**, **`sha256sum`**, **`sha512sum`** (Web Crypto where available), **`sum`**, **`base32`**, **`basenc`** (**`--base16`**). Files: **`truncate`**, **`unlink`**, **`install`**, **`df`** (synthetic Hyperdrive row; **`-h`** human sizes), **`sync`** (no-op). Session stubs: **`arch`**, **`groups`**, **`hostid`**, **`nproc`**, **`uptime`**, **`users`**, **`who`**. Plus earlier parity: **`man`**, **`sed`** (including **`-z`**, **`BARE_OS_SED_NULL_MAX_RECORDS`**), **`awk`**, **`grep`** (**`-r`** with **`--include`/`--exclude`/`--exclude-dir`**, **`BARE_OS_GREP_FILTER_MAX`**), **`cp`** (**`-u`/`-v`/`-p`**), **`mv`**, **`find`** (**`-regex`**, **`-exec`/`-ok`**, **`BARE_OS_FIND_EXEC_MAX`**), **`mktemp`**, **`git-pear`**, **`cksum`**, **`getconf`** (includes pipeline / cap names + **`-a`**), **`xargs`**, **`dircolors`**, **`theme`**, **`ls`**, **`uniq`**, **`realpath`**, **`base64`**, **`rm`** **`-d`**, **`stat`** **`%F`**. **`dir`** / **`vdir`** call **`ls`**. Large **`sed`/`awk`** are not byte-identical to GNU on all inputs. **`mkfifo`** → **`/run/bare-os/ipc/`**. Online help: **`/share/man/man.json`** and **`man`**. |
|
||||||
|
|
||||||
**Handbook:** [handbook/09-posix-utilities-shell-and-vfs.md](../../handbook/09-posix-utilities-shell-and-vfs.md) — narrative catalog, engine notes, and Issue 7 alignment. **Manual pages:** [handbook/10-manpages-and-online-help.md](../../handbook/10-manpages-and-online-help.md).
|
**Handbook:** [handbook/09-posix-utilities-shell-and-vfs.md](../../handbook/09-posix-utilities-shell-and-vfs.md) — narrative catalog, engine notes, and Issue 7 alignment. **Manual pages:** [handbook/10-manpages-and-online-help.md](../../handbook/10-manpages-and-online-help.md).
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ Non-TTY mode skips splash noise; automation uses **`BARE_OS_SKIP_REPL=1`**.
|
|||||||
|
|
||||||
After the **system** and **personal** drives exist:
|
After the **system** and **personal** drives exist:
|
||||||
|
|
||||||
1. Build **`shellEnv`** (guest defaults: `HOME`, `PATH`, `USER`, …, **`BARE_OS_CTX_API_VERSION`**), copying host keys when set — including **`BARE_OS_PIPELINE_*`**, boot and audit toggles (**`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_TRACE`**, **`BARE_OS_KERNEL_SELFTEST`**, **`BARE_OS_SELFTEST_FORMAT`**, **`BARE_OS_AUDIT`**, **`BARE_OS_AUDIT_JSON`**, **`BARE_OS_AUDIT_REDACT`**, **`BARE_OS_BOOT_ALLOWLIST`**, **`BARE_OS_EXEC_MAX_DEPTH`**, **`BARE_OS_IPC_MAX_BYTES`**, **`BARE_OS_IPC_RPC_TOKEN`**, **`BARE_OS_IPC_FANOUT`**, **`BARE_OS_IPC_JSON_MAX_BYTES`**, **`BARE_OS_HTTP_ALLOWLIST`**, **`BARE_OS_HTTP_DENYLIST`**, **`BARE_OS_TLS_PIN_SHA256`**, **`BARE_OS_VFS_WATCH`**, **`BARE_OS_IMAGE_DIGEST`**, **`BARE_OS_PEAR_CHANNEL`**, **`BARE_OS_PEAR_RELEASE`**, **`BARE_OS_BARE_MODULES`**, **`BARE_OS_BARE_DRIVE_BUNDLES`**, **`PEAR_CHANNEL`**) — see [Chapter 7](07-operations-and-development.md) and the [context object developer guide](../developer-guide/02-the-context-object.md). Set **`BARE_OS_BOOT_PROFILE_RESOLVED`** from the host override or the first line of **`/etc/bare-os/profile`** on the system drive; assign **`BARE_OS_SESSION_ID`** for **`/run/bare-os/session`**. Seed **`/run/bare-os/boot.json`** fields **`imageDigest`**, **`pearChannel`**, **`pearRelease`**, and accumulate **`booterPhases`** (**`vfs`**, **`ctx`**, **`repl`**, **`initd`**, **`kernel_invoke`**) for observability alongside kernel **`phases`**.
|
1. Build **`shellEnv`** (guest defaults: `HOME`, `PATH`, `USER`, …, **`BARE_OS_CTX_API_VERSION`**), copying host keys when set — including **`BARE_OS_PIPELINE_*`**, boot and audit toggles (**`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_TRACE`**, **`BARE_OS_KERNEL_SELFTEST`**, **`BARE_OS_SELFTEST_FORMAT`**, **`BARE_OS_AUDIT`**, **`BARE_OS_AUDIT_JSON`**, **`BARE_OS_AUDIT_REDACT`**, **`BARE_OS_BOOT_ALLOWLIST`**, **`BARE_OS_EXEC_MAX_DEPTH`**, **`BARE_OS_IPC_*`**, **`BARE_OS_HTTP_ALLOWLIST`**, **`BARE_OS_HTTP_DENYLIST`**, **`BARE_OS_TLS_PIN_SHA256`**, **`BARE_OS_VFS_WATCH`**, **`BARE_OS_VFS_UNION_PREFIXES`**, **`BARE_OS_VFS_BIN_CACHE`**, **`BARE_OS_IMAGE_DIGEST`**, **`BARE_OS_PEAR_CHANNEL`**, **`BARE_OS_PEAR_RELEASE`**, **`BARE_OS_BARE_MODULES`**, **`BARE_OS_BARE_DRIVE_BUNDLES`**, **`BARE_OS_BOOT_MANIFEST`**, **`BARE_OS_BOOT_MANIFEST_SIGN`**, **`BARE_OS_BOOT_MANIFEST_PUBKEY_HEX`**, **`BARE_OS_SANDBOX_SCRIPT`**, **`BARE_OS_SANDBOX_WORKER`**, **`BARE_OS_INITD_MAX_PARALLEL`**, **`BARE_OS_URANDOM_CRYPTO`**, **`BARE_OS_BLIND_BOOTSTRAP_URL`**, **`BARE_OS_BLIND_BOOTSTRAP_JSON`**, **`BARE_OS_MIRROR_READ_KEY`**, **`PEAR_CHANNEL`**) — see [Chapter 7](07-operations-and-development.md), the [environment appendix](../docs/reference/environment-and-posix-appendix.md#14-environment-variables-complete-list), and the [context object developer guide](../developer-guide/02-the-context-object.md). Set **`BARE_OS_BOOT_PROFILE_RESOLVED`** from the host override or the first line of **`/etc/bare-os/profile`** on the system drive; assign **`BARE_OS_SESSION_ID`** for **`/run/bare-os/session`**. Seed **`/run/bare-os/boot.json`** fields **`imageDigest`**, **`pearChannel`**, **`pearRelease`**, and accumulate **`booterPhases`** (**`vfs`**, **`ctx`**, **`repl`**, **`initd`**, **`kernel_invoke`**) for observability alongside kernel **`phases`**.
|
||||||
2. **`createBareOsIpc({ maxFifoBytes, ipcRpcToken?, enableFanout?, maxJsonRpcLineBytes? })`** — in-memory FIFOs under **`/run/bare-os/ipc/<name>`**; **`pushJson`/`takeJson`** with optional shared-secret field **`bareOsIpcToken`** when **`BARE_OS_IPC_RPC_TOKEN`** is set; **fan-out** topics **`fanoutPublish`/`fanoutSubscribe`** (disable with **`BARE_OS_IPC_FANOUT=0`**); **`stats`** includes fan-out subscriber counts.
|
2. **`createBareOsIpc({ maxFifoBytes, ipcRpcToken?, enableFanout?, maxJsonRpcLineBytes? })`** — in-memory FIFOs under **`/run/bare-os/ipc/<name>`**; **`pushJson`/`takeJson`** with optional shared-secret field **`bareOsIpcToken`** when **`BARE_OS_IPC_RPC_TOKEN`** is set; **fan-out** topics **`fanoutPublish`/`fanoutSubscribe`** (disable with **`BARE_OS_IPC_FANOUT=0`**); **`createDuplexBridge(baseName)`** pairs two **`push`/`take`** sides for bidirectional byte streams; **`duplexJsonRoundTrip(side, request)`** sends one JSON object and awaits one JSON reply (bounded); **`stats`** includes fan-out subscriber counts.
|
||||||
3. **`createVfs(drive, personalDrive, shellEnv, mntRef, vfsOptions?)`** — the two-drive router; optional **`vfsOptions`** supply **`procSnapshot`** (**`/proc/version`**, **`cmdline`**), dynamic text for **`/proc/net/dev`**, **`/proc/diskstats`**, **`/proc/bare_os_quotas`**, **`/proc/bare_os_resources`**, **`/proc/bare_os_features`**, **`/sys/class/net/lo`**, **`bootProfileText`**, **`sessionText`**, **`initdRunText`**, **`bootReadyJsonText`**, **`getVirtualReaders`** for **`/run/bare-os/virtual/<name>`**, etc.
|
3. **`createVfs(drive, personalDrive, shellEnv, mntRef, vfsOptions?)`** — the two-drive router; optional **`vfsOptions`** supply **`procSnapshot`** (**`/proc/version`**, **`cmdline`**), dynamic text for **`/proc/net/dev`**, **`/proc/diskstats`**, **`/proc/bare_os_quotas`**, **`/proc/bare_os_resources`**, **`/proc/bare_os_features`**, **`/sys/class/net/lo`**, **`bootProfileText`**, **`sessionText`**, **`initdRunText`**, **`bootReadyJsonText`**, **`getVirtualReaders`** for **`/run/bare-os/virtual/<name>`**, etc.
|
||||||
4. After VFS: **`buildBareCtxObjectFromHost`** + **`maybeMergeBareFromDrive`** fill a **`bareLibrary`** map (host imports first; drive bundles only add **missing** keys). **`/lib/bare/manifest.json`** and **`/lib/bare/bundles/*.js`** are **trusted** like **`/bin`**. See [developer guide ch.12](../developer-guide/12-bare-modules-and-pear-ecosystem.md).
|
4. After VFS: **`buildBareCtxObjectFromHost`** + **`maybeMergeBareFromDrive`** fill a **`bareLibrary`** map (host imports first; drive bundles only add **missing** keys). **`/lib/bare/manifest.json`** and **`/lib/bare/bundles/*.js`** are **trusted** like **`/bin`**. See [developer guide ch.12](../developer-guide/12-bare-modules-and-pear-ecosystem.md).
|
||||||
5. Construct **`ctx`**: disks, `vfs`, **`bareOsIpc`**, `env`, `b4a`, optional **`bare: Object.freeze(bareLibrary)`** when **`BARE_OS_BARE_MODULES`** is enabled, `topic`, **`bareOsRuntimeCaps`** (pipeline limits, **`quotas`**, pseudo path list, feature flags such as **`vfsWatch`**, **`ipcRpcJson`**, **`ipcFanout`**, **`initdSocketActivation`**, **`bareCtxModules`**, **`bareDriveBundles`**), optional **`httpFetch`** (policy-wrapped **`fetch`** when the host provides global **`fetch`**), optional **`bareOsHostStats`** from the **`bare-os`** module, **`bareOsGetResourceStatus`**, **`bareOsRegisterVirtualFile`**, Pear/sandbox stubs, identity hooks, **`bareOsSubscribeBootEvent`** / **`bareOsEmitBootEvent`**, **`bareOsSubscribeHdmsLifecycle`**, **`bareOsAwaitInitdUnits`**, **`runHdms`**, **`requestBooterExit`**, **`bareOsPublishBootReady`**, …
|
5. Construct **`ctx`**: disks, `vfs`, **`bareOsIpc`**, `env`, `b4a`, optional **`bare: Object.freeze(bareLibrary)`** when **`BARE_OS_BARE_MODULES`** is enabled, `topic`, **`bareOsRuntimeCaps`** (pipeline limits, **`quotas`**, pseudo path list, feature flags such as **`vfsWatch`**, **`ipcRpcJson`**, **`ipcFanout`**, **`initdSocketActivation`**, **`bareCtxModules`**, **`bareDriveBundles`**), optional **`httpFetch`** (policy-wrapped **`fetch`** when the host provides global **`fetch`**), optional **`bareOsHostStats`** from the **`bare-os`** module, **`bareOsGetResourceStatus`**, **`bareOsRegisterVirtualFile`**, Pear/sandbox stubs, identity hooks, **`bareOsSubscribeBootEvent`** / **`bareOsEmitBootEvent`**, **`bareOsSubscribeHdmsLifecycle`**, **`bareOsAwaitInitdUnits`**, **`runHdms`**, **`requestBooterExit`**, **`bareOsPublishBootReady`**, …
|
||||||
@@ -45,10 +45,11 @@ Virtual listings include **`/home`** (session-specific), **`/mnt`** when HDMS mo
|
|||||||
|
|
||||||
These paths are **synthetic** (not stored on either Hyperdrive). They exist for inspection and scripting ergonomics, **not** Linux ABI compatibility.
|
These paths are **synthetic** (not stored on either Hyperdrive). They exist for inspection and scripting ergonomics, **not** Linux ABI compatibility.
|
||||||
|
|
||||||
- **`/proc`**: **`version`**, **`bare_os_version`**, **`uptime`**, **`meminfo`**, **`cpuinfo`**, **`loadavg`**, **`mounts`**, **`diskstats`** (stub text), **`bare_os_quotas`** (JSON: pipeline limits, exec-depth cap, IPC cap, session stats, FIFO **`stats`**), **`bare_os_resources`** (unified operator snapshot), **`bare_os_features`** (protocol/kernel feature bitmask doc), **`net/dev`** (P2P-oriented stub), **`self/`** with **`environ`**, **`cmdline`**, **`exe`**, plus **`bare_os_session_stats`**. **`environ`** omits keys whose names look secret-bearing (e.g. **`PASSWORD`**, **`TOKEN`**, **`VAULT`**) and only includes a small public set plus **`BARE_OS_*`** (including **`BARE_OS_SESSION_ID`**).
|
- **`/proc`**: **`version`**, **`bare_os_version`**, **`uptime`**, **`meminfo`**, **`cpuinfo`**, **`loadavg`**, **`mounts`**, **`diskstats`** (stub text), **`bare_os_quotas`** (JSON: pipeline limits, exec-depth cap, IPC cap, session stats, FIFO **`stats`**), **`bare_os_resources`**, **`bare_os_features`**, **`bare_os_swarm`** (bounded P2P / session snapshot when the host provides it), **`bare_os_replication`** (JSON hints: version, peers, last error when known), **`bare_os_capabilities`** (human-readable dump of **`ctx.bareOsRuntimeCaps`**; **`bare_os_capabilities.json`** or **`Accept: application/json`** for machine output), **`bare_os_bootstrap`** (optional blind-peer style hints: **`BARE_OS_BLIND_BOOTSTRAP_URL`** or parsed **`BARE_OS_BLIND_BOOTSTRAP_JSON`** surfaced as JSON for operators — no automatic network fetch from the guest), **`net/dev`** (P2P-oriented stub), **`self/`** with **`environ`**, **`cmdline`**, **`exe`**, plus **`bare_os_session_stats`**. **`environ`** omits keys whose names look secret-bearing (e.g. **`PASSWORD`**, **`TOKEN`**, **`VAULT`**) and only includes a small public set plus **`BARE_OS_*`** (including **`BARE_OS_SESSION_ID`**).
|
||||||
- **`/sys`**: **`fs/bare_os/version`**, **`fs/bare_os/build_id`**, **`class/net/lo`** (stub **`operstate`** / **`carrier`**).
|
- **`/sys`**: **`fs/bare_os/version`**, **`fs/bare_os/build_id`**, **`class/net/lo`** (stub **`operstate`** / **`carrier`**).
|
||||||
- **`/sys/fs/bare_os/version`**: same text as **`/proc/version`**.
|
- **`/sys/fs/bare_os/version`**: same text as **`/proc/version`**.
|
||||||
- **`/run/bare-os/units`**: tab-separated snapshot of **bare-initd** registered units (phase, start time, description).
|
- **`/run/bare-os/units`**: tab-separated snapshot of **bare-initd** registered units (phase, start time, description).
|
||||||
|
- **`/run/bare-os/unit-journal/`**: append-only **NDJSON** per unit (**`<name>.ndjson`**) for start/stop/health/restart events (size-capped); **`journalctl -u`** also tails this file when present.
|
||||||
- **`/run/bare-os/boot_profile`**: one line (plus newline): resolved boot profile name (**`BARE_OS_BOOT_PROFILE`** from the host, else first line of **`/etc/bare-os/profile`**, else empty). Listed in **`ctx.bareOsRuntimeCaps.pseudoFsPaths`**.
|
- **`/run/bare-os/boot_profile`**: one line (plus newline): resolved boot profile name (**`BARE_OS_BOOT_PROFILE`** from the host, else first line of **`/etc/bare-os/profile`**, else empty). Listed in **`ctx.bareOsRuntimeCaps.pseudoFsPaths`**.
|
||||||
- **`/run/bare-os/session`**: session UUID (from **`BARE_OS_SESSION_ID`**) plus newline.
|
- **`/run/bare-os/session`**: session UUID (from **`BARE_OS_SESSION_ID`**) plus newline.
|
||||||
- **`/run/bare-os/virtual/`**: optional kernel-registered synthetic files via **`ctx.bareOsRegisterVirtualFile`**.
|
- **`/run/bare-os/virtual/`**: optional kernel-registered synthetic files via **`ctx.bareOsRegisterVirtualFile`**.
|
||||||
@@ -58,6 +59,8 @@ These paths are **synthetic** (not stored on either Hyperdrive). They exist for
|
|||||||
|
|
||||||
**`vfs.watch(logicalPath)`** (Hyperdrive-backed paths only — not pseudo roots): returns **`{ watcher, destroy, logicalAbs, driveFolder }`**; use Hyperdrive’s async iterator on **`watcher`**. Disabled when host sets **`BARE_OS_VFS_WATCH=0`**. See **`ctx.bareOsRuntimeCaps.features.vfsWatch`**.
|
**`vfs.watch(logicalPath)`** (Hyperdrive-backed paths only — not pseudo roots): returns **`{ watcher, destroy, logicalAbs, driveFolder }`**; use Hyperdrive’s async iterator on **`watcher`**. Disabled when host sets **`BARE_OS_VFS_WATCH=0`**. See **`ctx.bareOsRuntimeCaps.features.vfsWatch`**.
|
||||||
|
|
||||||
|
**Union read:** when **`BARE_OS_VFS_UNION_PREFIXES`** lists comma-separated logical prefixes, **`readFile`** / **`readlink`** on those paths may merge the **system** image with overlays (see **`vfs.js`** and **`ctx.bareOsRuntimeCaps.features.vfsUnionRead`**). **`BARE_OS_VFS_BIN_CACHE=1`** enables a small LRU read cache for **`/bin`** entries, invalidated when **`vfs.watch`** notifications fire on watched prefixes.
|
||||||
|
|
||||||
**Implementation note:** pseudo-file content is UTF-8 encoded with **`b4a`**, not **`TextEncoder`**, because some Bare/Pear runtimes omit the Web Encoding globals (`TextEncoder` / `TextDecoder`). The same applies elsewhere in the booter and in-image utilities that must run on Bare.
|
**Implementation note:** pseudo-file content is UTF-8 encoded with **`b4a`**, not **`TextEncoder`**, because some Bare/Pear runtimes omit the Web Encoding globals (`TextEncoder` / `TextDecoder`). The same applies elsewhere in the booter and in-image utilities that must run on Bare.
|
||||||
|
|
||||||
### **`/dev`** and **`/run`** (minimal subset)
|
### **`/dev`** and **`/run`** (minimal subset)
|
||||||
@@ -88,7 +91,7 @@ Work is sequenced for **POSIX/script ergonomics first**, then networking and lon
|
|||||||
**`execShellLine`** (`lib/shell.js`):
|
**`execShellLine`** (`lib/shell.js`):
|
||||||
|
|
||||||
- Tokenizes words, quotes, escapes, **`$VAR`**, pipelines **`|`**, redirections **`>` / `>>` / `<`**.
|
- Tokenizes words, quotes, escapes, **`$VAR`**, pipelines **`|`**, redirections **`>` / `>>` / `<`**.
|
||||||
- Builtins: **`alias`**, **`unalias`**, **`cd`**, **`export`**, **`unset`**, **`readonly`**, **`umask`**, **`:`**, **`command`**, **`type`**, **`login`**, **`logout`**, **`exit`** — plus external commands via **`runBinCommand`**. **`readonly`** blocks **`export`** and assignment writes to the same name; **`command -v` / `-V`** and **`type`** use **`resolveBinInPath`** for **`PATH`** lookup.
|
- Builtins: **`alias`**, **`unalias`**, **`cd`**, **`export`**, **`unset`**, **`readonly`**, **`umask`**, **`:`**, **`command`**, **`type`**, **`login`**, **`logout`**, **`exit`**, bounded **`if`/`while`/`for`/`case`** — plus external commands via **`runBinCommand`**. **`readonly`** blocks **`export`** and assignment writes to the same name; **`command -v` / `-V`** and **`type`** use **`resolveBinInPath`** for **`PATH`** lookup.
|
||||||
- First-word **aliases** (defaults like **`ll` → `ls -la`**, **`nano` → `edit`**) expand after **`$VAR`** substitution; **`alias`** / **`unalias`** match the restricted **`~/.barerc`** syntax (not full POSIX **`sh`**).
|
- First-word **aliases** (defaults like **`ll` → `ls -la`**, **`nano` → `edit`**) expand after **`$VAR`** substitution; **`alias`** / **`unalias`** match the restricted **`~/.barerc`** syntax (not full POSIX **`sh`**).
|
||||||
- Pipes capture **`console.log`** into the next stage or a string sink.
|
- Pipes capture **`console.log`** into the next stage or a string sink.
|
||||||
|
|
||||||
@@ -118,7 +121,7 @@ There is no arbitrary command execution, **`source`**, or control flow — it is
|
|||||||
**`bare-initd.js`**:
|
**`bare-initd.js`**:
|
||||||
|
|
||||||
- **`registerBareService({ name, start, stop?, description?, logPath? })`** — optional **`stop`** enables **`systemctl stop`** / **`restart`** for that unit; **`logPath`** is a logical VFS path for **`systemctl status`** / **`logs`**
|
- **`registerBareService({ name, start, stop?, description?, logPath? })`** — optional **`stop`** enables **`systemctl stop`** / **`restart`** for that unit; **`logPath`** is a logical VFS path for **`systemctl status`** / **`logs`**
|
||||||
- **`startBareInitd(ctx)`** — ensures **`/var/log/bare-os`** (see below), then ordered start (skips units listed in **`~/.config/bare-os/initd/disabled.txt`** on the personal drive; optional **`~/.config/bare-os/units/<name>.unit`** with **`[Unit]`** keys **`After=`**, **`Requires=`**, **`Wants=`**, **`TimeoutStartSec=`**, **`TimeoutStopSec=`**, **`Restart=`**, **`RestartSec=`**, **`ExecStartPost=`**, **`SocketActivationIpc=`** — the last defers **`start()`** until the first read on that logical FIFO name — plus **`ReadinessPath=`** (VFS path that must exist before the unit is marked **`active`**) and **`ReadinessTimeoutSec=`** (poll budget, default 30s)). Default **`bare-cron`** runs after **`kernel-logger`**. Per-service `try/catch`, `[bare-initd] name: err` on failure; failures append to **`/var/log/bare-os/initd.log`**; runtime state **`active`** / **`failed`** / **`inactive`**. **`waitForBareInitdUnits(names, timeoutMs)`** polls until listed units are **`active`** (exposed on **`ctx`** as **`bareOsAwaitInitdUnits`**).
|
- **`startBareInitd(ctx)`** — ensures **`/var/log/bare-os`** (see below), then **DAG-ordered** start with optional parallelism (**`BARE_OS_INITD_MAX_PARALLEL`**, default **1**). Skips units listed in **`~/.config/bare-os/initd/disabled.txt`**. Drop-ins **`~/.config/bare-os/units/<name>.unit`** support **`[Unit]`** keys **`After=`**, **`Before=`**, **`Requires=`**, **`Wants=`**, **`TimeoutStartSec=`**, **`TimeoutStopSec=`**, **`Restart=`**, **`RestartSec=`**, **`OnFailure=`**, **`FailureAction=`**, **`ExecStartPost=`**, **`SocketActivationIpc=`** (defers **`start()`** until the first read on that FIFO), **`ReadinessPath=`**, **`ReadinessTimeoutSec=`**. Cycles in **`After`/`Before`** are detected and logged; affected units fail start. Default **`bare-cron`** runs after **`kernel-logger`**. Per-service `try/catch`, `[bare-initd] name: err` on failure; failures append to **`/var/log/bare-os/initd.log`** and the structured unit journal; runtime state **`active`** / **`failed`** / **`inactive`**. **`waitForBareInitdUnits(names, timeoutMs)`** polls until listed units are **`active`** (exposed as **`ctx.bareOsAwaitInitdUnits`**).
|
||||||
- **`listBareServices()`**, **`getBareServiceRuntime(name)`**, **`findBareServiceDefinition(name)`**, **`startBareService` / `stopBareService` / `restartBareService`** — introspection and lifecycle (used by the CLI below)
|
- **`listBareServices()`**, **`getBareServiceRuntime(name)`**, **`findBareServiceDefinition(name)`**, **`startBareService` / `stopBareService` / `restartBareService`** — introspection and lifecycle (used by the CLI below)
|
||||||
- **`registerKernelShutdownHook(fn)`** + **`runKernelShutdownHooks()`** — async-friendly teardown before disposers (REPL **`session.cleanup`** awaits hooks, then **`stopBareInitd()`**)
|
- **`registerKernelShutdownHook(fn)`** + **`runKernelShutdownHooks()`** — async-friendly teardown before disposers (REPL **`session.cleanup`** awaits hooks, then **`stopBareInitd()`**)
|
||||||
- **`registerBareInitdDisposer(fn)`** + **`stopBareInitd()`** — for intervals and synchronous teardown
|
- **`registerBareInitdDisposer(fn)`** + **`stopBareInitd()`** — for intervals and synchronous teardown
|
||||||
|
|||||||
@@ -39,6 +39,10 @@ The **prompt** (`[user@host:path] > `) is applied by the booter’s readline lay
|
|||||||
| **`BARE_OS_BOOT_ALLOWLIST=1`** | Only run boot snippet lines whose first shell token is listed in **`/etc/bare-os/boot.allow`** (plus safe builtins); distributors can start from **`etc/bare-os/boot.allow.example`** on the system image. Pair with **`BARE_OS_BOOT_STRICT`** to exit on the first disallowed or failing line. |
|
| **`BARE_OS_BOOT_ALLOWLIST=1`** | Only run boot snippet lines whose first shell token is listed in **`/etc/bare-os/boot.allow`** (plus safe builtins); distributors can start from **`etc/bare-os/boot.allow.example`** on the system image. Pair with **`BARE_OS_BOOT_STRICT`** to exit on the first disallowed or failing line. |
|
||||||
| **`BARE_OS_EXEC_MAX_DEPTH`** | Max nested **`execLine`** depth (host → session; default **64**). |
|
| **`BARE_OS_EXEC_MAX_DEPTH`** | Max nested **`execLine`** depth (host → session; default **64**). |
|
||||||
| **`ctx.bareOsPublishBootReady(...)`** | Kernel calls this when boot is complete; populates **`/run/bare-os/ready`** (**`1`** / **`0`**) and **`/run/bare-os/boot.json`** (**`imageDigest`**, **`pearChannel`**, **`pearRelease`** are pre-seeded from host env when set — see [environment variables reference](../docs/reference/environment-and-posix-appendix.md#14-environment-variables-complete-list)). |
|
| **`ctx.bareOsPublishBootReady(...)`** | Kernel calls this when boot is complete; populates **`/run/bare-os/ready`** (**`1`** / **`0`**) and **`/run/bare-os/boot.json`** (**`imageDigest`**, **`pearChannel`**, **`pearRelease`** are pre-seeded from host env when set — see [environment variables reference](../docs/reference/environment-and-posix-appendix.md#14-environment-variables-complete-list)). |
|
||||||
|
| **`BARE_OS_BOOT_MANIFEST`** | When **`1`** / **`true`**, the stock kernel verifies **`/etc/bare-os/boot.manifest.json`** against an expected SHA-256 (see kernel **`init.js`** and **`ctx.bareOsBootFileSha256Hex`**). Example layout: [`kernel/etc/bare-os/boot.manifest.example.json`](../kernel/etc/bare-os/boot.manifest.example.json). |
|
||||||
|
| **`BARE_OS_BOOT_MANIFEST_SIGN`**, **`BARE_OS_BOOT_MANIFEST_PUBKEY_HEX`** | Optional **Ed25519** over the **raw manifest bytes**: expects **`/etc/bare-os/boot.manifest.sig`** and **`ctx.bareOsVerifyBootManifestSignature`** (host/booter). Mismatch fails boot with a clear stderr line. |
|
||||||
|
| **`ctx.bareOsRegisterBootPhaseHook` / `bareOsInvokeBootPhaseHooks`** | Kernel extensions can observe **`before:` / `after:`** stock phases (e.g. **`rc`**, **`repl`**) without replacing **`init.js`**. See [developer guide §2](../developer-guide/02-the-context-object.md). |
|
||||||
|
| **`/etc/bare-os/selftest.d/`** | Optional digit-prefixed **`*.sh`** snippets run after the main boot path when **`BARE_OS_KERNEL_SELFTEST`** is enabled (same **`execLine`** rules as other boot snippets). Pair with **`BARE_OS_SELFTEST_FORMAT=tap`** for CI-friendly stderr. |
|
||||||
|
|
||||||
### Custom `init.js` contract
|
### Custom `init.js` contract
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ Bare OS targets a **usable subset** of [POSIX.1-2017](https://pubs.opengroup.org
|
|||||||
|
|
||||||
| Expectation (full POSIX) | Bare OS reality |
|
| Expectation (full POSIX) | Bare OS reality |
|
||||||
| ---------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| ---------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| **`sh`** grammar (`for`, subshells, …) | Line-at-a-time shell: builtins + **`/bin`**; lists with **`;`** (outside **`if`/`fi`**), **`&&`**, **`\|`** pipelines; bounded **`if`** **`then`** **[** **`else`** **]** **`fi`** (condition = last exit status like **`&&`**)—no **`for`**, groups, or full **`sh`** grammar. |
|
| **`sh`** grammar (full POSIX) | Line-at-a-time shell: builtins + **`/bin`**; lists with **`;`** (outside compound statements), **`&&`**, **`\|`** pipelines; bounded **`if`** … **`fi`**; bounded **`while`** / **`for`** (iteration cap **`BARE_OS_SHELL_LOOP_MAX`**, default **10000**); bounded **`case`** … **`esac`** (**`BARE_OS_SHELL_CASE_MAX_BRANCHES`**, default **32**). No subshells, **`$(…)`**, or full **`sh`** grammar. |
|
||||||
| **Processes, `fork`, pipes as OS primitives** | Pipelines are simulated by capturing **`console.log`** (and merged **`console.error`** when **`2>&1`** is used) into the next command. |
|
| **Processes, `fork`, pipes as OS primitives** | Pipelines are simulated by capturing **`console.log`** (and merged **`console.error`** when **`2>&1`** is used) into the next command. |
|
||||||
| **`chown` / `chgrp` / real UIDs across users** | Single-session identity; metadata carries **uid/gid** for display and checks. |
|
| **`chown` / `chgrp` / real UIDs across users** | Single-session identity; metadata carries **uid/gid** for display and checks. |
|
||||||
| **FIFOs, `mknod`, real devices** | No kernel FIFOs or **`mknod`**; **`mkfifo`** creates **in-memory** channels under **`/run/bare-os/ipc/<name>`** (see **`bare-os-ipc.js`**). |
|
| **FIFOs, `mknod`, real devices** | No kernel FIFOs or **`mknod`**; **`mkfifo`** creates **in-memory** channels under **`/run/bare-os/ipc/<name>`** (see **`bare-os-ipc.js`**). |
|
||||||
@@ -37,7 +37,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/loadavg`**, **`/proc/net/dev`**, **`/proc/diskstats`**, **`/proc/bare_os_quotas`** (JSON quotas + session stats), **`/proc/bare_os_resources`** (unified resource snapshot), **`/proc/bare_os_features`** (documented capability bitmask), **`/proc/bare_os_session_stats`**, **`/proc/self/exe`**, **`/sys/class/net/lo`**, **`/sys/fs/bare_os/build_id`**, **`/run/bare-os/virtual/*`** (optional kernel-registered readers), and **`/dev/urandom`** (each **`readFile`** of urandom returns a **bounded** buffer; **not** suitable for cryptography).
|
- **`/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/loadavg`**, **`/proc/net/dev`**, **`/proc/diskstats`**, **`/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_capabilities`** (and **`/proc/bare_os_capabilities.json`**), **`/proc/bare_os_bootstrap`**, **`/proc/self/exe`**, **`/sys/class/net/lo`**, **`/sys/fs/bare_os/build_id`**, **`/run/bare-os/virtual/*`**, **`/run/bare-os/unit-journal/*.ndjson`**, and **`/dev/urandom`** (each **`readFile`** of urandom returns a **bounded** buffer; **not** suitable for cryptography unless **`BARE_OS_URANDOM_CRYPTO`** is left at default).
|
||||||
- **`/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)
|
||||||
@@ -58,7 +58,7 @@ Hyperdrive does not always behave like a POSIX directory tree. Empty directories
|
|||||||
|
|
||||||
## 3. Shell lists, pipelines, and builtins (`packages/bare-os-booter/lib/shell.js`)
|
## 3. Shell lists, pipelines, and builtins (`packages/bare-os-booter/lib/shell.js`)
|
||||||
|
|
||||||
**Top-level syntax:** the line is split on **`;`** into separate lists (semicolons **inside** an **`if`** … **`fi`** block 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**. 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 glob pattern (token-safe); 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; **`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; **`fg`** awaits the selected (or latest) job; **`bg`** is a stub (no stopped-job resume). This is **not** POSIX job control (no real processes or signals).
|
||||||
|
|
||||||
@@ -136,7 +136,7 @@ Sources: **`packages/bare-os-coreutils/src/<name>.js`**. **Authoritative sorted
|
|||||||
| **`tee`** | **`-a`** append; duplicates stdin to files and stdout. |
|
| **`tee`** | **`-a`** append; duplicates stdin to files and stdout. |
|
||||||
| **`sed`** | Large subset — see §6. |
|
| **`sed`** | Large subset — see §6. |
|
||||||
| **`awk`** | Substantial interpreter — see §7. |
|
| **`awk`** | Substantial interpreter — see §7. |
|
||||||
| **`grep`** | **`-F`**, **`-i`**, **`-v`**, **`-w`**, **`-x`**, **`-n`**, **`-c`**, **`-l`**, **`-o`**, **`-m`**, **`-A`/`-B`/`-C`**, \*\*`--color=never | always | auto`**, **`-e`**, **`-f`**, etc.; JS **`RegExp`\*\* (not PCRE / full GNU). |
|
| **`grep`** | **`-F`**, **`-i`**, **`-v`**, **`-w`**, **`-x`**, **`-n`**, **`-c`**, **`-l`**, **`-o`**, **`-m`**, **`-r`/`-R`** with **`--include`**, **`--exclude`**, **`--exclude-dir`** (glob count capped by **`BARE_OS_GREP_FILTER_MAX`**, default **32**), **`-A`/`-B`/`-C`**, **`--color=never` / `always` / `auto`**, **`-e`**, **`-f`**, etc.; JS **`RegExp`** (not PCRE / full GNU). |
|
||||||
| **`base64`** | **`-d`/`--decode`**, **`-w`** line wrap; decode uses **`bareOsEmitRaw`** when **`console.log`** would corrupt binary. |
|
| **`base64`** | **`-d`/`--decode`**, **`-w`** line wrap; decode uses **`bareOsEmitRaw`** when **`console.log`** would corrupt binary. |
|
||||||
| **`base32`** | RFC 4648 encode/decode; decode path same raw-output contract as **`base64`**. |
|
| **`base32`** | RFC 4648 encode/decode; decode path same raw-output contract as **`base64`**. |
|
||||||
| **`basenc`** | **`--base16`** (hex) encode/decode only; other alphabets not implemented. |
|
| **`basenc`** | **`--base16`** (hex) encode/decode only; other alphabets not implemented. |
|
||||||
@@ -215,7 +215,7 @@ All other commands from **`build.mjs`** not listed here follow the summaries in
|
|||||||
|
|
||||||
**Engine:** **`packages/bare-os-coreutils/lib/sed-engine.js`** (prepended before **`src/sed.js`** at build time).
|
**Engine:** **`packages/bare-os-coreutils/lib/sed-engine.js`** (prepended before **`src/sed.js`** at build time).
|
||||||
|
|
||||||
**CLI:** **`sed`** supports **`-n`**, **`-E`/`-r`**, multiple **`-e`**, **`-f`**, and operands as files or stdin.
|
**CLI:** **`sed`** supports **`-n`**, **`-E`/`-r`**, **`-z`** (NUL-separated “lines”; max records **`BARE_OS_SED_NULL_MAX_RECORDS`**, default **100000**), multiple **`-e`**, **`-f`**, and operands as files or stdin.
|
||||||
|
|
||||||
**Broadly supported:** line addresses (**`#`**, **`$`**, **`/re/`**, ranges, **`first~step`**), **`s///`** with common flags (**`g`**, **`p`**, digit), **`y///`**, **`d`/`D`/`p`/`P`/`n`/`N`**, hold space (**`h`/`H`/`g`/`G`/`x`**), **`b`/`t`/`:label`**, **`q`**, **`r`/`w`**, **`=`**, **`l`**, **`a`/`i`/`c`** (backslash forms). **`r`** reads paths via a preload scan + **`vfs.readFile`**; **`w`** appends via **`vfs.writeFile`**.
|
**Broadly supported:** line addresses (**`#`**, **`$`**, **`/re/`**, ranges, **`first~step`**), **`s///`** with common flags (**`g`**, **`p`**, digit), **`y///`**, **`d`/`D`/`p`/`P`/`n`/`N`**, hold space (**`h`/`H`/`g`/`G`/`x`**), **`b`/`t`/`:label`**, **`q`**, **`r`/`w`**, **`=`**, **`l`**, **`a`/`i`/`c`** (backslash forms). **`r`** reads paths via a preload scan + **`vfs.readFile`**; **`w`** appends via **`vfs.writeFile`**.
|
||||||
|
|
||||||
|
|||||||
+82
-6
@@ -115,6 +115,12 @@ async function run(ctx, argv) {
|
|||||||
/** @type {'never' | 'always' | 'auto'} */
|
/** @type {'never' | 'always' | 'auto'} */
|
||||||
let colorMode = 'never'
|
let colorMode = 'never'
|
||||||
let recursive = false
|
let recursive = false
|
||||||
|
/** @type {string[]} */
|
||||||
|
const includeGlobs = []
|
||||||
|
/** @type {string[]} */
|
||||||
|
const excludeGlobs = []
|
||||||
|
/** @type {string[]} */
|
||||||
|
const excludeDirGlobs = []
|
||||||
|
|
||||||
const args = argv.slice(1)
|
const args = argv.slice(1)
|
||||||
let i = 0
|
let i = 0
|
||||||
@@ -182,6 +188,36 @@ async function run(ctx, argv) {
|
|||||||
i++
|
i++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if (a === '--include' && args[i + 1]) {
|
||||||
|
includeGlobs.push(args[++i])
|
||||||
|
i++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (a.startsWith('--include=')) {
|
||||||
|
includeGlobs.push(a.slice('--include='.length))
|
||||||
|
i++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (a === '--exclude' && args[i + 1]) {
|
||||||
|
excludeGlobs.push(args[++i])
|
||||||
|
i++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (a.startsWith('--exclude=')) {
|
||||||
|
excludeGlobs.push(a.slice('--exclude='.length))
|
||||||
|
i++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (a === '--exclude-dir' && args[i + 1]) {
|
||||||
|
excludeDirGlobs.push(args[++i])
|
||||||
|
i++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (a.startsWith('--exclude-dir=')) {
|
||||||
|
excludeDirGlobs.push(a.slice('--exclude-dir='.length))
|
||||||
|
i++
|
||||||
|
continue
|
||||||
|
}
|
||||||
ctx.console.error('grep: unknown option ' + a)
|
ctx.console.error('grep: unknown option ' + a)
|
||||||
ctx.exitCode = 2
|
ctx.exitCode = 2
|
||||||
return
|
return
|
||||||
@@ -393,7 +429,15 @@ async function run(ctx, argv) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (st.type === 'directory') {
|
if (st.type === 'directory') {
|
||||||
await grepWalkFiles(ctx, p, acc, suppressErrors)
|
await grepWalkFiles(ctx, p, acc, suppressErrors, {
|
||||||
|
includeGlobs,
|
||||||
|
excludeGlobs,
|
||||||
|
excludeDirGlobs,
|
||||||
|
filterCap: Number.parseInt(
|
||||||
|
String(ctx.vfs?.env?.BARE_OS_GREP_FILTER_MAX || '32'),
|
||||||
|
10
|
||||||
|
) || 32
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
acc.push(p)
|
acc.push(p)
|
||||||
}
|
}
|
||||||
@@ -589,8 +633,31 @@ const GREP_RECURSE_MAX_DEPTH = 64
|
|||||||
* @param {string[]} acc
|
* @param {string[]} acc
|
||||||
* @param {boolean} suppressErrors
|
* @param {boolean} suppressErrors
|
||||||
*/
|
*/
|
||||||
async function grepWalkFiles(ctx, dir, acc, suppressErrors) {
|
/**
|
||||||
|
* @param {string} name
|
||||||
|
* @param {string} pat
|
||||||
|
*/
|
||||||
|
function grepSimpleGlobMatch(name, pat) {
|
||||||
|
if (!pat || pat === '*') return true
|
||||||
|
if (pat.includes('/')) return name === pat
|
||||||
|
if (pat.startsWith('*') && pat.length > 1 && pat.endsWith('*')) {
|
||||||
|
const mid = pat.slice(1, -1)
|
||||||
|
return mid !== '' && name.includes(mid)
|
||||||
|
}
|
||||||
|
if (pat.startsWith('*')) return name.endsWith(pat.slice(1))
|
||||||
|
if (pat.endsWith('*')) return name.startsWith(pat.slice(0, -1))
|
||||||
|
return name === pat
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {{ includeGlobs?: string[], excludeGlobs?: string[], excludeDirGlobs?: string[], filterCap?: number }} [opts]
|
||||||
|
*/
|
||||||
|
async function grepWalkFiles(ctx, dir, acc, suppressErrors, opts = {}) {
|
||||||
const vfs = ctx.vfs
|
const vfs = ctx.vfs
|
||||||
|
const cap = opts.filterCap && opts.filterCap > 0 ? opts.filterCap : 32
|
||||||
|
const inc = (opts.includeGlobs || []).slice(0, cap)
|
||||||
|
const exc = (opts.excludeGlobs || []).slice(0, cap)
|
||||||
|
const excd = (opts.excludeDirGlobs || []).slice(0, cap)
|
||||||
/** @param {string} d @param {number} depth */
|
/** @param {string} d @param {number} depth */
|
||||||
const walk = async (d, depth) => {
|
const walk = async (d, depth) => {
|
||||||
if (depth > GREP_RECURSE_MAX_DEPTH) return
|
if (depth > GREP_RECURSE_MAX_DEPTH) return
|
||||||
@@ -612,12 +679,21 @@ async function grepWalkFiles(ctx, dir, acc, suppressErrors) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (!st) continue
|
if (!st) continue
|
||||||
if (st.type === 'directory') await walk(sub, depth + 1)
|
if (st.type === 'directory') {
|
||||||
else if (st.type === 'file') acc.push(sub)
|
if (excd.some((p) => grepSimpleGlobMatch(n, p))) continue
|
||||||
else if (st.type === 'symlink') {
|
await walk(sub, depth + 1)
|
||||||
|
} else if (st.type === 'file') {
|
||||||
|
if (inc.length && !inc.some((p) => grepSimpleGlobMatch(n, p))) continue
|
||||||
|
if (exc.some((p) => grepSimpleGlobMatch(n, p))) continue
|
||||||
|
acc.push(sub)
|
||||||
|
} else if (st.type === 'symlink') {
|
||||||
try {
|
try {
|
||||||
const ft = await vfs.stat(sub)
|
const ft = await vfs.stat(sub)
|
||||||
if (ft && ft.type === 'file') acc.push(sub)
|
if (ft && ft.type === 'file') {
|
||||||
|
if (inc.length && !inc.some((p) => grepSimpleGlobMatch(n, p))) continue
|
||||||
|
if (exc.some((p) => grepSimpleGlobMatch(n, p))) continue
|
||||||
|
acc.push(sub)
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
/* skip */
|
/* skip */
|
||||||
}
|
}
|
||||||
|
|||||||
+38
-18
@@ -603,12 +603,13 @@ function bareSedMatchAddr(
|
|||||||
/**
|
/**
|
||||||
* @param {string[]} lines
|
* @param {string[]} lines
|
||||||
* @param {string[]} scripts
|
* @param {string[]} scripts
|
||||||
* @param {{ silent?: boolean, extended?: boolean, readFile?: (p: string) => string | null, writeFile?: (p: string, chunk: string) => void, lastLineHint?: number }} opts
|
* @param {{ silent?: boolean, extended?: boolean, nullData?: boolean, readFile?: (p: string) => string | null, writeFile?: (p: string, chunk: string) => void, lastLineHint?: number }} opts
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
function bareSedRun(lines, scripts, opts) {
|
function bareSedRun(lines, scripts, opts) {
|
||||||
const silent = !!opts.silent
|
const silent = !!opts.silent
|
||||||
const extended = !!opts.extended
|
const extended = !!opts.extended
|
||||||
|
const eol = opts.nullData ? '\0' : '\n'
|
||||||
const readF = opts.readFile || (() => null)
|
const readF = opts.readFile || (() => null)
|
||||||
const writeF = opts.writeFile || (() => {})
|
const writeF = opts.writeFile || (() => {})
|
||||||
const fullScript = scripts.join('\n')
|
const fullScript = scripts.join('\n')
|
||||||
@@ -697,7 +698,7 @@ function bareSedRun(lines, scripts, opts) {
|
|||||||
}
|
}
|
||||||
if (count) {
|
if (count) {
|
||||||
ps = res + str.slice(pos)
|
ps = res + str.slice(pos)
|
||||||
if (fl.p) emit(ps + '\n')
|
if (fl.p) emit(ps + eol)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -726,15 +727,15 @@ function bareSedRun(lines, scripts, opts) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
case 'print':
|
case 'print':
|
||||||
emit(ps + '\n')
|
emit(ps + eol)
|
||||||
break
|
break
|
||||||
case 'printFirst': {
|
case 'printFirst': {
|
||||||
const nl = ps.indexOf('\n')
|
const nl = ps.indexOf('\n')
|
||||||
emit((nl === -1 ? ps : ps.slice(0, nl)) + '\n')
|
emit((nl === -1 ? ps : ps.slice(0, nl)) + eol)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case 'nextLine':
|
case 'nextLine':
|
||||||
if (autoPrint && !silent) emit(ps + '\n')
|
if (autoPrint && !silent) emit(ps + eol)
|
||||||
lineIdx++
|
lineIdx++
|
||||||
nextRead = true
|
nextRead = true
|
||||||
ci = cmds.length
|
ci = cmds.length
|
||||||
@@ -763,25 +764,25 @@ function bareSedRun(lines, scripts, opts) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
case 'quit':
|
case 'quit':
|
||||||
if (autoPrint && !silent) emit(ps + '\n')
|
if (autoPrint && !silent) emit(ps + eol)
|
||||||
quit = /** @type {number} */ (cmd.quitCode) || 0
|
quit = /** @type {number} */ (cmd.quitCode) || 0
|
||||||
break
|
break
|
||||||
case 'list':
|
case 'list':
|
||||||
emit(bareSedListLine(ps) + '\n')
|
emit(bareSedListLine(ps) + eol)
|
||||||
break
|
break
|
||||||
case 'lineNum':
|
case 'lineNum':
|
||||||
emit(String(lineNo) + '\n')
|
emit(String(lineNo) + eol)
|
||||||
break
|
break
|
||||||
case 'readFile': {
|
case 'readFile': {
|
||||||
const text = readF(/** @type {string} */ (cmd.path))
|
const text = readF(/** @type {string} */ (cmd.path))
|
||||||
if (text) emit(text.endsWith('\n') ? text : text + '\n')
|
if (text) emit(text.endsWith(eol) ? text : text + eol)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case 'writeFile':
|
case 'writeFile':
|
||||||
writeF(/** @type {string} */ (cmd.path), ps + '\n')
|
writeF(/** @type {string} */ (cmd.path), ps + '\n')
|
||||||
break
|
break
|
||||||
case 'append':
|
case 'append':
|
||||||
emit(/** @type {string} */ (cmd.text) + '\n')
|
emit(/** @type {string} */ (cmd.text) + eol)
|
||||||
break
|
break
|
||||||
case 'insert':
|
case 'insert':
|
||||||
/* handled as emit before line — approximated by prepending to output before autoPrint */
|
/* handled as emit before line — approximated by prepending to output before autoPrint */
|
||||||
@@ -789,7 +790,7 @@ function bareSedRun(lines, scripts, opts) {
|
|||||||
break
|
break
|
||||||
case 'change':
|
case 'change':
|
||||||
autoPrint = false
|
autoPrint = false
|
||||||
emit(/** @type {string} */ (cmd.text) + '\n')
|
emit(/** @type {string} */ (cmd.text) + eol)
|
||||||
delLine = true
|
delLine = true
|
||||||
break
|
break
|
||||||
case 'b': {
|
case 'b': {
|
||||||
@@ -815,7 +816,7 @@ function bareSedRun(lines, scripts, opts) {
|
|||||||
|
|
||||||
if (quit) break
|
if (quit) break
|
||||||
if (nextRead) continue
|
if (nextRead) continue
|
||||||
if (!delLine && autoPrint) emit(ps + '\n')
|
if (!delLine && autoPrint) emit(ps + eol)
|
||||||
lineIdx++
|
lineIdx++
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -825,6 +826,7 @@ function bareSedRun(lines, scripts, opts) {
|
|||||||
async function run(ctx, argv) {
|
async function run(ctx, argv) {
|
||||||
let silent = false
|
let silent = false
|
||||||
let extended = false
|
let extended = false
|
||||||
|
let nullData = false
|
||||||
/** @type {string[]} */
|
/** @type {string[]} */
|
||||||
const scripts = []
|
const scripts = []
|
||||||
/** @type {string[]} */
|
/** @type {string[]} */
|
||||||
@@ -835,6 +837,10 @@ async function run(ctx, argv) {
|
|||||||
silent = true
|
silent = true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if (a === '-z' || a === '--null-data') {
|
||||||
|
nullData = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
if (a === '-E' || a === '-r') {
|
if (a === '-E' || a === '-r') {
|
||||||
extended = true
|
extended = true
|
||||||
continue
|
continue
|
||||||
@@ -913,6 +919,12 @@ async function run(ctx, argv) {
|
|||||||
readCache[rp] = buf ? ctx.b4a.toString(buf) : ''
|
readCache[rp] = buf ? ctx.b4a.toString(buf) : ''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const maxNull =
|
||||||
|
Number.parseInt(
|
||||||
|
String(ctx.vfs?.env?.BARE_OS_SED_NULL_MAX_RECORDS || '100000'),
|
||||||
|
10
|
||||||
|
) || 100000
|
||||||
|
|
||||||
/** @type {string[]} */
|
/** @type {string[]} */
|
||||||
const lines = []
|
const lines = []
|
||||||
async function pushFile(path) {
|
async function pushFile(path) {
|
||||||
@@ -923,16 +935,22 @@ async function run(ctx, argv) {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
const t = ctx.b4a.toString(buf)
|
const t = ctx.b4a.toString(buf)
|
||||||
const ls = t.split(/\r?\n/)
|
if (nullData) {
|
||||||
if (ls.length && ls[ls.length - 1] === '') ls.pop()
|
const rec = t.split('\0')
|
||||||
lines.push(...ls)
|
const room = maxNull - lines.length
|
||||||
|
lines.push(...rec.slice(0, Math.max(0, room)))
|
||||||
|
} else {
|
||||||
|
const ls = t.split(/\r?\n/)
|
||||||
|
if (ls.length && ls[ls.length - 1] === '') ls.pop()
|
||||||
|
lines.push(...ls)
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!files.length) {
|
if (!files.length) {
|
||||||
const s = bareStdin(ctx)
|
const s = bareStdin(ctx)
|
||||||
const ls = s.split(/\r?\n/)
|
const ls = nullData ? s.split('\0').slice(0, maxNull) : s.split(/\r?\n/)
|
||||||
if (ls.length && ls[ls.length - 1] === '') ls.pop()
|
if (!nullData && ls.length && ls[ls.length - 1] === '') ls.pop()
|
||||||
lines.push(...ls)
|
lines.push(...ls)
|
||||||
} else {
|
} else {
|
||||||
for (const f of files) {
|
for (const f of files) {
|
||||||
@@ -945,6 +963,7 @@ async function run(ctx, argv) {
|
|||||||
const out = bareSedRun(lines, scripts, {
|
const out = bareSedRun(lines, scripts, {
|
||||||
silent,
|
silent,
|
||||||
extended,
|
extended,
|
||||||
|
nullData,
|
||||||
readFile: (p) => readCache[p] ?? null,
|
readFile: (p) => readCache[p] ?? null,
|
||||||
writeFile: (p, chunk) => {
|
writeFile: (p, chunk) => {
|
||||||
wAccum[p] = (wAccum[p] || '') + chunk
|
wAccum[p] = (wAccum[p] || '') + chunk
|
||||||
@@ -965,6 +984,7 @@ async function run(ctx, argv) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const t = out.replace(/\n$/, '')
|
const trail = nullData ? /\0$/ : /\n$/
|
||||||
|
const t = out.replace(trail, '')
|
||||||
ctx.console.log(t)
|
ctx.console.log(t)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,9 @@
|
|||||||
* BARE_OS_SELFTEST_FORMAT=tap: TAP-style lines on stderr for CI parsers.
|
* BARE_OS_SELFTEST_FORMAT=tap: TAP-style lines on stderr for CI parsers.
|
||||||
* BARE_OS_BOOT_ALLOWLIST=1 and /etc/bare-os/boot.allow: only first-word commands in that file (plus shell builtins) run from trusted rc/onboot snippets.
|
* BARE_OS_BOOT_ALLOWLIST=1 and /etc/bare-os/boot.allow: only first-word commands in that file (plus shell builtins) run from trusted rc/onboot snippets.
|
||||||
*
|
*
|
||||||
|
* BARE_OS_BOOT_MANIFEST_SIGN=1: verify Ed25519 signature in /etc/bare-os/boot.manifest.sig over the raw
|
||||||
|
* manifest bytes; public key from BARE_OS_BOOT_MANIFEST_PUBKEY_HEX (64 hex chars). Uses ctx.bareOsVerifyBootManifestSignature.
|
||||||
|
*
|
||||||
* BARE_OS_BOOT_STRICT=1 or true: first execLine throw in trusted boot snippets calls
|
* BARE_OS_BOOT_STRICT=1 or true: first execLine throw in trusted boot snippets calls
|
||||||
* requestBooterExit(1) and stops further boot phases.
|
* requestBooterExit(1) and stops further boot phases.
|
||||||
*
|
*
|
||||||
@@ -260,6 +263,30 @@ async function loadBootManifest(ctx) {
|
|||||||
bootManifestMemo = null
|
bootManifestMemo = null
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
const signOn =
|
||||||
|
ctx.env &&
|
||||||
|
(ctx.env.BARE_OS_BOOT_MANIFEST_SIGN === '1' ||
|
||||||
|
ctx.env.BARE_OS_BOOT_MANIFEST_SIGN === 'true')
|
||||||
|
if (signOn) {
|
||||||
|
const sigBuf = await drive.get('/etc/bare-os/boot.manifest.sig')
|
||||||
|
const pub =
|
||||||
|
ctx.env && ctx.env.BARE_OS_BOOT_MANIFEST_PUBKEY_HEX
|
||||||
|
? String(ctx.env.BARE_OS_BOOT_MANIFEST_PUBKEY_HEX).trim()
|
||||||
|
: ''
|
||||||
|
const verifyFn = ctx.bareOsVerifyBootManifestSignature
|
||||||
|
if (typeof verifyFn !== 'function' || !pub) {
|
||||||
|
console.error(
|
||||||
|
'[boot] signed manifest requires ctx.bareOsVerifyBootManifestSignature and BARE_OS_BOOT_MANIFEST_PUBKEY_HEX'
|
||||||
|
)
|
||||||
|
bootManifestMemo = null
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
if (!verifyFn(buf, sigBuf, pub)) {
|
||||||
|
console.error('[boot] boot.manifest.json Ed25519 signature verification failed')
|
||||||
|
bootManifestMemo = null
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
bootManifestMemo = JSON.parse(b4a.toString(buf))
|
bootManifestMemo = JSON.parse(b4a.toString(buf))
|
||||||
return bootManifestMemo
|
return bootManifestMemo
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
+190
-190
@@ -7,18 +7,18 @@
|
|||||||
"b4a"
|
"b4a"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/safetyCatch.js",
|
|
||||||
"keys": [
|
|
||||||
"safetyCatch"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/hypercoreIdEncoding.js",
|
"path": "/lib/bare/bundles/hypercoreIdEncoding.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"hypercoreIdEncoding"
|
"hypercoreIdEncoding"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/safetyCatch.js",
|
||||||
|
"keys": [
|
||||||
|
"safetyCatch"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/compactEncoding.js",
|
"path": "/lib/bare/bundles/compactEncoding.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -31,6 +31,12 @@
|
|||||||
"bareUrl"
|
"bareUrl"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/protomux.js",
|
||||||
|
"keys": [
|
||||||
|
"protomux"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareEncoding.js",
|
"path": "/lib/bare/bundles/bareEncoding.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -43,12 +49,6 @@
|
|||||||
"bareEvents"
|
"bareEvents"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/protomux.js",
|
|
||||||
"keys": [
|
|
||||||
"protomux"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/barePath.js",
|
"path": "/lib/bare/bundles/barePath.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -109,12 +109,6 @@
|
|||||||
"bareAsyncHooks"
|
"bareAsyncHooks"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/fetch.js",
|
|
||||||
"keys": [
|
|
||||||
"fetch"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareAtomics.js",
|
"path": "/lib/bare/bundles/bareAtomics.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -127,6 +121,12 @@
|
|||||||
"bareAssert"
|
"bareAssert"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/fetch.js",
|
||||||
|
"keys": [
|
||||||
|
"fetch"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareBmp.js",
|
"path": "/lib/bare/bundles/bareBmp.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -175,24 +175,18 @@
|
|||||||
"bareConsole"
|
"bareConsole"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareDebugLog.js",
|
|
||||||
"keys": [
|
|
||||||
"bareDebugLog"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareDaemon.js",
|
|
||||||
"keys": [
|
|
||||||
"bareDaemon"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareBundleId.js",
|
"path": "/lib/bare/bundles/bareBundleId.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareBundleId"
|
"bareBundleId"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareDebugLog.js",
|
||||||
|
"keys": [
|
||||||
|
"bareDebugLog"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareChannel.js",
|
"path": "/lib/bare/bundles/bareChannel.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -205,6 +199,12 @@
|
|||||||
"bareDelta"
|
"bareDelta"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareDaemon.js",
|
||||||
|
"keys": [
|
||||||
|
"bareDaemon"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareDns.js",
|
"path": "/lib/bare/bundles/bareDns.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -217,6 +217,12 @@
|
|||||||
"bareDiagnosticsChannel"
|
"bareDiagnosticsChannel"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareCov.js",
|
||||||
|
"keys": [
|
||||||
|
"bareCov"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareEnv.js",
|
"path": "/lib/bare/bundles/bareEnv.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -229,36 +235,12 @@
|
|||||||
"bareExif"
|
"bareExif"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareCov.js",
|
|
||||||
"keys": [
|
|
||||||
"bareCov"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareDgram.js",
|
"path": "/lib/bare/bundles/bareDgram.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareDgram"
|
"bareDgram"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareFfmpeg.js",
|
|
||||||
"keys": [
|
|
||||||
"bareFfmpeg"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareFfmpegEncodings.js",
|
|
||||||
"keys": [
|
|
||||||
"bareFfmpegEncodings"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareFormat.js",
|
|
||||||
"keys": [
|
|
||||||
"bareFormat"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareFormData.js",
|
"path": "/lib/bare/bundles/bareFormData.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -266,9 +248,21 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareHeif.js",
|
"path": "/lib/bare/bundles/bareFfmpegEncodings.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareHeif"
|
"bareFfmpegEncodings"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareFfmpeg.js",
|
||||||
|
"keys": [
|
||||||
|
"bareFfmpeg"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareFormat.js",
|
||||||
|
"keys": [
|
||||||
|
"bareFormat"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -283,6 +277,12 @@
|
|||||||
"bareGif"
|
"bareGif"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareHeif.js",
|
||||||
|
"keys": [
|
||||||
|
"bareHeif"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareGtk.js",
|
"path": "/lib/bare/bundles/bareGtk.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -295,42 +295,36 @@
|
|||||||
"bareHrtime"
|
"bareHrtime"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareFs.js",
|
|
||||||
"keys": [
|
|
||||||
"bareFs"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareHttpParser.js",
|
"path": "/lib/bare/bundles/bareHttpParser.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareHttpParser"
|
"bareHttpParser"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareFs.js",
|
||||||
|
"keys": [
|
||||||
|
"bareFs"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareIco.js",
|
"path": "/lib/bare/bundles/bareIco.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareIco"
|
"bareIco"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareImageResample.js",
|
|
||||||
"keys": [
|
|
||||||
"bareImageResample"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareInspect.js",
|
|
||||||
"keys": [
|
|
||||||
"bareInspect"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareHttp1.js",
|
"path": "/lib/bare/bundles/bareHttp1.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareHttp1"
|
"bareHttp1"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareImageResample.js",
|
||||||
|
"keys": [
|
||||||
|
"bareImageResample"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareHttps.js",
|
"path": "/lib/bare/bundles/bareHttps.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -338,15 +332,15 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareJpeg.js",
|
"path": "/lib/bare/bundles/bareInspect.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareJpeg"
|
"bareInspect"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareIntl.js",
|
"path": "/lib/bare/bundles/bareJpeg.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareIntl"
|
"bareJpeg"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -355,24 +349,30 @@
|
|||||||
"bareIpc"
|
"bareIpc"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareIntl.js",
|
||||||
|
"keys": [
|
||||||
|
"bareIntl"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareLief.js",
|
"path": "/lib/bare/bundles/bareLief.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareLief"
|
"bareLief"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareLogger.js",
|
|
||||||
"keys": [
|
|
||||||
"bareLogger"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareLink.js",
|
"path": "/lib/bare/bundles/bareLink.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareLink"
|
"bareLink"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareLogger.js",
|
||||||
|
"keys": [
|
||||||
|
"bareLogger"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareInspector.js",
|
"path": "/lib/bare/bundles/bareInspector.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -386,9 +386,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareModuleResolve.js",
|
"path": "/lib/bare/bundles/bareModule.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareModuleResolve"
|
"bareModule"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -397,24 +397,18 @@
|
|||||||
"bareModuleLexer"
|
"bareModuleLexer"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareModuleResolve.js",
|
||||||
|
"keys": [
|
||||||
|
"bareModuleResolve"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareModuleTraverse.js",
|
"path": "/lib/bare/bundles/bareModuleTraverse.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareModuleTraverse"
|
"bareModuleTraverse"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareModule.js",
|
|
||||||
"keys": [
|
|
||||||
"bareModule"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareNodeFetch.js",
|
|
||||||
"keys": [
|
|
||||||
"bareNodeFetch"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareNdk.js",
|
"path": "/lib/bare/bundles/bareNdk.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -428,21 +422,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareDev.js",
|
"path": "/lib/bare/bundles/bareNodeFetch.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareDev"
|
"bareNodeFetch"
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareOs.js",
|
|
||||||
"keys": [
|
|
||||||
"bareOs"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareOpen.js",
|
|
||||||
"keys": [
|
|
||||||
"bareOpen"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -458,15 +440,21 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/barePerformance.js",
|
"path": "/lib/bare/bundles/bareOpen.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"barePerformance"
|
"bareOpen"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/barePng.js",
|
"path": "/lib/bare/bundles/bareOs.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"barePng"
|
"bareOs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/barePerformance.js",
|
||||||
|
"keys": [
|
||||||
|
"barePerformance"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -476,9 +464,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/barePack.js",
|
"path": "/lib/bare/bundles/barePng.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"barePack"
|
"barePng"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -487,18 +475,36 @@
|
|||||||
"barePipe"
|
"barePipe"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/barePack.js",
|
||||||
|
"keys": [
|
||||||
|
"barePack"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/barePunycode.js",
|
"path": "/lib/bare/bundles/barePunycode.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"barePunycode"
|
"barePunycode"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareDev.js",
|
||||||
|
"keys": [
|
||||||
|
"bareDev"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareQuerystring.js",
|
"path": "/lib/bare/bundles/bareQuerystring.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareQuerystring"
|
"bareQuerystring"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareNodeRuntime.js",
|
||||||
|
"keys": [
|
||||||
|
"bareNodeRuntime"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/barePrebuild.js",
|
"path": "/lib/bare/bundles/barePrebuild.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -512,9 +518,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareNodeRuntime.js",
|
"path": "/lib/bare/bundles/bareProcess.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareNodeRuntime"
|
"bareProcess"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -524,15 +530,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareProcess.js",
|
"path": "/lib/bare/bundles/bareSdl.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareProcess"
|
"bareSdl"
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/barePromClient.js",
|
|
||||||
"keys": [
|
|
||||||
"barePromClient"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -542,9 +542,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareRepl.js",
|
"path": "/lib/bare/bundles/barePromClient.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareRepl"
|
"barePromClient"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -553,18 +553,18 @@
|
|||||||
"bareRpc"
|
"bareRpc"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareSdl.js",
|
|
||||||
"keys": [
|
|
||||||
"bareSdl"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareSemver.js",
|
"path": "/lib/bare/bundles/bareSemver.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareSemver"
|
"bareSemver"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareSidecar.js",
|
||||||
|
"keys": [
|
||||||
|
"bareSidecar"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareRun.js",
|
"path": "/lib/bare/bundles/bareRun.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -578,27 +578,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareSidecar.js",
|
"path": "/lib/bare/bundles/bareRepl.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareSidecar"
|
"bareRepl"
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareStream.js",
|
|
||||||
"keys": [
|
|
||||||
"bareStream"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareStorage.js",
|
|
||||||
"keys": [
|
|
||||||
"bareStorage"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareStdio.js",
|
|
||||||
"keys": [
|
|
||||||
"bareStdio"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -607,6 +589,12 @@
|
|||||||
"bareStringDecoder"
|
"bareStringDecoder"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareStorage.js",
|
||||||
|
"keys": [
|
||||||
|
"bareStorage"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareSvg.js",
|
"path": "/lib/bare/bundles/bareSvg.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -614,9 +602,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareSystemLogger.js",
|
"path": "/lib/bare/bundles/bareStream.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareSystemLogger"
|
"bareStream"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -626,15 +614,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareTiff.js",
|
"path": "/lib/bare/bundles/bareStdio.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareTiff"
|
"bareStdio"
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareTap.js",
|
|
||||||
"keys": [
|
|
||||||
"bareTap"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -644,15 +626,21 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareThread.js",
|
"path": "/lib/bare/bundles/bareTap.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareThread"
|
"bareTap"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareTcp.js",
|
"path": "/lib/bare/bundles/bareTiff.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareTcp"
|
"bareTiff"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareSystemLogger.js",
|
||||||
|
"keys": [
|
||||||
|
"bareSystemLogger"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -668,15 +656,15 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareTls.js",
|
"path": "/lib/bare/bundles/bareThread.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareTls"
|
"bareThread"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareTty.js",
|
"path": "/lib/bare/bundles/bareTcp.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareTty"
|
"bareTcp"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -692,9 +680,15 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareUnpack.js",
|
"path": "/lib/bare/bundles/bareTty.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareUnpack"
|
"bareTty"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareTls.js",
|
||||||
|
"keys": [
|
||||||
|
"bareTls"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -704,9 +698,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareWalkHandles.js",
|
"path": "/lib/bare/bundles/bareUnpack.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareWalkHandles"
|
"bareUnpack"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -716,9 +710,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareUnionBundle.js",
|
"path": "/lib/bare/bundles/bareWalkHandles.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareUnionBundle"
|
"bareWalkHandles"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -740,9 +734,15 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareWhich.js",
|
"path": "/lib/bare/bundles/bareUnionBundle.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareWhich"
|
"bareUnionBundle"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareV8ToIstanbul.js",
|
||||||
|
"keys": [
|
||||||
|
"bareV8ToIstanbul"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -752,9 +752,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareV8ToIstanbul.js",
|
"path": "/lib/bare/bundles/bareWhich.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareV8ToIstanbul"
|
"bareWhich"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -769,12 +769,6 @@
|
|||||||
"bareXdiff"
|
"bareXdiff"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareWs.js",
|
|
||||||
"keys": [
|
|
||||||
"bareWs"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareZlib.js",
|
"path": "/lib/bare/bundles/bareZlib.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -787,6 +781,12 @@
|
|||||||
"bareWorker"
|
"bareWorker"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareWs.js",
|
||||||
|
"keys": [
|
||||||
|
"bareWs"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareZmq.js",
|
"path": "/lib/bare/bundles/bareZmq.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Generated
+1
-2
@@ -1243,7 +1243,6 @@
|
|||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/bare-env/-/bare-env-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/bare-env/-/bare-env-3.0.0.tgz",
|
||||||
"integrity": "sha512-0u964P5ZLAxTi+lW4Kjp7YRJQ5gZr9ycYOtjLxsSrupgMz3sn5Z9n4SH/JIifHwvadsf1brA2JAjP+9IOWwTiw==",
|
"integrity": "sha512-0u964P5ZLAxTi+lW4Kjp7YRJQ5gZr9ycYOtjLxsSrupgMz3sn5Z9n4SH/JIifHwvadsf1brA2JAjP+9IOWwTiw==",
|
||||||
"devOptional": true,
|
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bare-os": "^3.0.1"
|
"bare-os": "^3.0.1"
|
||||||
@@ -2794,7 +2793,6 @@
|
|||||||
"version": "5.2.3",
|
"version": "5.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/bare-subprocess/-/bare-subprocess-5.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/bare-subprocess/-/bare-subprocess-5.2.3.tgz",
|
||||||
"integrity": "sha512-07wwswlV7M3sC9IykbZRZ/jHAkrXFWVLqdBWGv1y0ojCimtRD9hGwxdHmR5FUFmDUZLNsBmTYJNQqgio5+A85Q==",
|
"integrity": "sha512-07wwswlV7M3sC9IykbZRZ/jHAkrXFWVLqdBWGv1y0ojCimtRD9hGwxdHmR5FUFmDUZLNsBmTYJNQqgio5+A85Q==",
|
||||||
"devOptional": true,
|
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bare-env": "^3.0.0",
|
"bare-env": "^3.0.0",
|
||||||
@@ -7020,6 +7018,7 @@
|
|||||||
"b4a": "^1.6.7",
|
"b4a": "^1.6.7",
|
||||||
"bare-os": "^3.8.7",
|
"bare-os": "^3.8.7",
|
||||||
"bare-os-protocol": "*",
|
"bare-os-protocol": "*",
|
||||||
|
"bare-subprocess": "^5.2.3",
|
||||||
"compact-encoding": "^2.18.0",
|
"compact-encoding": "^2.18.0",
|
||||||
"corestore": "^7.2.1",
|
"corestore": "^7.2.1",
|
||||||
"hyperdrive": "^13.3.2",
|
"hyperdrive": "^13.3.2",
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
"pretest": "npm run build -w bare-os-coreutils && npm run build -w bare-os-bare-libs && node scripts/verify-kernel-seeder-parity.mjs && npm run smoke:bare-manifest",
|
"pretest": "npm run build -w bare-os-coreutils && npm run build -w bare-os-bare-libs && node scripts/verify-kernel-seeder-parity.mjs && npm run smoke:bare-manifest",
|
||||||
"test": "npm run test --workspaces --if-present",
|
"test": "npm run test --workspaces --if-present",
|
||||||
"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",
|
||||||
"format": "prettier --write .",
|
"format": "prettier --write .",
|
||||||
"lint": "prettier --check .",
|
"lint": "prettier --check .",
|
||||||
"os:seeder": "npm run build -w bare-os-coreutils && npm run build -w bare-os-bare-libs && node scripts/ensure-pear-node-modules.mjs packages/bare-os-seeder && cd packages/bare-os-seeder && pear run --dev .",
|
"os:seeder": "npm run build -w bare-os-coreutils && npm run build -w bare-os-bare-libs && node scripts/ensure-pear-node-modules.mjs packages/bare-os-seeder && cd packages/bare-os-seeder && pear run --dev .",
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
| Version | Booter (workspace) | Notes |
|
| Version | Booter (workspace) | Notes |
|
||||||
| ------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
| ------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||||
|
| 1.9.0 | 0.1.0 | **`bareOsVerifyBootManifestSignature`**, signed manifest (**`BARE_OS_BOOT_MANIFEST_SIGN`**, **`/etc/bare-os/boot.manifest.sig`**), **`/proc/bare_os_replication`**, **`/proc/bare_os_capabilities`** (+ **`.json`**), **`/proc/bare_os_bootstrap`**, **`/run/bare-os/unit-journal/*.ndjson`**, initd **`BARE_OS_INITD_MAX_PARALLEL`**, unit journal + **`OnFailure=`** / **`FailureAction=`** / **`Before=`**, **`bareOsIpc.duplexJsonRoundTrip`**, **`BARE_OS_VFS_BIN_CACHE`**, **`bareOsRequestMirror`**, **`bareOsExportPersonalSnapshot`**, **`bareOsPearIpcEmit`**, sandbox worker hint (**`BARE_OS_SANDBOX_WORKER`**). Seeder: optional **`BARE_OS_HYPER_MULTISIG_VERIFY`** + **`hyper-multisig`**. Coreutils: **`grep -r`** **`--include`/`--exclude`/`--exclude-dir`**, **`sed -z`**. |
|
||||||
| 1.8.0 | 0.1.0 | **`ctx.bareOsSandboxRunScript`**, **`bareOsBootFileSha256Hex`**, boot phase hooks (**`bareOsRegisterBootPhaseHook`** / **`bareOsInvokeBootPhaseHooks`**), virtual file meta (**`bareOsInvalidateVirtualFile`**, **`bareOsUpdateVirtualFileMeta`**), **`bareOsIpc.createDuplexBridge`**, **`/proc/bare_os_swarm`**, VFS union read (**`BARE_OS_VFS_UNION_PREFIXES`**), **`BARE_OS_URANDOM_CRYPTO=0`**, async **`bareOsRequestPearReload`**, **`bareOsPublishBootReady.subsystems`**, **`verifyBareModuleLockfile`**. Kernel: **`BARE_OS_BOOT_MANIFEST`**, **`/etc/bare-os/selftest.d/`**, stock init hooks. |
|
| 1.8.0 | 0.1.0 | **`ctx.bareOsSandboxRunScript`**, **`bareOsBootFileSha256Hex`**, boot phase hooks (**`bareOsRegisterBootPhaseHook`** / **`bareOsInvokeBootPhaseHooks`**), virtual file meta (**`bareOsInvalidateVirtualFile`**, **`bareOsUpdateVirtualFileMeta`**), **`bareOsIpc.createDuplexBridge`**, **`/proc/bare_os_swarm`**, VFS union read (**`BARE_OS_VFS_UNION_PREFIXES`**), **`BARE_OS_URANDOM_CRYPTO=0`**, async **`bareOsRequestPearReload`**, **`bareOsPublishBootReady.subsystems`**, **`verifyBareModuleLockfile`**. Kernel: **`BARE_OS_BOOT_MANIFEST`**, **`/etc/bare-os/selftest.d/`**, stock init hooks. |
|
||||||
| 1.7.0 | 0.1.0 | **`ctx.bare`**: frozen map of Holepunch-style npm modules for in-image scripts (manifest-driven host `import()` + optional trusted **`/lib/bare/bundles/*.js`** merge). Caps **`bareCtxModules`**, **`bareDriveBundles`**. Env **`BARE_OS_BARE_MODULES`**, **`BARE_OS_BARE_DRIVE_BUNDLES`**. Workspace **`bare-os-bare-libs`** builds seeded bundles. |
|
| 1.7.0 | 0.1.0 | **`ctx.bare`**: frozen map of Holepunch-style npm modules for in-image scripts (manifest-driven host `import()` + optional trusted **`/lib/bare/bundles/*.js`** merge). Caps **`bareCtxModules`**, **`bareDriveBundles`**. Env **`BARE_OS_BARE_MODULES`**, **`BARE_OS_BARE_DRIVE_BUNDLES`**. Workspace **`bare-os-bare-libs`** builds seeded bundles. |
|
||||||
| 1.6.0 | 0.1.0 | Abort/timeout on `execLine`, `readLine`, `runBinCommand`, VFS `readFile`/`writeFile`; IPC fan-out + JSON-RPC token/line limits; HTTP allow/deny + audit; `/proc/bare_os_resources`, `/proc/bare_os_features`; `/run/bare-os/virtual/*`; booter boot phases in `boot.json` (`booterPhases`); optional `ctx.bareOsHostStats`, `ctx.httpFetch` policy wrapper; initd `ReadinessPath` / `ReadinessTimeoutSec`; Pear/sandbox stubs. |
|
| 1.6.0 | 0.1.0 | Abort/timeout on `execLine`, `readLine`, `runBinCommand`, VFS `readFile`/`writeFile`; IPC fan-out + JSON-RPC token/line limits; HTTP allow/deny + audit; `/proc/bare_os_resources`, `/proc/bare_os_features`; `/run/bare-os/virtual/*`; booter boot phases in `boot.json` (`booterPhases`); optional `ctx.bareOsHostStats`, `ctx.httpFetch` policy wrapper; initd `ReadinessPath` / `ReadinessTimeoutSec`; Pear/sandbox stubs. |
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ import {
|
|||||||
runBinCommand,
|
runBinCommand,
|
||||||
runUserScriptFromSource
|
runUserScriptFromSource
|
||||||
} from './lib/kernel-runner.js'
|
} from './lib/kernel-runner.js'
|
||||||
|
import { verifyBootManifestEd25519 } from '#bare-os-boot-manifest-sig'
|
||||||
|
import { getBareInitdJournalNdjson } from './lib/bare-initd-journal.js'
|
||||||
import { createBareOsSandboxContext } from './lib/bare-os-sandbox.js'
|
import { createBareOsSandboxContext } from './lib/bare-os-sandbox.js'
|
||||||
import { createVfs } from './lib/vfs.js'
|
import { createVfs } from './lib/vfs.js'
|
||||||
import { createBareOsIpc } from './lib/bare-os-ipc.js'
|
import { createBareOsIpc } from './lib/bare-os-ipc.js'
|
||||||
@@ -76,7 +78,7 @@ import {
|
|||||||
bareOsListThemeNames
|
bareOsListThemeNames
|
||||||
} from './lib/bare-os-theme-presets.js'
|
} from './lib/bare-os-theme-presets.js'
|
||||||
import './lib/bare-cron.js'
|
import './lib/bare-cron.js'
|
||||||
import { createHash } from 'node:crypto'
|
import { createHash } from 'bare-crypto'
|
||||||
|
|
||||||
const { randomUUID, randomBytes } = bareCrypto
|
const { randomUUID, randomBytes } = bareCrypto
|
||||||
|
|
||||||
@@ -319,7 +321,19 @@ async function executeKernel(disk, store, swarm, initSource) {
|
|||||||
'BARE_OS_YES_MAX_LINES',
|
'BARE_OS_YES_MAX_LINES',
|
||||||
'BARE_OS_SHUF_MAX_LINES',
|
'BARE_OS_SHUF_MAX_LINES',
|
||||||
'BARE_OS_SPLIT_MAX_FILES',
|
'BARE_OS_SPLIT_MAX_FILES',
|
||||||
'BARE_OS_NPROC'
|
'BARE_OS_NPROC',
|
||||||
|
'BARE_OS_BOOT_MANIFEST',
|
||||||
|
'BARE_OS_BOOT_MANIFEST_SIGN',
|
||||||
|
'BARE_OS_BOOT_MANIFEST_PUBKEY_HEX',
|
||||||
|
'BARE_OS_SANDBOX_SCRIPT',
|
||||||
|
'BARE_OS_VFS_UNION_PREFIXES',
|
||||||
|
'BARE_OS_URANDOM_CRYPTO',
|
||||||
|
'BARE_OS_INITD_MAX_PARALLEL',
|
||||||
|
'BARE_OS_VFS_BIN_CACHE',
|
||||||
|
'BARE_OS_SANDBOX_WORKER',
|
||||||
|
'BARE_OS_BLIND_BOOTSTRAP_URL',
|
||||||
|
'BARE_OS_BLIND_BOOTSTRAP_JSON',
|
||||||
|
'BARE_OS_MIRROR_READ_KEY'
|
||||||
]) {
|
]) {
|
||||||
const v = hostEnv[k]
|
const v = hostEnv[k]
|
||||||
if (v != null && v !== '') shellEnv[k] = v
|
if (v != null && v !== '') shellEnv[k] = v
|
||||||
@@ -538,6 +552,57 @@ async function executeKernel(disk, store, swarm, initSource) {
|
|||||||
protocol: 'bare-os-v1'
|
protocol: 'bare-os-v1'
|
||||||
})}\n`
|
})}\n`
|
||||||
},
|
},
|
||||||
|
procBareOsReplicationText() {
|
||||||
|
const tk = topicKey()
|
||||||
|
const peers = disk.peers?.size ?? 0
|
||||||
|
const mk = shellEnv.BARE_OS_MIRROR_READ_KEY
|
||||||
|
return `${JSON.stringify({
|
||||||
|
topicHex: b4a.toString(tk, 'hex'),
|
||||||
|
peerCount: peers,
|
||||||
|
session: sessionStatsRef,
|
||||||
|
mirrorHint: !!(mk && String(mk).trim()),
|
||||||
|
atMs: Date.now()
|
||||||
|
})}\n`
|
||||||
|
},
|
||||||
|
procBareOsCapabilitiesText() {
|
||||||
|
const c = buildBareOsRuntimeCaps(shellEnv)
|
||||||
|
const paths = (c.pseudoFsPaths || []).join('\n ')
|
||||||
|
return [
|
||||||
|
'# bare_os_capabilities (summary; see .json for machine-readable)',
|
||||||
|
`ctxApiVersion: ${c.ctxApiVersion}`,
|
||||||
|
'pipeline: ' + JSON.stringify(c.pipeline),
|
||||||
|
'quotas: ' + JSON.stringify(c.quotas),
|
||||||
|
'pseudoFsPaths:',
|
||||||
|
' ' + paths,
|
||||||
|
'features: ' + JSON.stringify(c.features),
|
||||||
|
''
|
||||||
|
].join('\n')
|
||||||
|
},
|
||||||
|
procBareOsCapabilitiesJsonText() {
|
||||||
|
return `${JSON.stringify(buildBareOsRuntimeCaps(shellEnv), null, 2)}\n`
|
||||||
|
},
|
||||||
|
procBareOsBootstrapText() {
|
||||||
|
const url = shellEnv.BARE_OS_BLIND_BOOTSTRAP_URL
|
||||||
|
const j = shellEnv.BARE_OS_BLIND_BOOTSTRAP_JSON
|
||||||
|
if (url && String(url).trim()) {
|
||||||
|
return `${JSON.stringify({
|
||||||
|
blindBootstrapUrl: String(url).trim(),
|
||||||
|
source: 'BARE_OS_BLIND_BOOTSTRAP_URL'
|
||||||
|
})}\n`
|
||||||
|
}
|
||||||
|
if (j && String(j).trim()) {
|
||||||
|
try {
|
||||||
|
return `${JSON.stringify({
|
||||||
|
blindBootstrap: JSON.parse(String(j)),
|
||||||
|
source: 'BARE_OS_BLIND_BOOTSTRAP_JSON'
|
||||||
|
})}\n`
|
||||||
|
} catch {
|
||||||
|
return `${JSON.stringify({ error: 'invalid BARE_OS_BLIND_BOOTSTRAP_JSON' })}\n`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return '{}\n'
|
||||||
|
},
|
||||||
|
getUnitJournalNdjson: (unit) => getBareInitdJournalNdjson(unit),
|
||||||
getVirtualReaders: () => virtualReaderEntries,
|
getVirtualReaders: () => virtualReaderEntries,
|
||||||
unionReadPrefixes: parseUnionReadPrefixes(shellEnv),
|
unionReadPrefixes: parseUnionReadPrefixes(shellEnv),
|
||||||
sysClassNetLoText() {
|
sysClassNetLoText() {
|
||||||
@@ -888,6 +953,18 @@ async function executeKernel(disk, store, swarm, initSource) {
|
|||||||
if (shellEnv.BARE_OS_SANDBOX_SCRIPT === '0') {
|
if (shellEnv.BARE_OS_SANDBOX_SCRIPT === '0') {
|
||||||
throw new Error('bareOsSandboxRunScript: disabled by BARE_OS_SANDBOX_SCRIPT=0')
|
throw new Error('bareOsSandboxRunScript: disabled by BARE_OS_SANDBOX_SCRIPT=0')
|
||||||
}
|
}
|
||||||
|
if (
|
||||||
|
shellEnv.BARE_OS_SANDBOX_WORKER === '1' ||
|
||||||
|
shellEnv.BARE_OS_SANDBOX_WORKER === 'true'
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
this.console?.log?.(
|
||||||
|
'[bare-os] BARE_OS_SANDBOX_WORKER: running inline (bare-thread needs a file entrypoint)'
|
||||||
|
)
|
||||||
|
} catch {
|
||||||
|
/* ignore */
|
||||||
|
}
|
||||||
|
}
|
||||||
const sb = createBareOsSandboxContext(this)
|
const sb = createBareOsSandboxContext(this)
|
||||||
const { raceWithAbortAndTimeout } = await import('./lib/bare-os-abort.js')
|
const { raceWithAbortAndTimeout } = await import('./lib/bare-os-abort.js')
|
||||||
return raceWithAbortAndTimeout(
|
return raceWithAbortAndTimeout(
|
||||||
@@ -957,6 +1034,86 @@ async function executeKernel(disk, store, swarm, initSource) {
|
|||||||
bareOsBootFileSha256Hex(buf) {
|
bareOsBootFileSha256Hex(buf) {
|
||||||
const u8 = buf instanceof Uint8Array ? buf : new Uint8Array(buf)
|
const u8 = buf instanceof Uint8Array ? buf : new Uint8Array(buf)
|
||||||
return createHash('sha256').update(u8).digest('hex')
|
return createHash('sha256').update(u8).digest('hex')
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Ed25519 verify for `/etc/bare-os/boot.manifest.json` when `BARE_OS_BOOT_MANIFEST_SIGN=1`.
|
||||||
|
* @param {Uint8Array | ArrayBuffer | null | undefined} manifestBytes
|
||||||
|
* @param {Uint8Array | ArrayBuffer | null | undefined} signatureBytes
|
||||||
|
* @param {string} [publicKeyHex] optional override; else `BARE_OS_BOOT_MANIFEST_PUBKEY_HEX`
|
||||||
|
*/
|
||||||
|
bareOsVerifyBootManifestSignature(manifestBytes, signatureBytes, publicKeyHex) {
|
||||||
|
const pub =
|
||||||
|
publicKeyHex != null && String(publicKeyHex).trim()
|
||||||
|
? String(publicKeyHex).trim()
|
||||||
|
: shellEnv.BARE_OS_BOOT_MANIFEST_PUBKEY_HEX || ''
|
||||||
|
const m =
|
||||||
|
manifestBytes instanceof Uint8Array
|
||||||
|
? manifestBytes
|
||||||
|
: manifestBytes
|
||||||
|
? new Uint8Array(manifestBytes)
|
||||||
|
: null
|
||||||
|
const s =
|
||||||
|
signatureBytes instanceof Uint8Array
|
||||||
|
? signatureBytes
|
||||||
|
: signatureBytes
|
||||||
|
? new Uint8Array(signatureBytes)
|
||||||
|
: null
|
||||||
|
return verifyBootManifestEd25519(m, s, pub)
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Ask host to attach a mirror read key (Pear / Holepunch); emits process event when available.
|
||||||
|
* @param {{ key?: string, label?: string }} [opts]
|
||||||
|
*/
|
||||||
|
async bareOsRequestMirror(opts = {}) {
|
||||||
|
if (typeof globalThis.process?.emit === 'function') {
|
||||||
|
try {
|
||||||
|
globalThis.process.emit('bare-os:mirror-request', {
|
||||||
|
key: opts.key != null ? String(opts.key) : '',
|
||||||
|
label: opts.label != null ? String(opts.label) : ''
|
||||||
|
})
|
||||||
|
} catch {
|
||||||
|
/* ignore */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { ok: true, hint: 'Host should listen for process "bare-os:mirror-request".' }
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Hint host to export a personal-drive snapshot (corestore-snapshot style).
|
||||||
|
* @param {{ label?: string }} [opts]
|
||||||
|
*/
|
||||||
|
async bareOsExportPersonalSnapshot(opts = {}) {
|
||||||
|
if (typeof globalThis.process?.emit === 'function') {
|
||||||
|
try {
|
||||||
|
globalThis.process.emit('bare-os:export-personal-snapshot', {
|
||||||
|
label: opts.label != null ? String(opts.label) : 'default',
|
||||||
|
ts: Date.now()
|
||||||
|
})
|
||||||
|
} catch {
|
||||||
|
/* ignore */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
ok: true,
|
||||||
|
hint: 'Host listens for process "bare-os:export-personal-snapshot".'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Passthrough for Pear IPC–style host bridges (see developer guide).
|
||||||
|
* @param {string} channel
|
||||||
|
* @param {Record<string, unknown>} payload
|
||||||
|
*/
|
||||||
|
bareOsPearIpcEmit(channel, payload) {
|
||||||
|
if (typeof globalThis.process?.emit !== 'function') return false
|
||||||
|
try {
|
||||||
|
globalThis.process.emit('bare-os:pear-ipc', {
|
||||||
|
channel: String(channel || ''),
|
||||||
|
payload: payload && typeof payload === 'object' ? payload : {},
|
||||||
|
ts: Date.now()
|
||||||
|
})
|
||||||
|
return true
|
||||||
|
} catch {
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
/**
|
||||||
|
* In-memory structured initd journal (NDJSON lines) exposed under /run/bare-os/unit-journal/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const MAX_LINES_PER_UNIT = 400
|
||||||
|
const MAX_LINE_UTF8 = 4096
|
||||||
|
|
||||||
|
/** @type {Map<string, string[]>} */
|
||||||
|
const linesByUnit = new Map()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} unit
|
||||||
|
* @param {Record<string, unknown>} rec
|
||||||
|
*/
|
||||||
|
export function appendBareInitdJournal(unit, rec) {
|
||||||
|
if (!unit || !/^[a-zA-Z0-9._-]+$/.test(unit)) return
|
||||||
|
const payload = { ts: Date.now(), unit, ...rec }
|
||||||
|
let line
|
||||||
|
try {
|
||||||
|
line = JSON.stringify(payload) + '\n'
|
||||||
|
} catch {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (line.length > MAX_LINE_UTF8) return
|
||||||
|
let arr = linesByUnit.get(unit)
|
||||||
|
if (!arr) {
|
||||||
|
arr = []
|
||||||
|
linesByUnit.set(unit, arr)
|
||||||
|
}
|
||||||
|
arr.push(line)
|
||||||
|
while (arr.length > MAX_LINES_PER_UNIT) arr.shift()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} unit
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export function getBareInitdJournalNdjson(unit) {
|
||||||
|
const arr = linesByUnit.get(unit)
|
||||||
|
return arr ? arr.join('') : ''
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @returns {string[]} */
|
||||||
|
export function listBareInitdJournalUnits() {
|
||||||
|
return [...linesByUnit.keys()].sort()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function clearBareInitdJournalForTests() {
|
||||||
|
linesByUnit.clear()
|
||||||
|
}
|
||||||
@@ -12,7 +12,9 @@ export const BARE_INITD_DISABLED_FILE = '~/.config/bare-os/initd/disabled.txt'
|
|||||||
* Optional `name.unit` files under this directory. Supports `[Unit]` keys:
|
* Optional `name.unit` files under this directory. Supports `[Unit]` keys:
|
||||||
* After=, Requires=, Wants=, TimeoutStartSec=, TimeoutStopSec=, Restart=, RestartSec=, ExecStartPost=, SocketActivationIpc=,
|
* After=, Requires=, Wants=, TimeoutStartSec=, TimeoutStopSec=, Restart=, RestartSec=, ExecStartPost=, SocketActivationIpc=,
|
||||||
* ReadinessPath= (VFS path until exists), ReadinessTimeoutSec=,
|
* ReadinessPath= (VFS path until exists), ReadinessTimeoutSec=,
|
||||||
* ExecHealthCmd=, HealthIntervalSec=, HealthFailureThreshold=, BareMaxExecDepth=.
|
* ExecHealthCmd=, HealthIntervalSec=, HealthFailureThreshold=, BareMaxExecDepth=,
|
||||||
|
* Before= (reverse edge: listed units start after this one), OnFailure= (execLine after restart exhausted),
|
||||||
|
* FailureAction=exec|none (default exec when OnFailure is set).
|
||||||
*/
|
*/
|
||||||
export const BARE_INITD_UNITS_DIR = '~/.config/bare-os/units'
|
export const BARE_INITD_UNITS_DIR = '~/.config/bare-os/units'
|
||||||
|
|
||||||
@@ -43,7 +45,10 @@ export const BARE_INITD_DEFAULT_AFTER = Object.freeze({
|
|||||||
* execHealthCmd: string | null,
|
* execHealthCmd: string | null,
|
||||||
* healthIntervalSec: number | null,
|
* healthIntervalSec: number | null,
|
||||||
* healthFailureThreshold: number | null,
|
* healthFailureThreshold: number | null,
|
||||||
* bareMaxExecDepth: number | null
|
* bareMaxExecDepth: number | null,
|
||||||
|
* before: string[],
|
||||||
|
* onFailure: string | null,
|
||||||
|
* failureAction: 'exec' | 'none'
|
||||||
* }} BareInitdUnitDropIn
|
* }} BareInitdUnitDropIn
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -64,7 +69,10 @@ export function emptyUnitDropIn() {
|
|||||||
execHealthCmd: null,
|
execHealthCmd: null,
|
||||||
healthIntervalSec: null,
|
healthIntervalSec: null,
|
||||||
healthFailureThreshold: null,
|
healthFailureThreshold: null,
|
||||||
bareMaxExecDepth: null
|
bareMaxExecDepth: null,
|
||||||
|
before: [],
|
||||||
|
onFailure: null,
|
||||||
|
failureAction: 'exec'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,11 +141,20 @@ export function parseUnitDropInText(text) {
|
|||||||
} else if (key === 'baremaxexecdepth') {
|
} else if (key === 'baremaxexecdepth') {
|
||||||
const n = Number.parseInt(val, 10)
|
const n = Number.parseInt(val, 10)
|
||||||
if (Number.isFinite(n) && n > 0) out.bareMaxExecDepth = n
|
if (Number.isFinite(n) && n > 0) out.bareMaxExecDepth = n
|
||||||
|
} else if (key === 'before') {
|
||||||
|
out.before.push(...parseList(val))
|
||||||
|
} else if (key === 'onfailure') {
|
||||||
|
if (val.trim()) out.onFailure = val
|
||||||
|
} else if (key === 'failureaction') {
|
||||||
|
const v = val.toLowerCase()
|
||||||
|
if (v === 'none') out.failureAction = 'none'
|
||||||
|
else out.failureAction = 'exec'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out.after = [...new Set(out.after)]
|
out.after = [...new Set(out.after)]
|
||||||
out.requires = [...new Set(out.requires)]
|
out.requires = [...new Set(out.requires)]
|
||||||
out.wants = [...new Set(out.wants)]
|
out.wants = [...new Set(out.wants)]
|
||||||
|
out.before = [...new Set(out.before)]
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,7 +183,10 @@ export function mergeUnitDropIns(base, user) {
|
|||||||
healthIntervalSec: user.healthIntervalSec ?? base.healthIntervalSec,
|
healthIntervalSec: user.healthIntervalSec ?? base.healthIntervalSec,
|
||||||
healthFailureThreshold:
|
healthFailureThreshold:
|
||||||
user.healthFailureThreshold ?? base.healthFailureThreshold,
|
user.healthFailureThreshold ?? base.healthFailureThreshold,
|
||||||
bareMaxExecDepth: user.bareMaxExecDepth ?? base.bareMaxExecDepth
|
bareMaxExecDepth: user.bareMaxExecDepth ?? base.bareMaxExecDepth,
|
||||||
|
before: pickArr(user.before, base.before),
|
||||||
|
onFailure: user.onFailure ?? base.onFailure,
|
||||||
|
failureAction: user.failureAction ?? base.failureAction
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -271,6 +291,15 @@ export async function loadInitdUnitDropIns(vfs, services, defaultAfter) {
|
|||||||
fromFile.after = [...new Set([...def, ...fromFile.after])]
|
fromFile.after = [...new Set([...def, ...fromFile.after])]
|
||||||
map.set(s.name, fromFile)
|
map.set(s.name, fromFile)
|
||||||
}
|
}
|
||||||
|
for (const [name, di] of map) {
|
||||||
|
for (const b of di.before) {
|
||||||
|
if (!/^[a-zA-Z0-9._-]+$/.test(b)) continue
|
||||||
|
const target = map.get(b)
|
||||||
|
if (target) {
|
||||||
|
target.after = [...new Set([...target.after, name])]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return map
|
return map
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
INITD_LOG,
|
INITD_LOG,
|
||||||
KERNEL_CONSOLE_LOG
|
KERNEL_CONSOLE_LOG
|
||||||
} from './bare-os-var-log.js'
|
} from './bare-os-var-log.js'
|
||||||
|
import { appendBareInitdJournal } from './bare-initd-journal.js'
|
||||||
import {
|
import {
|
||||||
BARE_INITD_DEFAULT_AFTER,
|
BARE_INITD_DEFAULT_AFTER,
|
||||||
emptyUnitDropIn,
|
emptyUnitDropIn,
|
||||||
@@ -287,6 +288,7 @@ function scheduleUnitHealth(ctx, name, dropIn) {
|
|||||||
/* ignore */
|
/* ignore */
|
||||||
}
|
}
|
||||||
void appendVarLog(ctx, INITD_LOG, name, msg)
|
void appendVarLog(ctx, INITD_LOG, name, msg)
|
||||||
|
appendBareInitdJournal(name, { event: 'health_failed', error: msg })
|
||||||
}
|
}
|
||||||
})()
|
})()
|
||||||
}, intervalMs)
|
}, intervalMs)
|
||||||
@@ -370,6 +372,94 @@ export async function restartBareService(ctx, name) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {BareService} s
|
||||||
|
* @param {import('./bare-initd-user.js').BareInitdUnitDropIn} dropIn
|
||||||
|
*/
|
||||||
|
async function runOnFailureHookForUnit(ctx, s, dropIn) {
|
||||||
|
if (!dropIn.onFailure?.trim() || dropIn.failureAction === 'none') return
|
||||||
|
if (typeof ctx.execLine !== 'function') return
|
||||||
|
try {
|
||||||
|
await ctx.execLine(dropIn.onFailure.trim())
|
||||||
|
appendBareInitdJournal(s.name, { event: 'on_failure_ran' })
|
||||||
|
} catch (e) {
|
||||||
|
const msg = e?.message || String(e)
|
||||||
|
try {
|
||||||
|
ctx.console?.error?.(`[bare-initd] ${s.name} OnFailure: ${msg}`)
|
||||||
|
} catch {
|
||||||
|
/* ignore */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Record<string, unknown>} ctx
|
||||||
|
* @param {BareService} s
|
||||||
|
* @param {import('./bare-initd-user.js').BareInitdUnitDropIn} dropIn
|
||||||
|
*/
|
||||||
|
async function startNormalBareInitdUnit(ctx, s, dropIn) {
|
||||||
|
const t0 = Date.now()
|
||||||
|
const startSec = dropIn.timeoutStartSec
|
||||||
|
const maxAttempts =
|
||||||
|
dropIn.restart === 'on-failure' || dropIn.restart === 'always' ? 3 : 1
|
||||||
|
const restartDelayMs =
|
||||||
|
dropIn.restartSec != null && dropIn.restartSec >= 0
|
||||||
|
? Math.round(dropIn.restartSec * 1000)
|
||||||
|
: 1000
|
||||||
|
appendBareInitdJournal(s.name, { event: 'start_scheduled' })
|
||||||
|
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
||||||
|
try {
|
||||||
|
if (attempt > 0) {
|
||||||
|
appendBareInitdJournal(s.name, { event: 'restart_attempt', attempt })
|
||||||
|
await new Promise((r) => setTimeout(r, restartDelayMs))
|
||||||
|
}
|
||||||
|
await withTimeoutSec(s.start(ctx), startSec, `start ${s.name}`)
|
||||||
|
if (dropIn.readinessPath) {
|
||||||
|
const rsec = dropIn.readinessTimeoutSec ?? 30
|
||||||
|
await waitForReadinessPath(ctx, dropIn.readinessPath, rsec)
|
||||||
|
}
|
||||||
|
runtime.set(s.name, { phase: 'active', startedAtMs: t0 })
|
||||||
|
appendBareInitdJournal(s.name, { event: 'active', attempt })
|
||||||
|
const post = dropIn.execStartPost
|
||||||
|
if (post && typeof ctx.execLine === 'function' && post.trim()) {
|
||||||
|
try {
|
||||||
|
await ctx.execLine(post.trim())
|
||||||
|
} catch (e) {
|
||||||
|
const msg = e?.message || String(e)
|
||||||
|
try {
|
||||||
|
ctx.console?.error?.(
|
||||||
|
`[bare-initd] ${s.name} ExecStartPost: ${msg}`
|
||||||
|
)
|
||||||
|
} catch {
|
||||||
|
/* ignore */
|
||||||
|
}
|
||||||
|
void appendVarLog(ctx, INITD_LOG, s.name, 'ExecStartPost: ' + msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scheduleUnitHealth(ctx, s.name, dropIn)
|
||||||
|
return
|
||||||
|
} catch (e) {
|
||||||
|
const msg = e?.message || String(e)
|
||||||
|
appendBareInitdJournal(s.name, {
|
||||||
|
event: 'start_error',
|
||||||
|
attempt,
|
||||||
|
error: msg
|
||||||
|
})
|
||||||
|
if (attempt === maxAttempts - 1) {
|
||||||
|
runtime.set(s.name, { phase: 'failed', startedAtMs: t0, error: msg })
|
||||||
|
try {
|
||||||
|
ctx.console?.error?.(`[bare-initd] ${s.name}: ${msg}`)
|
||||||
|
} catch {
|
||||||
|
/* ignore */
|
||||||
|
}
|
||||||
|
void appendVarLog(ctx, INITD_LOG, s.name, msg)
|
||||||
|
await runOnFailureHookForUnit(ctx, s, dropIn)
|
||||||
|
appendBareInitdJournal(s.name, { event: 'failed_final', error: msg })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Record<string, unknown>} ctx
|
* @param {Record<string, unknown>} ctx
|
||||||
*/
|
*/
|
||||||
@@ -377,10 +467,12 @@ export async function startBareInitd(ctx) {
|
|||||||
runtime.clear()
|
runtime.clear()
|
||||||
await ensureBareOsVarLogTree(ctx)
|
await ensureBareOsVarLogTree(ctx)
|
||||||
const vfs = ctx.vfs
|
const vfs = ctx.vfs
|
||||||
|
/** @type {Map<string, import('./bare-initd-user.js').BareInitdUnitDropIn>} */
|
||||||
|
let dropInsMap = new Map()
|
||||||
let ordered = registry
|
let ordered = registry
|
||||||
if (vfs && typeof vfs.readFile === 'function') {
|
if (vfs && typeof vfs.readFile === 'function') {
|
||||||
const disabled = await readInitdDisabledSet(vfs)
|
const disabled = await readInitdDisabledSet(vfs)
|
||||||
const dropIns = await loadInitdUnitDropIns(
|
dropInsMap = await loadInitdUnitDropIns(
|
||||||
vfs,
|
vfs,
|
||||||
registry,
|
registry,
|
||||||
BARE_INITD_DEFAULT_AFTER
|
BARE_INITD_DEFAULT_AFTER
|
||||||
@@ -391,7 +483,7 @@ export async function startBareInitd(ctx) {
|
|||||||
const wantsMap = new Map()
|
const wantsMap = new Map()
|
||||||
/** @type {Map<string, string[]>} */
|
/** @type {Map<string, string[]>} */
|
||||||
const afterMap = new Map()
|
const afterMap = new Map()
|
||||||
for (const [name, di] of dropIns) {
|
for (const [name, di] of dropInsMap) {
|
||||||
afterMap.set(name, di.after)
|
afterMap.set(name, di.after)
|
||||||
requiresMap.set(name, di.requires)
|
requiresMap.set(name, di.requires)
|
||||||
wantsMap.set(name, di.wants)
|
wantsMap.set(name, di.wants)
|
||||||
@@ -404,34 +496,88 @@ export async function startBareInitd(ctx) {
|
|||||||
wantsMap
|
wantsMap
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
for (const s of ordered) {
|
|
||||||
const t0 = Date.now()
|
|
||||||
/** @type {import('./bare-initd-user.js').BareInitdUnitDropIn} */
|
|
||||||
const dropIn =
|
|
||||||
vfs && typeof vfs.readFile === 'function'
|
|
||||||
? await readUnitDropIn(vfs, s.name)
|
|
||||||
: emptyUnitDropIn()
|
|
||||||
|
|
||||||
if (dropIn.socketActivationIpc && ctx.bareOsIpc) {
|
const rawPar = ctx.env && ctx.env.BARE_OS_INITD_MAX_PARALLEL
|
||||||
|
const maxP = Math.max(
|
||||||
|
1,
|
||||||
|
Math.min(32, Number.parseInt(String(rawPar ?? '1'), 10) || 1)
|
||||||
|
)
|
||||||
|
|
||||||
|
const activeNames = new Set(ordered.map((s) => s.name))
|
||||||
|
/** @type {Map<string, Set<string>>} */
|
||||||
|
const prereq = new Map()
|
||||||
|
for (const s of ordered) {
|
||||||
|
const di = dropInsMap.get(s.name) || emptyUnitDropIn()
|
||||||
|
const inc = new Set()
|
||||||
|
for (const a of di.after) if (activeNames.has(a)) inc.add(a)
|
||||||
|
for (const r of di.requires) if (activeNames.has(r)) inc.add(r)
|
||||||
|
for (const w of di.wants) if (activeNames.has(w)) inc.add(w)
|
||||||
|
prereq.set(s.name, inc)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param {string[]} names @returns {string[][]} */
|
||||||
|
function computeLevels(names) {
|
||||||
|
const remaining = new Set(names)
|
||||||
|
/** @type {string[][]} */
|
||||||
|
const levels = []
|
||||||
|
while (remaining.size) {
|
||||||
|
const ready = [...remaining].filter((n) => {
|
||||||
|
for (const p of prereq.get(n) || []) {
|
||||||
|
if (remaining.has(p)) return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
if (!ready.length) {
|
||||||
|
const n = [...remaining].sort()[0]
|
||||||
|
levels.push([n])
|
||||||
|
remaining.delete(n)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ready.sort()
|
||||||
|
levels.push(ready)
|
||||||
|
for (const n of ready) remaining.delete(n)
|
||||||
|
}
|
||||||
|
return levels
|
||||||
|
}
|
||||||
|
|
||||||
|
const levels = computeLevels(ordered.map((s) => s.name))
|
||||||
|
|
||||||
|
for (const level of levels) {
|
||||||
|
/** @type {{ s: BareService, di: import('./bare-initd-user.js').BareInitdUnitDropIn }[]} */
|
||||||
|
const sockets = []
|
||||||
|
/** @type {{ s: BareService, di: import('./bare-initd-user.js').BareInitdUnitDropIn }[]} */
|
||||||
|
const normals = []
|
||||||
|
for (const name of level) {
|
||||||
|
const s = registry.find((x) => x.name === name)
|
||||||
|
if (!s) continue
|
||||||
|
const di = dropInsMap.get(s.name) || emptyUnitDropIn()
|
||||||
|
if (di.socketActivationIpc && ctx.bareOsIpc) sockets.push({ s, di })
|
||||||
|
else normals.push({ s, di })
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const { s, di } of sockets) {
|
||||||
|
const t0 = Date.now()
|
||||||
|
appendBareInitdJournal(s.name, { event: 'socket_wait' })
|
||||||
try {
|
try {
|
||||||
ctx.bareOsIpc.create(dropIn.socketActivationIpc)
|
ctx.bareOsIpc.create(di.socketActivationIpc)
|
||||||
} catch {
|
} catch {
|
||||||
/* exists */
|
/* exists */
|
||||||
}
|
}
|
||||||
runtime.set(s.name, { phase: 'inactive', startedAtMs: t0 })
|
runtime.set(s.name, { phase: 'inactive', startedAtMs: t0 })
|
||||||
const ipcName = dropIn.socketActivationIpc
|
const ipcName = di.socketActivationIpc
|
||||||
const startSec = dropIn.timeoutStartSec
|
const startSec = di.timeoutStartSec
|
||||||
void (async () => {
|
void (async () => {
|
||||||
try {
|
try {
|
||||||
await ctx.bareOsIpc.take(ipcName)
|
await ctx.bareOsIpc.take(ipcName)
|
||||||
const t1 = Date.now()
|
const t1 = Date.now()
|
||||||
await withTimeoutSec(s.start(ctx), startSec, `start ${s.name}`)
|
await withTimeoutSec(s.start(ctx), startSec, `start ${s.name}`)
|
||||||
if (dropIn.readinessPath) {
|
if (di.readinessPath) {
|
||||||
const rsec = dropIn.readinessTimeoutSec ?? 30
|
const rsec = di.readinessTimeoutSec ?? 30
|
||||||
await waitForReadinessPath(ctx, dropIn.readinessPath, rsec)
|
await waitForReadinessPath(ctx, di.readinessPath, rsec)
|
||||||
}
|
}
|
||||||
runtime.set(s.name, { phase: 'active', startedAtMs: t1 })
|
runtime.set(s.name, { phase: 'active', startedAtMs: t1 })
|
||||||
const post = dropIn.execStartPost
|
appendBareInitdJournal(s.name, { event: 'active', socket: true })
|
||||||
|
const post = di.execStartPost
|
||||||
if (post && typeof ctx.execLine === 'function' && post.trim()) {
|
if (post && typeof ctx.execLine === 'function' && post.trim()) {
|
||||||
try {
|
try {
|
||||||
await ctx.execLine(post.trim())
|
await ctx.execLine(post.trim())
|
||||||
@@ -447,7 +593,7 @@ export async function startBareInitd(ctx) {
|
|||||||
void appendVarLog(ctx, INITD_LOG, s.name, 'ExecStartPost: ' + msg)
|
void appendVarLog(ctx, INITD_LOG, s.name, 'ExecStartPost: ' + msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
scheduleUnitHealth(ctx, s.name, dropIn)
|
scheduleUnitHealth(ctx, s.name, di)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const msg = e?.message || String(e)
|
const msg = e?.message || String(e)
|
||||||
runtime.set(s.name, { phase: 'failed', startedAtMs: t0, error: msg })
|
runtime.set(s.name, { phase: 'failed', startedAtMs: t0, error: msg })
|
||||||
@@ -457,59 +603,17 @@ export async function startBareInitd(ctx) {
|
|||||||
/* ignore */
|
/* ignore */
|
||||||
}
|
}
|
||||||
void appendVarLog(ctx, INITD_LOG, s.name, msg)
|
void appendVarLog(ctx, INITD_LOG, s.name, msg)
|
||||||
|
appendBareInitdJournal(s.name, { event: 'failed_socket', error: msg })
|
||||||
|
await runOnFailureHookForUnit(ctx, s, di)
|
||||||
}
|
}
|
||||||
})()
|
})()
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const startSec = dropIn.timeoutStartSec
|
for (let i = 0; i < normals.length; i += maxP) {
|
||||||
const maxAttempts =
|
const chunk = normals.slice(i, i + maxP)
|
||||||
dropIn.restart === 'on-failure' || dropIn.restart === 'always' ? 3 : 1
|
await Promise.all(
|
||||||
const restartDelayMs =
|
chunk.map(({ s, di }) => startNormalBareInitdUnit(ctx, s, di))
|
||||||
dropIn.restartSec != null && dropIn.restartSec >= 0
|
)
|
||||||
? Math.round(dropIn.restartSec * 1000)
|
|
||||||
: 1000
|
|
||||||
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
||||||
try {
|
|
||||||
if (attempt > 0) {
|
|
||||||
await new Promise((r) => setTimeout(r, restartDelayMs))
|
|
||||||
}
|
|
||||||
await withTimeoutSec(s.start(ctx), startSec, `start ${s.name}`)
|
|
||||||
if (dropIn.readinessPath) {
|
|
||||||
const rsec = dropIn.readinessTimeoutSec ?? 30
|
|
||||||
await waitForReadinessPath(ctx, dropIn.readinessPath, rsec)
|
|
||||||
}
|
|
||||||
runtime.set(s.name, { phase: 'active', startedAtMs: t0 })
|
|
||||||
const post = dropIn.execStartPost
|
|
||||||
if (post && typeof ctx.execLine === 'function' && post.trim()) {
|
|
||||||
try {
|
|
||||||
await ctx.execLine(post.trim())
|
|
||||||
} catch (e) {
|
|
||||||
const msg = e?.message || String(e)
|
|
||||||
try {
|
|
||||||
ctx.console?.error?.(
|
|
||||||
`[bare-initd] ${s.name} ExecStartPost: ${msg}`
|
|
||||||
)
|
|
||||||
} catch {
|
|
||||||
/* ignore */
|
|
||||||
}
|
|
||||||
void appendVarLog(ctx, INITD_LOG, s.name, 'ExecStartPost: ' + msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
scheduleUnitHealth(ctx, s.name, dropIn)
|
|
||||||
break
|
|
||||||
} catch (e) {
|
|
||||||
const msg = e?.message || String(e)
|
|
||||||
if (attempt === maxAttempts - 1) {
|
|
||||||
runtime.set(s.name, { phase: 'failed', startedAtMs: t0, error: msg })
|
|
||||||
try {
|
|
||||||
ctx.console?.error?.(`[bare-initd] ${s.name}: ${msg}`)
|
|
||||||
} catch {
|
|
||||||
/* ignore */
|
|
||||||
}
|
|
||||||
void appendVarLog(ctx, INITD_LOG, s.name, msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
'use strict'
|
||||||
|
/**
|
||||||
|
* CJS bridge: `require('bare-crypto/lib/key.js')` bypasses package `exports` on Node and avoids
|
||||||
|
* pulling Bare-only addon graphs into ESM static analysis. Pear/Bare executes this file with CJS
|
||||||
|
* `require` (no Node `module` built-in needed from our ESM entry).
|
||||||
|
*/
|
||||||
|
const { verify } = require('bare-crypto')
|
||||||
|
const { Ed25519PublicKey } = require('bare-crypto/lib/key.js')
|
||||||
|
module.exports = { verify, Ed25519PublicKey }
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
/**
|
||||||
|
* Pear / Bare: load crypto via CJS bridge (`require` provided by runtime). No `node:module`.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import b4a from 'b4a'
|
||||||
|
import bridge from './bare-os-boot-manifest-sig-bridge.cjs'
|
||||||
|
|
||||||
|
const { verify, Ed25519PublicKey } = bridge
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Uint8Array | null | undefined} manifestBytes
|
||||||
|
* @param {Uint8Array | null | undefined} signatureBytes
|
||||||
|
* @param {string} publicKeyHex 64 hex chars (32-byte Ed25519 public key)
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
export function verifyBootManifestEd25519(manifestBytes, signatureBytes, publicKeyHex) {
|
||||||
|
if (!manifestBytes?.length || !signatureBytes?.length) return false
|
||||||
|
const hex = String(publicKeyHex || '').trim().toLowerCase().replace(/^0x/, '')
|
||||||
|
if (!/^[0-9a-f]{64}$/.test(hex)) return false
|
||||||
|
/** @type {Uint8Array} */
|
||||||
|
let sig
|
||||||
|
if (signatureBytes.length === 64) {
|
||||||
|
sig = signatureBytes
|
||||||
|
} else {
|
||||||
|
const t = b4a.toString(signatureBytes, 'utf8').trim()
|
||||||
|
if (/^[0-9a-f]{128}$/i.test(t)) {
|
||||||
|
sig = b4a.from(t, 'hex')
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
sig = b4a.from(t, 'base64')
|
||||||
|
} catch {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sig.length !== 64) return false
|
||||||
|
try {
|
||||||
|
const pub = b4a.from(hex, 'hex')
|
||||||
|
const key = new Ed25519PublicKey(pub)
|
||||||
|
return verify('ed25519', manifestBytes, key, sig)
|
||||||
|
} catch {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/**
|
||||||
|
* Node (brittle-node tests / `node index.js`): lazy `require('bare-crypto')` inside try/catch.
|
||||||
|
* `bare-crypto` is Bare-oriented and typically throws under Node; callers still get a boolean.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import b4a from 'b4a'
|
||||||
|
import { createRequire } from 'node:module'
|
||||||
|
|
||||||
|
const require = createRequire(import.meta.url)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Uint8Array | null | undefined} manifestBytes
|
||||||
|
* @param {Uint8Array | null | undefined} signatureBytes
|
||||||
|
* @param {string} publicKeyHex 64 hex chars (32-byte Ed25519 public key)
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
export function verifyBootManifestEd25519(manifestBytes, signatureBytes, publicKeyHex) {
|
||||||
|
if (!manifestBytes?.length || !signatureBytes?.length) return false
|
||||||
|
const hex = String(publicKeyHex || '').trim().toLowerCase().replace(/^0x/, '')
|
||||||
|
if (!/^[0-9a-f]{64}$/.test(hex)) return false
|
||||||
|
/** @type {Uint8Array} */
|
||||||
|
let sig
|
||||||
|
if (signatureBytes.length === 64) {
|
||||||
|
sig = signatureBytes
|
||||||
|
} else {
|
||||||
|
const t = b4a.toString(signatureBytes, 'utf8').trim()
|
||||||
|
if (/^[0-9a-f]{128}$/i.test(t)) {
|
||||||
|
sig = b4a.from(t, 'hex')
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
sig = b4a.from(t, 'base64')
|
||||||
|
} catch {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sig.length !== 64) return false
|
||||||
|
try {
|
||||||
|
const { verify } = require('bare-crypto')
|
||||||
|
const { Ed25519PublicKey } = require('bare-crypto/lib/key.js')
|
||||||
|
const pub = b4a.from(hex, 'hex')
|
||||||
|
const key = new Ed25519PublicKey(pub)
|
||||||
|
return verify('ed25519', manifestBytes, key, sig)
|
||||||
|
} catch {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.8.0'
|
export const BARE_OS_CTX_API_VERSION = '1.9.0'
|
||||||
|
|||||||
+18
@@ -56,6 +56,11 @@ export interface BareOsIpc {
|
|||||||
take: () => Promise<Uint8Array>
|
take: () => Promise<Uint8Array>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
duplexJsonRoundTrip(
|
||||||
|
baseName: string,
|
||||||
|
payload: Record<string, unknown>,
|
||||||
|
opts?: { timeoutMs?: number }
|
||||||
|
): Promise<Record<string, unknown>>
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BareOsHostStats {
|
export interface BareOsHostStats {
|
||||||
@@ -120,6 +125,19 @@ export interface BareOsKernelContext {
|
|||||||
opts?: BareOsAbortOpts
|
opts?: BareOsAbortOpts
|
||||||
): Promise<void>
|
): Promise<void>
|
||||||
bareOsBootFileSha256Hex(buf: Uint8Array | ArrayBuffer): string
|
bareOsBootFileSha256Hex(buf: Uint8Array | ArrayBuffer): string
|
||||||
|
bareOsVerifyBootManifestSignature(
|
||||||
|
manifestBytes: Uint8Array | ArrayBuffer | null | undefined,
|
||||||
|
signatureBytes: Uint8Array | ArrayBuffer | null | undefined,
|
||||||
|
publicKeyHex?: string
|
||||||
|
): boolean
|
||||||
|
bareOsRequestMirror(opts?: {
|
||||||
|
key?: string
|
||||||
|
label?: string
|
||||||
|
}): Promise<{ ok: boolean; hint: string }>
|
||||||
|
bareOsExportPersonalSnapshot(opts?: {
|
||||||
|
label?: string
|
||||||
|
}): Promise<{ ok: boolean; hint: string }>
|
||||||
|
bareOsPearIpcEmit(channel: string, payload: Record<string, unknown>): boolean
|
||||||
bareOsRequestPearReload(opts?: {
|
bareOsRequestPearReload(opts?: {
|
||||||
persistRequest?: boolean
|
persistRequest?: boolean
|
||||||
}): Promise<{
|
}): Promise<{
|
||||||
|
|||||||
@@ -295,6 +295,31 @@ export function createBareOsIpc(opts = {}) {
|
|||||||
take: () => ba.take()
|
take: () => ba.take()
|
||||||
}
|
}
|
||||||
return { left, right }
|
return { left, right }
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* One JSON line request/response over a {@link createDuplexBridge} side (`bareOsRpc: "2"`).
|
||||||
|
* @param {{ push: (buf: Uint8Array | ArrayBuffer) => void, take: () => Promise<Uint8Array> }} side
|
||||||
|
* @param {Record<string, unknown>} request
|
||||||
|
*/
|
||||||
|
async duplexJsonRoundTrip(side, request) {
|
||||||
|
if (!request || typeof request !== 'object')
|
||||||
|
throw new Error('bare-os ipc: invalid duplex request')
|
||||||
|
const line =
|
||||||
|
JSON.stringify({
|
||||||
|
bareOsRpc: '2',
|
||||||
|
...request
|
||||||
|
}) + '\n'
|
||||||
|
if (line.length > maxJsonLine) {
|
||||||
|
throw new Error('bare-os ipc: duplex JSON line exceeds max length')
|
||||||
|
}
|
||||||
|
side.push(b4a.from(line, 'utf8'))
|
||||||
|
const u8 = await side.take()
|
||||||
|
const res = b4a.toString(u8, 'utf8').trim()
|
||||||
|
if (res.length > maxJsonLine) {
|
||||||
|
throw new Error('bare-os ipc: duplex response too large')
|
||||||
|
}
|
||||||
|
return /** @type {Record<string, unknown>} */ (JSON.parse(res))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,10 @@ export const BARE_OS_PSEUDO_FS_PATHS = Object.freeze([
|
|||||||
'/proc/bare_os_quotas',
|
'/proc/bare_os_quotas',
|
||||||
'/proc/bare_os_resources',
|
'/proc/bare_os_resources',
|
||||||
'/proc/bare_os_swarm',
|
'/proc/bare_os_swarm',
|
||||||
|
'/proc/bare_os_replication',
|
||||||
|
'/proc/bare_os_capabilities',
|
||||||
|
'/proc/bare_os_capabilities.json',
|
||||||
|
'/proc/bare_os_bootstrap',
|
||||||
'/proc/bare_os_session_stats',
|
'/proc/bare_os_session_stats',
|
||||||
'/proc/bare_os_version',
|
'/proc/bare_os_version',
|
||||||
'/proc/cpuinfo',
|
'/proc/cpuinfo',
|
||||||
@@ -52,6 +56,7 @@ export const BARE_OS_PSEUDO_FS_PATHS = Object.freeze([
|
|||||||
'/run/bare-os/ready',
|
'/run/bare-os/ready',
|
||||||
'/run/bare-os/session',
|
'/run/bare-os/session',
|
||||||
'/run/bare-os/units',
|
'/run/bare-os/units',
|
||||||
|
'/run/bare-os/unit-journal',
|
||||||
'/run/bare-os/virtual',
|
'/run/bare-os/virtual',
|
||||||
'/run/bare-os/ipc',
|
'/run/bare-os/ipc',
|
||||||
'/sys',
|
'/sys',
|
||||||
@@ -165,7 +170,29 @@ export function buildBareOsRuntimeCaps(shellEnv) {
|
|||||||
shellEnv.BARE_OS_BOOT_MANIFEST === '1' ||
|
shellEnv.BARE_OS_BOOT_MANIFEST === '1' ||
|
||||||
shellEnv.BARE_OS_BOOT_MANIFEST === 'true',
|
shellEnv.BARE_OS_BOOT_MANIFEST === 'true',
|
||||||
bareModuleLockfile: true,
|
bareModuleLockfile: true,
|
||||||
oidcPublishHook: true
|
oidcPublishHook: true,
|
||||||
|
ipcDuplexJsonRpc: true,
|
||||||
|
procReplicationSnapshot: true,
|
||||||
|
procCapabilitiesExport: true,
|
||||||
|
blindBootstrapProc: true,
|
||||||
|
initdUnitJournal: true,
|
||||||
|
initdParallelStart:
|
||||||
|
Number.parseInt(
|
||||||
|
String(shellEnv.BARE_OS_INITD_MAX_PARALLEL || '1'),
|
||||||
|
10
|
||||||
|
) > 1,
|
||||||
|
vfsBinReadCache:
|
||||||
|
shellEnv.BARE_OS_VFS_BIN_CACHE === '1' ||
|
||||||
|
shellEnv.BARE_OS_VFS_BIN_CACHE === 'true',
|
||||||
|
bootManifestEd25519:
|
||||||
|
shellEnv.BARE_OS_BOOT_MANIFEST_SIGN === '1' ||
|
||||||
|
shellEnv.BARE_OS_BOOT_MANIFEST_SIGN === 'true',
|
||||||
|
sandboxWorkerHint:
|
||||||
|
shellEnv.BARE_OS_SANDBOX_WORKER === '1' ||
|
||||||
|
shellEnv.BARE_OS_SANDBOX_WORKER === 'true',
|
||||||
|
mirrorRequestHook: true,
|
||||||
|
personalSnapshotExportHook: true,
|
||||||
|
pearIpcEmitHook: true
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -367,6 +367,16 @@ export function tokenize(line) {
|
|||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if (c === '(') {
|
||||||
|
tokens.push({ type: 'op', value: '(' })
|
||||||
|
i++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (c === ')') {
|
||||||
|
tokens.push({ type: 'op', value: ')' })
|
||||||
|
i++
|
||||||
|
continue
|
||||||
|
}
|
||||||
if (c === '<') {
|
if (c === '<') {
|
||||||
if (line[i + 1] === '<' && line[i + 2] === '<') {
|
if (line[i + 1] === '<' && line[i + 2] === '<') {
|
||||||
tokens.push({ type: 'op', value: '<<<' })
|
tokens.push({ type: 'op', value: '<<<' })
|
||||||
@@ -418,7 +428,9 @@ export function tokenize(line) {
|
|||||||
ch === '>' ||
|
ch === '>' ||
|
||||||
ch === '<' ||
|
ch === '<' ||
|
||||||
ch === ';' ||
|
ch === ';' ||
|
||||||
ch === '&'
|
ch === '&' ||
|
||||||
|
ch === '(' ||
|
||||||
|
ch === ')'
|
||||||
)
|
)
|
||||||
break
|
break
|
||||||
word += ch
|
word += ch
|
||||||
@@ -1175,6 +1187,8 @@ function splitTopLevelStatements(tokens) {
|
|||||||
else if (t.value === 'fi') depth = Math.max(0, depth - 1)
|
else if (t.value === 'fi') depth = Math.max(0, depth - 1)
|
||||||
else if (t.value === 'while' || t.value === 'for') depth++
|
else if (t.value === 'while' || t.value === 'for') depth++
|
||||||
else if (t.value === 'done') depth = Math.max(0, depth - 1)
|
else if (t.value === 'done') depth = Math.max(0, depth - 1)
|
||||||
|
else if (t.value === 'case') depth++
|
||||||
|
else if (t.value === 'esac') depth = Math.max(0, depth - 1)
|
||||||
}
|
}
|
||||||
if (t.type === 'op' && t.value === ';' && depth === 0) {
|
if (t.type === 'op' && t.value === ';' && depth === 0) {
|
||||||
if (cur.length) out.push(cur)
|
if (cur.length) out.push(cur)
|
||||||
@@ -1204,6 +1218,8 @@ function splitTopLevelByAmpersand(tokens) {
|
|||||||
else if (t.value === 'fi') depth = Math.max(0, depth - 1)
|
else if (t.value === 'fi') depth = Math.max(0, depth - 1)
|
||||||
else if (t.value === 'while' || t.value === 'for') depth++
|
else if (t.value === 'while' || t.value === 'for') depth++
|
||||||
else if (t.value === 'done') depth = Math.max(0, depth - 1)
|
else if (t.value === 'done') depth = Math.max(0, depth - 1)
|
||||||
|
else if (t.value === 'case') depth++
|
||||||
|
else if (t.value === 'esac') depth = Math.max(0, depth - 1)
|
||||||
}
|
}
|
||||||
if (t.type === 'op' && t.value === '&' && depth === 0) {
|
if (t.type === 'op' && t.value === '&' && depth === 0) {
|
||||||
out.push(cur)
|
out.push(cur)
|
||||||
@@ -1447,6 +1463,129 @@ async function execForConstruct(ctx, tokens) {
|
|||||||
return 'ok'
|
return 'ok'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Token[]} toks
|
||||||
|
* @param {Record<string, string>} env
|
||||||
|
* @returns {string[]}
|
||||||
|
*/
|
||||||
|
function casePatternList(toks, env) {
|
||||||
|
/** @type {string[]} */
|
||||||
|
const out = []
|
||||||
|
/** @type {Token[]} */
|
||||||
|
let cur = []
|
||||||
|
for (const t of toks) {
|
||||||
|
if (t.type === 'op' && t.value === '|') {
|
||||||
|
if (cur.length) {
|
||||||
|
const s = cur.map((w) => w.value).join(' ')
|
||||||
|
out.push(expandWord(s.trim(), env))
|
||||||
|
cur = []
|
||||||
|
}
|
||||||
|
} else if (t.type === 'word') {
|
||||||
|
cur.push(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cur.length) {
|
||||||
|
const s = cur.map((w) => w.value).join(' ')
|
||||||
|
out.push(expandWord(s.trim(), env))
|
||||||
|
}
|
||||||
|
return out.filter(Boolean)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} subject
|
||||||
|
* @param {string} pat
|
||||||
|
*/
|
||||||
|
function casePatternMatches(subject, pat) {
|
||||||
|
if (pat === '*') return true
|
||||||
|
return subject === pat
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `case WORD in pattern) list ;; … esac` — bounded branches; patterns support `|` alternation and `*`.
|
||||||
|
* @param {Record<string, unknown>} ctx
|
||||||
|
* @param {Token[]} tokens
|
||||||
|
* @returns {Promise<'exit' | 'ok'>}
|
||||||
|
*/
|
||||||
|
async function execCaseConstruct(ctx, tokens) {
|
||||||
|
const last = tokens[tokens.length - 1]
|
||||||
|
if (last.type !== 'word' || last.value !== 'esac') {
|
||||||
|
ctx.console.error('shell: case: missing esac')
|
||||||
|
ctx.exitCode = 2
|
||||||
|
return 'ok'
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
tokens.length < 5 ||
|
||||||
|
tokens[1].type !== 'word' ||
|
||||||
|
tokens[2].type !== 'word' ||
|
||||||
|
tokens[2].value !== 'in'
|
||||||
|
) {
|
||||||
|
ctx.console.error('shell: case: expected `case WORD in`')
|
||||||
|
ctx.exitCode = 2
|
||||||
|
return 'ok'
|
||||||
|
}
|
||||||
|
const env = ctx.vfs.env
|
||||||
|
const subj = expandWord(tokens[1].value, env)
|
||||||
|
const maxBranches = Number.parseInt(
|
||||||
|
ctx.vfs?.env?.BARE_OS_SHELL_CASE_MAX_BRANCHES || '32',
|
||||||
|
10
|
||||||
|
)
|
||||||
|
const cap = Number.isFinite(maxBranches) && maxBranches > 0 ? maxBranches : 32
|
||||||
|
let i = 3
|
||||||
|
let branches = 0
|
||||||
|
while (i < tokens.length - 1) {
|
||||||
|
if (++branches > cap) {
|
||||||
|
ctx.console.error('shell: case: too many branches (see BARE_OS_SHELL_CASE_MAX_BRANCHES)')
|
||||||
|
ctx.exitCode = 2
|
||||||
|
return 'ok'
|
||||||
|
}
|
||||||
|
let paren = -1
|
||||||
|
for (let k = i; k < tokens.length - 1; k++) {
|
||||||
|
const t = tokens[k]
|
||||||
|
if (t.type === 'op' && t.value === ')') {
|
||||||
|
paren = k
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (paren < 0) {
|
||||||
|
ctx.console.error('shell: case: expected )')
|
||||||
|
ctx.exitCode = 2
|
||||||
|
return 'ok'
|
||||||
|
}
|
||||||
|
const patToks = tokens.slice(i, paren)
|
||||||
|
let dsemi = -1
|
||||||
|
for (let k = paren + 1; k < tokens.length - 1; k++) {
|
||||||
|
const t = tokens[k]
|
||||||
|
const n = tokens[k + 1]
|
||||||
|
if (
|
||||||
|
t.type === 'op' &&
|
||||||
|
t.value === ';' &&
|
||||||
|
n &&
|
||||||
|
n.type === 'op' &&
|
||||||
|
n.value === ';'
|
||||||
|
) {
|
||||||
|
dsemi = k
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (dsemi < 0) {
|
||||||
|
ctx.console.error('shell: case: expected ;;')
|
||||||
|
ctx.exitCode = 2
|
||||||
|
return 'ok'
|
||||||
|
}
|
||||||
|
const bodyToks = tokens.slice(paren + 1, dsemi)
|
||||||
|
const pats = casePatternList(patToks, env)
|
||||||
|
const matched = pats.some((p) => casePatternMatches(subj, p))
|
||||||
|
if (matched) {
|
||||||
|
const r = await execSemicolonLists(ctx, bodyToks)
|
||||||
|
if (r === 'exit') return 'exit'
|
||||||
|
return 'ok'
|
||||||
|
}
|
||||||
|
i = dsemi + 2
|
||||||
|
}
|
||||||
|
ctx.exitCode = 0
|
||||||
|
return 'ok'
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Record<string, unknown>} ctx
|
* @param {Record<string, unknown>} ctx
|
||||||
* @param {Token[]} stmt
|
* @param {Token[]} stmt
|
||||||
@@ -1460,6 +1599,8 @@ async function dispatchShellStatement(ctx, stmt) {
|
|||||||
return execWhileConstruct(ctx, stmt)
|
return execWhileConstruct(ctx, stmt)
|
||||||
if (head?.type === 'word' && head.value === 'for')
|
if (head?.type === 'word' && head.value === 'for')
|
||||||
return execForConstruct(ctx, stmt)
|
return execForConstruct(ctx, stmt)
|
||||||
|
if (head?.type === 'word' && head.value === 'case')
|
||||||
|
return execCaseConstruct(ctx, stmt)
|
||||||
return execAndOrList(ctx, stmt)
|
return execAndOrList(ctx, stmt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
readInitdDisabledSet,
|
readInitdDisabledSet,
|
||||||
writeInitdDisabledSet
|
writeInitdDisabledSet
|
||||||
} from './bare-initd-user.js'
|
} from './bare-initd-user.js'
|
||||||
|
import { getBareInitdJournalNdjson } from './bare-initd-journal.js'
|
||||||
|
|
||||||
/** @param {Uint8Array | null} buf @param {number} maxLines */
|
/** @param {Uint8Array | null} buf @param {number} maxLines */
|
||||||
function tailUtf8Lines(buf, maxLines) {
|
function tailUtf8Lines(buf, maxLines) {
|
||||||
@@ -112,6 +113,13 @@ export async function runSystemctlCli(ctx, argv) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
await printLogTail(ctx, def.logPath, lines, 'journalctl')
|
await printLogTail(ctx, def.logPath, lines, 'journalctl')
|
||||||
|
const jtext = getBareInitdJournalNdjson(unit)
|
||||||
|
if (jtext.trim()) {
|
||||||
|
const jl = jtext.trimEnd().split(/\r?\n/)
|
||||||
|
const tailJ = jl.slice(-lines).join('\n')
|
||||||
|
ctx.console.log('--- unit journal (NDJSON tail) ---')
|
||||||
|
ctx.console.log(tailJ)
|
||||||
|
}
|
||||||
ctx.exitCode = ctx.exitCode ?? 0
|
ctx.exitCode = ctx.exitCode ?? 0
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import unixPathResolve from 'unix-path-resolve'
|
import unixPathResolve from 'unix-path-resolve'
|
||||||
import b4a from 'b4a'
|
import b4a from 'b4a'
|
||||||
import { raceWithAbortAndTimeout } from './bare-os-abort.js'
|
import { raceWithAbortAndTimeout } from './bare-os-abort.js'
|
||||||
|
import { listBareInitdJournalUnits } from './bare-initd-journal.js'
|
||||||
import {
|
import {
|
||||||
extractBareOs,
|
extractBareOs,
|
||||||
identityNames,
|
identityNames,
|
||||||
@@ -48,6 +49,11 @@ const DIR_MARKER = '.bareos_empty'
|
|||||||
* procBareOsResourcesText?: () => string,
|
* procBareOsResourcesText?: () => string,
|
||||||
* procBareOsFeaturesText?: () => string,
|
* procBareOsFeaturesText?: () => string,
|
||||||
* procBareOsSwarmText?: () => string,
|
* procBareOsSwarmText?: () => string,
|
||||||
|
* procBareOsReplicationText?: () => string,
|
||||||
|
* procBareOsCapabilitiesText?: () => string,
|
||||||
|
* procBareOsCapabilitiesJsonText?: () => string,
|
||||||
|
* procBareOsBootstrapText?: () => string,
|
||||||
|
* getUnitJournalNdjson?: (unit: string) => string,
|
||||||
* getVirtualReaders?: () => Map<string, unknown>,
|
* getVirtualReaders?: () => Map<string, unknown>,
|
||||||
* unionReadPrefixes?: readonly string[],
|
* unionReadPrefixes?: readonly string[],
|
||||||
* sysClassNetLoText?: () => string
|
* sysClassNetLoText?: () => string
|
||||||
@@ -118,6 +124,26 @@ export function createVfs(
|
|||||||
typeof vfsOptions.procBareOsSwarmText === 'function'
|
typeof vfsOptions.procBareOsSwarmText === 'function'
|
||||||
? vfsOptions.procBareOsSwarmText
|
? vfsOptions.procBareOsSwarmText
|
||||||
: null
|
: null
|
||||||
|
const procBareOsReplicationText =
|
||||||
|
typeof vfsOptions.procBareOsReplicationText === 'function'
|
||||||
|
? vfsOptions.procBareOsReplicationText
|
||||||
|
: null
|
||||||
|
const procBareOsCapabilitiesText =
|
||||||
|
typeof vfsOptions.procBareOsCapabilitiesText === 'function'
|
||||||
|
? vfsOptions.procBareOsCapabilitiesText
|
||||||
|
: null
|
||||||
|
const procBareOsCapabilitiesJsonText =
|
||||||
|
typeof vfsOptions.procBareOsCapabilitiesJsonText === 'function'
|
||||||
|
? vfsOptions.procBareOsCapabilitiesJsonText
|
||||||
|
: null
|
||||||
|
const procBareOsBootstrapText =
|
||||||
|
typeof vfsOptions.procBareOsBootstrapText === 'function'
|
||||||
|
? vfsOptions.procBareOsBootstrapText
|
||||||
|
: null
|
||||||
|
const getUnitJournalNdjson =
|
||||||
|
typeof vfsOptions.getUnitJournalNdjson === 'function'
|
||||||
|
? vfsOptions.getUnitJournalNdjson
|
||||||
|
: null
|
||||||
const getVirtualReaders =
|
const getVirtualReaders =
|
||||||
typeof vfsOptions.getVirtualReaders === 'function'
|
typeof vfsOptions.getVirtualReaders === 'function'
|
||||||
? vfsOptions.getVirtualReaders
|
? vfsOptions.getVirtualReaders
|
||||||
@@ -127,6 +153,11 @@ export function createVfs(
|
|||||||
(s) => typeof s === 'string' && s.startsWith('/')
|
(s) => typeof s === 'string' && s.startsWith('/')
|
||||||
)
|
)
|
||||||
: []
|
: []
|
||||||
|
const binCacheEnabled =
|
||||||
|
env.BARE_OS_VFS_BIN_CACHE === '1' || env.BARE_OS_VFS_BIN_CACHE === 'true'
|
||||||
|
/** @type {Map<string, Uint8Array>} */
|
||||||
|
const binReadCache = binCacheEnabled ? new Map() : null
|
||||||
|
const BIN_READ_CACHE_MAX = 64
|
||||||
const sysClassNetLoText =
|
const sysClassNetLoText =
|
||||||
typeof vfsOptions.sysClassNetLoText === 'function'
|
typeof vfsOptions.sysClassNetLoText === 'function'
|
||||||
? vfsOptions.sysClassNetLoText
|
? vfsOptions.sysClassNetLoText
|
||||||
@@ -321,6 +352,30 @@ export function createVfs(
|
|||||||
const t = procBareOsSwarmText ? procBareOsSwarmText() : '{}\n'
|
const t = procBareOsSwarmText ? procBareOsSwarmText() : '{}\n'
|
||||||
return utf8Encode(t)
|
return utf8Encode(t)
|
||||||
}
|
}
|
||||||
|
if (f === 'bare_os_replication') {
|
||||||
|
const t = procBareOsReplicationText
|
||||||
|
? procBareOsReplicationText()
|
||||||
|
: '{}\n'
|
||||||
|
return utf8Encode(t)
|
||||||
|
}
|
||||||
|
if (f === 'bare_os_capabilities') {
|
||||||
|
const t = procBareOsCapabilitiesText
|
||||||
|
? procBareOsCapabilitiesText()
|
||||||
|
: '(no snapshot)\n'
|
||||||
|
return utf8Encode(t)
|
||||||
|
}
|
||||||
|
if (f === 'bare_os_capabilities_json') {
|
||||||
|
const t = procBareOsCapabilitiesJsonText
|
||||||
|
? procBareOsCapabilitiesJsonText()
|
||||||
|
: '{}\n'
|
||||||
|
return utf8Encode(t)
|
||||||
|
}
|
||||||
|
if (f === 'bare_os_bootstrap') {
|
||||||
|
const t = procBareOsBootstrapText
|
||||||
|
? procBareOsBootstrapText()
|
||||||
|
: '{}\n'
|
||||||
|
return utf8Encode(t)
|
||||||
|
}
|
||||||
if (f === 'net_dev') {
|
if (f === 'net_dev') {
|
||||||
const t = procNetDevText
|
const t = procNetDevText
|
||||||
? procNetDevText()
|
? procNetDevText()
|
||||||
@@ -338,6 +393,13 @@ export function createVfs(
|
|||||||
: '# bare-initd: no snapshot provider\n'
|
: '# bare-initd: no snapshot provider\n'
|
||||||
return utf8Encode(t)
|
return utf8Encode(t)
|
||||||
}
|
}
|
||||||
|
if (k === 'run' && f === 'unit_journal' && getUnitJournalNdjson) {
|
||||||
|
const u =
|
||||||
|
/** @type {{ unitJournalName?: string }} */ (routePseudo).unitJournalName ||
|
||||||
|
''
|
||||||
|
const t = getUnitJournalNdjson(u)
|
||||||
|
return utf8Encode(t || '')
|
||||||
|
}
|
||||||
if (k === 'run' && f === 'boot_profile') {
|
if (k === 'run' && f === 'boot_profile') {
|
||||||
const t = bootProfileText ? bootProfileText() : '\n'
|
const t = bootProfileText ? bootProfileText() : '\n'
|
||||||
return utf8Encode(t)
|
return utf8Encode(t)
|
||||||
@@ -489,6 +551,38 @@ export function createVfs(
|
|||||||
file: 'bare_os_swarm'
|
file: 'bare_os_swarm'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (sub === 'bare_os_replication') {
|
||||||
|
return {
|
||||||
|
virtualPseudo: true,
|
||||||
|
kind: 'proc',
|
||||||
|
node: 'file',
|
||||||
|
file: 'bare_os_replication'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sub === 'bare_os_capabilities') {
|
||||||
|
return {
|
||||||
|
virtualPseudo: true,
|
||||||
|
kind: 'proc',
|
||||||
|
node: 'file',
|
||||||
|
file: 'bare_os_capabilities'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sub === 'bare_os_capabilities.json') {
|
||||||
|
return {
|
||||||
|
virtualPseudo: true,
|
||||||
|
kind: 'proc',
|
||||||
|
node: 'file',
|
||||||
|
file: 'bare_os_capabilities_json'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sub === 'bare_os_bootstrap') {
|
||||||
|
return {
|
||||||
|
virtualPseudo: true,
|
||||||
|
kind: 'proc',
|
||||||
|
node: 'file',
|
||||||
|
file: 'bare_os_bootstrap'
|
||||||
|
}
|
||||||
|
}
|
||||||
if (sub === 'net' || sub === 'net/') {
|
if (sub === 'net' || sub === 'net/') {
|
||||||
return { virtualPseudo: true, kind: 'proc', node: 'dir', dir: 'net' }
|
return { virtualPseudo: true, kind: 'proc', node: 'dir', dir: 'net' }
|
||||||
}
|
}
|
||||||
@@ -512,6 +606,29 @@ export function createVfs(
|
|||||||
if (n === '/run/bare-os/units') {
|
if (n === '/run/bare-os/units') {
|
||||||
return { virtualPseudo: true, kind: 'run', node: 'file', file: 'units' }
|
return { virtualPseudo: true, kind: 'run', node: 'file', file: 'units' }
|
||||||
}
|
}
|
||||||
|
if (n === '/run/bare-os/unit-journal' || n === '/run/bare-os/unit-journal/') {
|
||||||
|
return {
|
||||||
|
virtualPseudo: true,
|
||||||
|
kind: 'run',
|
||||||
|
node: 'dir',
|
||||||
|
dir: 'unit_journal_root'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const uj = '/run/bare-os/unit-journal/'
|
||||||
|
if (n.startsWith(uj)) {
|
||||||
|
const seg = n.slice(uj.length).replace(/\/+$/, '')
|
||||||
|
if (/^[a-zA-Z0-9._-]+\.ndjson$/.test(seg)) {
|
||||||
|
return {
|
||||||
|
virtualPseudo: true,
|
||||||
|
kind: 'run',
|
||||||
|
node: 'file',
|
||||||
|
file: 'unit_journal',
|
||||||
|
unitJournalName: seg.replace(/\.ndjson$/, '')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (n === '/run/bare-os/boot_profile') {
|
if (n === '/run/bare-os/boot_profile') {
|
||||||
return {
|
return {
|
||||||
virtualPseudo: true,
|
virtualPseudo: true,
|
||||||
@@ -1197,8 +1314,12 @@ export function createVfs(
|
|||||||
}
|
}
|
||||||
if (pr.kind === 'proc' && pr.node === 'root') {
|
if (pr.kind === 'proc' && pr.node === 'root') {
|
||||||
return [
|
return [
|
||||||
|
'bare_os_bootstrap',
|
||||||
|
'bare_os_capabilities',
|
||||||
|
'bare_os_capabilities.json',
|
||||||
'bare_os_features',
|
'bare_os_features',
|
||||||
'bare_os_quotas',
|
'bare_os_quotas',
|
||||||
|
'bare_os_replication',
|
||||||
'bare_os_resources',
|
'bare_os_resources',
|
||||||
'bare_os_session_stats',
|
'bare_os_session_stats',
|
||||||
'bare_os_swarm',
|
'bare_os_swarm',
|
||||||
@@ -1245,11 +1366,20 @@ export function createVfs(
|
|||||||
'ipc',
|
'ipc',
|
||||||
'ready',
|
'ready',
|
||||||
'session',
|
'session',
|
||||||
|
'unit-journal',
|
||||||
'units',
|
'units',
|
||||||
'virtual'
|
'virtual'
|
||||||
]
|
]
|
||||||
return bareOsIpc ? base : base.filter((x) => x !== 'ipc')
|
return bareOsIpc ? base : base.filter((x) => x !== 'ipc')
|
||||||
}
|
}
|
||||||
|
if (
|
||||||
|
pr.kind === 'run' &&
|
||||||
|
pr.node === 'dir' &&
|
||||||
|
pr.dir === 'unit_journal_root' &&
|
||||||
|
getUnitJournalNdjson
|
||||||
|
) {
|
||||||
|
return listBareInitdJournalUnits().map((u) => `${u}.ndjson`)
|
||||||
|
}
|
||||||
if (
|
if (
|
||||||
pr.kind === 'run' &&
|
pr.kind === 'run' &&
|
||||||
pr.node === 'dir' &&
|
pr.node === 'dir' &&
|
||||||
@@ -1546,7 +1676,24 @@ export function createVfs(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return drive.get(p, { follow: true })
|
if (binReadCache && drive === systemDrive && abs.startsWith('/bin/')) {
|
||||||
|
const hit = binReadCache.get(abs)
|
||||||
|
if (hit) return new Uint8Array(hit)
|
||||||
|
}
|
||||||
|
const got = await drive.get(p, { follow: true })
|
||||||
|
if (
|
||||||
|
binReadCache &&
|
||||||
|
drive === systemDrive &&
|
||||||
|
abs.startsWith('/bin/') &&
|
||||||
|
got
|
||||||
|
) {
|
||||||
|
if (binReadCache.size >= BIN_READ_CACHE_MAX) {
|
||||||
|
const first = binReadCache.keys().next().value
|
||||||
|
binReadCache.delete(first)
|
||||||
|
}
|
||||||
|
binReadCache.set(abs, new Uint8Array(got))
|
||||||
|
}
|
||||||
|
return got
|
||||||
})(),
|
})(),
|
||||||
abortOpts,
|
abortOpts,
|
||||||
'vfs.readFile'
|
'vfs.readFile'
|
||||||
|
|||||||
@@ -75,6 +75,10 @@
|
|||||||
"node:url": {
|
"node:url": {
|
||||||
"bare": "bare-url",
|
"bare": "bare-url",
|
||||||
"default": "node:url"
|
"default": "node:url"
|
||||||
|
},
|
||||||
|
"#bare-os-boot-manifest-sig": {
|
||||||
|
"bare": "./lib/bare-os-boot-manifest-sig.bare.js",
|
||||||
|
"default": "./lib/bare-os-boot-manifest-sig.node.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
|
|||||||
@@ -855,8 +855,12 @@ test('vfs /proc /sys read-only pseudo files; write rejected', async (t) => {
|
|||||||
t.ok(root.includes('run'))
|
t.ok(root.includes('run'))
|
||||||
t.ok(root.includes('dev'))
|
t.ok(root.includes('dev'))
|
||||||
t.alike(await vfs.readdir('/proc').then((a) => [...a].sort()), [
|
t.alike(await vfs.readdir('/proc').then((a) => [...a].sort()), [
|
||||||
|
'bare_os_bootstrap',
|
||||||
|
'bare_os_capabilities',
|
||||||
|
'bare_os_capabilities.json',
|
||||||
'bare_os_features',
|
'bare_os_features',
|
||||||
'bare_os_quotas',
|
'bare_os_quotas',
|
||||||
|
'bare_os_replication',
|
||||||
'bare_os_resources',
|
'bare_os_resources',
|
||||||
'bare_os_session_stats',
|
'bare_os_session_stats',
|
||||||
'bare_os_swarm',
|
'bare_os_swarm',
|
||||||
@@ -917,6 +921,7 @@ test('vfs /proc /sys read-only pseudo files; write rejected', async (t) => {
|
|||||||
'ipc',
|
'ipc',
|
||||||
'ready',
|
'ready',
|
||||||
'session',
|
'session',
|
||||||
|
'unit-journal',
|
||||||
'units',
|
'units',
|
||||||
'virtual'
|
'virtual'
|
||||||
])
|
])
|
||||||
@@ -1199,6 +1204,7 @@ test('buildBareOsRuntimeCaps matches ctx API version and pipeline env', async (t
|
|||||||
t.ok(caps.pseudoFsPaths.includes('/proc/mounts'))
|
t.ok(caps.pseudoFsPaths.includes('/proc/mounts'))
|
||||||
t.ok(caps.pseudoFsPaths.includes('/proc/bare_os_resources'))
|
t.ok(caps.pseudoFsPaths.includes('/proc/bare_os_resources'))
|
||||||
t.ok(caps.pseudoFsPaths.includes('/run/bare-os/virtual'))
|
t.ok(caps.pseudoFsPaths.includes('/run/bare-os/virtual'))
|
||||||
|
t.ok(caps.pseudoFsPaths.includes('/run/bare-os/unit-journal'))
|
||||||
t.is(caps.features.simulatedPipelines, true)
|
t.is(caps.features.simulatedPipelines, true)
|
||||||
t.is(caps.features.httpDelegate, true)
|
t.is(caps.features.httpDelegate, true)
|
||||||
t.is(caps.features.gitDelegate, true)
|
t.is(caps.features.gitDelegate, true)
|
||||||
@@ -3904,6 +3910,63 @@ test('coreutils gnu-gap batch: paste tac rev md5sum expr tsort numfmt truncate i
|
|||||||
rmSync(dir, { recursive: true, force: true })
|
rmSync(dir, { recursive: true, force: true })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('verifyBootManifestEd25519 rejects invalid inputs', async (t) => {
|
||||||
|
const { verifyBootManifestEd25519 } = await import('#bare-os-boot-manifest-sig')
|
||||||
|
const msg = b4a.from('manifest-bytes', 'utf8')
|
||||||
|
t.absent(verifyBootManifestEd25519(msg, null, ''))
|
||||||
|
t.absent(verifyBootManifestEd25519(msg, msg, '00ff'))
|
||||||
|
const z64 = '0'.repeat(64)
|
||||||
|
t.absent(verifyBootManifestEd25519(msg, new Uint8Array(64), z64))
|
||||||
|
})
|
||||||
|
|
||||||
|
test('bareOsIpc.duplexJsonRoundTrip', async (t) => {
|
||||||
|
const ipc = createBareOsIpc()
|
||||||
|
const { left, right } = ipc.createDuplexBridge('dupjx')
|
||||||
|
const respP = ipc.duplexJsonRoundTrip(right, { id: 7, method: 'ping' })
|
||||||
|
const u8 = await left.take()
|
||||||
|
const req = JSON.parse(b4a.toString(u8, 'utf8'))
|
||||||
|
t.is(req.method, 'ping')
|
||||||
|
left.push(
|
||||||
|
b4a.from(
|
||||||
|
JSON.stringify({ bareOsRpc: '2', id: 7, result: 'pong' }) + '\n',
|
||||||
|
'utf8'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
const out = await respP
|
||||||
|
t.is(out.result, 'pong')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('unit journal exposed under /run/bare-os/unit-journal', async (t) => {
|
||||||
|
const {
|
||||||
|
appendBareInitdJournal,
|
||||||
|
clearBareInitdJournalForTests,
|
||||||
|
getBareInitdJournalNdjson
|
||||||
|
} = await import('./lib/bare-initd-journal.js')
|
||||||
|
clearBareInitdJournalForTests()
|
||||||
|
appendBareInitdJournal('demo', { event: 'unit_test' })
|
||||||
|
const dir = testCorestoreDir('vj')
|
||||||
|
const store = new Corestore(dir)
|
||||||
|
const sys = new Hyperdrive(store)
|
||||||
|
const personal = new Hyperdrive(store.namespace('pvj'))
|
||||||
|
await sys.ready()
|
||||||
|
await personal.ready()
|
||||||
|
const env = {
|
||||||
|
HOME: '/home/guest',
|
||||||
|
PWD: '/home/guest',
|
||||||
|
PATH: '/bin',
|
||||||
|
USER: 'guest'
|
||||||
|
}
|
||||||
|
const vfs = createVfs(sys, personal, env, null, {
|
||||||
|
getUnitJournalNdjson: (u) => getBareInitdJournalNdjson(u)
|
||||||
|
})
|
||||||
|
t.ok((await vfs.readdir('/run/bare-os/unit-journal')).includes('demo.ndjson'))
|
||||||
|
const j = b4a.toString(await vfs.readFile('/run/bare-os/unit-journal/demo.ndjson'))
|
||||||
|
t.ok(j.includes('unit_test'))
|
||||||
|
clearBareInitdJournalForTests()
|
||||||
|
await store.close()
|
||||||
|
rmSync(dir, { recursive: true, force: true })
|
||||||
|
})
|
||||||
|
|
||||||
async function readBuiltBin(name) {
|
async function readBuiltBin(name) {
|
||||||
const fs = await import('node:fs/promises')
|
const fs = await import('node:fs/promises')
|
||||||
const p = path.join(__dirname, '../../kernel/bin', name)
|
const p = path.join(__dirname, '../../kernel/bin', name)
|
||||||
|
|||||||
@@ -514,12 +514,13 @@ function bareSedMatchAddr(
|
|||||||
/**
|
/**
|
||||||
* @param {string[]} lines
|
* @param {string[]} lines
|
||||||
* @param {string[]} scripts
|
* @param {string[]} scripts
|
||||||
* @param {{ silent?: boolean, extended?: boolean, readFile?: (p: string) => string | null, writeFile?: (p: string, chunk: string) => void, lastLineHint?: number }} opts
|
* @param {{ silent?: boolean, extended?: boolean, nullData?: boolean, readFile?: (p: string) => string | null, writeFile?: (p: string, chunk: string) => void, lastLineHint?: number }} opts
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
function bareSedRun(lines, scripts, opts) {
|
function bareSedRun(lines, scripts, opts) {
|
||||||
const silent = !!opts.silent
|
const silent = !!opts.silent
|
||||||
const extended = !!opts.extended
|
const extended = !!opts.extended
|
||||||
|
const eol = opts.nullData ? '\0' : '\n'
|
||||||
const readF = opts.readFile || (() => null)
|
const readF = opts.readFile || (() => null)
|
||||||
const writeF = opts.writeFile || (() => {})
|
const writeF = opts.writeFile || (() => {})
|
||||||
const fullScript = scripts.join('\n')
|
const fullScript = scripts.join('\n')
|
||||||
@@ -608,7 +609,7 @@ function bareSedRun(lines, scripts, opts) {
|
|||||||
}
|
}
|
||||||
if (count) {
|
if (count) {
|
||||||
ps = res + str.slice(pos)
|
ps = res + str.slice(pos)
|
||||||
if (fl.p) emit(ps + '\n')
|
if (fl.p) emit(ps + eol)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -637,15 +638,15 @@ function bareSedRun(lines, scripts, opts) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
case 'print':
|
case 'print':
|
||||||
emit(ps + '\n')
|
emit(ps + eol)
|
||||||
break
|
break
|
||||||
case 'printFirst': {
|
case 'printFirst': {
|
||||||
const nl = ps.indexOf('\n')
|
const nl = ps.indexOf('\n')
|
||||||
emit((nl === -1 ? ps : ps.slice(0, nl)) + '\n')
|
emit((nl === -1 ? ps : ps.slice(0, nl)) + eol)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case 'nextLine':
|
case 'nextLine':
|
||||||
if (autoPrint && !silent) emit(ps + '\n')
|
if (autoPrint && !silent) emit(ps + eol)
|
||||||
lineIdx++
|
lineIdx++
|
||||||
nextRead = true
|
nextRead = true
|
||||||
ci = cmds.length
|
ci = cmds.length
|
||||||
@@ -674,25 +675,25 @@ function bareSedRun(lines, scripts, opts) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
case 'quit':
|
case 'quit':
|
||||||
if (autoPrint && !silent) emit(ps + '\n')
|
if (autoPrint && !silent) emit(ps + eol)
|
||||||
quit = /** @type {number} */ (cmd.quitCode) || 0
|
quit = /** @type {number} */ (cmd.quitCode) || 0
|
||||||
break
|
break
|
||||||
case 'list':
|
case 'list':
|
||||||
emit(bareSedListLine(ps) + '\n')
|
emit(bareSedListLine(ps) + eol)
|
||||||
break
|
break
|
||||||
case 'lineNum':
|
case 'lineNum':
|
||||||
emit(String(lineNo) + '\n')
|
emit(String(lineNo) + eol)
|
||||||
break
|
break
|
||||||
case 'readFile': {
|
case 'readFile': {
|
||||||
const text = readF(/** @type {string} */ (cmd.path))
|
const text = readF(/** @type {string} */ (cmd.path))
|
||||||
if (text) emit(text.endsWith('\n') ? text : text + '\n')
|
if (text) emit(text.endsWith(eol) ? text : text + eol)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case 'writeFile':
|
case 'writeFile':
|
||||||
writeF(/** @type {string} */ (cmd.path), ps + '\n')
|
writeF(/** @type {string} */ (cmd.path), ps + '\n')
|
||||||
break
|
break
|
||||||
case 'append':
|
case 'append':
|
||||||
emit(/** @type {string} */ (cmd.text) + '\n')
|
emit(/** @type {string} */ (cmd.text) + eol)
|
||||||
break
|
break
|
||||||
case 'insert':
|
case 'insert':
|
||||||
/* handled as emit before line — approximated by prepending to output before autoPrint */
|
/* handled as emit before line — approximated by prepending to output before autoPrint */
|
||||||
@@ -700,7 +701,7 @@ function bareSedRun(lines, scripts, opts) {
|
|||||||
break
|
break
|
||||||
case 'change':
|
case 'change':
|
||||||
autoPrint = false
|
autoPrint = false
|
||||||
emit(/** @type {string} */ (cmd.text) + '\n')
|
emit(/** @type {string} */ (cmd.text) + eol)
|
||||||
delLine = true
|
delLine = true
|
||||||
break
|
break
|
||||||
case 'b': {
|
case 'b': {
|
||||||
@@ -726,7 +727,7 @@ function bareSedRun(lines, scripts, opts) {
|
|||||||
|
|
||||||
if (quit) break
|
if (quit) break
|
||||||
if (nextRead) continue
|
if (nextRead) continue
|
||||||
if (!delLine && autoPrint) emit(ps + '\n')
|
if (!delLine && autoPrint) emit(ps + eol)
|
||||||
lineIdx++
|
lineIdx++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,12 @@ async function run(ctx, argv) {
|
|||||||
/** @type {'never' | 'always' | 'auto'} */
|
/** @type {'never' | 'always' | 'auto'} */
|
||||||
let colorMode = 'never'
|
let colorMode = 'never'
|
||||||
let recursive = false
|
let recursive = false
|
||||||
|
/** @type {string[]} */
|
||||||
|
const includeGlobs = []
|
||||||
|
/** @type {string[]} */
|
||||||
|
const excludeGlobs = []
|
||||||
|
/** @type {string[]} */
|
||||||
|
const excludeDirGlobs = []
|
||||||
|
|
||||||
const args = argv.slice(1)
|
const args = argv.slice(1)
|
||||||
let i = 0
|
let i = 0
|
||||||
@@ -93,6 +99,36 @@ async function run(ctx, argv) {
|
|||||||
i++
|
i++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if (a === '--include' && args[i + 1]) {
|
||||||
|
includeGlobs.push(args[++i])
|
||||||
|
i++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (a.startsWith('--include=')) {
|
||||||
|
includeGlobs.push(a.slice('--include='.length))
|
||||||
|
i++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (a === '--exclude' && args[i + 1]) {
|
||||||
|
excludeGlobs.push(args[++i])
|
||||||
|
i++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (a.startsWith('--exclude=')) {
|
||||||
|
excludeGlobs.push(a.slice('--exclude='.length))
|
||||||
|
i++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (a === '--exclude-dir' && args[i + 1]) {
|
||||||
|
excludeDirGlobs.push(args[++i])
|
||||||
|
i++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (a.startsWith('--exclude-dir=')) {
|
||||||
|
excludeDirGlobs.push(a.slice('--exclude-dir='.length))
|
||||||
|
i++
|
||||||
|
continue
|
||||||
|
}
|
||||||
ctx.console.error('grep: unknown option ' + a)
|
ctx.console.error('grep: unknown option ' + a)
|
||||||
ctx.exitCode = 2
|
ctx.exitCode = 2
|
||||||
return
|
return
|
||||||
@@ -304,7 +340,15 @@ async function run(ctx, argv) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (st.type === 'directory') {
|
if (st.type === 'directory') {
|
||||||
await grepWalkFiles(ctx, p, acc, suppressErrors)
|
await grepWalkFiles(ctx, p, acc, suppressErrors, {
|
||||||
|
includeGlobs,
|
||||||
|
excludeGlobs,
|
||||||
|
excludeDirGlobs,
|
||||||
|
filterCap: Number.parseInt(
|
||||||
|
String(ctx.vfs?.env?.BARE_OS_GREP_FILTER_MAX || '32'),
|
||||||
|
10
|
||||||
|
) || 32
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
acc.push(p)
|
acc.push(p)
|
||||||
}
|
}
|
||||||
@@ -500,8 +544,31 @@ const GREP_RECURSE_MAX_DEPTH = 64
|
|||||||
* @param {string[]} acc
|
* @param {string[]} acc
|
||||||
* @param {boolean} suppressErrors
|
* @param {boolean} suppressErrors
|
||||||
*/
|
*/
|
||||||
async function grepWalkFiles(ctx, dir, acc, suppressErrors) {
|
/**
|
||||||
|
* @param {string} name
|
||||||
|
* @param {string} pat
|
||||||
|
*/
|
||||||
|
function grepSimpleGlobMatch(name, pat) {
|
||||||
|
if (!pat || pat === '*') return true
|
||||||
|
if (pat.includes('/')) return name === pat
|
||||||
|
if (pat.startsWith('*') && pat.length > 1 && pat.endsWith('*')) {
|
||||||
|
const mid = pat.slice(1, -1)
|
||||||
|
return mid !== '' && name.includes(mid)
|
||||||
|
}
|
||||||
|
if (pat.startsWith('*')) return name.endsWith(pat.slice(1))
|
||||||
|
if (pat.endsWith('*')) return name.startsWith(pat.slice(0, -1))
|
||||||
|
return name === pat
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {{ includeGlobs?: string[], excludeGlobs?: string[], excludeDirGlobs?: string[], filterCap?: number }} [opts]
|
||||||
|
*/
|
||||||
|
async function grepWalkFiles(ctx, dir, acc, suppressErrors, opts = {}) {
|
||||||
const vfs = ctx.vfs
|
const vfs = ctx.vfs
|
||||||
|
const cap = opts.filterCap && opts.filterCap > 0 ? opts.filterCap : 32
|
||||||
|
const inc = (opts.includeGlobs || []).slice(0, cap)
|
||||||
|
const exc = (opts.excludeGlobs || []).slice(0, cap)
|
||||||
|
const excd = (opts.excludeDirGlobs || []).slice(0, cap)
|
||||||
/** @param {string} d @param {number} depth */
|
/** @param {string} d @param {number} depth */
|
||||||
const walk = async (d, depth) => {
|
const walk = async (d, depth) => {
|
||||||
if (depth > GREP_RECURSE_MAX_DEPTH) return
|
if (depth > GREP_RECURSE_MAX_DEPTH) return
|
||||||
@@ -523,12 +590,21 @@ async function grepWalkFiles(ctx, dir, acc, suppressErrors) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (!st) continue
|
if (!st) continue
|
||||||
if (st.type === 'directory') await walk(sub, depth + 1)
|
if (st.type === 'directory') {
|
||||||
else if (st.type === 'file') acc.push(sub)
|
if (excd.some((p) => grepSimpleGlobMatch(n, p))) continue
|
||||||
else if (st.type === 'symlink') {
|
await walk(sub, depth + 1)
|
||||||
|
} else if (st.type === 'file') {
|
||||||
|
if (inc.length && !inc.some((p) => grepSimpleGlobMatch(n, p))) continue
|
||||||
|
if (exc.some((p) => grepSimpleGlobMatch(n, p))) continue
|
||||||
|
acc.push(sub)
|
||||||
|
} else if (st.type === 'symlink') {
|
||||||
try {
|
try {
|
||||||
const ft = await vfs.stat(sub)
|
const ft = await vfs.stat(sub)
|
||||||
if (ft && ft.type === 'file') acc.push(sub)
|
if (ft && ft.type === 'file') {
|
||||||
|
if (inc.length && !inc.some((p) => grepSimpleGlobMatch(n, p))) continue
|
||||||
|
if (exc.some((p) => grepSimpleGlobMatch(n, p))) continue
|
||||||
|
acc.push(sub)
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
/* skip */
|
/* skip */
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
async function run(ctx, argv) {
|
async function run(ctx, argv) {
|
||||||
let silent = false
|
let silent = false
|
||||||
let extended = false
|
let extended = false
|
||||||
|
let nullData = false
|
||||||
/** @type {string[]} */
|
/** @type {string[]} */
|
||||||
const scripts = []
|
const scripts = []
|
||||||
/** @type {string[]} */
|
/** @type {string[]} */
|
||||||
@@ -11,6 +12,10 @@ async function run(ctx, argv) {
|
|||||||
silent = true
|
silent = true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if (a === '-z' || a === '--null-data') {
|
||||||
|
nullData = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
if (a === '-E' || a === '-r') {
|
if (a === '-E' || a === '-r') {
|
||||||
extended = true
|
extended = true
|
||||||
continue
|
continue
|
||||||
@@ -89,6 +94,12 @@ async function run(ctx, argv) {
|
|||||||
readCache[rp] = buf ? ctx.b4a.toString(buf) : ''
|
readCache[rp] = buf ? ctx.b4a.toString(buf) : ''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const maxNull =
|
||||||
|
Number.parseInt(
|
||||||
|
String(ctx.vfs?.env?.BARE_OS_SED_NULL_MAX_RECORDS || '100000'),
|
||||||
|
10
|
||||||
|
) || 100000
|
||||||
|
|
||||||
/** @type {string[]} */
|
/** @type {string[]} */
|
||||||
const lines = []
|
const lines = []
|
||||||
async function pushFile(path) {
|
async function pushFile(path) {
|
||||||
@@ -99,16 +110,22 @@ async function run(ctx, argv) {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
const t = ctx.b4a.toString(buf)
|
const t = ctx.b4a.toString(buf)
|
||||||
const ls = t.split(/\r?\n/)
|
if (nullData) {
|
||||||
if (ls.length && ls[ls.length - 1] === '') ls.pop()
|
const rec = t.split('\0')
|
||||||
lines.push(...ls)
|
const room = maxNull - lines.length
|
||||||
|
lines.push(...rec.slice(0, Math.max(0, room)))
|
||||||
|
} else {
|
||||||
|
const ls = t.split(/\r?\n/)
|
||||||
|
if (ls.length && ls[ls.length - 1] === '') ls.pop()
|
||||||
|
lines.push(...ls)
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!files.length) {
|
if (!files.length) {
|
||||||
const s = bareStdin(ctx)
|
const s = bareStdin(ctx)
|
||||||
const ls = s.split(/\r?\n/)
|
const ls = nullData ? s.split('\0').slice(0, maxNull) : s.split(/\r?\n/)
|
||||||
if (ls.length && ls[ls.length - 1] === '') ls.pop()
|
if (!nullData && ls.length && ls[ls.length - 1] === '') ls.pop()
|
||||||
lines.push(...ls)
|
lines.push(...ls)
|
||||||
} else {
|
} else {
|
||||||
for (const f of files) {
|
for (const f of files) {
|
||||||
@@ -121,6 +138,7 @@ async function run(ctx, argv) {
|
|||||||
const out = bareSedRun(lines, scripts, {
|
const out = bareSedRun(lines, scripts, {
|
||||||
silent,
|
silent,
|
||||||
extended,
|
extended,
|
||||||
|
nullData,
|
||||||
readFile: (p) => readCache[p] ?? null,
|
readFile: (p) => readCache[p] ?? null,
|
||||||
writeFile: (p, chunk) => {
|
writeFile: (p, chunk) => {
|
||||||
wAccum[p] = (wAccum[p] || '') + chunk
|
wAccum[p] = (wAccum[p] || '') + chunk
|
||||||
@@ -141,6 +159,7 @@ async function run(ctx, argv) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const t = out.replace(/\n$/, '')
|
const trail = nullData ? /\0$/ : /\n$/
|
||||||
|
const t = out.replace(trail, '')
|
||||||
ctx.console.log(t)
|
ctx.console.log(t)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -115,6 +115,12 @@ async function run(ctx, argv) {
|
|||||||
/** @type {'never' | 'always' | 'auto'} */
|
/** @type {'never' | 'always' | 'auto'} */
|
||||||
let colorMode = 'never'
|
let colorMode = 'never'
|
||||||
let recursive = false
|
let recursive = false
|
||||||
|
/** @type {string[]} */
|
||||||
|
const includeGlobs = []
|
||||||
|
/** @type {string[]} */
|
||||||
|
const excludeGlobs = []
|
||||||
|
/** @type {string[]} */
|
||||||
|
const excludeDirGlobs = []
|
||||||
|
|
||||||
const args = argv.slice(1)
|
const args = argv.slice(1)
|
||||||
let i = 0
|
let i = 0
|
||||||
@@ -182,6 +188,36 @@ async function run(ctx, argv) {
|
|||||||
i++
|
i++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if (a === '--include' && args[i + 1]) {
|
||||||
|
includeGlobs.push(args[++i])
|
||||||
|
i++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (a.startsWith('--include=')) {
|
||||||
|
includeGlobs.push(a.slice('--include='.length))
|
||||||
|
i++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (a === '--exclude' && args[i + 1]) {
|
||||||
|
excludeGlobs.push(args[++i])
|
||||||
|
i++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (a.startsWith('--exclude=')) {
|
||||||
|
excludeGlobs.push(a.slice('--exclude='.length))
|
||||||
|
i++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (a === '--exclude-dir' && args[i + 1]) {
|
||||||
|
excludeDirGlobs.push(args[++i])
|
||||||
|
i++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (a.startsWith('--exclude-dir=')) {
|
||||||
|
excludeDirGlobs.push(a.slice('--exclude-dir='.length))
|
||||||
|
i++
|
||||||
|
continue
|
||||||
|
}
|
||||||
ctx.console.error('grep: unknown option ' + a)
|
ctx.console.error('grep: unknown option ' + a)
|
||||||
ctx.exitCode = 2
|
ctx.exitCode = 2
|
||||||
return
|
return
|
||||||
@@ -393,7 +429,15 @@ async function run(ctx, argv) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (st.type === 'directory') {
|
if (st.type === 'directory') {
|
||||||
await grepWalkFiles(ctx, p, acc, suppressErrors)
|
await grepWalkFiles(ctx, p, acc, suppressErrors, {
|
||||||
|
includeGlobs,
|
||||||
|
excludeGlobs,
|
||||||
|
excludeDirGlobs,
|
||||||
|
filterCap: Number.parseInt(
|
||||||
|
String(ctx.vfs?.env?.BARE_OS_GREP_FILTER_MAX || '32'),
|
||||||
|
10
|
||||||
|
) || 32
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
acc.push(p)
|
acc.push(p)
|
||||||
}
|
}
|
||||||
@@ -589,8 +633,31 @@ const GREP_RECURSE_MAX_DEPTH = 64
|
|||||||
* @param {string[]} acc
|
* @param {string[]} acc
|
||||||
* @param {boolean} suppressErrors
|
* @param {boolean} suppressErrors
|
||||||
*/
|
*/
|
||||||
async function grepWalkFiles(ctx, dir, acc, suppressErrors) {
|
/**
|
||||||
|
* @param {string} name
|
||||||
|
* @param {string} pat
|
||||||
|
*/
|
||||||
|
function grepSimpleGlobMatch(name, pat) {
|
||||||
|
if (!pat || pat === '*') return true
|
||||||
|
if (pat.includes('/')) return name === pat
|
||||||
|
if (pat.startsWith('*') && pat.length > 1 && pat.endsWith('*')) {
|
||||||
|
const mid = pat.slice(1, -1)
|
||||||
|
return mid !== '' && name.includes(mid)
|
||||||
|
}
|
||||||
|
if (pat.startsWith('*')) return name.endsWith(pat.slice(1))
|
||||||
|
if (pat.endsWith('*')) return name.startsWith(pat.slice(0, -1))
|
||||||
|
return name === pat
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {{ includeGlobs?: string[], excludeGlobs?: string[], excludeDirGlobs?: string[], filterCap?: number }} [opts]
|
||||||
|
*/
|
||||||
|
async function grepWalkFiles(ctx, dir, acc, suppressErrors, opts = {}) {
|
||||||
const vfs = ctx.vfs
|
const vfs = ctx.vfs
|
||||||
|
const cap = opts.filterCap && opts.filterCap > 0 ? opts.filterCap : 32
|
||||||
|
const inc = (opts.includeGlobs || []).slice(0, cap)
|
||||||
|
const exc = (opts.excludeGlobs || []).slice(0, cap)
|
||||||
|
const excd = (opts.excludeDirGlobs || []).slice(0, cap)
|
||||||
/** @param {string} d @param {number} depth */
|
/** @param {string} d @param {number} depth */
|
||||||
const walk = async (d, depth) => {
|
const walk = async (d, depth) => {
|
||||||
if (depth > GREP_RECURSE_MAX_DEPTH) return
|
if (depth > GREP_RECURSE_MAX_DEPTH) return
|
||||||
@@ -612,12 +679,21 @@ async function grepWalkFiles(ctx, dir, acc, suppressErrors) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (!st) continue
|
if (!st) continue
|
||||||
if (st.type === 'directory') await walk(sub, depth + 1)
|
if (st.type === 'directory') {
|
||||||
else if (st.type === 'file') acc.push(sub)
|
if (excd.some((p) => grepSimpleGlobMatch(n, p))) continue
|
||||||
else if (st.type === 'symlink') {
|
await walk(sub, depth + 1)
|
||||||
|
} else if (st.type === 'file') {
|
||||||
|
if (inc.length && !inc.some((p) => grepSimpleGlobMatch(n, p))) continue
|
||||||
|
if (exc.some((p) => grepSimpleGlobMatch(n, p))) continue
|
||||||
|
acc.push(sub)
|
||||||
|
} else if (st.type === 'symlink') {
|
||||||
try {
|
try {
|
||||||
const ft = await vfs.stat(sub)
|
const ft = await vfs.stat(sub)
|
||||||
if (ft && ft.type === 'file') acc.push(sub)
|
if (ft && ft.type === 'file') {
|
||||||
|
if (inc.length && !inc.some((p) => grepSimpleGlobMatch(n, p))) continue
|
||||||
|
if (exc.some((p) => grepSimpleGlobMatch(n, p))) continue
|
||||||
|
acc.push(sub)
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
/* skip */
|
/* skip */
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -603,12 +603,13 @@ function bareSedMatchAddr(
|
|||||||
/**
|
/**
|
||||||
* @param {string[]} lines
|
* @param {string[]} lines
|
||||||
* @param {string[]} scripts
|
* @param {string[]} scripts
|
||||||
* @param {{ silent?: boolean, extended?: boolean, readFile?: (p: string) => string | null, writeFile?: (p: string, chunk: string) => void, lastLineHint?: number }} opts
|
* @param {{ silent?: boolean, extended?: boolean, nullData?: boolean, readFile?: (p: string) => string | null, writeFile?: (p: string, chunk: string) => void, lastLineHint?: number }} opts
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
function bareSedRun(lines, scripts, opts) {
|
function bareSedRun(lines, scripts, opts) {
|
||||||
const silent = !!opts.silent
|
const silent = !!opts.silent
|
||||||
const extended = !!opts.extended
|
const extended = !!opts.extended
|
||||||
|
const eol = opts.nullData ? '\0' : '\n'
|
||||||
const readF = opts.readFile || (() => null)
|
const readF = opts.readFile || (() => null)
|
||||||
const writeF = opts.writeFile || (() => {})
|
const writeF = opts.writeFile || (() => {})
|
||||||
const fullScript = scripts.join('\n')
|
const fullScript = scripts.join('\n')
|
||||||
@@ -697,7 +698,7 @@ function bareSedRun(lines, scripts, opts) {
|
|||||||
}
|
}
|
||||||
if (count) {
|
if (count) {
|
||||||
ps = res + str.slice(pos)
|
ps = res + str.slice(pos)
|
||||||
if (fl.p) emit(ps + '\n')
|
if (fl.p) emit(ps + eol)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -726,15 +727,15 @@ function bareSedRun(lines, scripts, opts) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
case 'print':
|
case 'print':
|
||||||
emit(ps + '\n')
|
emit(ps + eol)
|
||||||
break
|
break
|
||||||
case 'printFirst': {
|
case 'printFirst': {
|
||||||
const nl = ps.indexOf('\n')
|
const nl = ps.indexOf('\n')
|
||||||
emit((nl === -1 ? ps : ps.slice(0, nl)) + '\n')
|
emit((nl === -1 ? ps : ps.slice(0, nl)) + eol)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case 'nextLine':
|
case 'nextLine':
|
||||||
if (autoPrint && !silent) emit(ps + '\n')
|
if (autoPrint && !silent) emit(ps + eol)
|
||||||
lineIdx++
|
lineIdx++
|
||||||
nextRead = true
|
nextRead = true
|
||||||
ci = cmds.length
|
ci = cmds.length
|
||||||
@@ -763,25 +764,25 @@ function bareSedRun(lines, scripts, opts) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
case 'quit':
|
case 'quit':
|
||||||
if (autoPrint && !silent) emit(ps + '\n')
|
if (autoPrint && !silent) emit(ps + eol)
|
||||||
quit = /** @type {number} */ (cmd.quitCode) || 0
|
quit = /** @type {number} */ (cmd.quitCode) || 0
|
||||||
break
|
break
|
||||||
case 'list':
|
case 'list':
|
||||||
emit(bareSedListLine(ps) + '\n')
|
emit(bareSedListLine(ps) + eol)
|
||||||
break
|
break
|
||||||
case 'lineNum':
|
case 'lineNum':
|
||||||
emit(String(lineNo) + '\n')
|
emit(String(lineNo) + eol)
|
||||||
break
|
break
|
||||||
case 'readFile': {
|
case 'readFile': {
|
||||||
const text = readF(/** @type {string} */ (cmd.path))
|
const text = readF(/** @type {string} */ (cmd.path))
|
||||||
if (text) emit(text.endsWith('\n') ? text : text + '\n')
|
if (text) emit(text.endsWith(eol) ? text : text + eol)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case 'writeFile':
|
case 'writeFile':
|
||||||
writeF(/** @type {string} */ (cmd.path), ps + '\n')
|
writeF(/** @type {string} */ (cmd.path), ps + '\n')
|
||||||
break
|
break
|
||||||
case 'append':
|
case 'append':
|
||||||
emit(/** @type {string} */ (cmd.text) + '\n')
|
emit(/** @type {string} */ (cmd.text) + eol)
|
||||||
break
|
break
|
||||||
case 'insert':
|
case 'insert':
|
||||||
/* handled as emit before line — approximated by prepending to output before autoPrint */
|
/* handled as emit before line — approximated by prepending to output before autoPrint */
|
||||||
@@ -789,7 +790,7 @@ function bareSedRun(lines, scripts, opts) {
|
|||||||
break
|
break
|
||||||
case 'change':
|
case 'change':
|
||||||
autoPrint = false
|
autoPrint = false
|
||||||
emit(/** @type {string} */ (cmd.text) + '\n')
|
emit(/** @type {string} */ (cmd.text) + eol)
|
||||||
delLine = true
|
delLine = true
|
||||||
break
|
break
|
||||||
case 'b': {
|
case 'b': {
|
||||||
@@ -815,7 +816,7 @@ function bareSedRun(lines, scripts, opts) {
|
|||||||
|
|
||||||
if (quit) break
|
if (quit) break
|
||||||
if (nextRead) continue
|
if (nextRead) continue
|
||||||
if (!delLine && autoPrint) emit(ps + '\n')
|
if (!delLine && autoPrint) emit(ps + eol)
|
||||||
lineIdx++
|
lineIdx++
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -825,6 +826,7 @@ function bareSedRun(lines, scripts, opts) {
|
|||||||
async function run(ctx, argv) {
|
async function run(ctx, argv) {
|
||||||
let silent = false
|
let silent = false
|
||||||
let extended = false
|
let extended = false
|
||||||
|
let nullData = false
|
||||||
/** @type {string[]} */
|
/** @type {string[]} */
|
||||||
const scripts = []
|
const scripts = []
|
||||||
/** @type {string[]} */
|
/** @type {string[]} */
|
||||||
@@ -835,6 +837,10 @@ async function run(ctx, argv) {
|
|||||||
silent = true
|
silent = true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if (a === '-z' || a === '--null-data') {
|
||||||
|
nullData = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
if (a === '-E' || a === '-r') {
|
if (a === '-E' || a === '-r') {
|
||||||
extended = true
|
extended = true
|
||||||
continue
|
continue
|
||||||
@@ -913,6 +919,12 @@ async function run(ctx, argv) {
|
|||||||
readCache[rp] = buf ? ctx.b4a.toString(buf) : ''
|
readCache[rp] = buf ? ctx.b4a.toString(buf) : ''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const maxNull =
|
||||||
|
Number.parseInt(
|
||||||
|
String(ctx.vfs?.env?.BARE_OS_SED_NULL_MAX_RECORDS || '100000'),
|
||||||
|
10
|
||||||
|
) || 100000
|
||||||
|
|
||||||
/** @type {string[]} */
|
/** @type {string[]} */
|
||||||
const lines = []
|
const lines = []
|
||||||
async function pushFile(path) {
|
async function pushFile(path) {
|
||||||
@@ -923,16 +935,22 @@ async function run(ctx, argv) {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
const t = ctx.b4a.toString(buf)
|
const t = ctx.b4a.toString(buf)
|
||||||
const ls = t.split(/\r?\n/)
|
if (nullData) {
|
||||||
if (ls.length && ls[ls.length - 1] === '') ls.pop()
|
const rec = t.split('\0')
|
||||||
lines.push(...ls)
|
const room = maxNull - lines.length
|
||||||
|
lines.push(...rec.slice(0, Math.max(0, room)))
|
||||||
|
} else {
|
||||||
|
const ls = t.split(/\r?\n/)
|
||||||
|
if (ls.length && ls[ls.length - 1] === '') ls.pop()
|
||||||
|
lines.push(...ls)
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!files.length) {
|
if (!files.length) {
|
||||||
const s = bareStdin(ctx)
|
const s = bareStdin(ctx)
|
||||||
const ls = s.split(/\r?\n/)
|
const ls = nullData ? s.split('\0').slice(0, maxNull) : s.split(/\r?\n/)
|
||||||
if (ls.length && ls[ls.length - 1] === '') ls.pop()
|
if (!nullData && ls.length && ls[ls.length - 1] === '') ls.pop()
|
||||||
lines.push(...ls)
|
lines.push(...ls)
|
||||||
} else {
|
} else {
|
||||||
for (const f of files) {
|
for (const f of files) {
|
||||||
@@ -945,6 +963,7 @@ async function run(ctx, argv) {
|
|||||||
const out = bareSedRun(lines, scripts, {
|
const out = bareSedRun(lines, scripts, {
|
||||||
silent,
|
silent,
|
||||||
extended,
|
extended,
|
||||||
|
nullData,
|
||||||
readFile: (p) => readCache[p] ?? null,
|
readFile: (p) => readCache[p] ?? null,
|
||||||
writeFile: (p, chunk) => {
|
writeFile: (p, chunk) => {
|
||||||
wAccum[p] = (wAccum[p] || '') + chunk
|
wAccum[p] = (wAccum[p] || '') + chunk
|
||||||
@@ -965,6 +984,7 @@ async function run(ctx, argv) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const t = out.replace(/\n$/, '')
|
const trail = nullData ? /\0$/ : /\n$/
|
||||||
|
const t = out.replace(trail, '')
|
||||||
ctx.console.log(t)
|
ctx.console.log(t)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,9 @@
|
|||||||
* BARE_OS_SELFTEST_FORMAT=tap: TAP-style lines on stderr for CI parsers.
|
* BARE_OS_SELFTEST_FORMAT=tap: TAP-style lines on stderr for CI parsers.
|
||||||
* BARE_OS_BOOT_ALLOWLIST=1 and /etc/bare-os/boot.allow: only first-word commands in that file (plus shell builtins) run from trusted rc/onboot snippets.
|
* BARE_OS_BOOT_ALLOWLIST=1 and /etc/bare-os/boot.allow: only first-word commands in that file (plus shell builtins) run from trusted rc/onboot snippets.
|
||||||
*
|
*
|
||||||
|
* BARE_OS_BOOT_MANIFEST_SIGN=1: verify Ed25519 signature in /etc/bare-os/boot.manifest.sig over the raw
|
||||||
|
* manifest bytes; public key from BARE_OS_BOOT_MANIFEST_PUBKEY_HEX (64 hex chars). Uses ctx.bareOsVerifyBootManifestSignature.
|
||||||
|
*
|
||||||
* BARE_OS_BOOT_STRICT=1 or true: first execLine throw in trusted boot snippets calls
|
* BARE_OS_BOOT_STRICT=1 or true: first execLine throw in trusted boot snippets calls
|
||||||
* requestBooterExit(1) and stops further boot phases.
|
* requestBooterExit(1) and stops further boot phases.
|
||||||
*
|
*
|
||||||
@@ -260,6 +263,30 @@ async function loadBootManifest(ctx) {
|
|||||||
bootManifestMemo = null
|
bootManifestMemo = null
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
const signOn =
|
||||||
|
ctx.env &&
|
||||||
|
(ctx.env.BARE_OS_BOOT_MANIFEST_SIGN === '1' ||
|
||||||
|
ctx.env.BARE_OS_BOOT_MANIFEST_SIGN === 'true')
|
||||||
|
if (signOn) {
|
||||||
|
const sigBuf = await drive.get('/etc/bare-os/boot.manifest.sig')
|
||||||
|
const pub =
|
||||||
|
ctx.env && ctx.env.BARE_OS_BOOT_MANIFEST_PUBKEY_HEX
|
||||||
|
? String(ctx.env.BARE_OS_BOOT_MANIFEST_PUBKEY_HEX).trim()
|
||||||
|
: ''
|
||||||
|
const verifyFn = ctx.bareOsVerifyBootManifestSignature
|
||||||
|
if (typeof verifyFn !== 'function' || !pub) {
|
||||||
|
console.error(
|
||||||
|
'[boot] signed manifest requires ctx.bareOsVerifyBootManifestSignature and BARE_OS_BOOT_MANIFEST_PUBKEY_HEX'
|
||||||
|
)
|
||||||
|
bootManifestMemo = null
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
if (!verifyFn(buf, sigBuf, pub)) {
|
||||||
|
console.error('[boot] boot.manifest.json Ed25519 signature verification failed')
|
||||||
|
bootManifestMemo = null
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
bootManifestMemo = JSON.parse(b4a.toString(buf))
|
bootManifestMemo = JSON.parse(b4a.toString(buf))
|
||||||
return bootManifestMemo
|
return bootManifestMemo
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -7,18 +7,18 @@
|
|||||||
"b4a"
|
"b4a"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/safetyCatch.js",
|
|
||||||
"keys": [
|
|
||||||
"safetyCatch"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/hypercoreIdEncoding.js",
|
"path": "/lib/bare/bundles/hypercoreIdEncoding.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"hypercoreIdEncoding"
|
"hypercoreIdEncoding"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/safetyCatch.js",
|
||||||
|
"keys": [
|
||||||
|
"safetyCatch"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/compactEncoding.js",
|
"path": "/lib/bare/bundles/compactEncoding.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -31,6 +31,12 @@
|
|||||||
"bareUrl"
|
"bareUrl"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/protomux.js",
|
||||||
|
"keys": [
|
||||||
|
"protomux"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareEncoding.js",
|
"path": "/lib/bare/bundles/bareEncoding.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -43,12 +49,6 @@
|
|||||||
"bareEvents"
|
"bareEvents"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/protomux.js",
|
|
||||||
"keys": [
|
|
||||||
"protomux"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/barePath.js",
|
"path": "/lib/bare/bundles/barePath.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -109,12 +109,6 @@
|
|||||||
"bareAsyncHooks"
|
"bareAsyncHooks"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/fetch.js",
|
|
||||||
"keys": [
|
|
||||||
"fetch"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareAtomics.js",
|
"path": "/lib/bare/bundles/bareAtomics.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -127,6 +121,12 @@
|
|||||||
"bareAssert"
|
"bareAssert"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/fetch.js",
|
||||||
|
"keys": [
|
||||||
|
"fetch"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareBmp.js",
|
"path": "/lib/bare/bundles/bareBmp.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -175,24 +175,18 @@
|
|||||||
"bareConsole"
|
"bareConsole"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareDebugLog.js",
|
|
||||||
"keys": [
|
|
||||||
"bareDebugLog"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareDaemon.js",
|
|
||||||
"keys": [
|
|
||||||
"bareDaemon"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareBundleId.js",
|
"path": "/lib/bare/bundles/bareBundleId.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareBundleId"
|
"bareBundleId"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareDebugLog.js",
|
||||||
|
"keys": [
|
||||||
|
"bareDebugLog"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareChannel.js",
|
"path": "/lib/bare/bundles/bareChannel.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -205,6 +199,12 @@
|
|||||||
"bareDelta"
|
"bareDelta"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareDaemon.js",
|
||||||
|
"keys": [
|
||||||
|
"bareDaemon"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareDns.js",
|
"path": "/lib/bare/bundles/bareDns.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -217,6 +217,12 @@
|
|||||||
"bareDiagnosticsChannel"
|
"bareDiagnosticsChannel"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareCov.js",
|
||||||
|
"keys": [
|
||||||
|
"bareCov"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareEnv.js",
|
"path": "/lib/bare/bundles/bareEnv.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -229,36 +235,12 @@
|
|||||||
"bareExif"
|
"bareExif"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareCov.js",
|
|
||||||
"keys": [
|
|
||||||
"bareCov"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareDgram.js",
|
"path": "/lib/bare/bundles/bareDgram.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareDgram"
|
"bareDgram"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareFfmpeg.js",
|
|
||||||
"keys": [
|
|
||||||
"bareFfmpeg"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareFfmpegEncodings.js",
|
|
||||||
"keys": [
|
|
||||||
"bareFfmpegEncodings"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareFormat.js",
|
|
||||||
"keys": [
|
|
||||||
"bareFormat"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareFormData.js",
|
"path": "/lib/bare/bundles/bareFormData.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -266,9 +248,21 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareHeif.js",
|
"path": "/lib/bare/bundles/bareFfmpegEncodings.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareHeif"
|
"bareFfmpegEncodings"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareFfmpeg.js",
|
||||||
|
"keys": [
|
||||||
|
"bareFfmpeg"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareFormat.js",
|
||||||
|
"keys": [
|
||||||
|
"bareFormat"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -283,6 +277,12 @@
|
|||||||
"bareGif"
|
"bareGif"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareHeif.js",
|
||||||
|
"keys": [
|
||||||
|
"bareHeif"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareGtk.js",
|
"path": "/lib/bare/bundles/bareGtk.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -295,42 +295,36 @@
|
|||||||
"bareHrtime"
|
"bareHrtime"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareFs.js",
|
|
||||||
"keys": [
|
|
||||||
"bareFs"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareHttpParser.js",
|
"path": "/lib/bare/bundles/bareHttpParser.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareHttpParser"
|
"bareHttpParser"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareFs.js",
|
||||||
|
"keys": [
|
||||||
|
"bareFs"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareIco.js",
|
"path": "/lib/bare/bundles/bareIco.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareIco"
|
"bareIco"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareImageResample.js",
|
|
||||||
"keys": [
|
|
||||||
"bareImageResample"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareInspect.js",
|
|
||||||
"keys": [
|
|
||||||
"bareInspect"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareHttp1.js",
|
"path": "/lib/bare/bundles/bareHttp1.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareHttp1"
|
"bareHttp1"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareImageResample.js",
|
||||||
|
"keys": [
|
||||||
|
"bareImageResample"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareHttps.js",
|
"path": "/lib/bare/bundles/bareHttps.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -338,15 +332,15 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareJpeg.js",
|
"path": "/lib/bare/bundles/bareInspect.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareJpeg"
|
"bareInspect"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareIntl.js",
|
"path": "/lib/bare/bundles/bareJpeg.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareIntl"
|
"bareJpeg"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -355,24 +349,30 @@
|
|||||||
"bareIpc"
|
"bareIpc"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareIntl.js",
|
||||||
|
"keys": [
|
||||||
|
"bareIntl"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareLief.js",
|
"path": "/lib/bare/bundles/bareLief.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareLief"
|
"bareLief"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareLogger.js",
|
|
||||||
"keys": [
|
|
||||||
"bareLogger"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareLink.js",
|
"path": "/lib/bare/bundles/bareLink.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareLink"
|
"bareLink"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareLogger.js",
|
||||||
|
"keys": [
|
||||||
|
"bareLogger"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareInspector.js",
|
"path": "/lib/bare/bundles/bareInspector.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -386,9 +386,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareModuleResolve.js",
|
"path": "/lib/bare/bundles/bareModule.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareModuleResolve"
|
"bareModule"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -397,24 +397,18 @@
|
|||||||
"bareModuleLexer"
|
"bareModuleLexer"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareModuleResolve.js",
|
||||||
|
"keys": [
|
||||||
|
"bareModuleResolve"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareModuleTraverse.js",
|
"path": "/lib/bare/bundles/bareModuleTraverse.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareModuleTraverse"
|
"bareModuleTraverse"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareModule.js",
|
|
||||||
"keys": [
|
|
||||||
"bareModule"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareNodeFetch.js",
|
|
||||||
"keys": [
|
|
||||||
"bareNodeFetch"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareNdk.js",
|
"path": "/lib/bare/bundles/bareNdk.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -428,21 +422,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareDev.js",
|
"path": "/lib/bare/bundles/bareNodeFetch.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareDev"
|
"bareNodeFetch"
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareOs.js",
|
|
||||||
"keys": [
|
|
||||||
"bareOs"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareOpen.js",
|
|
||||||
"keys": [
|
|
||||||
"bareOpen"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -458,15 +440,21 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/barePerformance.js",
|
"path": "/lib/bare/bundles/bareOpen.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"barePerformance"
|
"bareOpen"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/barePng.js",
|
"path": "/lib/bare/bundles/bareOs.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"barePng"
|
"bareOs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/barePerformance.js",
|
||||||
|
"keys": [
|
||||||
|
"barePerformance"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -476,9 +464,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/barePack.js",
|
"path": "/lib/bare/bundles/barePng.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"barePack"
|
"barePng"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -487,18 +475,36 @@
|
|||||||
"barePipe"
|
"barePipe"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/barePack.js",
|
||||||
|
"keys": [
|
||||||
|
"barePack"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/barePunycode.js",
|
"path": "/lib/bare/bundles/barePunycode.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"barePunycode"
|
"barePunycode"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareDev.js",
|
||||||
|
"keys": [
|
||||||
|
"bareDev"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareQuerystring.js",
|
"path": "/lib/bare/bundles/bareQuerystring.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareQuerystring"
|
"bareQuerystring"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareNodeRuntime.js",
|
||||||
|
"keys": [
|
||||||
|
"bareNodeRuntime"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/barePrebuild.js",
|
"path": "/lib/bare/bundles/barePrebuild.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -512,9 +518,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareNodeRuntime.js",
|
"path": "/lib/bare/bundles/bareProcess.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareNodeRuntime"
|
"bareProcess"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -524,15 +530,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareProcess.js",
|
"path": "/lib/bare/bundles/bareSdl.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareProcess"
|
"bareSdl"
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/barePromClient.js",
|
|
||||||
"keys": [
|
|
||||||
"barePromClient"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -542,9 +542,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareRepl.js",
|
"path": "/lib/bare/bundles/barePromClient.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareRepl"
|
"barePromClient"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -553,18 +553,18 @@
|
|||||||
"bareRpc"
|
"bareRpc"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareSdl.js",
|
|
||||||
"keys": [
|
|
||||||
"bareSdl"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareSemver.js",
|
"path": "/lib/bare/bundles/bareSemver.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareSemver"
|
"bareSemver"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareSidecar.js",
|
||||||
|
"keys": [
|
||||||
|
"bareSidecar"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareRun.js",
|
"path": "/lib/bare/bundles/bareRun.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -578,27 +578,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareSidecar.js",
|
"path": "/lib/bare/bundles/bareRepl.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareSidecar"
|
"bareRepl"
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareStream.js",
|
|
||||||
"keys": [
|
|
||||||
"bareStream"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareStorage.js",
|
|
||||||
"keys": [
|
|
||||||
"bareStorage"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareStdio.js",
|
|
||||||
"keys": [
|
|
||||||
"bareStdio"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -607,6 +589,12 @@
|
|||||||
"bareStringDecoder"
|
"bareStringDecoder"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareStorage.js",
|
||||||
|
"keys": [
|
||||||
|
"bareStorage"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareSvg.js",
|
"path": "/lib/bare/bundles/bareSvg.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -614,9 +602,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareSystemLogger.js",
|
"path": "/lib/bare/bundles/bareStream.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareSystemLogger"
|
"bareStream"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -626,15 +614,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareTiff.js",
|
"path": "/lib/bare/bundles/bareStdio.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareTiff"
|
"bareStdio"
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareTap.js",
|
|
||||||
"keys": [
|
|
||||||
"bareTap"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -644,15 +626,21 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareThread.js",
|
"path": "/lib/bare/bundles/bareTap.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareThread"
|
"bareTap"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareTcp.js",
|
"path": "/lib/bare/bundles/bareTiff.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareTcp"
|
"bareTiff"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareSystemLogger.js",
|
||||||
|
"keys": [
|
||||||
|
"bareSystemLogger"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -668,15 +656,15 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareTls.js",
|
"path": "/lib/bare/bundles/bareThread.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareTls"
|
"bareThread"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareTty.js",
|
"path": "/lib/bare/bundles/bareTcp.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareTty"
|
"bareTcp"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -692,9 +680,15 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareUnpack.js",
|
"path": "/lib/bare/bundles/bareTty.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareUnpack"
|
"bareTty"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareTls.js",
|
||||||
|
"keys": [
|
||||||
|
"bareTls"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -704,9 +698,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareWalkHandles.js",
|
"path": "/lib/bare/bundles/bareUnpack.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareWalkHandles"
|
"bareUnpack"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -716,9 +710,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareUnionBundle.js",
|
"path": "/lib/bare/bundles/bareWalkHandles.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareUnionBundle"
|
"bareWalkHandles"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -740,9 +734,15 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareWhich.js",
|
"path": "/lib/bare/bundles/bareUnionBundle.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareWhich"
|
"bareUnionBundle"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareV8ToIstanbul.js",
|
||||||
|
"keys": [
|
||||||
|
"bareV8ToIstanbul"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -752,9 +752,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareV8ToIstanbul.js",
|
"path": "/lib/bare/bundles/bareWhich.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
"bareV8ToIstanbul"
|
"bareWhich"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -769,12 +769,6 @@
|
|||||||
"bareXdiff"
|
"bareXdiff"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "/lib/bare/bundles/bareWs.js",
|
|
||||||
"keys": [
|
|
||||||
"bareWs"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareZlib.js",
|
"path": "/lib/bare/bundles/bareZlib.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
@@ -787,6 +781,12 @@
|
|||||||
"bareWorker"
|
"bareWorker"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/lib/bare/bundles/bareWs.js",
|
||||||
|
"keys": [
|
||||||
|
"bareWs"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/lib/bare/bundles/bareZmq.js",
|
"path": "/lib/bare/bundles/bareZmq.js",
|
||||||
"keys": [
|
"keys": [
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1,6 +1,6 @@
|
|||||||
import path from 'path'
|
import path from 'path'
|
||||||
import { statSync } from 'fs'
|
import { statSync } from 'fs'
|
||||||
import { fileURLToPath } from 'node:url'
|
import { fileURLToPath } from 'url'
|
||||||
import os from 'bare-os'
|
import os from 'bare-os'
|
||||||
|
|
||||||
function cwd() {
|
function cwd() {
|
||||||
|
|||||||
@@ -1,9 +1,46 @@
|
|||||||
import { readFile } from 'fs/promises'
|
import { readFile } from 'fs/promises'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
|
import b4a from 'b4a'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {Promise<((cmd: string, args: string[], opts?: object) => object) | null>}
|
||||||
|
*/
|
||||||
|
async function loadSpawnSync() {
|
||||||
|
try {
|
||||||
|
const m = await import('bare-subprocess')
|
||||||
|
if (m && typeof m.spawnSync === 'function') return m.spawnSync
|
||||||
|
} catch {
|
||||||
|
/* bare-subprocess missing or native addon unavailable */
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const m = await import('child_process')
|
||||||
|
if (m && typeof m.spawnSync === 'function') return m.spawnSync
|
||||||
|
} catch {
|
||||||
|
/* Node child_process unavailable */
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {object} r spawnSync result (Node or bare-subprocess)
|
||||||
|
* @param {'stdout' | 'stderr'} key
|
||||||
|
*/
|
||||||
|
function pipeText(r, key) {
|
||||||
|
const v = r[key]
|
||||||
|
if (v == null) return ''
|
||||||
|
if (typeof v === 'string') return v
|
||||||
|
try {
|
||||||
|
return b4a.toString(v)
|
||||||
|
} catch {
|
||||||
|
return String(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When `pear.multisig.json` exists next to the kernel tree, validate shape and log status.
|
* When `pear.multisig.json` exists next to the kernel tree, validate shape and log status.
|
||||||
* Mirrors Holepunch `pear-multisig-link` style metadata (operational hint, not cryptography).
|
* Mirrors Holepunch `pear-multisig-link` style metadata (operational hint, not cryptography).
|
||||||
|
* Subprocess: prefers **`bare-subprocess`** (Pear/Bare); falls back to **`child_process`** on Node.
|
||||||
|
* Do not use **`node:child_process`** — it is not resolvable under Bare.
|
||||||
* @param {string} kernelRoot
|
* @param {string} kernelRoot
|
||||||
*/
|
*/
|
||||||
export async function logPearMultisigKernelHint(kernelRoot) {
|
export async function logPearMultisigKernelHint(kernelRoot) {
|
||||||
@@ -24,6 +61,33 @@ export async function logPearMultisigKernelHint(kernelRoot) {
|
|||||||
console.log(
|
console.log(
|
||||||
`[seeder] pear.multisig.json OK (${signers.length} signers, quorum ${quorum})`
|
`[seeder] pear.multisig.json OK (${signers.length} signers, quorum ${quorum})`
|
||||||
)
|
)
|
||||||
|
if (
|
||||||
|
process.env.BARE_OS_HYPER_MULTISIG_VERIFY === '1' ||
|
||||||
|
process.env.BARE_OS_HYPER_MULTISIG_VERIFY === 'true'
|
||||||
|
) {
|
||||||
|
const spawnSync = await loadSpawnSync()
|
||||||
|
if (!spawnSync) {
|
||||||
|
console.warn(
|
||||||
|
'[seeder] hyper-multisig verify skipped (no subprocess: install bare-subprocess or use Node)'
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const r = spawnSync('hyper-multisig', ['verify', f], {
|
||||||
|
encoding: 'utf8',
|
||||||
|
stdio: ['ignore', 'pipe', 'pipe']
|
||||||
|
})
|
||||||
|
if (r.error) {
|
||||||
|
console.warn(
|
||||||
|
'[seeder] hyper-multisig verify skipped (CLI not on PATH):',
|
||||||
|
r.error.message
|
||||||
|
)
|
||||||
|
} else if (r.status !== 0) {
|
||||||
|
const msg = (pipeText(r, 'stderr') || pipeText(r, 'stdout')).trim().slice(0, 400)
|
||||||
|
console.warn('[seeder] hyper-multisig verify failed:', msg || `exit ${r.status}`)
|
||||||
|
} else {
|
||||||
|
console.log('[seeder] hyper-multisig verify OK')
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (/** @type {NodeJS.ErrnoException} */ (e).code === 'ENOENT') return
|
if (/** @type {NodeJS.ErrnoException} */ (e).code === 'ENOENT') return
|
||||||
console.warn('[seeder] pear.multisig.json:', e?.message || e)
|
console.warn('[seeder] pear.multisig.json:', e?.message || e)
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
"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 ."
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"bare-subprocess": "^5.2.3",
|
||||||
"bare-os": "^3.8.7",
|
"bare-os": "^3.8.7",
|
||||||
"bare-os-protocol": "*",
|
"bare-os-protocol": "*",
|
||||||
"b4a": "^1.6.7",
|
"b4a": "^1.6.7",
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* Release checklist: workspace tests + kernel/seeder parity.
|
||||||
|
* Optional: set CHECK_DOC_LINKS=1 to grep handbook for broken relative links (heuristic).
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { spawnSync } from 'node:child_process'
|
||||||
|
import { readFileSync, readdirSync, statSync } from 'node:fs'
|
||||||
|
import path from 'node:path'
|
||||||
|
import { fileURLToPath } from 'node:url'
|
||||||
|
|
||||||
|
const root = path.join(path.dirname(fileURLToPath(import.meta.url)), '..')
|
||||||
|
|
||||||
|
function run(cmd, args, opts = {}) {
|
||||||
|
const r = spawnSync(cmd, args, {
|
||||||
|
cwd: root,
|
||||||
|
stdio: 'inherit',
|
||||||
|
...opts
|
||||||
|
})
|
||||||
|
if (r.status !== 0) process.exit(r.status ?? 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[release-checklist] npm test')
|
||||||
|
run('npm', ['test'])
|
||||||
|
|
||||||
|
console.log('[release-checklist] verify-kernel-seeder-parity')
|
||||||
|
run('node', ['scripts/verify-kernel-seeder-parity.mjs'])
|
||||||
|
|
||||||
|
if (process.env.CHECK_DOC_LINKS === '1') {
|
||||||
|
console.log('[release-checklist] doc link heuristic (CHECK_DOC_LINKS=1)')
|
||||||
|
const handbook = path.join(root, 'handbook')
|
||||||
|
const ref = path.join(root, 'docs', 'reference')
|
||||||
|
/** @param {string} dir */
|
||||||
|
function walkMd(dir) {
|
||||||
|
const out = []
|
||||||
|
for (const name of readdirSync(dir)) {
|
||||||
|
const p = path.join(dir, name)
|
||||||
|
const st = statSync(p)
|
||||||
|
if (st.isDirectory()) out.push(...walkMd(p))
|
||||||
|
else if (name.endsWith('.md')) out.push(p)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
let bad = 0
|
||||||
|
const linkRe = /\]\(([^)#\s]+\.(md|json))(?:#[^)]*)?\)/g
|
||||||
|
for (const file of [...walkMd(handbook), ...walkMd(ref)]) {
|
||||||
|
const text = readFileSync(file, 'utf8')
|
||||||
|
let m
|
||||||
|
while ((m = linkRe.exec(text))) {
|
||||||
|
const target = m[1]
|
||||||
|
if (target.startsWith('http')) continue
|
||||||
|
const resolved = path.normalize(path.join(path.dirname(file), target))
|
||||||
|
try {
|
||||||
|
statSync(resolved)
|
||||||
|
} catch {
|
||||||
|
console.warn(`[release-checklist] missing link target: ${path.relative(root, file)} -> ${target}`)
|
||||||
|
bad++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (bad) process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[release-checklist] done')
|
||||||
Reference in New Issue
Block a user