evidence · 9.4 KiB

shell-pid1-controller-host-2026-10-03.md

Committed at docs/evidence/mainline-system-trial/shell-pid1-controller-host-2026-10-03.md. This is what grounds the requirements that cite it.

Open raw source

Same-image Bash PID1 comparison: host preparation

Evidence class: source inspection, host artifact inspection, scripted transport and isolated host shell fixtures. Physical Bash readiness, UART reception and automatic protected return for this comparison are UNVERIFIED. Ordinary NixOS root, panel/glass touch and task 5b.5 remain open.

Branch mainline-shell-pid1-comparison, base 358b63241659f90e48b7249b913bfd7f71ac7d08, worktree /home/jadams/tmp/k230-mainline-shell-pid1-comparison. Owned changes are the initrd shell controller, tests/test_mainline_shell_pid1_comparison.py and this note. No board, UART, camera, build slot or network server was used. Existing kernel/system/bundle derivations and historical controller defaults are unchanged.

Exact comparison

Explicit --same-image-shell-pid1 is allowed only with --mode minimal, without clock-ignore, boot-debug or runtime-shutdown tracing. Before any serial access, preparation validates the original immutable bundle, manifest, selected system, hashes/sizes/CRCs, protected normal report and actual load ranges through the existing helpers. It then requires the exact original serial-only SBI-only artifact arguments, removes both trace-enable tokens and adds the three qualified controls plus initramfs_async=0 and sole rdinit=/bin/sh.

Compared with the preceding marker-free ordinary trial, only the initial PID1 program choice changes. The selected system’s sole init= remains present for identity qualification; Bash does not activate that system or switch root. The literal volatile command is fully qualified and checked below 512 bytes before opening UART. Actual printed arguments must match exactly. No persistent environment, profile or boot-file selector is changed.

Actual host preparation passed for:

The actual wrapper’s complete header/data CRCs, RISC-V ramdisk fields, length and payload match that selected initrd. A bounded newc walk verifies archived executable RISC-V ELF files rather than substituting host store symlinks. /init resolves to systemd 261.2; /bin/sh resolves to /nix/store/89hsc9vrrk2vr18yp9yrzs365fz490wv-bash-interactive-riscv64-unknown-linux-gnu-5.3p15/bin/bash. Both ELF PT_INTERP fields resolve to the same archived loader: /nix/store/syg6xw4x296w82267qnvrd3nn0cp6j31-glibc-riscv64-unknown-linux-gnu-2.42-84/lib/ld-linux-riscv64-lp64d.so.1, SHA-256 19835521e0d42cf163f139040c5c4497edb893153c68997db327bb4a6713f904. Readlink, true, mkdir, mount, cat and reboot are also archived executable RISC-V ELF files. Selected Zstandard inspection requires host Python 3.14; an unavailable decoder fails before serial access.

Runtime gates and limits

The existing fresh Linux → Run /bin/sh as init process → primary prompt gate precedes receipt, true, proc setup and uptime. Only afterward does the opt-in guard check outer-shell PID1, PPID0, UID0, Bash version/executable, kernel release, exact selected cmdline, fresh non-normal boot ID, initrd release and an in-memory root with no /sysroot mount or populated store. The exact same candidate boot identity is renewed immediately before one existing acknowledged /bin/reboot -ff request. Guards print fixed RC/match fields and the private boot UUID, not raw proc/cmdline contents. Each command is below 3072 bytes; the actual initial guard is 1804 bytes. These bounds do not establish a wall-clock deadline for a blocked child/kernel operation.

Each guard requires one exact fresh BEGIN/END pair, no intervening unknown text, no additional malformed/other-phase frames for that nonce, and the returned exact primary prompt. Missing/truncated/duplicate/unknown frames stop input; a completed failed guard also stops without requesting reboot. The raw private log is preserved unchanged. No signal, exit, recovery command or child retry is sent to an unknown candidate. The existing readiness (90 seconds), receipt (at most eight fresh attempts), probe (30 seconds per completion) and normal-return (180 seconds) bounds remain; recovery is not guaranteed if Linux or a child blocks.

Both uploaded normal phase helpers actually assert that registration is absent, including a dangling symlink. Existing protected normal identity/profile/kernel, three services, eight hashes and fresh-boot checks remain. Results explicitly record ordinary_init=NOT_ATTEMPTED, usable_root=UNVERIFIED, and touch=UNVERIFIED; the probe has its own k230-initrd-shell-pid1-v1 schema. Success would prove Bash/common-loader userspace and qualified UART progress, not ordinary systemd root or a cause of the previous silence. Silence cannot distinguish exec failure, return-to-user work, scheduling or console output loss.

