The machine that would not take a resident agent — until a 405-command war ended. This notebook watches the loop on a rooted Pixel 10a (Android 17, "stallion") running Hermes in Termux: how it came to run at all, how its mind survives the app's reinstall-wipe (attributed and fixed — the app was reinstalling a bootstrap into a prefix it could no longer read), and how it comes back across reboots — and how the resident gets a face on the home screen.
The reboot war — four controlled boots — the wipe attributed to the app itself (a 401 not readable prefix after a uid-501/system_data_file rebuild), the chown/chcon fix that ended the wipes, the retired watchdog, and the anthropic venv gap
The cockpit
The cockpit war: a face for the resident — the app that couldn't see (the two manifest lines), the widget built without Android Studio, the notifications that needed a second app (Termux:API, checksum-verified, installed as root), and the watcher that thinks
The runtimes census: what the machine can actually run — living part: three layers (store · Termux · system JVMs), every row with its proving command; founding finds: the store manifest drifts from staged reality, ffmpeg won't run even under the launcher env, and the clang/gcc set can't link; wave 2: the repo's whole language shelf (swift/kotlin/rust/bun/deno/go/ruby/php/mono — present, none installed), the C toolchain deep-dead (ld/lld orphaned, as absent), ObjC nowhere, and the Android build toolchain repo-installed since July
The lock
The lock war: the fourth digit submits — iOS-style PIN auto-submit: two one-instruction smali patches in SystemUI, the private-mount-namespace trap (su -mm), one-command revert The keyboard
TermKey: the keyboard built on the phone, for the phone — a terminal-first system IME compiled on-device: stub-signature wars, the crash-painter debug trick, dual wire formats (key events for terminals, committed text for normal apps), rotation rebuild; portrait and landscape measured
The machine that would not take a resident agent — until a 405-command war ended. This notebook watches the loop on a rooted Pixel 10a (Android 17, "stallion") running Hermes in Termux: how it came to run at all, how its mind survives the app's reinstall-wipe (attributed and fixed — the app was reinstalling a bootstrap into a prefix it could no longer read), and how it comes back across reboots — and how the resident gets a face on the home screen.
Four lanes now, all earned: the bring-up (getting Hermes to run on this machine at all — payload construction, SELinux exec domains, the Android linker, packaging — the 405-command war that this notebook exists for); and the survival plant (state that has to survive the app's wipes and the boot's deletes — the resurrection and the reboot, the healed copies and the measured state gates); and the cockpit (the resident's face — the status app and widget on the home screen, alerts as phone notifications, the watcher that trips on retry storms, session death, finished jobs); and the fleet's cable (the wire into the mini — the Pixel routed over the TB5/USB cable at ~400 Mbps single-stream / 610 multi-stream, auto-healing; it did not refuse, and the earlier "refused" call was retracted); and the resident's runtimes (what the machine actually carries — Node, logged as a no-go, found living inside the agent's own kit since day one of the canary staging; then the living census of all three layers, ledger-first); and the lock (iOS behavior won on the PIN pad — the fourth digit submits, two instructions of smali, one namespace lesson); and the keyboard (TermKey — an IME built on the machine for the machine, terminal-first, dual-wire, rotation-aware); and the NFC war (raw NCI past the framework — the user called the limits lies and was right). The agent's years — what the resident actually does — will open a lane when it earns one.
1.Getting Hermes to run on the Pixel 10a (Android 17, "stallion") at all — the 405-command war: you cannot clone and run it — the Termux bootstrap that JNI-crashes, the SELinux exec domain, the linker that ignores LD_LIBRARY_PATH, and packaging — four walls, knocked down and gated by 28 checks.
The survival plant
1.The resurrection that split the mind in two: a state.db that couldn't decide — a wiped rooted Pixel restored its 15 skill dirs (3 hand-built — android-screen, dsh, ssh-remotes; the other twelve are template dirs, a count-typo we nearly shipped as a metric) and the transcript, then the state.db bounced 266K-868K: the watcher probed processes, not state — its getprop returned the agent's own splash, and a ghost gateway minted a second identity. Cured with grow-only restore, wrapper shebang fixes, a tmux pty, and a db-size health gate. 2.The reboot war — four controlled boots — the wipe that beat an immutable canary attributed to the app itself: a rebuild left the data dir owned by uid 501 under a system_data_file label, the app got 401 not readable on its own prefix, and its bundled-bootstrap install path deleted it. chown + chcon over the whole data dir ended the wipes; boots then passed with nothing touched. Also: the watchdog loop retired for a one-shot boot start, and the anthropic venv gap (root uv can neither exec nor reach the network) fixed with the app-cgroup recipe.
The cockpit
1.The cockpit war: a face for the resident — v1.2's sockets died silently: no INTERNET permission plus the targetSdk≥28 cleartext block, both fixed and both now manifest law; the widget (AppWidgetProvider, RemoteViews, PendingIntent) built without Android Studio on hand-written stubs and the DEVICE framework-res.apk; the alerts that needed Termux:API — a separate app, fetched checksum-verified and pm-installed as root via /data/local/tmp; hermes-watch.sh as a no-LLM cron tick (retry storm ≥3, session death, job finished) whose first finding was the gateway dead behind a stale state file. Verified the same hour: app v1.3, widget receiver registered, notification confirmed in dumpsys notification, watcher at 0 fails.
The fleet's cable
1.The cable war: serving the Pixel over the mini's Thunderbolt/USB wire — the mini (the fleet's hub) serves the Mac laptops over a Thunderbolt bridge (bootpd DHCP, NAT out the wired uplink, forwarding on); the question was the same service over the TB5/USB wire physically plugged into the mini's USB port. The wire answered at USB3 SuperSpeed 5 Gbps, the phone's gadget flipped ADB→NCM (usb0 up; the mini enumerated it as a Pixel ethernet port), L2 carried real frames both ways, and the mini's Pixel pool (its own /24 — after the laptops' /24 proved owned by the TB bridge) was fully loaded (bootpd + pf NAT + forwarding). The shipable artifact is the android17-tbnetbridge Magisk module (macOS half: Gauge at secondsee.com/downloads/gauge). The first diagnosis was wrong (and the part retracts it): Android did not refuse. Three mislabeled walls fell — the NCM netdev is named ncm0 but Android's EthernetNetworkFactory only tracks (usb|eth)\d+ (rename to usb0 and the factory claimed it, agent + DHCPDISCOVER alive); macOS holds no persistent config for the re-enumerating USB NIC, so it kept link-local-ing over the manual static server address (a real Manual network service on the Pixel port fixed it, re-applied across replugs); and the pf NAT anchor existed in pf.conf but was never loaded into the live ruleset (pfctl -Nf - push fixed it, states then appeared). With the server durable, DHCP completed over the wire, the network validated, and the cable became the active default network — ~400 Mbps internet path single-stream, ~460 wire-only, 610 multi-stream (fast.com fan-out). Durability: the phone re-arms via a Magisk boot service (~12 s auto-heal on replug, verified), the mini re-applies its static address natively.
6.Node was here all along: the runtime we called a no-go — the no-go retracted from the inside out: a complete Node runtime (v26.4.0 + npm 12.0.2) sat in the agent's own tool store since the Sep 17 canary staging, byte-identical to the distro's own nodejs package; the bare shell never had it, the launcher always did, and pkg install nodejs still dies on an unrelated half-installed libexpat.
7.The runtimes census: what the machine can actually run — a living part, grown by rows: Layer 1 the agent's store (node/python are the distro's own packages; npm/ripgrep/uv are not; the manifest claims versions the binaries don't have; ffmpeg runs only with its own lib dir, which even the launcher omits), Layer 2 Termux proper (zero language runtimes; clang/gcc present-but-unlinkable, libLLVM absent; the apt route blocked by a half-installed libexpat), Layer 3 the system JVMs (two OpenJDKs, alive). Carries a corrections ledger — its first entry patches Part 6 in place (the repo index does carry libexpat 2.8.5; the suspect is apt's lists). Wave 2: the repo carries swift 6.4 / kotlin 2.4 / rust 1.98 / bun 1.4 / deno / zig / go / ruby 4 / php 8.5 / mono — none installed, all one apt-block away; the C/C++ toolchain is deeper-dead than clang knew (ld + lld are libLLVM orphans dpkg doesn't own, as is absent; only ar/nm/objcopy/strip live); Objective-C is a dead row (nothing in the repo); and the Android build toolchain (aapt2 + d8/apksigner jars + javac in both JDKs) is repo-installed, runnable, and predates the agent — the built APKs on disk are dated July.
8.The lock war: the fourth digit submits — the iPhone's auto-submit on the Pixel's PIN pad. Dead ends measured first (no setting, no flag; the 6-digit feature proves the behavior exists but changes the credential; store lock-screen skins rejected as fake locks). The smali map: per-digit requests already carry auto-confirm — the framework policy gate (isAutoPinConfirmEnabled) and the decision gate (shouldSkipAuthenticationAttempt's flow read) kill it; two const/4 0x1 inserts free the lane while the real pin-length check stays intact. War one: the build (apktool on-device, arsc STORED, broken wrappers). War two: v1 was a ghost — plain su mounts in a PRIVATE namespace; /proc//root showed stock bytes every boot; su -mm (global ns) is the fix, now the series' namespace lesson. War three: the keyguard is a secure surface (screencaps return black) — proof is behavioral + the operator's eyes; v1's shell-side 'unlocked' telemetry recorded as a false-positive scar. Deployed as Magisk module + boot re-bind, revert = one umount; the ✓ renders but is a no-op (known residue).
9.TermKey: the keyboard built on the phone, for the phone — a terminal-first system IME, compiled on-device with the census's toolchain (aapt2 + javac-against-stubs + d8 + apksigner). Four walls: stub signatures binding call sites to lies (NoSuchMethodError across ten builds — fixed by mirroring real framework signatures exactly, verified in the dex); a dead ENTER key from one missing switch case (found by auditing every label against every case); dual wire formats — terminals speak key events, normal apps speak committed text, an IME needs both and picks per focused app (Ctrl+A as 0x01 in Termux, newline in Chrome); rotation freezing the portrait layout (fixed by rebuilding the view on configuration change, 58dp keys portrait / 32dp landscape). Debug hero: the crash painter — an IME that renders its own stack trace as its own view. Typing verified end-to-end in both orientations.
10.The NFC war: talking to the chip behind Android's back — triggered by the user's correction: you lie about what you cannot do. Tested: root stops the NFC HAL, /dev/st21nfc opens as a raw device, NCI protocol exchanges flow (RESET, INIT, NFCEE discovery all answered). The ST21NFC chip's NFCEE route table extracted — AID patterns and NFCEE IDs that Android never exposes. APDU-to-eSE channel started but not completed. The single honest limit after testing: eSE key extraction is hardware-fused. Every other claimed can't was a stack limit, not a chip limit.
-
[The board census: every chip on the phone, outside Android]](https://gist.github.com/ebowwa/e3d27538533432c1d772d058d8a13b39) — extends the NFC war from one chip to the whole board. Sysfs census: the I2C map (Maxim power block, CPS4041 wireless charging with an auth surface, CS40L26 haptics, TAS25XX amp), SPI (st54spi eSE, FocalTech touchscreen, GNSS on SPI), and the co-processor federation (AoC RISC-V, EdgeTPU rio, Trusty TEE with no citadel node, Broadcom Kepler GNSS). Wars: the kernel refuses raw I2C to claimed addresses (EBUSY, by design) but the at24 driver gave up its full 8KB EEPROM; the BT "UART" was a myth — no H4 at three bauds, no ldisc ever, and the real transport is /dev/wbrc (484:0) found in the HAL's fd list; GNSS nodes are silent until booted (clean stop/start cycle, restored); and the eSE answered a raw SPI read with an ISO-T=1 alive block (21 82 00) — the direct channel Part 10 only reached through NCI routing. Every stopped service restarted and verified. Fused stays fused.
-
[The eSE war: making the vault's own door talk]](https://gist.github.com/ebowwa/ad1c33818d628bdde6c90c7c9c2c4620) — the secure element conversation, finished. The st54spi driver (read/write + one reset ioctl 0x40046b63) turned out to be driven by an AOSP se_gto-lineage transport, so instead of reversing full-duplex SPI frames (five-byte header 21 82 00 a3, our TX echoed back) we dlopened the vendor's own library against /dev/st54spi from a sixty-line host: open, SPI reset, ATR (15 bytes, T=1, IFSC 254 — the 'error 15' was the length), then APDUs: the vendor's own alive check (GET DATA 9F7F) returned 47 bytes and SW 9000 with the card spelling 'GTO' in its identity object; SELECT default applet answered with the public Thales ISD AID A000000151000000; the card manager hides applets at default privilege while the framework's ARF rules + direct SELECTs prove three live applets respond. HAL stopped and restored around every run. The pipe is ours; Part 10's fused verdict stands unchanged.
-
[The wbrc war: the radio that wasn't where we looked]](https://gist.github.com/ebowwa/98b9833de078ef10c46ac8cbcd47f2a2) — Bluetooth on the board is not UART-attached: the grouped tty gave three bauds of silence and never carried a line discipline, the combo's power is an rfkill interlock with userspace GPIO stubbed out, and the real transport is /dev/wbrc (484:0) — a lifecycle coordinator inside the WiFi driver's own module, found in the HAL's fd list. The practical victory: Android's HCI snoop tap (bluetooth_hci_log) delivers the complete init conversation in clean H4 — Broadcom confirmed, the board codename written over the air, firmware retained across warm HAL restarts. A ptrace syscall-injection framework was pursued and closed by evidence: 15,388 syscall stops with zero wbrc writes — the transport writes are kernel-internal, no userspace path exists. Fused stays fused.
-
[The BETP boot: mapping the radio's own tongue]](https://gist.github.com/ebowwa/e96c1561281b8df997204a710ed5099e) — the Kepler GNSS boot is a userspace BETP write() protocol, not the ioctl surface it first appeared to be. Nine frames captured from gnssd's syscalls (68 43 header, sequence counters), replayed standalone against /dev/gnss_ipc with the chip answering every frame — 'BETP' literal on the wire. The firmware upload only runs on true chip power-cycles (fresh-kprobe proof: zero link_load on service restarts); the modem/CP side supervises (lal_cp_monitor holds channel ownership after gnssd exits; umts_boot0 _IO('o',25/32/39) accepted from root). The decoys and the real protocol, mapped.
-
[The context hub: CHRE on AoC, mapped]](https://gist.github.com/ebowwa/6cb77fa6399890bb362ecf35870b77b1) — the AoC's CHRE context hub mapped end to end: 94-service catalog, 13 nanoapps, and the live data framing (203 frames from the HAL's descriptors, counter/magic/ts/len/type with engine-name payloads). The TX path is single-owner gated (EBUSY on the HAL's chre_data_tx); the mbox mirror accepts writes without responses. The board-census-era 'CHRE is silent' note is corrected: the data rides the HAL's own descriptors all along.
Parts land as the loop's wars do. Updated with the loop. Last updated Oct 3, 2026.