Files

8.4 KiB

name, version, description, tags, requires
name version description tags requires
bare-os-super-developer 1.0.0 Write Bare OS scripts and small apps using ctx — VFS, execLine, hooks, caps, and safe patterns (no Node in /bin/agent loop).
bare-os
ctx
scripts
vfs
kernel
developer
read_file
read_proc_file
run_js_script

bare-os-super-developer — Scripts, apps, and ctx

When to use

Use this skill when you (or the user) need to author or debug Bare OS JavaScript that runs inside the guest (kernel init, /bin/* utilities, kernel.ext.d, user .mjs run via run_js_script, or Pear/booter-hosted scripts). It complements repo docs: treat developer-guide/02-the-context-object.md and packages/bare-os-booter/lib/bare-os-ctx.d.ts as the canonical deep dives; this file is the fast mental model.

Core model

  1. Entrypoints — Kernel: async function start(ctx) in /boot/init.js (or your image). Commands: async function run(ctx, argv) where argv[0] is the invoked name (e.g. agent). You must set ctx.exitCode (number) before returning on failure paths.
  2. ctx is not Node — There is no full process, no require('node:fs'). The booter assembles ctx as the narrow syscall surface: vfs, env, execLine, runBinCommand, optional httpFetch, bare, identity helpers, diagnostics, etc. See the mermaid overview in Chapter 2 of the developer guide.
  3. /bin/agent is special — The agent bundle uses run_js_script for guest JS and a frozen tool surface; do not assume node exists. For general in-guest scripting, prefer ctx.vfs + ctx.execLine / ctx.runBinCommand and optional ctx.bare.* modules when enabled.

Writing scripts (run(ctx, argv))

  • Argvargv is a string array; argv[0] is how you were invoked (symlink name matters for multi-call binaries).
  • Stdout — Prefer ctx.console.log / ctx.console.error (session-aware). For binary or captured stdout, ctx.bareOsBinWrite may exist when the shell captures pipeline output.
  • Shell a subprocessawait ctx.execLine('some shell line', { signal, timeoutMs }) returns a string (see booter raceWithAbortAndTimeout). Heavy work: await ctx.runBinCommand(['/bin/grep', …], opts) for same resolution as the interactive shell.
  • Filesystemawait ctx.vfs.readFile(path)Uint8Array; await ctx.vfs.writeFile(path, buf, opts?). Decode with ctx.b4a.toString(buf) or TextDecoder. Always use absolute paths under /home, /tmp, /mnt, /bin, etc., per policy.
  • Environmentctx.env is mutable shell state (also ctx.vfs.env). After execLine, ctx.env.BARE_OS_EXIT_STATUS reflects last exit code when the booter sets it.
  • Exit — Set ctx.exitCode = 1 (or other code) on error; 0 on success. ctx.requestBooterExit(code) ends the whole session from builtins like exit.

Building “apps” (long-lived behaviour)

Think in layers the stock OS already uses:

Layer Mechanism Notes
Init bareOsRegisterBootStepHook, initd units Boot-order DAG (stock example: bare-os-www before bare-holesail for bare-www-* tunnels); pair registerKernelShutdownHook / initd disposers for teardown.
Virtual files bareOsRegisterVirtualFile(name, reader, opts?) Serves /run/bare-os/virtual/<name>; gated by runtime caps.
IPC ctx.bareOsIpc when present push/take, JSON helpers, fanout, duplex bridge — bounded; audit when BARE_OS_IPC_AUDIT=1.
Kernel extensions bareOsRegisterKernelExtensionRecord, bareOsRunImageScript under /lib/bare-os/extensions/ Trusted image paths only.
Sandboxed user JS bareOsSandboxRunScript(source, argv?, opts?) Restricted ctx; disable with BARE_OS_SANDBOX_SCRIPT=0.
Pear / host bareOsPearIpcEmit, bareOsPearIpcRequest, mirror/export hints Host must cooperate; return { ok, hint } patterns.

Before touching sensitive ctx methods in hardened images, call ctx.bareOsIsCtxMethodAllowed?.(name) when boot policy BARE_OS_BOOT_POLICY_ALLOWED_CTX_METHODS is set.

ctx field map (cheat sheet)

Always read live truth for capability bits: read_proc_file on /proc/bare_os/features and /proc/bare_os/capabilities.json — do not infer from man pages alone (see bare-os-kernel-proc skill).

Files, drives, process

  • ctx.vfsreadFile, writeFile, mkdir, readdir, stat/lstat, chmod, … Path routing: system vs personal Hyperdrive; ctx.drive vs ctx.personalDrive for advanced use.
  • ctx.env, ctx.drive, ctx.personalDrive, ctx.disk — Session identity and mounts.
  • ctx.execLine, ctx.runBinCommand — Shell and /bin execution with shared policy (timeouts, abort).

Bytes, console, session

  • ctx.b4a — Buffer/string helpers for Hyperdrive payloads.
  • ctx.console, ctx.readLine, ctx.writeScreen — REPL session ( readLine may be Fish-backed when enabled).
  • ctx.exitCode, ctx.requestBooterExit, ctx.registerKernelShutdownHook

Bare OS introspection & policy

  • ctx.bareOsCtxApiVersion, ctx.bareOsRuntimeCaps (frozen)
  • ctx.bareOsAdvertisedKernelCapabilityWords, ctx.bareOsSeedKernelCapabilityWords>>> 0 when testing bits.
  • ctx.bareOsGetResourceStatus, ctx.bareOsReadProcMetricsLive, ctx.bareOsReadBareTopSnapshot
  • ctx.bareOsRegisterVirtualFile, ctx.bareOsInvalidateVirtualFile, ctx.bareOsUpdateVirtualFileMeta
  • ctx.bareOsEmitIpcAudit, ctx.bareOsEvaluatePeerAdmission, ctx.bareOsEmitMirrorDriveHint

Optional / host-dependent

  • ctx.httpFetch — Same allow/deny policy as curl / wget; every host for web_fetch must be allowlisted.
  • ctx.bare — Frozen map of vendored modules (b4a, protomux, …) when BARE_OS_BARE_MODULES allows.
  • ctx.bareOsHostStats, ctx.bareOsChat*, ctx.bareOsPearIpc* — Present only when wired by the booter/host.

For the full list and semantics, open developer-guide/02-the-context-object.md in the repo (or read_file on a mounted checkout).

Minimal script template (guest)

/**
 * @param {Record<string, unknown>} ctx
 * @param {string[]} argv
 */
export async function run(ctx, argv) {
  const argv0 = argv[0] || 'script'
  const vfs = ctx.vfs
  if (!vfs?.readFile) {
    ctx.console.error(argv0 + ': no vfs')
    ctx.exitCode = 1
    return
  }
  try {
    const buf = await vfs.readFile('/proc/bare_os/features.json')
    const t =
      ctx.b4a && typeof ctx.b4a.toString === 'function'
        ? ctx.b4a.toString(buf)
        : new TextDecoder().decode(buf)
    ctx.console.log(t.slice(0, 500))
  } catch (e) {
    ctx.console.error(argv0 + ': ' + (e && e.message ? e.message : String(e)))
    ctx.exitCode = 1
  }
}

Save under /home/.../my-tool.mjs and run by absolute path (or register under /bin on the image). Do not rely on node in the guest.

Agent-specific note

When helping inside /bin/agent, the model uses tools (read_file, run_js_script, …) backed by the same personal/system VFS as the rest of the guest. run_js_script writes a temp .mjs under ~/.agent/ and executes it like a /bin script — the script body should use the same ctx patterns above when the harness passes ctx.

Checklist before shipping

  1. read_proc_file / get_system_info — confirm needed features and caps exist this session.
  2. Paths — absolute, no .. escape from allowed roots.
  3. Timeouts — pass AbortSignal / timeoutMs to execLine / readLine for network or slow FS.
  4. Teardown — unregister hooks, clear intervals, dispose IPC fanout subscribers.
  5. Secrets — never log ~/.agent/config.json or keys; redact in MEMORY.md.

Output format (when this skill was used)

  • What you read — which ctx fields or /proc files grounded the answer.
  • Plan — file paths, entrypoint (run vs start), and exit semantics.
  • Risks — policy gates, missing caps, host-only APIs.