the other half
Things I built because I wanted them to exist.
PANTHEON is the work. This is what the same instincts produce when nobody is paying for the outcome: a real sky on a desktop, an ephemeris with no dependencies, and a terminal that can draw. The discipline is identical; only the stakes change.
GNOME Shell · GPL-2.0 · ★3
TerraFirma: a real sky over your wallpaper.
Not an animation of a sky. The actual one, computed from an ephemeris for your latitude, longitude and clock.
The hot loop is measurable on purpose
The projection and atmosphere live in skymath.js,
which imports nothing from GNOME Shell. A file that can only run inside a session
can only be guessed at; one that runs standalone can be timed. That boundary is the
same reasoning as the purity lint that keeps PANTHEON's core out of its app layer.
Star catalogue and ephemeris are third-party and credited in NOTICE.md. Point it at a date, then look up, for this kind of software that is the only test that counts.
extracted, and published
Three packages that came out of it.
The same pattern as PANTHEON's six: build the thing, find the piece that is genuinely reusable, take it out, and publish it on its own terms. All three are MIT and installable right now.
braillecanvas v0.4.0 · MIT
Braille addresses 2×4 dots per character cell, so an 80×24 terminal is really a 160×96 canvas. That is the difference between a rendering and ASCII art.
The interesting part is colour. Each cell carries two colours, one for the lit dots, one for the gaps, so a cell straddling an edge, skin against sky, keeps the edge instead of averaging it into mud. Mean per-dot colour error on this image falls from 45.7 to 21.3.
Choosing which dots light up per cell, rather than taking the pattern from a fixed dither grid, cuts textureless cells from 35.1% to 1.6%.
Two limits stated in its own README: below about 90 columns photographs stop working, and monochrome cannot render this picture at all, her face measures 159 luminance against 192 for the sky behind her, so a 1-bit render is a silhouette by construction.
skymaths v0.1.2 · MIT
Positional astronomy with no dependencies, Sun, Moon, planets, twilight, rise and set. Extracted from TerraFirma's ephemeris, then corrected: the Espenak–Meeus polynomial it used is published for 1900–1920 only and was being applied to 1986, giving −534 seconds for 1950 against a measured +29. Found by checking it against a published almanac rather than against itself.
It is running on this site: in dark mode the background is the sky over Cardiff on Perseids night, 13 August 2026, computed in your browser: 5,044 stars in their real colours, the Moon's phase and the planets where they were. Scroll to turn round.
starwheel v0.2.3 · MIT · ★4
A live planisphere in your terminal, real star positions for your location and minute, drawn on braillecanvas. My most-starred repository, and the one that taught me a fix which moves published pixels is not the same fix twice: it bounds iterations where its sibling clips, deliberately.
/proc data, CPU, memory and
load average over a rolling window. Charts work far below the 90-column floor that
photographs need, because they are already high-contrast.on this page, right now
The art is generated, and the seed cannot escape.
Every piece of art on this site: the hero above, the favicon, the social cards, the avatars, comes out of PANTHEON's own generator. Deterministic and seeded: the same seed gives the same image, every time.
Same contract on both sides
A server-side engine renders the social cards and tenant sites; a browser engine draws the animated version you are looking at. Same rule: seed in, reproducible art out.
Injection-safe by construction
The seed feeds the PRNG and nothing else. it is never interpolated into markup or a style. A tenant name that is really a payload draws a different picture; it does not draw a script tag. The same instinct as the rest of the system, applied to decoration.
installable, not just readable
Six guarantees, extracted and published.
Each one came out of PANTHEON as a single verifiable promise, then went to PyPI so it stands on its own. Adversarial AI agents, self-run, not independent, went through them line by line and found real defects; the versions below are the ones after those fixes, each verified by installing from the registry into a clean environment and re-running the reviewer's own reproduction, not by trusting the build log.
pantheon-guardrails v0.3.2 · Apache-2.0
A constitution scorer that spends an LLM judge only on high-stakes
replies, and requires that judge to be a different model from the generator so
the two don't share blind spots. It used to clamp an out-of-range score upward:
a judge returning {"clarity": 99} scored a perfect 1.0 and passed every
threshold: a guardrail failing open. Now a judge that raises, hangs or returns nonsense
is an explicit failed evaluation that blocks by default.
pantheon-ssrf-guard v0.2.1 · Apache-2.0
A two-layer SSRF egress guard that survives DNS rebinding: it re-checks
the IP the socket actually reached at connect time, so a host that resolves
public then rebinds to loopback or cloud metadata is refused. The review found the opener
would still read file:// URLs, because Python installs handlers for other
schemes by default; there is now an explicit http/https allowlist at the boundary,
redirects included, and environment proxies are off unless you ask for them.
pantheon-tool-sanitizer v0.3.0 · Apache-2.0
Strips tool-protocol markup and Unicode smuggling from untrusted MCP
tool text before it reaches an agent's prompt. Honestly scoped: it does not claim to stop
plain-prose injection, which is an architecture problem rather than a string one. Two
fixes from the review: a 256 kB adversarial input took 26 seconds of CPU and is now
refused outright, and "Read\nthe\tfile" no longer becomes
"Readthefile", because a line break is a word boundary.
credit-ledger v0.3.0 · Apache-2.0
Overdraft-proof metering over Postgres in about 150 lines: 100 parallel charges against a balance of 50 and exactly 50 succeed, exactly-once billing under webhook replays, isolation enforced by row-level security. The published build used to accept a negative charge, which paid the customer, and amounts below the column's precision, which billed 0.0000 while reporting success. One amount rule now covers every path, and there is a tested migration for databases that already exist.
pantheon-ical v0.3.1 · Apache-2.0
Round-trip iCal (RFC 5545) for booking calendars in under 200 lines. A dropped busy period reads as free, so silence is the dangerous failure: an oversized calendar is refused rather than truncated, and a recurring rule that overflows says so instead of returning a short list. The last fix bounded the work as well as the result: a one-line hostile rule used to build 34 million occurrences over 76 seconds before being rejected; it now stops at 401.
pantheon-rls v0.1.1 · Apache-2.0
Tenant isolation as a Postgres guarantee rather than an application habit: force-RLS plus least-privilege grants, so the database itself refuses cross-tenant reads and writes. Fail-closed by construction, no tenant context returns zero rows, never all rows, which is the failure mode that matters when a bug reaches production at 3am.
Every fix above arrived the same way: reproduce the defect first, fix the boundary rather than the symptom, then prove the new test fails when the bug is put back. A test that cannot fail is not evidence.
what it runs on
The stack is boring. The boundary is not.
Python 3.12, FastAPI, Postgres, Redis, React with Vite, one Dockerfile behind nginx. None of that is a decision worth admiring, those are the parts you pick so your attention can go somewhere that matters.
146 files · 14,002 lines
pantheon-core: the substrate. Domain-agnostic by
contract: it may not import from residents or bundles,
and two independent checks fail CI when it does.
210 files · 24,216 lines
Tests, against 38,816 lines of Python. I don't hand-write the implementation, so the suite is not a safety net. It is the specification, and it is the artefact I actually author.
Five layers, one way
architect · runtime · guardrails · substrate · primitives, with
providers deliberately outside the stack, because it sits below two
layers at once and the contract says so out loud rather than bending.
That boundary is why six components could be lifted out and published as standalone libraries, extraction was mostly moving files that had never been allowed to reach downward. The long version, including the part I got wrong →
Same person, same rules, lower stakes.
Nobody audits a wallpaper. The reason these are built the way they are is that it is the only way I know how to build, measure it, extract the reusable part, publish the limits with it, and check the numbers against something outside the program.
Ask about any of this work.
- What role is Isaac looking for?
- Which of these is running in production?
- Show me a real bug he found and fixed