← board

PXX portable userland (mini OS-personality) — one shell, any kernel

The idea (one line)

PXX supplies the userland the kernel deliberately leaves out. The "kernel" below is just an ABI — Linux (syscalls) or FreeRTOS (tasks/IPC). The PAL ([[feature-platform-abstraction-layer]], landed) is the seam that swaps them, so one shell/app source runs as Linux PID 1, on ESP32/FreeRTOS, or as a desktop process — swap the PAL backend, not the app. This is the ir-as-substrate thesis made visible: thin frontend + shared IR + one platform seam ⇒ the app is kernel-agnostic by construction, not by luck.

Backends (the same userland, three kernels)

Flagship app — the NilPy shell (busybox/applet model)

A Unix-ish shell written in NilPy (Nil-Python): line editor → parse → applet dispatch → a | b | c. No fork/exec (no MMU on classic ESP32): one binary, commands are built-in applet functions dispatched by name (echo cat ls grep wc head tail ps), optionally run as tasks. A VFS gives the filesystem illusion (borrow from the sqlite file-VFS + PAL groundwork).

Why NilPy: it doubles as a frontend forcing function — the shell drags NilPy from "proven toy" (classes, control flow, auto-typing, C-import binding all work) to a real language, one concrete feature at a time. And it demos the cross-target thesis at the concurrency level (preemptive tasks on ESP, threads on desktop).

Why not busybox (decided — stop re-litigating)

busybox recurs in brainstorms as "the shell." It's welded to the Linux kernel model: fork/exec (multi-call binary re-invokes itself per applet), termios, signals, /proc, the full syscall + VFS surface. Porting it to ESP32/FreeRTOS (no processes, no MMU, no fork/exec, one address space) = reimplementing half a kernel — significant hacks, wrong effort. So busybox is NOT the ESP path; the hand-written NilPy applet shell is. Two legitimate busybox uses stay open, both Linux-only and separate from this demo's portable goal:

Real dependencies (NilPy frontend gaps the shell forces — Track A)

Phasing

  1. Desktop-first, NilPy: shell loop + parse + applet dispatch + a|b via buffers, on x86-64. Surfaces the collection/string gaps → drive them.
  2. Applet set: echo cat ls grep wc head tail ps over a VFS abstraction.
  3. Cross to ESP: desktop PAL → IDF PAL; applets as FreeRTOS tasks; pipes as stream buffers.
  4. Polish: history, redirects, & background = spare task.

Gate (file-ownership Track B for the app; Track A for the NilPy deps)

App/demo builds with $(PXX_STABLE) (Track B rule — never rebuild the compiler); make lib-test / demos green; desktop shell runs; ESP variant boots under the QEMU/IDF harness. NilPy-frontend deps carry Track A's gate (self-host byte-identical) since they touch shared frontend/RTL.

[[feature-platform-abstraction-layer]] (the seam) · [[feature-kernel-matrix-bootroom]] (Linux backend) · [[feature-nilpy-collections-and-string-methods]] (blocker) · [[project_nil_python_arc]] · thesis devdocs/dev/ir-as-substrate.md.

Log