Host proof

Commands run from the owned worktree:

python3 -B -m unittest discover -s tests -p test_mainline_shell_pid1_comparison.py
python3 -B -m unittest discover -s tests -p test_mainline_drm_initrd_shell_trial.py
openspec validate the-board-runs-a-mainline-kernel --strict
openspec validate --all
git diff --check

The focused 14 tests pass. The existing 100 initrd-controller tests pass. Strict validation passes; all 56 OpenSpec items pass; whitespace checks pass. The focused fixtures exercise actual safe literal transport, parser hostility, typed CLI conflicts/default preservation, actual newc/ELF/wrapper inspection, missing prerequisites, fresh/renewed guards, unknown-no-input persistence and single-reboot protected-phase wiring. Generated guards execute under host sh/bash against isolated identity/mount-table fixtures, including a false final entry, a missing table, persistent root and /sysroot. Simulated identity and prompt fields are fixtures; no host mount or real serial operation occurs. The actual production normal registration assertion is separately executed against absent, regular and dangling-symlink fixture paths.

Actual preparation command (private inputs are not printed or committed):

python3 -B - <<'PY'
import importlib.util
from pathlib import Path
s = importlib.util.spec_from_file_location('trial', 'tools/mainline-drm-initrd-shell-trial.py')
m = importlib.util.module_from_spec(s)
s.loader.exec_module(m)
p = m.prepare_trial(
    Path('/home/jadams/tmp/k230-mainline-shell-pid1-board/candidate-manifest.json'),
    Path('/nix/store/brmp1qf9cz65yciazabc8yg5j39nn643-k230-mainline-drm-trial-boot-files'),
    Path('/home/jadams/tmp/k230-mainline-shell-pid1-board/normal-report.json'))
p = m.prepare_shell_comparison(p)
assert len(p['transport'].encode()) == 332
assert p['bootargs'].split().count('rdinit=/bin/sh') == 1
assert p['bootargs'].split().count('initramfs_async=0') == 1
assert not any(v.partition('=')[0] in ('k230.boot_trace', 'k230.boot_trace_sbi_only')
               for v in p['bootargs'].split())
print('host preparation PASS; serial not opened')
PY

This passed. Its 332-byte command is the preceding 317-byte marker-free ordinary command plus exactly rdinit=/bin/sh. No Nix build was required.

Remaining operator gate

After review/landing and independently verified protected normal recovery, only the board operator may run the fresh private comparison:

python3 tools/mainline-drm-initrd-shell-trial.py \
  --mode minimal --same-image-shell-pid1 \
  --bundle /nix/store/brmp1qf9cz65yciazabc8yg5j39nn643-k230-mainline-drm-trial-boot-files \
  --manifest /home/jadams/tmp/k230-mainline-shell-pid1-board/candidate-manifest.json \
  --normal-report /home/jadams/tmp/k230-mainline-shell-pid1-board/normal-report.json \
  --log /home/jadams/tmp/k230-mainline-shell-pid1-board/trial.private.log \
  --result /home/jadams/tmp/k230-mainline-shell-pid1-board/result.private.json

Use new paths if either output already exists. Physical execution, candidate guard facts and protected automatic return are UNVERIFIED by this host note.

Coordinator integration proof

Root integrated reviewed source 00387e51 as 473d4ab3 on integrate/mainline-probe-path, worktree /home/jadams/tmp/k230-mainline-probe-integration, after recovery base 1088e3ee. Independent peer approval included 14 focused passes. Root ran:

TMPDIR="$HOME/tmp" python3 -m unittest discover -s tests -p 'test_mainline_drm*trial.py'
TMPDIR="$HOME/tmp" python3 -m unittest discover -s tests -p 'test_mainline_shell_pid1_comparison.py'
openspec validate the-board-runs-a-mainline-kernel --strict
openspec validate --all
git diff --check

All 214 existing trial tests and 14 new tests passed; strict/all56 validation and whitespace passed. Actual preparation against the same protected private manifest/report verifies identical system/manifest/normal/helper and expected arguments exactly equal to the preceding ordinary marker-free arguments plus rdinit=/bin/sh; literal transport 317 → 332 bytes. This is host proof with no serial access; private fixed preparation record is ~/tmp/k230-mainline-shell-pid1-board/host-prepare.json. CI now runs the 36 ordinary, 100 existing minimal-controller and 14 new focused tests. Source landing/deployment does not supply physical shell/root/touch acceptance.