I run a mesh of agents on old phones. They check invariants — is a watchdog's
lease longer than twice its producer's cadence? Is the battery in its longevity
band? Is the board log monotonic?
I was checking these with grep. Then I audited: 33 of 52 liveness gates
could never fail — each one grep'd its own source for a string, so it always
found itself. A gate you haven't seen fail is not a gate.
So I started moving them to Uxn — the
tiny virtual machine from Hundred Rabbits. Stack-based, 64 KB
address space, the emulator is ~42 KB of C89 with no deps beyond libc. A ROM you
assemble today runs unchanged on any architecture, forever.
The first gate
A lease-vs-cadence check, hand-written in Uxntal (the assembly): 44 lines,
134 bytes. Same bytes pushed to an old Android phone — a 32-bit ARM core
running the identical ROM.
Then the obvious question: can I stop writing assembly?
chibicc is Rui Ueyama's small C compiler;
someone retargeted it to emit Uxntal. The
same gate in 29 lines of plain C compiles to a 468-byte ROM — truth table
exact match, cross-arch verified:
void main(int argc, char *argv[]) {
unsigned int cad = parse_int(argv[1]);
unsigned int lease = parse_int(argv[2]);
if (lease >= 2 * cad) print_string("OK\n");
else print_string("RED\n");
}
cad=900 lease=1800 -> OK cad=900 lease=1799 -> RED
cad=900 lease=900 -> RED cad=60 lease=3600 -> OK
chibicc is now vendored — one cc-rom.sh goes from .c to .rom. Every gate
has a truth-table test that corrupts the arithmetic and watches it break. The
toolchain swap surfaced the best bug of the whole effort: old ROMs halted #01,
which maps to exit 1 under the modern emulator — and under set -o pipefail
(leaked from a sourced library), the entire audit died silently. No error, no
verdict, just gone. The fix was one byte.
A ROM is a fixed point
Then it got interesting. A ROM is behavior decided once at commit time, in a
system where everything else re-infers per tick. That makes it a fixed point —
and a fixed point is three things:
The thing you calibrate against. I run the ROM and a different
implementation (native 64-bit shell arithmetic) on the same inputs and log both.
Agreement is weak evidence; disagreement isolates cleanly to the
implementations and posts loudly. The ROM's 16-bit int wraps at 65536 — a
--pair 900 67335 input splits the two (ROM reads 1799, shell reads 67335) and
the calibrator catches it live. First fleet run: 208 pairs, 0 diverged.
The thing you watch with. The first fixed-point watcher is a board
invariant checker written in C, compiled to a ROM: it judges the last N board
lines for structure, monotonic timestamps, unknown nodes, and duplicate claims.
Text you control goes in; the ROM is the whole trust boundary.
The thing that travels. If a ROM is a fixed point, it can move. A ROM-as-
packet over SSH: the program ships in-band with the data
(uxp1 <rom_bytes> <sha1>\n + <rom raw> + <payload>). The receiving node — which
holds zero ROMs, only the 43 KB emulator — hashes what it actually got before
executing a byte. Declared hash ≠ actual is a loud refusal. This matters because
a tampered ROM that ran silently with rc=0 and empty output is indistinguishable
from consensus — so you verify, then execute.
Gates as data
The next step got me to the actual payoff. I wrote a micro Lisp evaluator ROM
(3.9 KB) where the expression is data:
(if (>= lease (* 2 cad)) 1 0)
That predicate ships as text and the fixed point runs it — homoiconicity on the
ROM, no recompile to change a threshold. Once the evaluator existed, the gates
collapsed into rows of a ledger:
- stage 1 — the lease and band gates expressed as s-expression data lines the evaluator runs.
-
stage 2 — scattered inline magic numbers (battery bands, thermal windows,
PSI ranges) became calibrated
threshold-ledgerrows, s-expr DATA, edited in one place. -
stage 3 — an admission harness: a generated candidate gate (proposed by
the cheapest available model through
mesh-relay) has to pass the same RED-first truth table a hand-written one does before it's adopted into the ledger. - stage 4 — the walker that drives generated candidates through that door unattended, one per run, drain-first. Nothing walked through stage 3's door on its own — the never-wired-reflex hole again — so the walker is the reflex.
NA-honesty carries through all of it: overflow, /0, unknown op, bad parens all
answer NA (rc 2), never a wrapped value. A check that can't reach its input
says n/a; it does not fake all-clear.
The body gates itself
This is where it stops being a thought experiment.
The first consumer of the mobile-code layer outside the uxn/ directory is a
body node — the Note3 phone — gating its own battery and thermal:
- Each run reads the phone's own sysfs (capacity, temperature in deci-°C).
- It substitutes those numbers into
threshold-ledgerrows as s-expressions — the thresholds live in the ledger, not on the phone. - It packs the pinned evaluator ROM + expression as one argv packet and runs
it on the phone, under busybox
sh+ the on-device ARMuxncli. - The receiver is the byte-identical
mesh-uxn-hopscript — it hashes the ROM it actually received against the declared sha1 before executing, and stamps the verdict with that hash.
The pin chain runs end to end: the ledger's # evaluator-sha1: == the ROM
packed at the workstation == the sha1 the phone verifies before executing == the
stamp that comes back. Any link broken is a loud refusal, never a wrapped
verdict.
The thing I want to underline: the program that gates the phone does not live
on the phone. Recalibration is a constants diff in the ledger, travelling
in-band on the next run. The phone is never edited. A 32-bit ARM core runs the
same bytes an x86 workstation assembles, judges its own battery against a
threshold it can't unilaterally change, and reports back. That's what "a ROM is
a fixed point" buys you — behavior that travels to the body and gates the body,
while staying fixed.
The RED-first proof pattern
Every gate corrupts its own arithmetic and watches the test break:
- lease:
#0002 MUL2→#0001shifts the boundary. - band: each
GTH2no-op'd in turn. - calibrate: verdict logic mutated to always-AGREE, suite seen RED.
- hop: a tampered ROM is refused (declared sha1 ≠ actual).
- body-gate: the pack-site hash comparison is neutered pre-dial; a second pin gate at the stamp catches it — sharpening the order of refusal.
A gate you haven't seen fail is not a gate.
Repro
cd scripts/uxn && ./build.sh --chibicc # build vendored chibicc
./cc-rom.sh chibicc-eval/lease-gate.c lease-gate-c.rom # .c → .rom
./mesh-rom-gate lease-gate-c.rom 900 1800 # → OK
./mesh-lease-audit # gate the live reflex set through the ROM
The whole lane — hand-written gates, the C compiler, the unified runner, the
cron-wired audit, the mobile-code layer, the watcher, the calibrator, the
self-gating body — is in the repo under scripts/uxn/. Every piece has a
red-first test you can break.
I'd like feedback on three things. Is the fixed-point framing real, or am I
overloading a cute word? Is shipping executable code over SSH as a hash-verified
packet madness, or obvious once you say it? And for anyone who's targeted Uxn
from a real compiler — how far do you take it before hand assembly wins again?
Top comments (5)
"A gate you haven't seen fail is not a gate" — I'm going to be quoting that for a long time.
I'm not a systems person; I build internal tools as a non-developer, and I have a static security scanner watching my own code. For months I'd only ever asked it "did you catch the bad thing?" — never "can you actually fail?" So I did your RED-first move: planted ten known-bad patterns on purpose. It caught seven. I'd been trusting a gate I had never once watched fail.
Your "33 of 52 gates grepped their own source and therefore couldn't fail" is the exact trap one level up — a check that includes itself in what it's checking will always pass, and that green is worse than no check at all. The tell was the same for me: the moment I added a "hardcoded secret" rule and finally pointed the thing at real code, it found three live API keys sitting in programs I'd already "reviewed." Seeding the detector was quietly an audit of everything it had been failing to see.
RED-first isn't paranoia. It's the only version of "it works" that isn't just the gate's opinion of itself.
Ten planted, seven caught — the three it missed are the more valuable artifact, and I'd keep them forever. That list is your scanner's blind-spot inventory, and it turns the seeding into a permanent regression suite: every rule change re-runs the ten, and a pattern that quietly goes from caught back to missed becomes visible instead of silent. Right now you know your coverage is 7/10; without the fixtures you'd only know it was "green".
One trap on the way there, since you already hit the self-inclusion version: where do the ten bad patterns LIVE? If they sit in the tree the scanner walks, you either eat ten permanent findings or you add an exclude path — and that exclude is the new thing nothing checks. Ours belong to the test, never the repo.
The other half of RED-first that took me longest to learn: watch it fail for the RIGHT reason. A gate that goes red because the fixture path was wrong, then green after you "fix" the code, was red both times for unrelated causes and never tested anything. Break exactly one thing, and confirm the failure message names that thing. Otherwise you've just watched a different gate fail.
All three of these are getting stolen, and the third one I'm a little embarrassed I didn't already have.
On the blind-spot inventory: yes. My ten live in a seed folder the scanner is told to skip — they belong to the test, not the repo, exactly as you framed it — so I don't eat permanent findings. But I'd been treating the three misses as "fixed and forgotten" the moment I wrote rules for them. Keeping them as fixtures that re-run on every rule change is the part I skipped. 7/10 that I can watch is a coverage number; "green" is a mood. A pattern silently regressing from caught back to missed is the exact failure I built the thing to prevent, and I had left myself no way to see it.
"The exclude is the new thing nothing checks" is going straight on the list, because that skip rule is load-bearing and unwatched. I think the cheap guard is to assert the seed count itself: point the scanner at that folder deliberately and it should report ten; if it reports zero the exclude is still holding, if it reports something else the exclude broke silently. Either number tells me what the green light won't.
But the third point is the one that actually changes code tonight. My seed test asserts "a finding fired," not "a finding fired for THIS pattern." So a fixture that goes red because I fat-fingered a path, then green after I "fix" some unrelated rule, sails through looking like a passing test — red and green both for reasons that had nothing to do with what I meant to check. Break one thing; make the failure name that thing. I've been watching a different gate fail and calling it proof.
Your seed-count guard is right in instinct and slightly off in aim — and the miss is the self-inclusion thing one more time. Pointing the scanner at the seed folder deliberately proves the scanner can see ten files when told to. The exclude that can break silently governs a different invocation: the production run over the whole tree. Two claims, and only one of them ships. So write the predicate on the run that ships — scan the tree exactly as CI does, and assert zero findings with paths under the seed dir. Exclude holds, zero. Exclude breaks, ten findings in the run that actually matters.
Keep the count assertion though, just move it into the harness instead of the scanner: "I loaded exactly ten fixtures" protects you from the nastiest shape here. Rename the seed folder and your ten-planted test scans nothing, finds nothing, and "no unexpected findings" is trivially true. Zero fixtures is the greenest possible run.
On per-pattern: pair each seed with the rule id it's supposed to trip, and assert the finding set for that file EQUALS that id. Set equality, not non-empty. It buys you the inverse failure too — a fixture tripping the wrong rule sails through "a finding fired", and an over-broad rule is exactly the kind that makes a scanner noisy enough that you start ignoring it.
Point 1 didn't just tighten the test — it caught a live leak. My exclude was
regex-testing itself (your self-inclusion trap, exactly), and meanwhile six
clean fixtures were being scored by the real production run because the skip
pattern silently failed on "seed-clean". Moved the predicate onto the shipping
invocation like you said, and the leak showed up immediately. Fixed. Set-equality
on per-file rule ids is in too — caught that my rules fire in related clusters,
so I pinned the measured set instead of asserting exactly-one.