Files
bare-operating-system/developer-guide/01-two-runtimes-host-vs-image.md
T
Raven Scott 7171618c74
Release rolling / release (push) Successful in 9m59s
Update Docs
2026-08-12 21:10:14 -04:00

6.7 KiB
Raw Blame History

Chapter 1 — Two runtimes: host (Pear/Node) vs in-image (AsyncFunction)

If you only remember one thing from this guide, remember this: Bare OS runs two different kinds of JavaScript, and they follow different rules.


The confusion in one sentence

You might paste a file into your home directory on the personal Hyperdrive that starts with import fs from 'node:fs' and expect it to run like a Node script. It will not. That file is loaded as a string and executed with the JavaScript AsyncFunction constructor—not as an ES module. There is no module graph, no import resolution, and no automatic node_modules on the drive.

The booter and seeder Pear packages, by contrast, are normal ESM projects: they use import, npm dependencies, and Pear bundling. They run on the host and host the environment that evaluates in-image code.

When the bare-os npm module is available on that host, the booter may attach a read-only ctx.bareOsHostStats snapshot (loadavg, cpus, networkInterfaces, …) and write /proc/bare_os/host_os.json (schema 2, including bare-os 3.9+ and optional bare-posix fields)—still not a general “run Node in the image” escape hatch; see Chapter 2.


Mental model: who loads whom

flowchart TB
  subgraph host [Host process Pear or Node]
    booterPkg[bare-os-booter package]
    seederPkg[bare-os-seeder package]
    esm[import and node_modules]
  end
  subgraph hyper [Hyperdrives]
    sys[System drive]
    pers[Personal drive]
  end
  subgraph evalLayer [Evaluated inside booter VM]
    kernel["start ctx from boot/init.js"]
    binutil["run ctx argv from bin/*"]
    userscript["run ctx argv from home script.js"]
  end
  booterPkg --> esm
  booterPkg --> sys
  booterPkg --> pers
  booterPkg -->|runKernelFromSource| kernel
  booterPkg -->|runBinCommand| binutil
  booterPkg -->|runBinCommand| userscript
  • Host code lives under packages/bare-os-booter/, packages/bare-os-seeder/, etc. It is trusted in the sense that you built or installed it; it opens Corestore, Hyperswarm, Hyperdrive, and constructs ctx.
  • In-image code is bytes on a drive (/boot/init.js, /bin/cat, ~/mytool.js). The booter reads those bytes as UTF-8 strings and passes them to new AsyncFunction(...) (see kernel-runner.js).

In-image execution (the AsyncFunction contract)

Two entry shapes matter:

  • async function start(ctx)/boot/init.js on the system drive — ctxrunKernelFromSource wraps the source and calls start(ctx)
  • async function run(ctx, argv) (optional for user scripts) — /bin/* always; or a *.js file resolved from the shell — ctx, argv (string array) — runScriptFromSource runs the file body, then awaits run(ctx, argv) if defined

The booter injects ctx and argv. Kernel start is required; for shell scripts, top-level statements may stand alone, or you may define run like /bin utilities. Top-level import is invalid in that evaluated string because the engine is not loading an ES module—it is compiling a function body.

Shebang lines (#!/usr/bin/env bare) are stripped before compile (stripShebang) so the first token the parser sees is valid JavaScript.


Host execution (Pear / Node packages)

When you edit packages/bare-os-booter/index.js, you are writing normal JavaScript for Node or Bare under Pear:

  • Use import Hyperdrive from 'hyperdrive'.
  • Add dependencies in package.json.
  • Use async I/O against real host APIs.

Web Encoding globals: some Bare/Pear builds do not define global TextEncoder / TextDecoder. Booter and in-image code should use b4a for UTF-8 instead (e.g. b4a.from(str, 'utf8'), b4a.toString(buf, 'utf8')), matching curl-cli.js. Relying on new TextEncoder() in booter lib/*.js can break at runtime (for example when statting or reading pseudo files under /proc or /sys).

This code creates ctx and passes it into the kernel. It does not run inside the simulated /bin environment unless you explicitly call runBinCommand(ctx, argv) with the same ctx the shell uses.


Trust: system drive vs personal drive

  • The system drive is the replicated OS image: /boot, /bin, /etc, /share. You should treat its contents as integrity-checked by replication from peers you chose to trust (same discovery key / topic as the rest of the project).
  • The personal drive holds $HOME, /.bare, user files, crontab, etc. It is writable by the session. User scripts you write live here by default.

A script you place in ~/exploit.js is your code; the booter will still AsyncFunction-evaluate it with full ctx power. That is convenient and dangerous—see Chapter 9.


Host source policy (Bare-first)

Pear-bundled booter and seeder runtime sources under packages/bare-os-booter/lib/ and packages/bare-os-seeder/lib/ must stay resolvable on Bare as well as Node: CI runs scripts/verify-pear-no-static-node-import.mjs, which rejects from 'node:…' imports, require('node:…'), and import('node:…') in those trees (with path-based exceptions). Use #host-fs, **#host-path**, and **#host-fs-promises** from package imports instead of bare fs / path specifiers. The only deliberate node:module usage today lives in bare-os-boot-manifest-sig.node.js (Node default entry for boot manifest verification); the bare export uses bare-os-boot-manifest-sig.bare.js. For guest-visible behavior, prefer Holepunch **bare-*** modules per Node → Bare module map.


When to use which runtime

  • Add a new /bin command shipped with the OS image — In-image pattern: coreutils src/*.js + build (Chapter 6)
  • One-off automation in your home directory — In-image script (top-level and/or optional run); no import (Chapters 45)
  • Change how networking, HDMS, or the REPL works — Host booter package (ESM)
  • Publish or replicate the system image — Host seeder package

See also


← Developer guide home · Context object →