Tillandsias

Tillandsia is the largest genus in the bromeliad family, with well over six hundred species.

A small cloud region on your own computer. Disposable workspaces, with work preserved through your git remote.

tillandsias.org · v56.9.12.2 · website last updated 56.9.12

An idempotent, ephemeral cloud region,
folded through your hypervisor.

Local hardware. Free software. Nothing rented, nothing metered, nothing left behind. Below is what it is and how it works, told five times over — pick the version that fits the person reading.

Verified — checked against the source, and working. Shortcoming — incomplete, pending, or overclaimed. Path — what the plan records as the fix, or that it records none. Shown — an argument we can point at the code or a test for. Plausible — sounds right; not yet demonstrated. Does not hold — an argument we tried, and it failed.
Linux
macOS
Windows

Each line fetches a short script from this site, which resolves the latest stable release on GitHub and runs that release’s own installer. Stable moves only when a daily build is promoted, so it normally trails the newest code. The scripts here are not rebuilt when the app releases; they look the release up every time they run.

The simplest way of putting it that is still true.

Tillandsias, explained like you are five

A real tillandsia needs no soil and no pot — it drinks from the air, and borrows nothing.

A tiny cloud on your own table

When you open a website, you borrow a computer in a huge building far away — the cloud. Tillandsias builds a tiny cloud inside your own computer, so you can work without sending your things to its makers. A service you choose to use can still receive what you send it.24

It is a doll house. Your computer is the table. Tillandsias puts a clean pretend-computer on it, and inside that, little rooms, one program each.

Everything you run there has one plain word: app — chosen on purpose and written down as a promise.1 A machine reads every line of the waking-up message and fails if a wrong word sneaks in.6

verified Every line of the waking-up message is machine-checked against a list of forbidden machine words, not just hoped for.

Nobody goes outside except the doorman

The rooms have no doors outside. They share one private hallway, and exactly one room — the doorman — may step out to the internet. Everyone else asks it.2

The honest bit: they once kept a list of who lived there, and six residents moved in unnoticed. So they binned it for a machine that reads the real building.10

Robot helpers may only play inside the doll house.11 If one breaks something, it was pretend.

verified One way out by design, and the list of who lives inside is kept honest by a machine.

Broken things get thrown away, not glued

the usual way this way it breakspoke at it still oddpoke again drifts somewhere nobody can reproduce it breaksthrow it away build from the recipe identical every time, on every machine
Nothing at runtime is repaired. It is thrown away and built again from the recipe — which is why it comes back identical.

If a room breaks, nobody glues it. It goes in the bin and Tillandsias builds a fresh one from the same recipe. Gluing is banned: gluing one layer tends to break the layer underneath next.3

Your real work lives outside the doll house and never gets swept.12

Getting it outside has its own rule. The doll house has a post box, and it does not say done until the shelf far away really has your parcel. If it cannot reach the shelf, it says so, instead of saying done and quietly keeping the parcel in a drawer.26

How the grown-ups keep getting better at it

First they write down what a thing should do, so clearly nobody could argue whether it came true. Then a machine presses the promise: green light or red.

Every time they fix something, they check that nothing else got worse23 — and they write down what they checked.

They are honest: less broken every time does not mean ending up perfect — only creeping toward some resting place, maybe not zero. They wrote that down.4

They work in many tiny fast tries, not one long careful think — small tries find the answer sooner, so long as each is only a little wrong.5

Imagine every saved try as a big scrapbook: bits of the recipe, the rules, what was found before, and the new note someone just made. A helper reads the useful pages, tries one small improvement, and the grown-ups keep it only after checking it.23 The scrapbook remembers old saved pages, but the newest page can still replace a bad drawing. So "more" means more checked history and evidence, not that every mark is automatically better.

Anyone may have it, read it and change it, free.14

plausible It sounds right and works for them. But the sum first written to prove it5 needs every try to stand alone, and theirs do not — each reads what the last one learned. They have taken that proof back:13 the idea stays, the proof is owed.

Things that are still broken, and one that got fixed

note This one got fixed. The doorman's key for sealed letters once sat unlocked in a shared scratch drawer, for anyone to read.7 Since August16 it is locked so only you can read it, and the doorman is handed his own sealed copy instead of reading it out of the drawer.15

shortcoming The key now lives in a folder under your own home,1720 but the program still creates the folder without first making it owner-only.25 The key itself is locked after it is made,15 and a "check the lock" chore runs on every grown-up's computer.19

path to green The move out of shared scratch space landed on 4 September 2026 and is now in the stable release.22 Locking it first, then retiring the chore, is the written next job.18

shortcoming The little status badge in the corner still says "Ready (podman starting…)", "Provisioning…" and "VM failed":9 machine words a second written promise forbids.21

path to green A written plan gives the badges one shared word list. It waits on a person, not a robot, to pick the wording: changing what you see needs a human's yes.8

Footnotes

Every link points at release v56.9.12.2 of the source repository, so line numbers match the text above; a link marked with its own release tag points at that newer release instead. A footnote number in the text opens its source in a new tab; hover it for the quoted lines.

  1. 1The rule that the user always hears "app", never the machine words openspec/specs/app-lifecycle/spec.md#L13-L23All container operations SHALL be presented to the user as "app" actions. The words "container", "pod", "image", or "runtime" MUST NOT appear in any user-facing text.
  2. 2One way out: only the doorman may step outside openspec/specs/enclave-network/spec.md#L10-L12Only the proxy is dual-homed for external access; all other members communicate exclusively through the enclave.
  3. 3Throw away and rebuild, never hand-patch — with the real story of gluing making it worse methodology/philosophy.yaml#L84-L99Therefore the DEFAULT response to a borked host/VM/guest/podman/stack layer is to DESTROY and RECREATE it, never to hand-patch it forward.
  4. 4The grown-ups writing down what they have *not* proven: "getting better every time" means nothing gets worse and the leftovers shrink, not "ends up perfect" methodology/math-foundations.yaml#L108-L120Without that metric proof, "monotonic convergence" means ordered non-regression plus finite residual descent, not metric contraction.
  5. 5Many small fast tries are the design guidance; the strong-law citation has been withdrawn methodology/philosophy.yaml#L18-L38Instead make each prompt SMALL and FAST with a CONTROLLED, BOUNDED skew, then iterate.
  6. 6The word list for the waking-up message, and the test that checks every line it can emit crates/tillandsias-headless/src/bringup_progress.rs#L119-L175fn no_internals_vocabulary_in_any_emitted_line() { for total in 1..=12 { let mut p = BringUpProgress::with_enabled(total, true); for _ in 0..total { let line = p.next_line().to_ascii_lowercase(); for banned in BANNED { assert!( !line.contains(banned),
  7. 7The original write-up of the doorman's key problem (15 August 2026): anyone on the computer could read the key, and the three fixes it proposed plan/issues/proxy-ca-private-key-world-readable-2026-08-15.md#L31-L461. Preferred: deliver the key as a podman secret (the entrypoint's secret branch already handles ownership correctly and becomes REACHABLE), or
  8. 8The open plan to give both badges one shared word list, and why a person must sign it off plan/issues/tray-string-parity-and-i18n-layer-2026-08-09.md#L104-L130Reconciling existing drift **changes a user-visible string on whichever platform loses**. `spec:tray-ux` "UX curation governance" forbids that without recorded operator approval for the exact surface change.
  9. 9The badge text that breaks the rule crates/tillandsias-macos-tray/src/action_host.rs#L257-L268VmPhase::Ready => "\u{1F7E1} Ready (podman starting\u{2026})".to_string(),
  10. 10Why the hand-written resident list was replaced by a checking machine openspec/specs/enclave-network/spec.md#L12-L12A hand-maintained prose list went stale by SIX members between 2026-07 and 2026-08-30 (order 245 P8) — it still said "forge, git, inference, and proxy" after vault, the router, the nix cache, the catalog service, the observatorium web and the ssh-lane sidecar had all joined.
  11. 11The rule that robot helpers run only inside the doll house openspec/specs/forge-as-only-runtime/spec.md#L12-L13Every coding agent, maintenance shell, and runtime utility executes inside the project's forge container; there is no host-side execution surface.
  12. 12Sweeping a room never touches your real work openspec/specs/app-lifecycle/spec.md#L79-L81the container is removed, project-specific cache data is deleted, but the project source directory in `~/src` is never touched
  13. 13The grown-ups taking the proof back, recorded 2026-09-03 in the daily channel: the rule they used needs each try to be independent, and theirs are not methodology/philosophy.yaml#L29-L37CITATION WITHDRAWN (order 976-bd3n, external review 2026-09-03). This block previously claimed the STRONG LLN — almost-sure convergence — for the stream of iterations. INDEPENDENCE IS A HYPOTHESIS OF THAT THEOREM, NOT A STYLISTIC ASSUMPTION, and this system deliberately violates it
  14. 14The licence: anyone may have it, read it and change it LICENSE#L1-L16the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program
  15. 15The fix: the key is locked to its owner, and reaches the doorman as a secret rather than through the file crates/tillandsias-headless/src/main.rs#L3069-L3080Clamp the CA private key to owner-only access (0600) — 755-qcxh.
  16. 16The ledger closing the key fix on 16 August 2026 plan/archive/packets-2026-08.yaml#L15094-L15133order: 755-qcxh status: completed
  17. 17The CA folder is the declared home-relative state root with its own ca leaf crates/tillandsias-core/src/ca_path.rs#L36-L50format!("{}/ca", ca_template())
  18. 18The written next job: make the folder private before any key is written, and retire the chore plan/index.yaml#L28424-L28430deliverable: CA material is created in a directory that is private by construction (XDG_RUNTIME_DIR, or DirBuilder::mode(0o700) before any key is written), and scripts/clamp-ca-material.sh gains a retirement condition or is deleted
  19. 19The "check the lock" chore that runs on every grown-up's computer at the start of each work cycle scripts/cycle-preflight.sh#L385-L395A checkout-side step reaches every host on its next cycle without waiting for a release
  20. 20The declared state root is under the user home images/default/ca-path.txt#L83-L83${HOME}/.local/state/tillandsias
  21. 21The second written promise: the badges' machine words are on the list of words the screen must never show openspec/specs/tray-ux/spec.md#L32-L33Internals vocabulary (VM, WSL, enclave, mirror, vault, container, podman, provisioning) MUST NOT appear in end-user-facing UX text.
  22. 22The ledger closing the move on 4 September 2026 plan/index.yaml#L59319-L59320- type: completed ts: "2026-09-04T09:17:37Z"
  23. 23The written-down check after every fix: the list of already-broken tests may not grow, and anything on it that starts passing must be struck off build.sh#L1521-L1526a failure not named in scripts/test-known-red.txt is a new regression, and a listed test that PASSED is a stale entry that must be deleted
  24. 24A chosen remote AI provider receives the content sent to it PRIVACY.md#L60-L63If you instead configure a remote provider, the content you send is transmitted to that provider under their terms.
  25. 25The CA folder is created before key generation without an owner-only directory mode crates/tillandsias-headless/src/main.rs#L3132-L3140std::fs::create_dir_all(&certs_dir)
  26. 26The post box only reports success once the faraway copy really has the work images/git/pre-receive-hook.sh#L6-L8Validates ledger YAML, then synchronously relays the proposed ref transaction upstream before accepting it locally. A client success therefore means the configured upstream has durably accepted the same atomic ref set.

Straight answers to what you are actually wondering: privacy, cost, and what breaks.Picks up where “like I’m 5” left off.

Tillandsias, explained simply

A tillandsia is an epiphyte, not a parasite: it rests on its tree and takes nothing from it.

What it is for, in one paragraph

The reason a grown-up wants one is narrower than "the cloud, but at home". Software you did not write — increasingly, AI assistants that write and run code for you — has to work somewhere. The choice is between your actual machine and a sealed room containing only the project folder you deliberately opened.1 Tillandsias is that room, plus the plumbing to prepare it and start your tools.

the boundary the specification draws must hold must NOT hold positive cases negative controls
A test that only ever passes proves nothing. Each boundary is pinned from both sides.

Is my stuff private, and who could see it?

The people who make Tillandsias operate no servers your copy talks to: no account, no sign-up, no usage tracking, no crash reporting, no identifier attached to you or your machine.2 Your logs stay on your own disk where you can read them, and leave only if you send them somewhere yourself. Any login you hand over goes into a secret store on your own machine — the project's own vault, whose key sits in your operating system's keychain, or on some hosts in a local file — never on anyone else's server.316

verified The privacy claim does not rest on anyone's intentions. There is no server of theirs to send anything to, so there is no decision to trust and none to reverse.2 It covers their side only — a service you sign into yourself still sees what you tell it.

Does it cost money? Does it need the internet?

It costs nothing: free software under a licence that lets anyone read, run, modify and pass it on.4 You have already paid for the only hardware involved.

The internet is needed only for ordinary, visible reasons: fetching the program, fetching updates, reaching a service you chose.17 Local work can continue without sending it to an AI provider: language models can run on your own machine.18 That does not make every action offline-ready. Fetching a missing tool or model needs a connection, and a push configured for GitHub fails when its upstream cannot be reached.31

What happens when I turn it off? Can it break my computer?

Turning it off is the expected motion, not an interruption. Anything you saved is a real file on your real disk, untouched by the rebuild. Even the internal secrets the pieces use to talk to each other are re-registered at every start, so yesterday's messy shutdown cannot jam today's start.5

Publishing your work has a rule that fits the same idea. Inside the sealed room you push to a mirror that lives on your own machine, not to the internet. The mirror passes the push onward and waits: it reports success only once the copy outside has durably accepted the same set of changes, so a success you can see is one that survives the room being thrown away.32 The mirror is also the only part that holds the credential for the outside service — it reads it when it pushes, and it never hands it to the room where your tools run.33

As for damage: a misbehaving tool inside the sealed room sees only the folder you gave it.1 Wiping the installation and rebuilding is documented and supported, not a last resort.6 The cost you do pay is ordinary and reversible — it is a real virtual machine, so it holds real memory while running and gives it back when it stops. The system image it downloaded stays cached on your disk so the next start is quick; uninstalling clears it, though on a Mac you have to ask for that explicitly.2030

verified That is written down as a required behaviour with named cases — the unclean shutdown among them — not left to the habits of whoever wrote the code.5

Sharpening one thing you were told

far close successive releases — each one re-raises the bar the floor — possibly not zero zero would mean “finished”. No proof it is reachable.
Schematic: if comparable residuals never rise, they approach a floor, and nobody has proven that floor is zero.

You were told they check that nothing got worse. Precisely: the project's stated objective is that the measured gap between what it specifies and what it builds must not grow,721 and its build gate refuses a change that points at a specification that does not exist, or that adds a new failing test — a machine's verdict, not a person's.2223

Two honest limits follow, in the project's own words. Passing tests are evidence, not proof — they show no contradiction was found, not that none exists.8 And "never worse" means settling toward some floor, not that the floor is zero; the stronger mathematical result that would guarantee zero is explicitly not claimed.9

Lots of small maps, one destination

An AI does not swallow a repository as one magic answer. It is given selected source, rules and a request; it suggests a small change; then people and tests decide whether to keep that change. Think of the selected material as a map, not as every file turned literally into a vector. The next job starts from the newly saved repository, so it can use what the earlier job learned.

Different jobs have different nearby destinations: a security repair, a clearer screen, or a working installer. They all share the larger destination of matching what the project promised. We can measure particular gaps and refuse a known regression,721 but a row of successful changes is not proof that the whole journey reaches perfection.89

Where it falls short today

shortcoming On Macs, the application has not yet passed Apple's inspection service, so a copy downloaded with a web browser is blocked on first launch; the recommended one-line install sidesteps that only by not going through a browser.10

path to green The fix — an Apple Developer identity and notarization — is written up and costed.11 The build script is already wired: hand it the signing identity and notary credentials and it notarizes.24 The release workflow has not yet been given them, and enrolment is still pending.2510

shortcoming One Windows package in a past release went out without its signature, which makes it not merely warned-about but impossible to install, with no user-side workaround.12

path to green That one package is now withheld from a release whenever it is unsigned;26 the other Windows downloads still publish unsigned, with the only warning in the build log,27 and the signing route has been chosen but not yet put in place.13

note A built-in "expert" feature once gave confident answers with no sources at all: it described a careful research procedure, did not run it, returned an empty citation list, and stamped its own output verified anyway.14

verified It was switched off and replaced: the shipped configuration now points the assistant at a grounded service that either cites the sources it actually used or plainly refuses.28 The replacement was checked live on 2026-09-02 and is now in the stable release.29 One limit remains below.

shortcoming After installing, you cannot tell from the program what was installed. It prints a version number that was baked in when it was compiled, and nothing on it records which snapshot of the code it was built from.34 So "this is the stable release, which trails the newest code on purpose" and "this download was stale" look exactly the same from the outside, and neither the person who installed it nor the people who built it can tell which happened.35

path to green The project has written the shortcoming down and is tracking it, including what a fix would have to record; it has not yet reached a released build, so the tracking entry is linked here on the development branch rather than in the release this page describes.35

shortcoming Portability evidence for the replacement’s small scripting layer is still missing from the Mac and Windows build lanes.15

Two install problems and one feature that once promised more than it delivered and has since been replaced. None touch the privacy story, and all three are here because the project's own checking found them.

path to green Run that layer on those hosts and record the evidence; the task explicitly remains open until then.15

Footnotes

Every link points at release v56.9.12.2 of the source repository, so line numbers match the text above; a link marked with its own release tag points at that newer release instead. A footnote number in the text opens its source in a new tab; hover it for the quoted lines.

  1. 1What a workspace can reach, and why that isolation exists to protect you from the tools PRIVACY.md#L32-L40Each workspace sees the project you opened, not your whole filesystem. That isolation exists to protect you from the tools, not to hide anything from you.
  2. 2"Tillandsias collects nothing" — no account, no telemetry, no analytics, no server PRIVACY.md#L5-L17**Tillandsias collects nothing.** There is no account to create, no telemetry, no analytics, and no server operated by us that your installation talks to.
  3. 3Credentials in a local secret store on your machine; logs kept on your own disk PRIVACY.md#L41-L44**Credentials you provide** (for example, a GitHub login you initiate) are stored in a local secret store on your machine.
  4. 4GNU General Public License, version 3 LICENSE#L1-L2GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007
  5. 5Internal secrets are removed and reissued at every start, so an unclean shutdown leaves nothing stale openspec/specs/ephemeral-secret-refresh/spec.md#L10-L25The system SHALL check for existing podman secrets before creation. If a secret exists from a prior unclean shutdown, it SHALL be removed and recreated with fresh content.
  6. 6"Designed to be wiped and rebuilt freely" PRIVACY.md#L46-L47You can remove all of it at any time by resetting or uninstalling; the application is designed to be wiped and rebuilt freely.
  7. 7The convergence objective: minimise the measured distance between specification, code and reality methodology/convergence.yaml#L2-L8objective: | Minimize: Δ(spec ↔ code) + Δ(code ↔ cheatsheet) + Δ(cheatsheet ↔ reality) + Δ(litmus_binding ↔ spec)
  8. 8Evidence boundaries — traceability and passing tests are evidence, not proof of correctness methodology/philosophy.yaml#L249-L252Traceability, version matching, litmus success, and CRDT metadata convergence are evidence. They are not proof of semantic correctness by themselves.
  9. 9The stronger contraction result is explicitly not claimed methodology/math-foundations.yaml#L107-L120The methodology does not currently claim Banach-style contraction. It can report decreasing residuals, but it has not proven a contraction constant over a complete metric space of project states.
  10. 10Install instructions warning that a browser download hits a Gatekeeper block on first launch README.md#L27-L45Tillandsias is signed but not yet notarized (Apple Developer enrollment is pending), and macOS tags anything a *browser* downloads with `com.apple.quarantine` — so the .dmg route hits a Gatekeeper block on first launch, while `curl` does not tag at all and the app opens normally.
  11. 11Recorded options and costs for Apple signing and notarization: which steps need paid membership plan/issues/macos-gatekeeper-signing-options-2026-08-29.md#L39-L48| Developer ID Application certificate | **Yes** — no free-tier equivalent.
  12. 12The unsigned Windows package is uninstallable, with no user-side workaround plan/issues/store-msix-submission-blockers-2026-08-31.md#L26-L40**Consequence for the pending signing decision.** The unsigned MSIX (`0x800B0100`, packet 722-w7a2) blocks the **GitHub-release** channel only. It does not block the Store channel at all.
  13. 13The chosen signing route for the Windows release channel plan/issues/windows-signing-research-2026-08-16.md#L1-L25**SignPath Foundation is the signing path for the GitHub-release channel.** Packet 722-w7a2 is reshaped, not closed: its deliverable changes from "an Azure Trusted Signing account" to the SignPath Foundation chain, with Azure **Artifact Signing** as the recorded fallback.
  14. 14Audit of the shipped "local expert" facade: answers stamped valid with no retrieval and no citations openspec/changes/expert-serve-grounded-pipeline/proposal.md#L1-L24The `pipeline` CLI arm did no retrieval and no validation, yet stamped every response `validated: true` with `confidence: 0.5` and `citations: []`
  15. 15The one item still open on the replacement: portability evidence from the Mac and Windows build lanes openspec/changes/expert-serve-grounded-pipeline/tasks.md#L52-L544.5 mlua portability evidence from the darwin/msys lanes (902-5bf9's blocking criterion) — OPEN: needs those hosts; the Lua surface kept here is deliberately thin enough to replace if evidence fails.
  16. 16Where no OS keyring is available, the key that unlocks the local vault falls back to a local file crates/tillandsias-headless/src/vault_bootstrap.rs#L1363-L1364Fallback: file (populated by keychain_set_blocking when keyring unavailable,
  17. 17Software sources — package repositories and release downloads, reached at your direction PRIVACY.md#L51-L56**Software sources** — package repositories and release downloads (for example GitHub, Linux distribution mirrors, and language package registries) to fetch the software it runs.
  18. 18AI providers only if you configure one; language models can run entirely on your own machine PRIVACY.md#L60-L63**AI providers, only if you configure one.** Tillandsias can run language models entirely on your own machine. If you instead configure a remote provider, the content you send is transmitted to that provider under their terms.
  19. 20The downloaded system image is cached on the host between runs openspec/specs/vm-provisioning-lifecycle/spec.md#L42-L49cached at `~/.local/share/tillandsias/rootfs-fedora-44-<sha256>.tar.xz` (on macOS: `~/Library/Application Support/tillandsias/rootfs-…`; on Windows: `%LOCALAPPDATA%\tillandsias\rootfs-…`).
  20. 21The rule that convergence is monotonic: once achieved, divergence must be detectable methodology/convergence.yaml#L56-L58- Convergence is monotonic under normal operation: once achieved, divergence must be detectable
  21. 22The build's trace ratchet: a new reference to a specification that does not exist fails the build build.sh#L965-L973The ratchet fails in BOTH directions: a new ghost, or a baseline entry
  22. 23The build's test verdict is a ratchet: a failure not on the known list is a new regression build.sh#L1521-L1526THE VERDICT IS A RATCHET, NOT CARGO'S EXIT CODE.
  23. 24The build script notarizes and staples when given a signing identity and the notary credentials scripts/build-macos-tray.sh#L255-L278say "notarize: submitting (this waits for Apple's verdict)"
  24. 25The release workflow runs the macOS build script with no credentials handed to it .github/workflows/release.yml#L468-L469run: scripts/build-macos-tray.sh
  25. 26The unsigned Windows installer package is withheld from a release rather than shipped uninstallable .github/workflows/release.yml#L647-L658::warning::withholding unsigned MSIX from release assets: $($_.Name) (uninstallable without a signature; set TILLANDSIAS_SIGNING_ACCOUNT to publish it)
  26. 27The other Windows downloads still publish unsigned, with a warning in the build log .github/workflows/release.yml#L641-L646::warning::TILLANDSIAS_SIGNING_ACCOUNT is unset — publishing UNSIGNED Windows artifacts (plan packet 722-w7a2)
  27. 28The shipped assistant configuration points at the grounded expert service: citations kept only if used, typed refusals otherwise images/default/config-overlay/opencode/config.json#L18-L30"description": "Grounded local experts: retrieval from the published spec index, citations kept only if used, typed unsupported: refusals — served by tillandsias-plan expert-serve beside the MCP servers (order 920-pxg6).",
  28. 29The live end-to-end check of the replacement, recorded 2026-09-02 in the daily channel openspec/changes/expert-serve-grounded-pipeline/tasks.md#L86-L985.10 Live OpenCode session verification against a running expert-serve — DONE on macuahuitl-tillandsias-forge 2026-09-02
  29. 30Uninstalling removes the cached image, except on macOS where it is preserved unless you ask for a full wipe scripts/uninstall.sh#L124-L128Preserving the VM image in $DATA_DIR (use --wipe to remove it).
  30. 31A failed upstream push is refused without partially updating refs openspec/specs/git-mirror-service/spec.md#L225-L230the forge's `git push` SHALL return non-zero
  31. 32A client's success means the upstream durably accepted the same refs images/git/pre-receive-hook.sh#L6-L8Validates ledger YAML, then synchronously relays the proposed ref transaction upstream before accepting it locally. A client success therefore means the configured upstream has durably accepted the same atomic ref set.
  32. 33The upstream token is read by the git service and never enters a workspace container openspec/specs/git-mirror-service/spec.md#L15-L16The git service reads the GitHub token from Vault at push time via Vault CLI; the token never crosses into a forge container.
  33. 34The version shown is the version file as it stood when the program was compiled crates/tillandsias-headless/src/main.rs#L121-L121pub(crate) const VERSION: &str = include_str!("../../../VERSION");
  34. 35The tracking entry for this shortcoming, on the development branch github.com/8007342/tillandsias/blob/linux-next/plan/index.d/20260914t185710z-1188-mm9y-installed-launcher-has-no-provenance-lenovinha.yaml

The anatomy: what runs where, what survives a teardown, and where the sharp edges are.Assumes the two levels before it.

Tillandsias, for a power user

Its roots only grip; the leaves do the drinking — a plant that runs rootless.

What you get the moment a forge opens

tillandsias --headless /path/to/project --claude starts a container with your project in it and an agent at the prompt. Nothing below is configured by you or by the agent: the context file the agent reads first lists the plumbing under the heading "all transparent — zero configuration needed".16 Every claim here is checked against the stable release, v56.9.12.2.

The forge container

A forge is a Fedora Minimal image in two layers: a heavy base carrying the toolchains — gcc, make, cmake, Rust with cargo and rust-analyzer, Go, Node, Java, Python, the debuggers — and a thin runtime layer with the entrypoints, cheatsheets and the agent's configuration.1718 The tag is a content hash of its sources, so an unchanged rebuild is a no-op.19 The coding agents are not baked in: every launch refreshes them into a per-project cache volume that outlives the container, rolling back to the last known-good version when upstream ships a broken one.2021 Cheatsheets, /tmp and the runtime directory sit on RAM-backed tmpfs with hard caps.22 The default clone-only lane now puts the source tree on tmpfs too, sized from the mirror’s pack; the opt-in host-mounted checkout keeps its host mount.30149 Allowlisted commands — tmux and lazygit among them — are shims that install userspace Homebrew on first use.23 On start the forge writes a context file into the checkout naming what is present, what is absent, and what needs no configuring.24

verified Works out of the box. The tool cache is a named volume, never a host path, so first-run installs survive the container's removal without opening a host-home surface.21

shortcoming The storage spec names four RAM-backed roots and a host RAM gate with a matching container memory ceiling; the default lane now mounts all four roots, but the host RAM gate and the matching container memory ceiling remain library code nothing calls.2526 The Homebrew prefix is backed by no volume, so every on-demand tool is re-installed at every launch and its bootstrap needs live egress each time.27 And the context file tells the agent each such install verifies a signed attestation per bottle; the shim states that attestation is off, because fetching it would need a GitHub credential no forge may hold — what remains is integrity from one publisher.2829

path to green The source tmpfs landed on 2026-09-04 and is now in stable.30144 The RAM gate and ceiling stay an open, ready item whose remaining slice is named: run the host RAM check before launch and emit equal memory limits.26 The recorded remedy for the prefix asks for a decision between persisting it and baking the tools into the image.27 For the context file's wording there is a recorded remedy too: fetch and pin the attestation bundles when the image is built, so a lane can verify one with no GitHub credential present at all.145

The HTTPS proxy and its cache

Every container is born with its proxy variables set and the proxy's certificate composed into the system trust bundle before any network client starts; no per-client CA variable is set, and a regression test refuses a launcher that tries.34353637 The allowlist of developer hosts — registries, GitHub, cloud SDKs, model providers — is generous enough that npm, cargo, pip and flutter work unconfigured.38 It decrypts exactly one of them, GitHub's release-asset CDN, so a large asset is fetched once for the whole machine.3940 Everything else passes through encrypted, so the proxy never sees, and never caches, crates.io, npm, PyPI or api.github.com.41 What makes the second cargo build fast is not the proxy but a per-project cache volume for cargo, npm, pip and go that outlives the forge.42 A blocked host gets no 403: the connection is reset, and only the proxy log names the domain.43

verified The trust wiring is pinned by a test: the forge launcher mounts the single runtime CA input read-only and sets none of the per-client CA variables.37

shortcoming The cache-hit claim for the one decrypted host has never been shown live. The fix for a rule order that made decryption unreachable shipped the config and static tests, and records that no real miss-then-hit trace was obtained.44

path to green The next step is written down: on a Podman host, request one release-asset URL twice through the strict proxy and record a miss followed by a hit with equal checksums.44

Local inference

The launcher prepares a shared model server at http://inference:11434, started on launch if it is not already running and shared across projects.4546146 The first run pulls the engine and one small tool-capable chat model into a host directory that outlives the containers.47489 Readiness is best-effort: a failed readiness wait warns and the forge launches anyway; errors while preparing the service can still fail launch.46 The context file tells the agent, in a closed vocabulary, what the host's accelerators are and whether the endpoint is ready — ready means at least one model is cached, never "starting up".4950 Larger tiers are opt-in behind an environment variable; the CPU floor is unconditional by spec.5152

verified A failed model-readiness wait is non-fatal, so it does not hold up the agent session.46 The CPU floor is the required fallback, rather than evidence that every fresh host has successfully loaded a model.52

shortcoming Three limits. The engine is downloaded at first run from an unpinned "latest" release with no checksum and no signature, then executed inside the enclave.4853 GPUs reach the container only with a vendor stack — NVIDIA with a CDI spec, AMD when ROCm reports the card — and NPUs are named on every platform and used on none: no device argument for one exists in the launcher, and the spec itself says the engine cannot use them.54555657 And the container's health check tests that the runner binary exists, not that a model loads.58

path to green Integrity is an open, ready item: verify the tarball against the checksum file upstream already publishes.53 NPUs are two open, ready items — device passthrough for the container, and a host-native sidecar for Metal and the NPUs a Linux guest cannot see.5960

path to green For the health check: No path to green is recorded in the repo. The entrypoint runs the real load check once at startup.58

The git mirror and push relay

For an agent, a refinement is a controlled data path: it receives selected source, rules, task context and tool results; it proposes a patch; local checks and review decide whether that patch becomes a commit. Git retains reachable commits, while the current tree is a snapshot that may replace or delete files. Calling the repository a "set of vectors" is therefore a useful description of the model's selected context, not Git's storage format and not a guarantee that every current file survives.616266

Several tasks can run this loop at once. Each needs its own declared target and measurement; the shared repository is the coordination point, not evidence that their measurements are already comparable. The relay makes an accepted push atomic,6263 which preserves the record of what was accepted, not the truth of the patch.

Why this is built rather than borrowed. The mirror has to satisfy two requirements at the same time. The credential for the upstream must stay on the server side, so a workspace can publish its own work without ever holding a token that could publish anything else — the git service reads that token when it pushes and it never crosses into a workspace container.152 And the push must be synchronously durable: the hook relays the proposed ref transaction upstream before accepting it locally, so a client's success means the upstream has durably accepted the same atomic ref set.153 The live relay performs that as a single atomic push.154

Tools in general offer one of those or the other. A managed push mirror holds the credential for you and copies asynchronously, reporting success before the copy lands — which is precisely the false success that strands an agent's work. A caching or redirecting git proxy relays synchronously but forwards the client's credentials, which is the isolation being bought in the first place. Needing both at once is the unusual requirement, and it is what the pre-receive relay exists to provide.

note The pairing is the argument, not novelty. The pattern of relaying a push

onward from a hook is documented elsewhere; what is specific here is refusing to

report success before the upstream has it, while the credential stays outside the

workspace.

Rolling the decision back is deliberately cheap: the relay is a bare repository plus a pre-receive hook, so undoing it means pointing the daemon back at the bare repository.

Git needs nothing from you inside a forge. A read-only global gitconfig redirects the project's GitHub URL to a bare mirror in its own named volume, so git remote -v still says github.com while clone, fetch and push hit the enclave.616 A push is not acknowledged until the mirror's hook has relayed exactly those refs upstream with one atomic push.6263 The token authorising that relay is read from the vault inside the git service at push time and never enters the forge.6465 Deletions and branch rewinds land nowhere;66 a project with no upstream keeps its pushes in the mirror.67

plausible That no off-the-shelf component satisfies both requirements together.

The comparison is recorded in the repository as a survey rather than as a

benchmark, and a reader should treat it as the reason a decision was taken, not

as a proof that no such tool exists.

verified Works out of the box once GitHub Login has run on the host; without a stored token the relay refuses the push and says so.68

shortcoming The push path inside the enclave is anonymous: any peer that can reach the mirror may create branches or fast-forward them and have the privileged relay carry those updates upstream. Being on an internal-only network is placement, not client authentication.69 The authenticated SSH lane that would replace it is built but dark behind an environment variable that defaults to off.70 Offline, a push simply fails — nothing is queued.71

path to green Flipping the SSH lane on by default is an open, ready item, pending the transport decision behind it.72 For the offline push: No path to green is recorded in the repo.

The vault

Secrets live in a Vault container at vault:8200, reached over TLS with the enclave certificate already trusted.7475 A lane with a credentialed provider is handed a single-purpose token on tmpfs at /run/secrets/vault-token whose policy reaches only that provider's paths; the generic forge policy cannot read the GitHub token, which the git mirror, the tray and the login flow can.76777879 Across a stop, Vault unseals itself from one Shamir share held in your host keychain, with no passphrase prompt.8

verified The token is injected at container start with nothing for the agent to do,76 and a test pins that every credentialed lane mounts its scoped lease while a credential-free lane mounts none.147

shortcoming The context file tells the agent the vault is at http://vault:8200. The shipped listener is TLS-only, so that address answers an HTTP error; only the in-image helper carries the right default.807581 The token defaults to a one-hour life and nothing in the lane renews it.82

path to green No path to green is recorded in the repo.

The experts and the project index

The agent finds four tool servers already registered, for Claude Code and OpenCode alike: git tools, a project index, the host-browser bridge, and a plan expert.8384 The index answers project type, status, commands and layout for any checkout, citing what it indexed; the plan expert returns cited answers or a typed refusal, never a guess, and answers only on a checkout carrying Tillandsias' own plan crate — elsewhere it reports itself degraded, by design.858687 Both start in the background after the clone and never gate the session.88 The status now distinguishes the running expert’s capabilities from what a relaunch would build: waiting for a build, needing a relaunch, and losing capabilities on relaunch have different machine-readable verdicts.150 A ready index is separate from a usable answer engine. Deterministic project questions can work without local inference; synthesis still returns a typed refusal when the synthesis capability is absent, even if inference itself is ready.151 The "on-demand" spec describes tray-side spawning and health checks that do not exist; the harness launches each server itself.89

verified Works out of the box on any project, with no registration step.85

shortcoming The context file tells the Claude lane about a conversational Local Experts mode, but only the OpenCode configuration carries an agent that reaches the grounded endpoint behind it.9091 And the pre-expert-binary trap is half closed: a forge seeded from a checkout without the expert sources now reports itself degraded instead of ready, but the fresh-forge criterion — an in-forge probe grading full marks with no manual step — is recorded open.92

path to green No path to green is recorded in the repo for the Claude-lane mismatch. The trap's entry stays open on purpose until the release-branch decision it waits on is made.92

Sibling web containers

Ask the agent to publish the project and it does not start a server in the forge. It asks the host, over a socket that exists only for that lane, to launch a sibling: a busybox httpd with the project mounted read-only, joined to the enclave network and given a router route.93949596 The URL comes back as http://www.<project>.localhost:<port>, fetchable from the forge through the proxy.9697 The project is attributed from the socket the request arrived on, never from the request.95

shortcoming Works with caveats, and they are real. The sibling serves the repository root, so a site kept in a subdirectory answers 404 at /, and .git is reachable through the router: the deny that closed it was applied by hand to a running router, while the route a publish generates carries no dotfile rule at this release.98 Only www is routed, not the apex.96 Nothing reaps the sibling when the forge that published it dies: its name matches none of the stack sweep's patterns.99 The project must live under ~/src on the host,100 the web image is built by initialisation rather than by a forge launch, so a host that never initialised hits a phantom registry pull,101102103 and the only end-to-end test of publishing has never been run by any suite.104

path to green The document-root convention and the ~/src assumption are open, ready items.98100

path to green For the orphaned sibling and the phantom pull: No path to green is recorded in the repo.

Chromium

Two browser images are built at initialisation: a minimal headless core, and a framework layer on top of it with GUI Chromium, Node and Playwright.101105106 The runtime launches the framework image as a read-only, capability-dropped, incognito app window — but only for the OpenCode-web and observatory modes, and only on a machine with a display.107108 The browser tool the agent sees — open, screenshot, click, type, close; eval switched off — is a different thing: it drives a host Chrome from the user's cache directory that nothing shipped installs, so a fresh host answers "browser unavailable".109110111112

shortcoming Spec-only in three places: the headless core is built and never run;105 the safe variant's proxy-only egress is a requirement without a launch flag, the script defaulting to host networking;114115 and the on-demand Chrome download the browser tool depends on has no caller.111 Partial in one: the tool's allowlist demands exactly three host labels, so a project whose directory name contains a dot — this site's does — can never open a window.113

path to green The installer is an open, ready item — wire a real consumer or tombstone the script, its test and its spec.112

path to green For the allowlist and the safe variant: No path to green is recorded in the repo.

The anatomy

The region is one Fedora guest — a VM, or a WSL2 distro on Windows — running a rootless Podman stack. Among the members:

  • a forge, the container your coding agent runs in, one per project;
  • a git mirror, a bare repo on the internal network;
  • a vault holding your tokens;
  • a forward proxy, the single outbound door;
  • a router, a Caddy reverse proxy giving web services stable names;
  • an inference container, the shared model server.

The roster is longer — a Nix cache, a catalog service, the observatory's web container, an SSH-lane sidecar — and most of those do not run in an ordinary session.1 Membership is not a hand-kept list: the spec names the attach sites by function symbol, and a guard compares the attach sites it finds in the source with that list, refusing the build if either side drifts. It reads code, not a live network.132

your computer · your disk · your electricity a clean Linux machine (made for you on Mac/Windows) the enclave — a private network with no way out appapp appagent the one guarded door out — everything else is refused
A region is layers, not a machine. Each ring only trusts what is inside it.

One rule carries the isolation: the containers share a Podman network created with --internal — no gateway, no route out.1 Only the proxy is dual-homed onto an egress network, and the launcher creates both networks on the first launch and reuses them after.131

The router publishes to one loopback address — port 80 by default, with fallbacks, or the port you pass with --port — and maps <service>.<project>.localhost onto internal ports. Nothing binds 0.0.0.0.2116

Starting one is a curl on Linux, an installer script on macOS, a tray executable on Windows. Then tillandsias --headless /path/to/project --claude.

verified Isolation is structural — one --internal network, exactly one dual-homed container — not a firewall ruleset you maintain. The launcher the tray runs passes the flag,131 and the membership guard runs in the build gate.32148

verified A standing audit corrected the routing spec against the live runtime: it had pinned a privileged port a rootless host cannot always bind. The invariant it protects, loopback-only, was untouched.2

shortcoming One thing the spec asks for is missing; another is enforced more narrowly than it reads. The spec requires the network to be removed at application exit when empty, and nothing implements that — the only removal is the destructive reset.133 And the membership guard, which does run, counts only builder and launcher functions by name, so an attach site written any other way is invisible to it.32

path to green No path to green is recorded in the repo.

verified The stack-orchestration script now creates the network with --internal and refuses to reuse a network lacking isolation.3117 Its fix landed on 3 September 2026 and is now in stable.119 The tray launcher also passes the flag.31

shortcoming Two sibling scripts — a per-project runner and a proxy diagnostic — still create the network without the flag and pass the gate, which checks a fixed list of three files.120121118

path to green No path to green is recorded in the repo.

What the walls are made of

Agent tooling exists only inside the forge image, never on your host $PATH; a missing image makes the launcher refuse rather than fall back to a host binary.4122 Mounts are enumerated — your project, a read-only CA certificate, the tmpfs roots, a per-launch temp dir, the persistent tool-cache volumes and a Tillandsias-written read-only gitconfig — so $HOME, ~/.config and your keychain are simply not addressable from inside, and a regression test asserts no user home is ever mounted.5

the usual way this way it breakspoke at it still oddpoke again drifts somewhere nobody can reproduce it breaksthrow it away build from the recipe identical every time, on every machine
Nothing at runtime is repaired. It is thrown away and built again from the recipe — which is why it comes back identical.

The forge does not touch your working tree by default: it clones fresh from the enclave mirror, and pushes travel back through the mirror on their way upstream.123124 An opt-in escape hatch bind-mounts your real checkout read-write and prints a reduced-isolation warning.125 Hence the blunt operating rule: a finding you did not push is a finding you destroyed.7

What survives, exactly

Idempotence is what makes destroy-and-recreate a repair procedure rather than a loss. Every layer comes up clean from scratch, so the boundary worth memorising is not "will it break" but "what is on which side of the wipe":

  • Survives a stop: the bare mirror in its per-project volume, with everything you pushed to it.6
  • Survives a stop: vault data, auto-unsealed from a single Shamir share held in your host keychain, with no passphrase prompt. The spec requires the share to reach the vault only through a tmpfs-mounted secret;8 the shipped guest also keeps a fallback copy of the share on its own disk when no keyring is reachable, and inside a VM guest that file is written on every initialisation.126127
  • Survives even podman system reset, on Linux: the model cache, because it is a host bind mount and not a named volume. The spec spells that distinction out, having once cost someone a wrong answer.9
  • Does not survive --reset-guest: it wipes the vault and every enclave container, volume, secret and network, then re-initialises, keeping the model cache. Push first.10128129

verified On Linux that boundary is documented per artifact and matches the code.9129

shortcoming The spec also promises that your host working copy is fast-forwarded after every successful push. No code implements it at this release: the file the spec names does not exist, and the tray-managed host checkout it fed was removed by ruling. After a forge push, your host checkout moves only when you pull.130143

path to green No path to green is recorded in the repo.

shortcoming On macOS the "cache survives" line is not yet proven. The VM at this release already boots with a second shared directory for the model cache, and the guest's first boot mounts it, so the durable path exists.131132 What remains open: guests provisioned before that change are not migrated, because the mount is written on first boot only, and survival across a VM rebuild has not been demonstrated end to end.11133 The shipped uninstaller now preserves the VM unless asked to wipe.134135 A partially restored cache is worse than none — the inference service answers a version check and then fails every request.11

path to green An open, ready item: prove survival end to end, which needs a re-provision nobody has authorised, and migrate the guests provisioned before the share. The next recorded step, dated 2026-09-04, is to re-measure on a warm cache: the one cold measurement taken so far came in far below the re-download cost the item was filed with, which now stands as unverified in magnitude.133136

shortcoming On Windows, wiping the guest used to leave the old unseal share in Credential Manager, and the tray pushed that dead key into the fresh guest, permanently breaking GitHub login. The wipe paths now clear the stale share; a host that already holds one still delivers it, and the guest cannot tell the host it was rejected.12137

path to green The reconcile half is blocked: the delivery reply carries no accept-or-reject signal, so a delivered share that fails to authenticate cannot yet lose to the guest's own secret.138 Until then, remove the stale credential by hand. A second wipe path that still missed the clearing was found and fixed on 2026-09-02 and is included in stable.139

shortcoming A host's container engine used to carry a global configuration line routing all registry traffic through the enclave proxy unconditionally, so with the stack down podman build and podman pull died on a raw DNS error that names nothing. Initialising a host now removes that line and hands the proxy setting to each container instead; a host never re-initialised still carries it, and a build that needs the absent proxy still fails without naming it.14014114213

path to green The global-config half is done: re-running initialisation converges the file.141 The rest is an open, ready item no one has picked up: a build must either not depend on a service that may be absent, or fail with a verdict that names the proxy.13

Driving it, and where the gate sits

the boundary the specification draws must hold must NOT hold positive cases negative controls
A test that only ever passes proves nothing. Each boundary is pinned from both sides.

The verification the earlier levels described is, operationally, a local gate: specs declare intent, litmus tests turn each requirement into an executable signal, traces prove the signal ran, and the gate refuses a push that breaks the chain. Two things follow that change how you work.

First, read a green gate with care. Eleven litmus files sit on disk and have never been executed by any suite — grandfathered onto a shrink-only list so that switching the gate on did not flip them all red at once.14 The list may shrink; nothing may join it.

Second, the gate on your machine is the only gate there is.

note Where a shortcoming below says No path to green is recorded in the repo, that is literal: the defect is described and no remedy exists in the plan. Those are collected into one tracking entry so an outside report has somewhere to land, rather than being rediscovered independently.155

shortcoming There is no push CI and no PR CI. Actions runs exactly one workflow, the release, because the signing keys exist only in the cloud. Nothing server-side validates a push; the local gate is the sole trunk protection — made load-bearing after an agent pushed unparseable code and every developer inherited the red build.15

path to green Not restoration. This is a deliberate budget trade, carrying a standing obligation to run the local gate before every push and an explicit instruction not to add a workflow to catch what a local gate should have caught.15

Footnotes

Every link points at release v56.9.12.2 of the source repository, so line numbers match the text above; a link marked with its own release tag points at that newer release instead. A footnote number in the text opens its source in a new tab; hover it for the quoted lines.

  1. 1Enclave network requirements — `--internal` creation, reuse, cleanup on exit, proxy-only egress, and the symbol-anchored membership list the guard enforces openspec/specs/enclave-network/spec.md#L19-L71A new enclave member MUST be added to this list in the same commit that attaches it; `scripts/check-enclave-membership-documented.sh` refuses the divergence in both directions.
  2. 2Loopback-only publish, the host-port fallback chain, and the 2026-09 freshness audit that corrected the drifted literals openspec/specs/subdomain-routing-via-reverse-proxy/spec.md#L3-L43What did NOT change, and what the requirement is actually protecting, is the loopback-only invariant: the publish is `127.0.0.1:{host_port}:8080` in every branch.
  3. 3The stack orchestration path now creates an internal network scripts/orchestrate-enclave.sh#L90-L103--internal
  4. 4Forge-as-only-runtime: the agent binaries must resolve inside a fresh forge, and the host must not need them openspec/specs/forge-as-only-runtime/spec.md#L48-L68`command -v claude codex opencode bash` MUST print four valid paths from inside a freshly built forge container.
  5. 5Regression test asserting no user home is mounted into a forge, and the mounts it deliberately allows crates/tillandsias-headless/src/main.rs#L20381-L20431must not mount a host .config dir into the forge; got source in: {arg}
  6. 6Bare mirror per project in a named volume; forge pushes persist there openspec/specs/git-mirror-service/spec.md#L19-L39The system SHALL create and maintain a bare mirror repository for each project at `/srv/git/<project>` inside the git service container, backed by the named Podman volume `tillandsias-mirror-<project>`.
  7. 7The ephemerality rule, stated for agents AGENTS.md#L61-L61**IN A FORGE, A FINDING YOU DID NOT PUSH IS A FINDING YOU DESTROYED.**
  8. 8Auto-unseal from the host native keychain, no passphrase prompt, share on tmpfs only openspec/specs/tillandsias-vault/spec.md#L78-L97The unseal secret SHALL be loaded into a podman secret and mounted at `/run/secrets/vault-unseal` on tmpfs only.
  9. 9Model cache is a host bind mount, NOT a named volume — and why that decides what `podman system reset` destroys openspec/specs/inference-container/spec.md#L29-L32Models SHALL be stored in a HOST DIRECTORY (a bind mount, NOT a named Podman volume) at `~/.cache/tillandsias/models/`, mounted into the inference container at `/home/ollama/.ollama/models/`.
  10. 10What `--reset-guest` wipes and what it keeps crates/tillandsias-headless/src/main.rs#L1428-L1432keeps the model cache) and re-initialize.
  11. 11macOS model cache and engine payload inside the VM disk image; the destroyers, the outage a lost engine caused, and the ~2.47 GB figure as filed (status: ready) plan/index.yaml#L30585-L30632every VM-directory deletion forces a ~2.47 GB re-download — the macOS twin of 518
  12. 12Windows Credential Manager retains a stale unseal share across a guest wipe; the wipe half landed, the reconcile half still open (status: ready) plan/index.yaml#L29523-L29569every Windows reset path (--reset-guest, installer -Purge, the e2e smoke) wipes the guest vault but never clears vault-shamir-share-v1 from Credential Manager
  13. 13Global container config hardcodes the enclave proxy, so builds fail on DNS when the stack is down; the open deliverable is a verdict naming the proxy (status: ready) plan/index.yaml#L41828-L41839~/.config/containers/containers.conf hardcodes http_proxy=http://proxy:3128 unconditionally, so when the enclave is down EVERY image build fails on DNS — and with it the release gate
  14. 14The ratchet list of litmus files that have never run, and why they are exempt openspec/litmus-tests/unbound-grandfathered.txt#L1-L25THE ONLY LEGITIMATE EDIT TO THIS FILE IS A DELETION
  15. 15The Actions budget ruling: one workflow, what was removed, and what was given up methodology/ci.yaml#L286-L324GitHub Actions runs EXACTLY ONE workflow: the release. Nothing else may consume cloud minutes — no push CI, no PR CI, no scheduled jobs, no cache warming, no webhooks. Every other gate runs locally.
  16. 16The startup context's infrastructure section: everything transparent, nothing to configure images/default/lib-common.sh#L4415-L4444You never need to configure git remotes, tokens, SSH keys, proxy settings, or CA certs.
  17. 17The heavy base layer: Fedora Minimal with the toolchains in one package set images/default/Containerfile.base#L5-L35rust cargo clippy rustfmt rust-analyzer cargo-deny
  18. 18The thin runtime layer on top of the base images/default/Containerfile#L1-L6Builds upon the heavy base image to inject configuration,
  19. 19Image identity is a content hash of the sources openspec/specs/default-image/spec.md#L100-L104The default forge image SHALL use a content-hash canonical tag derived from the image source set.
  20. 20Harnesses refresh at every launch into the persistent per-project tool cache, and a freshly installed binary that fails its contracts is replaced by the last good one openspec/specs/default-image/spec.md#L196-L237A freshly installed OpenCode that violates any of those contracts SHALL be rejected and replaced by that last-good binary.
  21. 21The tool cache is a podman named volume, not a host bind-mount, so it cannot become a credential-leak path crates/tillandsias-headless/src/main.rs#L14472-L14483A named volume — not a host bind-mount —
  22. 22The three RAM-backed roots the launcher mounts at this release crates/tillandsias-headless/src/main.rs#L14747-L14749.tmpfs("/tmp:size=256m,mode=1777")
  23. 23The on-demand tool allowlist in full: every listed command becomes a PATH shim that installs on first use images/default/brew-tools-allowlist.txt#L1-L122Commands listed here get a PATH shim in the forge: running the command
  24. 24Where the startup context file is written images/default/lib-common.sh#L4116-L4116local ctx_file="$project_dir/.forge-startup-context.md"
  25. 25The hot/cold spec: exactly four RAM-backed roots, the source tree among them openspec/specs/forge-hot-cold-split/spec.md#L11-L24Only these four path roots are HOT. "Maybe a hot path" is a HARD NO.
  26. 26The mount-topology packet: source tmpfs, host RAM gate and memory ceiling still to wire (status: ready) plan/index.yaml#L8317-L8341production launch runs check_host_ram, derives compute_memory_ceiling_mb, and emits equal --memory and --memory-swap limits
  27. 27The Homebrew prefix is ephemeral; every launch re-installs on-demand tools (status: ready) plan/index.yaml#L12661-L12665The brew prefix is ephemeral, so every forge launch re-installs and re-attests every tool — that is the rate-limit multiplier
  28. 28The startup context's claim of a Sigstore attestation per bottle images/default/lib-common.sh#L4515-L4520it verifies a Sigstore attestation per bottle
  29. 29What the shim verifies today: integrity from one publisher, attestation off images/default/brew-shim-exec.sh#L12-L21Sigstore verification is OFF because it requires a GitHub credential to FETCH
  30. 30The source tmpfs budget is calculated from the mirror pack size crates/tillandsias-headless/src/main.rs#L14506-L14512format!("/home/forge/src:size={budget}m,mode=0777")
  31. 31The launcher creates the enclave network with `--internal` on first launch, after ensuring the egress network crates/tillandsias-headless/src/main.rs#L2869-L2900command.args([ "network", "create", "--internal",
  32. 32The membership guard: every attach site's enclosing function must be in the spec and vice versa; it counts only builder and launcher functions scripts/check-enclave-membership-documented.sh#L21-L75if (fname ~ /^(build|launch)_/) { print fname }
  33. 33The only network removal in the launcher: the destructive reset crates/tillandsias-headless/src/main.rs#L8856-L8866command.args(["network", "rm", "-f", &name]);
  34. 34Proxy variables injected into every enclave container from one source crates/tillandsias-headless/src/main.rs#L1706-L1727"http_proxy=http://proxy:3128".into(),
  35. 35The forge composes vendor roots plus the proxy CA into the system-default bundle before any network client starts images/default/lib-common.sh#L12-L23Compose the per-install CA into that target atomically, before any network
  36. 36No per-client CA variables; trust flows through the distribution's standard path openspec/specs/transparent-https-caching/spec.md#L57-L62launchers and entrypoints SHALL NOT select CA files with `GIT_SSL_CAINFO`, `SSL_CERT_FILE`, `REQUESTS_CA_BUNDLE`, or `NODE_EXTRA_CA_CERTS`.
  37. 37A regression test pins the CA mount and the absence of CA overrides crates/tillandsias-headless/src/main.rs#L20525-L20535typed forge launcher must mount the single runtime CA input
  38. 38Package-manager workflows must work with no configuration openspec/specs/proxy-container/spec.md#L157-L157common development workflows (npm install, cargo build, pip install, flutter pub get) work out of the box without configuration.
  39. 39Proxy purpose: allowlisted caching forward proxy, bump only the release-asset CDN, splice all other TLS openspec/specs/proxy-container/spec.md#L8-L14HTTPS interception is deliberately exceptional: the proxy bumps only the exact GitHub release-asset CDN hostname and splices all other TLS traffic end to end.
  40. 40The three ssl_bump rules: peek once, bump one exact host, splice everything else images/proxy/squid.conf#L120-L124ssl_bump peek ssl_bump_step1 ssl_bump bump github_release_assets ssl_bump splice all
  41. 41Spliced HTTPS is never cached by the proxy openspec/specs/proxy-container/spec.md#L44-L49the HTTPS response SHALL NOT be cached (tunneled traffic is opaque to squid)
  42. 42cargo, Go, npm and pip caches all redirected into the per-project cache images/default/lib-common.sh#L2020-L2052export CARGO_HOME="$PROJECT_CACHE/cargo"
  43. 43Blocked hosts get a TCP reset, not a 403 images/proxy/squid.conf#L136-L141deny_info TCP_RESET strict_deny_acl
  44. 44The live miss-then-hit trace for the bumped host was never recorded, and the next action that would record it (status: ready) plan/index.yaml#L7533-L7576no rebuilt-image parser result or real identical-key MISS-to-HIT trace is claimed
  45. 45The consumer contract: one endpoint at inference:11434, downloads through the proxy openspec/specs/inference-container/spec.md#L13-L14Forge containers SHALL access it via `OLLAMA_HOST=http://inference:11434`. The inference container SHALL use the proxy for model downloads.
  46. 46Inference readiness is best-effort, never a launch gate crates/tillandsias-headless/src/main.rs#L14139-L14146Local inference readiness (order 392) is BEST-EFFORT, not a launch
  47. 47The single default chat model pulled on first run images/inference/entrypoint.sh#L120-L120DEFAULT_MODELS="${TILLANDSIAS_DEFAULT_MODELS:-qwen2.5:0.5b}"
  48. 48The engine self-installs from an unpinned latest release images/inference/entrypoint.sh#L293-L293OLLAMA_URL="https://github.com/ollama/ollama/releases/latest/download/ollama-linux-${OLLAMA_ARCH}.tar.zst"
  49. 49The closed-vocabulary accelerator line every forge receives crates/tillandsias-headless/src/accel_probe.rs#L3256-L3259accel_class=<workstation-gpu|mobile-npu|hybrid-gpu-npu|cpu-only>
  50. 50Ready means the endpoint answers and at least one model is cached; there is no "starting up" images/default/lib-inference-state.sh#L14-L24Reason vocabulary (CLOSED SET — there is deliberately no "starting up"):
  51. 51Larger tier models are opt-in, not default images/inference/entrypoint.sh#L690-L693tier pulls opt-in (set TILLANDSIAS_INFERENCE_TIER_PULLS=1) — skipping runtime tier pulls
  52. 52The CPU floor is unconditional by spec openspec/specs/inference-container/spec.md#L147-L152Tier-S SHALL be available on every host regardless of GPU, NPU, driver, or engine availability, and SHALL NOT be gated on any device probe.
  53. 53The engine payload is fetched with no integrity check (status: ready) plan/index.yaml#L10674-L10681The inference engine payload is downloaded and executed with NO integrity verification
  54. 54NVIDIA delivery is gated on a CDI spec; without one the host degrades to CPU with a loud remedy crates/tillandsias-headless/src/main.rs#L4943-L4960a GPU host without CDI degrades to CPU with a LOUD remedy
  55. 55The AMD lane is selected only when ROCm reports a graphics agent crates/tillandsias-headless/src/main.rs#L4456-L4462let rocm = std::process::Command::new("rocminfo")
  56. 56The spec: NPU rows are never attempted on the shipped engine openspec/specs/inference-container/spec.md#L179-L186N rows SHALL NOT be attempted on the `ollama` engine kind. Ollama cannot use any NPU
  57. 57On Windows the NPU cannot be seen from inside the guest at all scripts/windows-host-capability-probe.sh#L12-L14The NPU is not merely undetected in the guest, it is STRUCTURALLY invisible:
  58. 58The health check tests that the runner binary exists; the real load check runs once at startup images/inference/Containerfile#L112-L121A real generate would be the strongest assertion but is far too expensive at a
  59. 59NPU passthrough for the container is an open packet (status: ready) plan/index.yaml#L9392-L9399Impl: NPU /dev/accel passthrough plumbing for the inference container (AMD XDNA2 first lane)
  60. 60The host-native sidecar for Metal and the NPUs is an open packet (status: ready) plan/index.yaml#L9548-L9553Impl: host-native inference sidecar registry (macOS Metal/MLX, AMD XDNA2 flm/Lemonade, Intel NPU OpenVINO) behind the enclave proxy
  61. 61Agents configure nothing and still see the original GitHub URL openspec/specs/git-mirror-service/spec.md#L470-L476`git push`, `git fetch`, and `git clone` SHALL work with zero agent-side configuration
  62. 62One atomic push of explicit refspecs images/git/relay-refs.sh#L266-L266git push --atomic "$PUSH_URL" "$@"
  63. 63The mirror acknowledges only after upstream durably accepts the ref transaction images/git/pre-receive-hook.sh#L582-L582Push rejected: configured upstream did not durably accept the ref transaction
  64. 64The GitHub token is read from Vault at push time inside the git service and never crosses into a forge openspec/specs/git-mirror-service/spec.md#L14-L16The git service reads the GitHub token from Vault at push time via Vault CLI; the token never crosses into a forge container.
  65. 65The credential helper fetches the token from Vault over git's credential protocol images/git/git-credential-tillandsias.sh#L47-L47TOKEN="$(vault-cli read -field=token secret/github/token 2>/dev/null || true)"
  66. 66Every start re-applies receive hardening: no deletes, no rewinds, object checks openspec/specs/git-mirror-service/spec.md#L98-L106the git service SHALL set `receive.denyNonFastForwards=true`, `receive.denyDeletes=true`, and `receive.fsckObjects=true`.
  67. 67A project with no upstream keeps pushes durably in the local-only mirror images/git/relay-refs.sh#L157-L157No upstream configured; accepting as a durable local-only mirror update
  68. 68Without a stored token the relay refuses and names the remedy images/git/relay-refs.sh#L193-L193HTTPS upstream credential is unavailable; run GitHub Login before pushing
  69. 69The mirror daemon's push path is anonymous inside the enclave, and the repo says so in the same breath as the hardening that limits the damage images/git/entrypoint.sh#L413-L431The daemon remains anonymous inside the enclave: any reachable peer can still
  70. 70The authenticated SSH push lane is dark behind one flag until the default flip crates/tillandsias-headless/src/main.rs#L10470-L10476std::env::var("TILLANDSIAS_MIRROR_SSHD")
  71. 71Offline or credential-less, the forge's push returns non-zero and nothing is partially applied openspec/specs/git-mirror-service/spec.md#L224-L230the forge's `git push` SHALL return non-zero
  72. 72The SSH-lane default flip is an open packet (status: ready) plan/index.yaml#L21583-L21590T11+T12 — flip one lane behind a flag once the §4a matrix is green
  73. 74Vault is the default and only Linux secrets backend; GitHub token and short-lived per-container tokens openspec/specs/tillandsias-vault/spec.md#L11-L14Run a HashiCorp Vault container as the default and ONLY Linux secrets backend for Tillandsias.
  74. 75The shipped Vault listener is TLS-only images/vault/vault.hcl#L16-L24tls_cert_file = "/run/secrets/tillandsias-vault-tls-cert"
  75. 76Every OAuth-credentialed agent lane mounts one scoped Vault token crates/tillandsias-headless/src/main.rs#L14788-L14796Every OAuth-credentialed agent lane mounts a scoped Vault token so its
  76. 77Forge containers get no broad Vault token, only a provider-scoped short-lived one openspec/specs/tillandsias-vault/spec.md#L317-L322Forge containers SHALL NOT receive a broad Vault token.
  77. 78The generic forge policy cannot read the GitHub token images/vault/policies/forge.hcl#L1-L8Explicitly NO github or token access — forge containers must remain
  78. 79The tray policy reads the whole secret tree images/vault/policies/tray.hcl#L1-L6path "secret/*" {
  79. 80The startup context advertises http for a TLS-only Vault images/default/lib-common.sh#L4442-L4442http://vault:8200
  80. 81The in-image helper defaults to https and the tmpfs token path images/default/vault-cli.sh#L23-L24VAULT_ADDR="${VAULT_ADDR:-https://vault:8200}"
  81. 82Client tokens default to a one-hour TTL openspec/specs/tillandsias-vault/spec.md#L221-L225Every client token, including tokens minted by Vault Agent, SHALL default to TTL 1h with a maximum TTL of 24h.
  82. 83Four MCP servers registered for Claude Code inside the forge image images/default/config-overlay/claude/mcp.json#L2-L23"command": "/home/forge/.config-overlay/mcp/forge-plan.sh"
  83. 84The same four servers registered for OpenCode images/default/config-overlay/opencode/config.json#L66-L87"command": ["/home/forge/.config-overlay/mcp/forge-plan.sh"],
  84. 85Spec: the generic project expert bootstraps for any project with no registration or configuration openspec/specs/forge-environment-discoverability/spec.md#L171-L173The forge MUST bootstrap a project expert surface for an ARBITRARY mounted project without manual registration, configuration, or repository-type knowledge from the caller.
  85. 86The plan expert's refusal rule: zero citations means unsupported images/default/config-overlay/mcp/forge-plan.sh#L1215-L1215An answer with zero citations is returned as confidence=unsupported — the expert refuses rather than guesses.
  86. 87The plan expert is degraded by design on a project without the plan crate images/default/lib-common.sh#L4433-L4433(this project has no plan expert — expected off-tillandsias)
  87. 88Discovery, expert build and the grounded endpoint all backgrounded and fail-soft after the clone images/default/lib-common.sh#L1445-L1451ensure_forge_experts >>/tmp/forge-lifecycle.log 2>&1 || true
  88. 89The on-demand spec obliges the tray to detect and spawn servers openspec/specs/mcp-on-demand/spec.md#L32-L37the tray MUST detect that the filesystem MCP server is not running
  89. 90The startup context every lane reads names Local Experts mode images/default/lib-common.sh#L4452-L4452Local Experts mode (the
  90. 91Only the OpenCode configuration carries an agent bound to the grounded endpoint images/default/config-overlay/opencode/config.json#L18-L30"model": "tillandsias-experts/all",
  91. 92The pre-expert-binary trap: criterion 2 closed, the fresh-forge criterion still open (status: ready) plan/index.yaml#L6796-L6873criterion 1 (release/branch decision) still OPEN — packet intentionally stays `ready` (partial reduction)
  92. 93The catalog is an allowlist: WEB only, refused host-side otherwise openspec/specs/enclave-service-catalog/spec.md#L22-L26Requests outside the catalog are refused host-side; the guest cannot mint categories.
  93. 94The web image: busybox httpd on 8080, document root /var/www openspec/specs/web-image/spec.md#L24-L25The image MUST serve static files from `/var/www` on port 8080 using busybox httpd with no additional packages or configuration.
  94. 95One socket per lane, bind-mounted into the forge; attribution comes from the listener that accepted the connection, never from the request openspec/specs/mcp-tool-socket/spec.md#L31-L64The project (and instance) a request acts on SHALL be derived from WHICH LISTENER accepted the connection. The tray SHALL NOT read the project from the request body, from the peer process's environment, or from any other peer-supplied source.
  95. 96The live publish path: worktree mounted read-only, a www route made public, an http URL on the router's host port crates/tillandsias-headless/src/main.rs#L17118-L17206"http://www.{project_name}.localhost:{router_host_port}"
  96. 97From inside the forge, .localhost requests are forwarded by the proxy to the router images/proxy/squid.conf#L170-L175cache_peer_access tillandsias-router allow localhost_subdomain
  97. 98Serving the repository root: the document-root rung (status: ready), and what a consumer found reachable plan/index.yaml#L50941-L50988/.git/config returned 200 through the router (full history + remote URLs reconstructible)
  98. 99The stack sweep's name patterns: a published sibling matches none of them crates/tillandsias-headless/src/main.rs#L6440-L6453name.starts_with("tillandsias-git-")
  99. 100Projects must live under ~/src on the host; discovery hardcodes it (status: ready) plan/index.yaml#L47130-L47143tray discover_projects hardcodes ~/src and run_opencode_mode starts no lane listener
  100. 101The declarative image set initialisation builds: both Chromium images and the web image among them crates/tillandsias-headless/src/main.rs#L7906-L7917"forge-base", "forge", "web",
  101. 102An ordinary forge launch ensures four images, not the web or browser ones crates/tillandsias-headless/src/main.rs#L14051-L14058let images = ["router", "git", "inference", "forge"];
  102. 103The phantom pull the status-check path was fixed for crates/tillandsias-headless/src/main.rs#L9014-L9026phantom registry pull (125) on any version handover.
  103. 104The end-to-end publish litmus has never been run by any suite openspec/litmus-tests/unbound-grandfathered.txt#L21-L21litmus:publish-local-e2e
  104. 105The headless core image and its entrypoint images/chromium/Containerfile.core#L36-L37ENTRYPOINT ["/usr/lib64/chromium-browser/headless_shell", "--headless=new"]
  105. 106The framework image extends the core with GUI Chromium, Node and Playwright images/chromium/Containerfile.framework#L15-L35RUN npm install -g --prefix=/usr \ playwright \
  106. 107The hardened, ephemeral browser container the runtime launches crates/tillandsias-headless/src/main.rs#L13058-L13063Hardened browser boundary: read-only rootfs, CAP_DROP=ALL, no-new-privileges,
  107. 108Browser launch requires a graphical session crates/tillandsias-headless/src/main.rs#L13015-L13019OpenCode Web browser launch requires a graphical session (DISPLAY or WAYLAND_DISPLAY)
  108. 109The browser tool spawns a host Chromium from the cache directory, not a container crates/tillandsias-browser-mcp/src/launcher.rs#L78-L82root.join("current/chrome"),
  109. 110A missing bundled Chromium returns BROWSER_UNAVAILABLE crates/tillandsias-browser-mcp/src/server.rs#L431-L435BROWSER_UNAVAILABLE: bundled chromium not yet downloaded
  110. 111The only Chromium installer has no consumer scripts/install-chromium.sh#L10-L16INTEGRATION STATUS (2026-08-16 freshness audit, 774-cfw8): NO consumer
  111. 112The wire-it-or-tombstone packet for the orphaned installer (status: ready) plan/index.yaml#L23374-L23381scripts/install-chromium.sh has ZERO consumers while its header claimed two integrations; a litmus pins a shape nothing ships — wire it or tombstone it
  112. 113The three-label allowlist a dotted project name cannot satisfy crates/tillandsias-browser-mcp/src/allowlist.rs#L234-L237if labels.len() != 3 || labels[2] != "localhost" {
  113. 114Safe variant: proxy-only egress is the spec openspec/specs/chromium-safe-variant/spec.md#L16-L19Full network isolation inside the enclave, with allowlist enforcement via the proxy only; no host-gateway internet fallback
  114. 115Safe variant: the script launcher defaults to host networking scripts/launch-chromium.sh#L90-L90"--network=${TILLANDSIAS_BROWSER_NETWORK:-host}"
  115. 116The router's host-port candidates: an explicit --port first, then 80 and the fallbacks crates/tillandsias-headless/src/main.rs#L5757-L5765let mut candidates = vec![80, 8080, 18080, 28080, 38080, 48080, 58080];
  116. 117Stack orchestration script now passes `--internal` and refuses to reuse an unisolated network scripts/orchestrate-enclave.sh#L82-L126Network $ENCLAVE_NET EXISTS BUT IS NOT INTERNAL — it was created without --internal, so every member has NAT egress and the proxy is not the only way out (order 972-a8vh, spec:enclave-network).
  117. 118The new gate check, and the fixed list of files it reads scripts/check-enclave-network-internal.sh#L1-L115sh_launcher="$ROOT/scripts/orchestrate-enclave.sh"
  118. 119The fix recorded complete on 2026-09-03, with isolation measured from inside a member container plan/index.yaml#L53620-L53665Both exit criteria met; fixed in de9560fb5.
  119. 120A per-project runner still creates the network without the flag scripts/run-forge-project.sh#L116-L119"$PODMAN_CTL" network create --driver bridge --subnet "$ENCLAVE_SUBNET" "$ENCLAVE_NET" >/dev/null
  120. 121The proxy diagnostic still creates the network without the flag scripts/diagnose-proxy.sh#L89-L93podman network create --driver bridge --subnet "10.0.42.0/24" "$ENCLAVE_NET"
  121. 122A missing forge image makes the launcher refuse, never fall back to a host binary openspec/specs/forge-as-only-runtime/spec.md#L108-L109The tray MUST refuse to launch an agent if the forge image is missing — it MUST NOT silently fall back to a host binary.
  122. 123Clone-only by default: the host checkout is the opt-in path crates/tillandsias-headless/src/main.rs#L14671-L14674Order 437: clone-only by default.
  123. 124Forge containers clone from the mirror and push through it openspec/specs/git-mirror-service/spec.md#L54-L61Forge containers SHALL clone from `git://git-service/<project>`
  124. 125The reduced-isolation warning the host-mount escape hatch prints crates/tillandsias-headless/src/main.rs#L6183-L6191WARNING: WORKSPACE ENCLAVE ISOLATION IS REDUCED
  125. 126Inside a VM guest the share is written to a fallback file on every initialisation crates/tillandsias-headless/src/vault_bootstrap.rs#L1269-L1316Inside the VM there is no OS keychain, so these files are the only durable
  126. 127Where no OS keyring is reachable the share falls back to a file in the cache directory crates/tillandsias-headless/src/vault_bootstrap.rs#L2903-L2919using fallback file (expected in VM guest and headless environments)
  127. 128What the reset wipes on the host side: only the vault data directory crates/tillandsias-headless/src/main.rs#L8764-L8766vec![cache_dir.join("vault-data")]
  128. 129The pin test: the model cache must never be in the reset wipe set crates/tillandsias-headless/src/main.rs#L27563-L27571the inference model cache must never be in the reset wipe set
  129. 130The host working-copy fast-forward is specified, and specified against a file that does not exist openspec/specs/git-mirror-service/spec.md#L344-L363The tray SHALL trigger a fast-forward attempt on the host working copy at `<watch_path>/<project>` for every successful push to the enclave bare mirror
  130. 131The VM boots with a second shared directory for the model cache crates/tillandsias-vm-layer/src/vz.rs#L2305-L2323tag: "model-cache".to_string(),
  131. 132The guest's first boot persists the model-cache mount crates/tillandsias-vm-layer/src/vz.rs#L874-L880model-cache /root/.cache/tillandsias/models virtiofs nofail 0 0
  132. 133The enabling change landed and the packet stayed ready: migration and end-to-end survival unverified plan/index.yaml#L30668-L30728guest wiring verified by source scan, MIGRATION UNVERIFIED.
  133. 134The shipped uninstaller preserves the VM unless asked to wipe scripts/uninstall.sh#L121-L126Preserving the VM image in $DATA_DIR (use --wipe to remove it).
  134. 135The uninstaller defect the macOS packet waited on, closed plan/archive/packets-2026-08.yaml#L23430-L23436shipped uninstall.sh deletes the 11.83 GiB VM directory with no --wipe, no root and no prompt
  135. 136The later cold measurement, and the figure recorded as unverified plan/index.yaml#L30583-L30587macneo measured the cold case at 514 MB transferred and 64 s / 357 s wall clock on a never-populated image, so the ~2.47 GB title figure is unverified in magnitude.
  136. 137The wipe half landed; a host already holding a stale share is unaffected plan/index.yaml#L29523-L29525a host that ALREADY holds a stale credential is unaffected
  137. 138The delivery reply carries no accept-or-reject signal (status: ready) plan/index.yaml#L42152-L42159DeliverCredentials reply says "I received it", never "I accepted it"
  138. 139A second wipe path found and fixed in the daily channel plan/index.yaml#L29708-L29717found Part A missing on the SECOND purge path (build-and-install-windows-local.ps1); unifying and gating it
  139. 140Initialisation removes the orphaned global proxy block crates/tillandsias-headless/src/main.rs#L7766-L7769Remove the orphaned `[engine] env` proxy block.
  140. 141What initialisation prints when it converges the file crates/tillandsias-headless/src/main.rs#L7874-L7885removed the orphaned [engine] env proxy block from {} (923-rmtw); containers receive proxy env per-container
  141. 142The global-config fix, archived as completed plan/archive/packets-2026-08.yaml#L46813-L46818containers-conf-env-line-is-orphaned-and-never-converges
  142. 143The operator directive that removed the tray-managed host checkout plan/index.yaml#L16896-L16896It is REMOVED ENTIRELY.
  143. 144The daily channel's record of the source tmpfs landing, dated plan/index.yaml#L53896-L53900TMPFS SLICE LANDED (997-e4v2, slice 1 of 3). compute_hot_budget() has a real caller for the first time since it was archived as complete on 2026-04-27.
  144. 145Fetch and pin the attestation bundles at image build time so no lane needs a GitHub identity (status: ready) plan/index.yaml#L12556-L12578a forge lane installs an allowlisted tool with attestation verified and NO GitHub credential present anywhere in the lane
  145. 146The shared inference container is created only when it is not already running, so sibling forges share one crates/tillandsias-headless/src/main.rs#L14177-L14187inference is recreate-if-not-running (a --replace would drop loaded models and interrupt a sibling's inference mid-flight).
  146. 147The test pinning the scoped lease for every credentialed lane, and its absence from the credential-free ones crates/tillandsias-headless/src/main.rs#L24085-L24135Credential-free lanes never mount a provider lease.
  147. 148The check gate invokes the membership guard and fails the build when it refuses build.sh#L3872-L3882if ! _run bash "$SCRIPT_DIR/scripts/check-enclave-membership-documented.sh" 2>&1; then
  148. 149Only the clone-only lane mounts the source tmpfs crates/tillandsias-headless/src/main.rs#L14671-L14693spec.tmpfs(forge_hot_src_tmpfs(project_name))
  149. 150The context distinguishes waiting, relaunching and relaunch regression images/default/lib-common.sh#L4431-L4440\`relaunch-regresses\` means the running binary has MORE than the checkout and a relaunch would REMOVE capability.
  150. 151Ready inference alone does not provide the project index with a synthesis tier images/default/config-overlay/mcp/project-info.sh#L726-L733no synthesis tier is wired into project_answer yet
  151. 152The upstream token is read by the git service at push time and never enters a workspace container openspec/specs/git-mirror-service/spec.md#L15-L16The git service reads the GitHub token from Vault at push time via Vault CLI; the token never crosses into a forge container.
  152. 153The relay is synchronous: local acceptance follows upstream acceptance images/git/pre-receive-hook.sh#L6-L8Validates ledger YAML, then synchronously relays the proposed ref transaction upstream before accepting it locally. A client success therefore means the configured upstream has durably accepted the same atomic ref set.
  153. 154The live relay pushes the refs as one atomic transaction images/git/relay-refs.sh#L266-L266if OUTPUT="$(GIT_TERMINAL_PROMPT=0 git push --atomic "$PUSH_URL" "$@" 2>&1)"; then
  154. 155The entry tracking every shortcoming here for which no remedy is recorded github.com/8007342/tillandsias/blob/linux-next/plan/index.d/20260915t215254z-1213-rbt9-website-found-fourteen-shortcomings-the-ledger-does-not-track-macuahuitl.yaml

The architecture interrogated rather than described — boundaries, egress, provenance, and what the tests do not actually test.Assumes the three levels before it.

Tillandsias, for an IT / security reader

Silvery leaf scales open to take water in, then trap air to keep it: every exchange across one surface.

Where the boundary actually is

The anatomy does not answer the security question: when an agent does something you did not intend, how far does it get?

First, a correction. The region was described as one Fedora guest — a VM, or WSL2 on Windows. True on two platforms, false on the third: Linux provisions no VM, and the orchestrator drives rootless containers directly on your host.1 The promise is that this costs nothing but Podman; the price is that the enclave is a hypervisor boundary on macOS and Windows and a namespace boundary on Linux — where an escape lands as your own uid, with your $HOME.2

Inside, every launch is meant to carry four flags — userns mapped to your uid, all capabilities dropped, no-new-privileges, and label-disable, which is to say SELinux labelling off on every container — audited by a policy module that rejects privileged mode, a non-identity userns, or a blanket capability add.498 Why this envelope: keep-id makes an escape land as you rather than root, and a checker that never runs Podman is one small place to ask whether a launch still carries it. The compromise is the fourth flag, bought so bind mounts work on an SELinux-enforcing host.9294 Read the envelope as defaults on a struct, not an invariant — and not quite every container: the vault adds one capability back, omits --rm, and never passes the checker at all.53

verified The threat model is written down in the project's own voice, including the parts that make it look bad — the architecture notes state plainly that the allowlist is generous by design and that an attacker can encode data in DNS queries or headers to an allowed domain.3

The litmus now derives its launch flags from the product's declared envelope.58 Its repaired user-namespace probe checks ownership of a host-owned mounted file, because inspecting the namespace label or comparing the process uid alone could give a misleading answer.100 That is evidence about the declared envelope and Podman on the tested host, not proof that every production launch uses it.

verified Production hardening checks now survive release compilation: the checked serializer returns an error for a stripped envelope, and both attached and delegated forge launch paths refuse it.6555657 The general capability-add escape hatch has also been removed from the typed container builder.27 Previously the check was only a debug assertion and the security litmus printed success on both branches; those defects are repaired in this stable release.7

shortcoming Enforcement is still incomplete across launch paths: proxy, mirror, observatorium and a host-browser launch assemble or serialize arguments without the policy check, and the Podman client accepts raw arguments.6490959697 The typed builder retains private boolean fields, although its public constructor turns all four on and exposes no setters to turn them off.63 A separate test defect remains: the capability probe treats empty output as success without checking whether the inspection ran, so a failed podman exec can still print CAPABILITIES_DROPPED.101

path to green The checked serializer and forge callers provide the existing enforcement mechanism; extending that mechanism to every launch remains unimplemented.5565 The build now rejects identical success/failure tokens across the litmus corpus,6061 but that narrow scanner does not catch the empty-output failure above.62 The older advisory scanner and diff-scoped quality rule also remain bounded instruments, not a clean bill of health for the existing corpus.854

does not hold "The litmus proves every launch is hardened." It launches a probe from declared flags, leaving production call-site coverage to other checks; even its cleanup step reports success unconditionally.5859

shortcoming SELinux confinement is disabled on every forge, so on a Fedora or RHEL host the agent's container is less confined than a default one. The project does confine elsewhere — the vault carries a loadable policy module,50 the Windows guest embeds policies for the control daemon and the vault51 — but on a rootless Linux host the vault too runs unconfined.52

path to green MCS labelling of the forge is recorded as a planned later phase — intent, with no implementation and no litmus behind it.5 For the missing hypervisor on Linux no design tradeoff is recorded, only the blast radius of an escape — your uid, your $HOME2 — mitigated by defence in depth rather than a stronger boundary.

plausible Defence in depth is argued layer by layer below; nothing demonstrates that the layers compose to bound an escape.

Egress: intercepted versus passed through

The intended egress boundary is network placement plus proxy environment variables. The proxy configuration supplies hostname rules; it does not establish that the host or container networking stack uses no packet filters. An agent can unset HTTP_PROXY; what stops it is that the namespace has nowhere to send the packet. Then the question everyone asks on hearing "trusted CA in the container":

the boundary the specification draws must hold must NOT hold positive cases negative controls
A test that only ever passes proves nothing. Each boundary is pinned from both sides.

verified This is not a blanket MITM. The proxy peeks at the handshake and then splices — passes through undecrypted — everything but one exact hostname, GitHub's release-asset CDN, bumped so large binaries can be cached. Registries, GitHub's APIs and every auth endpoint stay end-to-end encrypted.9

shown The policy is three lines of a config file you can read, and the bump is scoped to one client-requested server name.9

The cost of that restraint: with one host decrypted the proxy does no payload inspection — hostnames, not content — and its default-deny allowlist admits anyone-can-publish namespaces, so an allowlist hit is no evidence of a benign destination.330 What it buys is a denial that looks like a network fault: a refused runtime request gets a TCP reset, not a page to negotiate with — the config's behaviour; the spec still promises a 403.3738

verified Where it does terminate TLS it still verifies upstream against the system trust store, and the config carries a standing written order never to disable peer or domain verification — that would hide a real origin MITM behind a proxy-issued certificate.10

shortcoming The proxy's second, permissive port is a reachable allowlist bypass. Squid listens on it on every interface,31 the rules allow every destination on it with no source restriction,32 and the spec says outright that domain filtering does not apply there.33 The config calls it a port that serves nobody, because no launcher points a proxy variable at it34 — but an agent can point its own, and network placement does not stop that: the proxy sits on the same network.

path to green No path to green is recorded for the bypass itself, only an operator decision — route image builds through the port, or delete it — open in the config35 and still open in the network audit.36

verified The stable release now puts the CA bundle under per-user persistent state instead of /tmp, so reboot cleanup no longer removes its declared location.284391 On Unix, new key material is clamped to owner-only before publication, and the existing-key path attempts the same repair; the non-Unix helper is a documented no-op.394041 This addresses the code side of a defect whose earlier fix had not reached material already created on hosts.11

shortcoming Moving the directory does not make its permissions private by construction. The launcher still uses ordinary recursive directory creation, and the repair script remains part of preflight.10244 Historical installation reports establish that two hosts received the earlier key fix, not that every current host's material has been inspected.42

path to green The recorded remedy is still to create the directory privately and retire or give a retirement condition to the repair script.45 The persistent-path change has landed; the stronger directory-permission guarantee has not. The script clamps the directory before the key to remove traversal by other users first.11

shortcoming The published proxy spec describes something the code does not do: a fresh two-level EC P-256 chain generated per launch, held only on tmpfs, no CA key ever touching disk.12 The shipped launcher generates one self-signed RSA-2048 certificate with a 30-day life, stored in persistent per-user state and reused until refresh is needed.1328102

path to green No path to green is recorded in the repo — the spec has not been marked stale, and no change proposal reconciles it with the implementation.

Secrets, and the channel that guards them

The GitHub token never enters the agent container: the mirror holds short-lived role credentials, relays pushes on the agent's behalf, and passes the credential on stdin rather than argv, closing a process-listing leak. One clause on the relay: it wires the GitHub helper for any HTTPS origin and the helper answers unconditionally,4849 so the origin decides where the token goes.

verified The mirror holds the upstream credential and mediates pushes; the agent-facing write path is separate from that credential-bearing relay.144849

does not hold "Exfiltrating a token requires compromising both the mirror and the proxy." The architecture says this,3 but the proxy already forwards permitted destinations and exposes a permissive port. A compromised mirror could use those routes without compromising the proxy process.303132 Network placement restricts routes; it does not prove that an allowed request cannot carry a secret.

shortcoming The write path the agent pushes to is an anonymous git daemon receive-pack listener. The spec is blunt: network placement SHALL NOT be described as client authentication — any process on the enclave network can write to any mirror.14

Why the relay is bespoke, in security terms. The design needs the upstream credential to stay server-side and the push to be synchronously durable, and those two requirements pull against the available tools. A managed push mirror holds the credential and copies asynchronously, so it reports success before the copy lands; a caching or redirecting git proxy relays synchronously but forwards the client's credentials, dissolving the separation this section is about. The hook takes the third option: it relays the proposed ref transaction upstream before accepting it locally, so a client's success means the upstream durably accepted the same atomic ref set.107

That is a security property and not only a convenience. A false success is indistinguishable, to the agent that received it, from work that is safely published — so an asynchronous mirror would put unpublished work behind a green light, on exactly the disposable machine the design expects to be thrown away.

path to green The spec labels it interim and constrains what may be built on it, but records no dated authenticated replacement.14 That replacement is built and dark: an SSH-CA push lane exists in the mirror image and the launcher, behind an environment variable that defaults off and that nothing in shipped packaging sets.4647

The host↔guest control channel now uses a Noise handshake by default. Its key is derived through HKDF from the guest binary's SHA-256, separated by build version, wire version and hop.15103 This binds compatibility to known release material; it is not remote attestation that the peer runs unmodified code.

note This says what the relay refuses to report, not that the credential

boundary is complete. The RED above about the anonymous receive-pack listener is

the live gap on the agent-facing side of the same path, and it is not closed by

anything in this paragraph.

A release exposed a second boundary: the tray and guest are different executables, so hashing each process's own binary produced different keys even at the same version. The host now derives its key from the guest digest embedded at build time, while the guest hashes itself.103104 The Windows caller refuses a missing embedded digest and includes tests for matching and mismatched guest digests.105106 A one-process round-trip test could never have detected the original two-binary mismatch; that limitation is now recorded beside the implementation.104

verified An absent secure-wire setting now selects encryption, blank or invalid values are refused, and the Windows tray uses the same parser as the other callers.16666769 The earlier listener-only flip was reverted before the shared decision landed; a source scanner now allows zero independent readers.68 The macOS host writes the resulting value into the guest unit.93 Windows and the VM image need no explicit environment line to obtain the new default from the listener.7071

shortcoming Encryption is on by default, but caller identity remains weaker than operator authentication: anyone with the published guest bytes and version inputs can derive the same key. The guest's debug path instead uses a public constant.1529103 Explicit off still enables plaintext.16 Neither possession of the digest nor a completed handshake proves the caller is the legitimate orchestrator.

path to green The maturity ladder still records deletion of the insecure path as a later stage.17 Mixing in a per-boot host secret is approved and deferred; the present derivation does not include it.18103

Supply chain and provenance

verified Release artifacts are signed with Sigstore cosign keyless — no long-lived key, identity asserted by the CI OIDC token, inclusion recorded in a public transparency log — and the Linux and Windows asset sets are checked for transitive coverage as a property over whatever is staged, not over a remembered list.197274

shortcoming The signature proves less than it looks like. Verification matches an identity regexp pinning only the repository name — not the owning organization, the workflow, or the ref — so a fork of the same name signing through the same CI provider satisfies it, in the published command and in the shipped verifier alike.1978 Beyond that there is no SBOM, no build-provenance attestation, no SLSA or in-toto predicate, no container-image signing; base images are pinned by mutable tag rather than digest, so the image you build tomorrow is not the one you built today.20

path to green Recorded only as research — a study of another distribution channel shows such provenance verifies with no verifier identity at all, and recommends shipping bundles plus a pinned root.2180 Nothing has been adopted here.

shortcoming The macOS lane is the exception: it signs an allow-list of three named assets and runs no integrity check, so its checksum manifest ships without a bundle73 — the allow-list shape the Windows lane's own comment blames for an installer once shipping bare.74

path to green No path to green is recorded in the repo.

shortcoming Nothing a user runs by default checks a signature; only the hand-run verifier does. The install scripts fetch the checksum manifest over the same channel as the binary and on Linux continue without it;757677 no install path verifies a cosign bundle, and the updater's spec asks for no verification.79

path to green No path to green is recorded in the repo.

Where ephemerality stops being a control

Ephemerality resets compute, not identity. Of what survives teardown, the security-relevant item is the Vault unseal share, which lives in the host OS keychain — outside every boundary the enclave draws.22 The reason is fair — no passphrase prompt, never wipe a vault the host can still open — but destroying the region does not destroy the ability to open what it held.

shortcoming On Linux the documented full-reset command is not credential-cold. A "pristine" re-initialization recovered an unseal share dated months earlier from the host keychain and preserved the existing data volume — so the vault re-initialization path the smoke test claims to exercise has never actually run there. Two hosts held shares of different ages, ruling out a shared fixture.23 The one-command reset fares no better: it wipes containers, secrets and the vault's storage but never touches the keychain entry.8182

path to green Filed as an open defect, framed honestly in the release notes: the gap predates the release that found it, which is simply the first whose validation looked.23 A third host reproduced it on 2026-08-31;89 the packet's deliverable is that the reset becomes credential-cold or the runbook stops claiming it is, and no fix has landed.88

shortcoming The keychain is not the only copy. Where no OS keyring is available the launcher writes the share to a plaintext file in its cache directory,83 inside a VM guest it writes that file unconditionally,84 nothing ever deletes it, and the "share present in keychain" check that preserves the old volume is satisfied by the file alone8586 — while the spec says every persistent on-disk copy is deleted immediately.87

path to green No path to green is recorded in the repo.

Blast radius, autonomy, auditability

An agent's reach: its forge, every mirror on the enclave network, any allowlisted hostname with nothing inspecting the payload. Not your keychain, $HOME, or real checkout — unless you took the host-mount escape hatch.

The audit trail is committed and unusually candid. It is also agent-self-reported and unsigned, with no commit signing evidenced: a tree once passed green while red, and the repo calls its own fix unforgeable by accident, not by intent.99 With nothing in this repo evidencing server-side validation of a push, the record's provenance rests on the honesty of the process that wrote it.

verified The one MCP surface that lets a contained agent drive a host browser ships with arbitrary JavaScript evaluation disabled — the tool is advertised but returns an explicit refusal, so it is visible without being live.24

What the assurance claim actually is

The convergence argument you already have is not a security argument, and the project does not offer it as one. What carries the weight is a pair of invariants — verification claims must be falsifiable, and evidence is not proof25 — plus the methodology's refusal to read its completion score as a probability.26 So: a passing suite is a bounded signal over the defects someone thought to write a litmus test for, and, as the hardening case shows, only over those whose tests distinguish the relevant failure from success. Finite litmus coverage is not proof of absence of defects; the repo says so before you do.

Security is one local target among several. An agent may add a check, a boundary or an audit record to the shared repository, but that does not make the next change independent, nor does it turn a passing security test into a statement about every other target. The useful engineering loop is narrower: make the claimed boundary explicit, try to break it, retain the evidence and record the counterexample when it fails.2526

note Several shortcomings on this page carry the line No path to green is recorded in the repo. That sentence is written after looking, and it means what it says: the defect is described here and no remedy is written down anywhere in the project's plan. Those cases are now collected and tracked as a single entry, so a reader who wants to argue with one — or report it — has something to attach it to.108

Footnotes

Every link points at release v56.9.12.2 of the source repository, so line numbers match the text above; a link marked with its own release tag points at that newer release instead. A footnote number in the text opens its source in a new tab; hover it for the quoted lines.

  1. 1Podman the only host dependency on Linux; macOS/Windows provision a VM README.md#L52-L53Podman is the only host dependency on Linux (auto-detected). macOS and Windows provision a lightweight Fedora-based utility VM; no host Podman required.
  2. 2Escape lands as the invoking user's UID on the host, with `$HOME` only openspec/specs/podman-idiomatic-patterns/spec.md#L209-L210- **THEN** the escaped process runs as the invoking user's UID on the host, not as root - **AND** it has access only to `$HOME` and user-owned resources
  3. 3Enclave architecture — attack scenarios and stated limits docs/cheatsheets/enclave-architecture.md#L217-L225The proxy allowlist is generous by design — a determined attacker could encode data in DNS queries or HTTP headers to an allowed domain.
  4. 4The mandatory hardening envelope and the argv policy checker crates/tillandsias-podman/src/policy.rs#L14-L22pub const MANDATORY_HARDENING_FLAGS: [&str; 4] = [ "--userns=keep-id", "--cap-drop=ALL", "--security-opt=no-new-privileges", "--security-opt=label=disable", ];
  5. 5SELinux MCS labelling recorded as a planned future phase openspec/specs/default-image/spec.md#L768-L769future release will apply SELinux MCS labels to the forge domain, adding mandatory access control at the boundary layer.
  6. 6Checked serialization refuses an invalid launch in every build profile crates/tillandsias-podman/src/container_spec.rs#L377-L397crate::policy::validate_launch_argv(&argv)?; Ok(argv)
  7. 7Historical false-pass defect and its corpus-wide guard openspec/litmus-tests/litmus-podman-idiomatic-security-flags.yaml#L7-L13Both branches printed what the harness was looking for, so the greps decided nothing
  8. 8The false-pass scanner: advisory, no caller gates on it, skips over half the corpus, keyed on a non-zero exit scripts/scan-litmus-false-pass.sh#L1-L41Advisory — no caller gates on it.
  9. 9Peek at step 1, bump one exact hostname, splice everything else images/proxy/squid.conf#L109-L124ssl_bump peek ssl_bump_step1 ssl_bump bump github_release_assets ssl_bump splice all
  10. 10Upstream verification retained; standing order against disabling it images/proxy/squid.conf#L75-L82verification MUST remain enabled. Never add DONT_VERIFY_PEER or DONT_VERIFY_DOMAIN: doing so would hide an origin MITM behind a proxy-issued certificate that enclave clients trust.
  11. 11Host-side CA re-clamp, with the reasoning for why a code fix was only half the job, and the two hosts found world-readable scripts/clamp-ca-material.sh#L1-L28Two hosts were found with a world-readable CA private key days after the packet closed
  12. 12Spec: per-launch EC P-256 root-and-intermediate chain on tmpfs openspec/specs/proxy-container/spec.md#L63-L74The system SHALL generate a fresh two-level CA chain on every proxy launch. The chain SHALL consist of a self-signed Root CA and an Intermediate CA signed by the root, both using EC P-256 keys. All key material SHALL be stored on tmpfs
  13. 13Launcher generates self-signed RSA-2048 CA with a 30-day life crates/tillandsias-headless/src/main.rs#L3187-L3205"req", "-x509", "-newkey", "rsa:2048",
  14. 14Anonymous receive-pack listener; network placement is not authentication; owners named, no date openspec/specs/git-mirror-service/spec.md#L52-L72Network placement SHALL NOT be described as client authentication: any enclave peer can still create or fast-forward refs and cause the privileged relay to carry them upstream.
  15. 15Noise PSK derived from the binary's own hash crates/tillandsias-secure-channel/src/lib.rs#L112-L114let exe = std::env::current_exe().expect("current_exe for self-hash"); let bytes = std::fs::read(&exe).expect("read self binary for hash"); Sha256::digest(&bytes).to_vec()
  16. 16Absent means encrypted; plaintext requires explicit off crates/tillandsias-control-wire/src/secure_wire_mode.rs#L85-L131Err(std::env::VarError::NotPresent) => Ok(SecureWireMode::On),
  17. 17The maturity ladder from default-OFF to secure-by-default to removing the plaintext path — status active plan/issues/secure-channel-maturity-ladder-2026-07-04.md#L1-L12implement the e2e encrypted socket channel in ALL places, enable it at runtime with a flag, then advance through STABLE VERIFIABLE MATURITY GATES to secure-by-default and finally to removal of the insecure path.
  18. 18Per-boot key hardening — approved, deferred, unimplemented plan/issues/encrypted-channel-perboot-key-hardening-2026-07-01.md#L1-L22This packet is the operator-approved **later hardening**: mix in a **per-boot secret** the host controls, so a leaked release secret alone no longer lets an attacker with a matching-release binary attach across a different VM boot.
  19. 19Keyless signing recorded in the Rekor transparency log, and the verification identity regexp that pins the repo name only .github/workflows/release.yml#L320-L326--certificate-identity-regexp "https://github.com/.*/tillandsias/"
  20. 20Base images pinned by mutable tag images/git/Containerfile#L19-L21FROM docker.io/hashicorp/vault:1.18 AS vault-agent FROM docker.io/library/alpine:3.20
  21. 21The ledger's finding that such provenance verifies with no verifier identity, and its recommendation of bundles plus a pinned root — status ready, not adopted plan/index.yaml#L12575-L12575verifying a Sigstore/SLSA attestation requires NO identity from the verifier. Reproduced with no token, no gh config, and inside a network namespace with no interface at all: exit 0, real certificate chain, correct signer; one flipped byte gives exit 1.
  22. 22Vault unseal share stored in the host OS keychain openspec/specs/tillandsias-vault/spec.md#L82-L91The unseal key (the single Shamir share generated by Vault during initialization) SHALL be stored directly in the host OS's native secure keychain (Secret Service/KWallet on Linux, Credential Manager on Windows, Keychain on macOS) under the versioned name `vault-shamir-share-v1`.
  23. 23Release notes: the non-credential-cold Linux reset README.md#L120-L120Linux found its `podman system reset` is **not credential-cold** — Vault recovered a Shamir share dated 2026-06-15 and logged `preserving existing data volume`
  24. 24Host-browser tool dispatcher refuses arbitrary JavaScript evaluation crates/tillandsias-browser-mcp/src/server.rs#L367-L379"browser.eval" => Self::tool_error( id, "EVAL_DISABLED: browser.eval is disabled in v1; see follow-up change", ),
  25. 25Falsifiability and evidence-is-not-proof invariants methodology/philosophy.yaml#L69-L71- verification_claims_must_be_falsifiable: true - convergence_requires_stability: true - evidence_is_not_proof: true
  26. 26Bounded ranking function, explicitly not a probability methodology/math-foundations.yaml#L91-L125CentiColons are a finite bounded ranking function.
  27. 27General capability-add field and builder method removed crates/tillandsias-podman/src/container_spec.rs#L85-L100there is deliberately no `cap_add`. The field existed as an unvalidated pass-through
  28. 28Shared CA path accessor composes the ca leaf under the state root crates/tillandsias-core/src/ca_path.rs#L37-L51format!("{}/ca", ca_template())
  29. 29Debug builds derive the channel key from a fixed, public seed crates/tillandsias-secure-channel/src/lib.rs#L63-L68Fixed, non-secret dev seed used in debug builds. It lets a locally-built host + guest of the *same* tree interoperate without a release build. It is intentionally NOT a secret
  30. 30The allowlist table: package registries and cloud namespaces anyone can publish to docs/cheatsheets/enclave-architecture.md#L144-L156| Package registries | `registry.npmjs.org`, `crates.io`, `pypi.org`, `rubygems.org` | Package installation |
  31. 31The permissive port listens with no bind address, on every interface images/proxy/squid.conf#L60-L64http_port 3129 ssl-bump
  32. 32Permissive port: allow everything, no source restriction images/proxy/squid.conf#L132-L134http_access allow CONNECT SSL_ports build_port http_access allow build_port
  33. 33Spec: on the permissive port domain filtering does not apply openspec/specs/proxy-container/spec.md#L114-L114- **AND** domain filtering SHALL NOT apply
  34. 34The permissive port described as serving nobody images/proxy/squid.conf#L9-L14So this port listens, bumps, and serves nobody.
  35. 35The port is kept pending an operator decision, not deleted images/proxy/squid.conf#L39-L44That is an operator decision, not a cleanup. Until it is taken, this comment is the honest state.
  36. 36Network audit: the permissive-port decision still open plan/issues/network-architecture-audit-2026-07-09.md#L531-L533**STILL OPEN as a decision**, with the false claim removed and the agreement now enforced
  37. 37Denied runtime traffic gets a TCP reset rather than a 403 images/proxy/squid.conf#L136-L141Send a TCP reset instead of an HTTP 403 error for strictly-denied runtime traffic.
  38. 38The spec still promises an HTTP 403 for denied domains openspec/specs/proxy-container/spec.md#L109-L109- **AND** all other domains SHALL be denied with HTTP 403
  39. 39The owner-only clamp on the CA key, in the stable release's source crates/tillandsias-headless/src/main.rs#L3077-L3079fn enforce_ca_key_mode(key: &Path) -> std::io::Result<()> { use std::os::unix::fs::PermissionsExt; std::fs::set_permissions(key, std::fs::Permissions::from_mode(0o600))
  40. 40On non-unix targets the clamp is a documented no-op crates/tillandsias-headless/src/main.rs#L3097-L3099This is deliberately NOT a security regression on Windows: the key is not protected by mode bits there in the first place, and the enclave's Windows path receives it as a podman secret rather than through this file.
  41. 41Pre-fix keys are healed down to owner-only on every pass crates/tillandsias-headless/src/main.rs#L3278-L3280Heal DOWN to 0600 every call so keys generated before this fix (deliberately world-readable 0644) are repaired without requiring a CA rotation.
  42. 42Historical report that two hosts installed the earlier key fix plan/index.yaml#L62096-L62099esme and pirria just installed v56.9.2.1
  43. 43State root is persistent and explicitly shared between consumers images/default/ca-path.txt#L1-L83${HOME}/.local/state/tillandsias
  44. 44The clamp script runs on every cycle preflight, best-effort scripts/cycle-preflight.sh#L394-L394bash "$ROOT/scripts/clamp-ca-material.sh" --fix >/dev/null 2>&1 || true
  45. 45Filed packet: make the CA directory private by construction and retire the clamp plan/index.yaml#L28430-L28430scripts/clamp-ca-material.sh gains a retirement condition or is deleted
  46. 46The authenticated push lane is gated on an environment variable that defaults to off crates/tillandsias-headless/src/main.rs#L10473-L10475std::env::var("TILLANDSIAS_MIRROR_SSHD") .map(|v| v == "1") .unwrap_or(false)
  47. 47The mirror's sshd starts only behind that flag images/git/entrypoint.sh#L440-L444Behind TILLANDSIAS_MIRROR_SSHD=1 until the T11 staged migration flips the default.
  48. 48The relay wires the GitHub credential helper for any HTTPS origin images/git/relay-refs.sh#L154-L183case "$REMOTE_URL" in https://*)
  49. 49The helper answers unconditionally, without reading which host git asked about images/git/git-credential-tillandsias.sh#L34-L37We do not branch on it: this helper is wired per-invocation by the relay for one specific remote, so answering unconditionally is correct
  50. 50The vault container carries an embedded SELinux policy module crates/tillandsias-headless/src/vault_bootstrap.rs#L2160-L2170const VAULT_SELINUX_CIL: &str = include_str!("../../../images/selinux/vault_container.cil");
  51. 51The Windows guest embeds policies for the control daemon and the vault crates/tillandsias-windows-tray/src/wsl_lifecycle.rs#L284-L284const SELINUX_VAULT_TE: &str = include_str!("../../../images/selinux/tillandsias_vault.te");
  52. 52On a rootless Linux host the vault too runs unconfined crates/tillandsias-headless/src/vault_bootstrap.rs#L2199-L2217`label=disable` runs the vault container unconfined on the host
  53. 53The vault launch adds a capability back and omits `--rm` crates/tillandsias-headless/src/vault_bootstrap.rs#L2407-L2416"--cap-drop".into(), "ALL".into(), "--cap-add".into(), "IPC_LOCK".into(),
  54. 54The approved bar-raise on litmus quality is diff-scoped by construction plan/index.yaml#L14790-L14790the check is DIFF-SCOPED BY CONSTRUCTION (examines added steps in the outgoing change only) and is structurally incapable of flagging the existing corpus
  55. 55Serialising a launch now refuses an argv that violates the envelope, in every build profile crates/tillandsias-podman/src/container_spec.rs#L393-L397crate::policy::validate_launch_argv(&argv)?; Ok(argv)
  56. 56The attached forge launch refuses on a hardening violation crates/tillandsias-headless/src/main.rs#L12137-L12137"refusing to launch {container_name}: hardening envelope violation: {err}"
  57. 57The delegated launch path refuses too crates/tillandsias-headless/src/main.rs#L11965-L11965[security] refusing to launch delegated {container_name}: {err}
  58. 58The rewritten litmus derives its launch flags from the product's declared envelope openspec/litmus-tests/litmus-podman-idiomatic-security-flags.yaml#L23-L25So the launch argv is now DERIVED from the product's own declared envelope, `MANDATORY_HARDENING_FLAGS` in crates/tillandsias-podman/src/policy.rs
  59. 59The ninth step, cleanup, still prints its token unconditionally openspec/litmus-tests/litmus-podman-idiomatic-security-flags.yaml#L131-L134expected_behavior: "CLEANED"
  60. 60A second scanner refuses litmus steps that print the same token on both branches scripts/check-litmus-steps-can-fail.sh#L5-L6Refuse a litmus step whose success and failure branches print the SAME token, because such a step passes whether the system works or not.
  61. 61The build gate fails on it build.sh#L3858-L3858a litmus step prints the same token on success and failure
  62. 62The second scanner refuses only the identical-token shape, by design scripts/check-litmus-steps-can-fail.sh#L26-L30Only identical tokens are refused, because only then is the branch a decoration.
  63. 63Private flags initialized on; serialization remains conditional crates/tillandsias-podman/src/container_spec.rs#L85-L130userns_keep_id: true, cap_drop_all: true, no_new_privileges: true, label_disable: true,
  64. 64A host-browser container still launches through the unchecked serialiser crates/tillandsias-headless/src/main.rs#L13318-L13318let args = spec.build_run_args();
  65. 65The repo's own admission that enforcement depends on call order crates/tillandsias-headless/src/main.rs#L11946-L11948An invariant that depends on call order is one refactor from being false, and this one decides whether a container runs unhardened.
  66. 66The one shared reader of the flag: a blank value is refused crates/tillandsias-control-wire/src/secure_wire_mode.rs#L85-L126is set but empty. Blank does NOT mean
  67. 67Windows tray uses shared mode reader and refuses bad values crates/tillandsias-windows-tray/src/hvsocket.rs#L74-L87let mode = match tillandsias_control_wire::secure_wire_mode::secure_wire_mode() { Ok(mode) => mode, Err(err) => return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, err)), };
  68. 68Default flip history and zero-reader ratchet scripts/check-secure-wire-single-reader.sh#L22-L40BASELINE REACHED 0 AND THE FLIP LANDED (commit B).
  69. 69Test pins the encrypted default through the shared parser crates/tillandsias-control-wire/src/secure_wire_mode.rs#L159-L165parse_secure_wire_mode(Err(VarError::NotPresent)).unwrap(), SecureWireMode::On,
  70. 70The Windows guest's service unit carries no secure-wire line crates/tillandsias-windows-tray/src/wsl_lifecycle.rs#L1852-L1853Environment=TILLANDSIAS_VAULT_API_BASE_URL=https://vault:8200 {low_power_env}ExecStart=/usr/local/bin/tillandsias-headless --listen-vsock 42420
  71. 71The VM image's default service unit carries no environment at all images/vm/bootstrap/20-tillandsias.sh#L92-L104ExecStart=/usr/local/bin/tillandsias-headless --listen-vsock 42420
  72. 72The Linux lane asserts transitive coverage as a property over what is staged .github/workflows/release.yml#L289-L293This asserts the PROPERTY over whatever is actually staged, so an asset added later cannot slip through unverifiable just because nobody added a line for it.
  73. 73The macOS lane signs an allow-list of three assets, and its checksum manifest is not among them .github/workflows/release.yml#L500-L517for artifact in tillandsias-tray-*-macos-arm64.tar.gz Tillandsias.dmg install-macos.sh; do
  74. 74The Windows lane's coverage check, why it follows the transitive path, and the allow-list it blames for an installer shipping bare .github/workflows/release.yml#L742-L772The check follows the TRANSITIVE path rather than counting unsigned files
  75. 75The Linux installer fetches the manifest over the same channel and continues without it scripts/install.sh#L226-L239say "SHA256SUMS not available; skipping checksum verification."
  76. 76The Windows installer compares a hash from the fetched manifest only scripts/install-windows.ps1#L370-L399Say "Verifying SHA-256..."
  77. 77The macOS installer likewise scripts/install-macos.sh#L106-L128curl -fsSL "$SHA_URL" -o "$TMP/SHA256SUMS-macos"
  78. 78The shipped verifier uses the same repository-name-only regexp scripts/verify.sh#L24-L25CERTIFICATE_IDENTITY_REGEXP="https://github.com/.*/tillandsias/"
  79. 79The in-app updater's spec: artifact selection, no verification requirement openspec/specs/update-system/spec.md#L1-L53The updater SHALL select the correct artifact for the current platform and handle platform-specific execution constraints.
  80. 80Sigstore/in-toto provenance studied for another channel plan/issues/homebrew-harness-distribution-research-2026-07-11.md#L27-L31every bottle built by Homebrew CI carries a **Sigstore/in-toto build-provenance attestation** (SLSA Build L2)
  81. 81The one-command reset deletes only the vault's storage directory on the host side crates/tillandsias-headless/src/main.rs#L8760-L8761Host-side directories the reset deletes under the init cache dir. ONLY `vault-data` (the vault storage backend)
  82. 82Its credential-discard step removes podman secrets; no keychain call in the function crates/tillandsias-headless/src/main.rs#L8844-L8845Secrets (vault unseal share + TLS material, CA, github token) — the credential-discard half of the ephemeral doctrine.
  83. 83Where no keyring is available the share is written to a fallback file in the cache directory crates/tillandsias-headless/src/vault_bootstrap.rs#L2903-L2925using fallback file (expected in VM guest and headless environments)
  84. 84Inside a VM guest both fallback writes are always attempted crates/tillandsias-headless/src/vault_bootstrap.rs#L1290-L1316BOTH writes are always ATTEMPTED — a token failure must never skip the share, because the share is the half that arms the wipe.
  85. 85The "share present in keychain" check falls through to the file crates/tillandsias-headless/src/vault_bootstrap.rs#L1345-L1372Fallback: file (populated by keychain_set_blocking when keyring unavailable, e.g. in a VM guest or headless environment without D-Bus)
  86. 86The preserve guard and the log line the smoke runs observed crates/tillandsias-headless/src/vault_bootstrap.rs#L2284-L2304[tillandsias-vault] preserving existing data volume (Shamir share present in keychain)
  87. 87Spec: every persistent on-disk copy of the share is deleted immediately openspec/specs/tillandsias-vault/spec.md#L101-L101delete all persistent on-disk copies immediately
  88. 88The open packet: make the Linux reset credential-cold, or stop claiming it is plan/index.yaml#L42956-L42956the Linux reset is credential-cold, or the runbook stops claiming it is
  89. 89The third-host reproduction, recorded 2026-08-31 plan/index.yaml#L43064-L43064Reproduced on a THIRD host during the v0.4.260830.5 curl-install smoke
  90. 90The Podman client runs whatever argv it is handed, with no policy call crates/tillandsias-podman/src/client.rs#L1008-L1011let mut full_args = vec!["run".to_string()]; full_args.extend_from_slice(args); match self.execute(OperationKind::Container, &full_args).await {
  91. 91Why the CA directory moved: /tmp is volatile by design and swept on reboot images/default/ca-path.txt#L36-L38/tmp works everywhere and is the bug — volatile by design, cleared on reboot and swept by systemd-tmpfiles
  92. 92Why label=disable: no `:z`/`:Z` relabelling, so bind mounts work under SELinux hosts openspec/specs/podman-orchestration/spec.md#L110-L110no `:z` or `:Z` suffix is needed because `--security-opt=label=disable` disables SELinux confinement for the container process
  93. 93The macOS host writes the secure-wire flag into the guest's service unit crates/tillandsias-vm-layer/src/vz.rs#L1067-L1067Environment=TILLANDSIAS_SECURE_CONTROL_WIRE=__SECURE_CONTROL_WIRE__
  94. 94Why the flag is applied — SELinux relabelling would otherwise break the bind mount docs/audits/browser-isolation-audit-2026-04-30.md#L58| `--security-opt=label=disable` | ✅ | Required for Silverblue bind mounts |
  95. 95The observatorium web launch serialises without the policy call crates/tillandsias-headless/src/main.rs#L11492-L11492.build_run_args()
  96. 96The proxy launch is assembled as a raw argv the checker never sees crates/tillandsias-headless/src/main.rs#L3431-L3431fn build_proxy_run_args(certs_dir: &Path, image: &str) -> Vec<String> {
  97. 97The mirror launch is assembled the same way crates/tillandsias-headless/src/main.rs#L4301-L4301fn build_git_run_args(
  98. 98What the checker refuses beside a missing flag crates/tillandsias-podman/src/policy.rs#L189-L193if arg == "--privileged" || arg.starts_with("--privileged=") || flag_value(arg, next, "--userns").is_some_and(|value| value != "keep-id") || flag_value(arg, next, "--cap-add").is_some_and(|value| value == "ALL")
  99. 99The release notes' own account of a red tree landing under a green stamp, and the scope claimed for the fix README.md#L122-L122A green gate now issues a one-shot pass token naming the tree it validated, consumed on use; the incident replays verbatim as a test arm, and peeling the onion found three more bare writes (release-gates 20/20). Claimed scope: unforgeable by accident, not by intent.
  100. 100Ownership probe distinguishes keep-id from misleading inspect labels openspec/litmus-tests/litmus-podman-idiomatic-security-flags.yaml#L41-L64The bind-mounted file discriminates — 1000 with keep-id, 0 without
  101. 101Capability probe ignores command exit status before declaring success openspec/litmus-tests/litmus-podman-idiomatic-security-flags.yaml#L111-L114expected_behavior: "CAPABILITIES_DROPPED"
  102. 102CA directory creation uses ordinary create_dir_all, followed by refresh decision crates/tillandsias-headless/src/main.rs#L3131-L3138std::fs::create_dir_all(&certs_dir) .map_err(|e| format!("Failed to create CA directory: {e}"))?;
  103. 103Host derives the control key from the build-time guest digest crates/tillandsias-secure-channel/src/lib.rs#L157-L182derive_psk(guest_binary_sha256, build_version, wire_version, hop)
  104. 104Why single-process tests could not reveal the host/guest digest mismatch crates/tillandsias-secure-channel/src/lib.rs#L205-L228The only integration proof is two DISTINCT binaries: a release-built tray and the release guest reaching Ready on a real cold provision.
  105. 105Windows refuses an absent embedded guest digest crates/tillandsias-windows-tray/src/hvsocket.rs#L25-L43EMBEDDED_GUEST_SHA256.as_ref().ok_or_else(|| {
  106. 106Windows handshake negative control rejects a different guest digest crates/tillandsias-windows-tray/src/hvsocket.rs#L525-L610a_guest_digest_mismatch_is_refused_not_carried
  107. 107Local acceptance follows upstream acceptance, so a reported success is a durable one images/git/pre-receive-hook.sh#L6-L8Validates ledger YAML, then synchronously relays the proposed ref transaction upstream before accepting it locally. A client success therefore means the configured upstream has durably accepted the same atomic ref set.
  108. 108The entry tracking every shortcoming here for which no remedy is recorded github.com/8007342/tillandsias/blob/linux-next/plan/index.d/20260915t215254z-1213-rbt9-website-found-fourteen-shortcomings-the-ledger-does-not-track-macuahuitl.yaml

And you would like me to be condescending about it. Very well.Assumes everything before it. Mathematics from here down.

Level 5 — For the MathWiz / Hacker

A monocot bromeliad flowers once and dies, leaving offsets behind — the pup is never the parent.

The lower levels gave you the object and its defects. What is left is the only part that can be attacked with a pencil: the formal content, stated as claims with hypotheses, sorted into (a) standard theorems being invoked, (b) what the repository actually asserts, (c) what follows, and (d) what does not. The security findings of level 4 are not repeated; they enter here only where a mathematical hypothesis depends on them.

The short verdict: the theory is small, finite, and unusually candid about the theorems it declines to claim. Its model now has generative property tests and stable requirement identifiers. Connecting those checks to live validation and justifying dependent iteration remain separate obligations.

The lattice, and the one step worth checking

An obligation is a named, auditable requirement, invariant, litmus signal, trace signal, provenance binding, or environment assumption carrying a stable identifier1. Its state ranges over a finite chain, seven long2:

\[\mathcal{O} = \{\,\texttt{absent} < \texttt{declared} < \texttt{traced} < \texttt{pos\_tested} < \texttt{neg\_tested} < \texttt{runtime\_obs} < \texttt{evidence\_bundled}\,\}\]
\[S_{\text{spec}} = \prod_{i \in \mathrm{Obl}} \mathcal{O}_i, \qquad S_{\text{proj}} = \prod_{s} S_{\text{spec}}(s), \qquad x \le y \iff \forall i,\; x_i \le_{\mathcal{O}} y_i\]

Fix the convention, since authors differ: the height of a finite poset is the maximum number of strict order steps in a chain — so a chain of \(n\) elements has height \(n-1\), and the one-element poset has height \(0\).

Proposition 1. \(S_{\text{proj}}\) is a finite complete lattice of height \(6|\mathrm{Obl}|\). Each \(\mathcal{O}_i\) is a finite chain, hence a complete lattice; a finite product of complete lattices is complete with \(\vee,\wedge\) computed componentwise; a chain of seven elements has height six and heights add over products3. Nothing here is in dispute — it is the least interesting true thing in the file, and the repository correctly declines to inflate it: "this proves monotonicity of the model, not truth of the modeled requirement or adequacy of the chosen obligation set"4.

Two hypotheses do quiet work. The order is componentwise after aligning stable IDs and tombstones1, so a renamed coordinate yields a different lattice, not a lower point in the same one; and \(|\mathrm{Obl}|\) must be finite and known, which is what makes the height bound a bound rather than an ordinal.

absentdeclared tracedtested + tested −observed less evidence → more evidence, one direction only going backwards is not a bug report, it is a rejected merge
Evidence only ever climbs. A claim may not descend a rung without an explicit, recorded scope change.
⊤ both fully evidenced ⊥ nothing evidenced joins
Two obligations, each a chain of three states, give a nine-element product lattice ordered componentwise. Real specs multiply many more chains than two.

The fixed point, in full

Let \(\mathrm{refine}: S \to S\) be validation followed by semantic distillation at a fixed bar \(B\). The repository's stated claim is idempotence5:

\[\mathrm{refine}(\mathrm{refine}(x)) = \mathrm{refine}(x)\]

Theorem 2 (Knaster–Tarski). A monotone map on a complete lattice has a complete lattice of fixed points, in particular a least one6. Applied here it gives existence, non-constructively, and says nothing about how to reach it.

Theorem 3 (finite monotone inflationary iteration). If \(f: L \to L\) is monotone and inflationary (\(x \le f(x)\)) on a finite lattice of height \(h\), then for every \(x\) the chain \(x \le f(x) \le f^2(x) \le \cdots\) stabilises after at most \(h\) strict increases, and its limit is the least fixed point \(\ge x\). The bound is immediate: each strict step rises at least one level in a poset of height \(h\). Leastness is a two-line argument worth doing, because it is the step people skip: if \(y = f(y)\) and \(y \ge x\), then monotonicity gives \(f^n(x) \le f^n(y) = y\) for every \(n\), so the limit is \(\le y\).

Note where Kleene's theorem proper — least fixed point as \(\bigsqcup_n f^n(\bot)\) for a Scott-continuous \(f\) on a CPO7 — would otherwise be invoked outside its hypotheses. Continuity is not assumed, it is free: in a finite poset every directed subset is finite and contains its own supremum, so every monotone map is Scott-continuous. That is the first place a referee probes, and it survives.

more less applications of the refinement operator lattice top — never reached, and need not be refine(refine(x)) = refine(x) the chain stops moving
Monotone refinement over a finite lattice cannot ascend forever. It stabilises — and the step where it stops is what “done” means. Provided refinement really is monotone.

So "done" is a computable predicate — \(x\) is closed iff \(\mathrm{refine}(x) = x\) — and governance supplies the hypothesis that makes it non-vacuous: the bar \(B_v\) is fixed within a release, rising only by explicit operator decision, with the automation forbidden from self-escalating it8. A continuously rising bar makes \(\mathrm{refine}\) a moving operator and destroys stabilisation; discrete, externally gated raises preserve it. That is the standing hypothesis of Theorem 3, not decoration.

And now the gap. Theorem 3 needs monotone and inflationary; the repository asserts idempotent. These are the three axioms of a closure operator, and the repository states one of them. Idempotence does not imply monotonicity: a map that sends \(a \mapsto \top\), \(b \mapsto b\) with \(a < b\) is idempotent and not monotone. Nor does it imply inflationarity. The validation program that would discharge the missing two is written down — enumerate the allowed transitions and property-test that each is order-preserving for every affected obligation9 — which is the right shape of obligation. That program now has executable tests over a committed rule set; it does not establish the same properties for every live validator41.

shortcoming The refinement model has generative tests for monotonicity, inflationarity and idempotence, but they exercise a committed rule set rather than every live validator41. They establish a model boundary, not semantic correctness of the running system.

path to green The staged validation program names the remaining obligation: enumerate permitted transitions and check every affected coordinate, treating tombstones and scope changes separately9.

verified Requirement identifiers now exist and a gate checks presence and uniqueness, including tombstoned specifications42. Whether an edit preserves meaning and should keep its identifier remains author judgement; the guard cannot decide that. The evidence-credit table still lists stable identifiers as a scoring input10, which by itself does not establish that credit is computed.

verified The negative results — no contraction, no Galois connection, no probabilities — are first-class claims carrying their own discharge conditions, not caveats appended to positive ones. Each names the object a future claim would have to construct.

The moving target, and what would force the floor to zero

Write \(T_v\) for the specifications at version \(v\), \(I_v\) for the implementation, \(d_v = d(I_v, T_v) \ge 0\). Because the specifications evolve, \(T\) moves and no final target exists11. The mechanically checkable release rule is non-increase.

Proposition 4 (monotone convergence). A non-increasing real sequence bounded below converges to its infimum12. Here:

\[d_{v+1} \le d_v \;\text{ for all } v, \quad d_v \ge 0 \qquad\Longrightarrow\qquad d_v \;\longrightarrow\; d_* \;=\; \inf_{v} d_v \;\ge\; 0\]

That is the whole result, and it is correct.

What does not follow is \(d_* = 0\), and the repository says so in those words11. The exact extra hypothesis is a uniform progress premise, in either of two forms:

\[\exists\,\varepsilon > 0 : \; d_v > 0 \implies d_{v+1} \le d_v - \varepsilon \qquad\text{or}\qquad \exists\,\theta < 1 : \; d_{v+1} \le \theta\, d_v\]

Either excludes positive residual fixed points and forces \(d_* = 0\) — the additive form in finitely many steps, the multiplicative one geometrically. The repository's phrase — "a validated progress premise that excludes positive residual fixed points"11 — is precisely this, asserted as a requirement on a future claim rather than as a fact.

far close successive releases — each one re-raises the bar the floor — possibly not zero zero would mean “finished”. No proof it is reachable.
Schematic: if comparable residuals never rise, they approach a floor, and nobody has proven that floor is zero.

The Banach alternative, itemised. A contraction claim13 would owe a complete metric space \((X,d)\), an operator \(F: X \to X\), and a constant \(0 \le c < 1\) with

\[d\big(F(x),F(y)\big) \le c\,d(x,y) \quad \forall x,y \in X \qquad\Longrightarrow\qquad \exists!\,x^* = F(x^*), \quad d\big(F^n(x),x^*\big) \le \frac{c^n}{1-c}\,d\big(x,F(x)\big)\]

Missing: (i) a metric, (ii) completeness, (iii) any bound on \(c\). The repository lists all three14. The binding one is (i), and it is worse than "unproven". \(d\) as used is not a function on \(X \times X\) at all — it is only ever evaluated at the single pair \((I_v, T_v)\), giving a sequence of numbers indexed by release, not a binary function on a set. Ask which axiom fails and the honest answers are:

\[\underbrace{d(x,y) = d(y,x)}_{\text{undefined}}, \quad \underbrace{d(x,z) \le d(x,y) + d(y,z)}_{\text{undefined}}, \quad \underbrace{d(x,y) = 0 \implies x = y}_{\text{false on the natural candidate}}\]

The first two are undefined rather than false, because \(d(x,y)\) for two arbitrary project states is never given a value; identity of indiscernibles fails outright, since two materially different implementations can close the same obligation set and score residual zero. Absent (i), items (ii) and (iii) are not merely unproven — they are not well-posed. Until a metric exists, "monotonic convergence" means ordered non-regression plus finite residual descent, which is what the file claims and no more14.

Be careful about the scope of that verdict, because it is easy to overstate and I will not. Everything above concerns the \(d\) the repository actually documents. It is not an argument that no suitable metric exists on project states, and nothing here forecloses one: a genuine metric — with a completion, and a contraction constant — may well be constructible, and if it were, the Banach route would reopen exactly as written. The claim is the narrow one. The contraction argument cannot be discharged by the object currently on offer, and the work of building a better one is open rather than blocked.

There is a further boundary. Under a fixed denominator, a non-increasing integer residual is eventually constant; strict progress while positive would force zero. Changing the obligation scope breaks that comparison. A ratio alone cannot repair it: adding easy obligations can improve the percentage without closing an old gap.

shortcoming The scorer marks a tombstone as a broken monotone regime, and the shell reports that regime4445. This is useful local instrumentation, but does not establish a cross-release comparison for arbitrary changes of weights or obligation scope.

path to green The validation program requires denominator changes to be reported separately9. A complete cross-release comparison must align the obligation set and weights; no general normalisation rule is established by the cited implementation.

A repository refinement is a model, not a theorem

Here is the engineering picture in a form precise enough to attack. Let \(R_k\) be the repository state visible to a task, \(p_k\) its prompt and \(C_k\) the selected context built from source, rules, retrieved material and task state. A model proposes a delta \(Delta_k\); review and validators decide whether to commit it:

\[C_k = \operatorname{select}(R_k,p_k), \qquad \Delta_k = M(C_k), \qquad R_{k+1} = \operatorname{accept}(R_k,\Delta_k).\]

"Vectors" is a useful picture of the representations the model constructs from \(C_k\); it is not Git's data model. A commit can retain reachable history while its new tree replaces or deletes files. Nor is an accepted patch automatically an improvement. The acceptance step is where tests, review and evidence enter; the finite obligation model says exactly what those validators can record, and no more.140

local tasks: source + prompt + checks → committed delta accessibilityproduct copydashboard local targetlocal targetlocal target shared residual shared target: specifications, aligned before comparison a checked commit preserves history; it does not prove every line improved
A schematic engineering model: each checked change can move one local residual, while the shared record is judged against aligned specifications. It is not a probability measurement or a convergence proof.

For a workstream \(i\) with a fixed specification \(T_i\), write \(d_i(R_k,T_i)\) for its declared, measured residual. Accessibility and language, marketing, dashboards and customer support may each have their own \(d_i\). A shared residual is meaningful only after the obligation identities, weights and scope have been aligned; tombstoning already marks the shipped scorer's comparison as broken.4445 Thus the diagram's local traces are engineering telemetry, not samples from a common distribution.

plausible Repeated small, checked refinements can reduce observed local residuals faster than one large unreviewed change, while commits preserve an inspectable history of accepted evidence. This is a useful operating method, not a proof that the residual reaches zero.

The tempting probabilistic upgrade is to treat \(Delta_k\) as one random refinement with a finite-step skew, then invoke a weak or strong law over many refinements. That argument needs a stochastic model of the quality functional, a target mean and conditions on the joint process. Large context windows, high-dimensional embeddings and a fresh prompt do not supply independence. The next input contains \(R_{k+1}\), which was made using the previous output; this is feedback, not a negligible detail. The possible formal routes are the ones below: establish a dependence structure that supports an ergodic or martingale theorem, or leave the claim empirical.161819

path to green Record each target, its scope and its negative controls; compare only aligned measurements; and retain counterexamples and retractions as first-class outcomes.940

Iteration: the law of large numbers at full strength

The methodology now withdraws its strong-law citation for dependent iterations16. The mathematics below explains why that correction matters. Set it up properly. Let \((X_k)_{k\ge1}\) be random elements on a probability space, \(X_k\) the value of a real quality functional on iteration \(k\), with \(\mathbb{E}|X_k| < \infty\) and \(\mathbb{E}[X_k] = \mu + b_k\), where \(b_k\) is the per-iteration skew. Write \(\bar{X}_N = N^{-1}\sum_{k \le N} X_k\) and \(\bar b = \lim_N N^{-1}\sum_{k\le N} b_k\) when that limit exists.

Theorem 5 (Kolmogorov's SLLN). Under independence, identical distribution, and integrability17:

\[X_k \text{ i.i.d.}, \;\; \mathbb{E}|X_1| < \infty \qquad\Longrightarrow\qquad \bar X_N \;\xrightarrow{\;\text{a.s.}\;}\; \mathbb{E}X_1\]

The iterations here are not identically distributed, so a candidate generalisation is Kolmogorov's variance criterion, which drops identical distribution and keeps independence18:

\[X_k \text{ independent}, \;\; \sum_{k\ge1} \frac{\mathrm{Var}(X_k)}{k^2} < \infty \qquad\Longrightarrow\qquad \bar X_N - \frac{1}{N}\sum_{k \le N} \mathbb{E}X_k \;\xrightarrow{\;\text{a.s.}\;}\; 0\]
\[\text{hence} \qquad \bar X_N \;\xrightarrow{\;\text{a.s.}\;}\; \mu + \bar b, \qquad \bar b = \lim_{N} \frac{1}{N}\sum_{k \le N} b_k\]

Same family as the three-series theorem; independence and the summable-variance condition are the whole price of admission.

Weak versus strong is convergence in probability versus almost-sure convergence; almost-sure convergence implies convergence in probability, but the converse need not hold. Under the criterion above and existence of the mean-bias limit, bounded bias bounds the limiting error. Bounded bias alone does not imply convergence, and unbounded skew does not by itself prove divergence. The methodology retains small, fast iterations as engineering guidance while withdrawing the almost-sure claim16.

one big slow iteration many small fast iterations truth truth lands off-target, and you cannot see by how much an illustrative running average no convergence theorem established here Bounded bias alone does not make the average reach the truth.
Schematic only: averaging can reduce variation under suitable hypotheses. Dependent iterations and persistent bias need a separate argument; this diagram is not evidence.

The objection the methodology now acknowledges is the load-bearing one. Every form of Theorem 5 quoted above requires independence. Iterations of an agent that reads its own prior output are not independent: \(X_{k+1}\) is a measurable function of \(X_k\) and the accumulated context. The architecture makes the dependence explicit and deliberate — retrieval is a cache of converged knowledge, updated on commits, with commits acting as a Lamport clock ordering the corpus against the code19. That is a feedback loop by construction, and it invalidates every independence hypothesis in the section above.

Two possible substitutes remain unestablished. Either the sequence is stationary and ergodic and one invokes Birkhoff's pointwise ergodic theorem20 — but stationarity is implausible for a process whose corpus grows monotonically, and ergodicity is exactly what fails if the chain can be absorbed. Or the increments form a martingale difference sequence with respect to the natural filtration \(\mathcal{F}_k = \sigma(X_1,\dots,X_k)\), giving an \(L^2\) or Azuma-type law — but that requires

\[\mathbb{E}\big[X_{k+1} \mid \mathcal{F}_k\big] = \mu \quad \text{a.s. for all } k\]

which is precisely the assertion that yesterday's output does not bias today's, and the cache design says it does. Under positive feedback an absorbing state is available: the stream converges almost surely to whatever the loop locked onto, and \(\bar X_N\) converges beautifully to a number with no relation to \(\mu\). Bounding per-prompt skew does not repair this; it is a hypothesis about marginals, and the failure is in the joint.

Be precise about what this objection kills. It does not show the iteration scheme is wrong, and it does not show the stream fails to converge — small fast iterations may well be the right engineering, and dependent sequences converge under plenty of other hypotheses. What it kills is the invocation of the strong law as stated: the theorem named is being applied to a sequence that does not satisfy its independence hypothesis, so the almost-sure conclusion is unearned rather than false. The repair is cheap in words and real in work — name the dependence structure, then cite the theorem that covers it.

verified The methodology explicitly withdraws the strong-law citation because the cache makes successive iterations dependent16. It names ergodic and martingale hypotheses as work needed for a future theorem, and presents the iteration practice as an empirical track record rather than an almost-sure guarantee.

What the repository renounces, and what that costs

No Galois connection. Specs and scores are described as abstractions that deliberately forget detail — the Cousot framing21 — and the file concedes that no adjunction is defined between program semantics and spec obligations22. State the missing object precisely. With \(C\) the lattice of sets of concrete executions and \(A\) the obligation lattice, a Galois connection \(\alpha \dashv \gamma\) requires monotone \(\alpha: C \to A\), \(\gamma: A \to C\) with

\[\alpha(c) \sqsubseteq a \iff c \sqsubseteq \gamma(a) \qquad\text{equivalently}\qquad c \sqsubseteq \gamma(\alpha(c)) \;\text{ and }\; \alpha(\gamma(a)) \sqsubseteq a\]
concrete: program behaviours abstract: obligation states α γ α(c) ⊑ a ⇔ c ⊑ γ(a) — not established
Without the adjunction, a closed obligation does not transport back to a statement about the program. The arrows exist; the law relating them is not proven.

What is forfeited is the transport theorem, and only that — but that is everything. Given the adjunction and a sound abstract operator (\(\alpha \circ F \sqsubseteq F^\# \circ \alpha\)), one gets \(\alpha(\mathrm{lfp}\,F) \sqsubseteq \mathrm{lfp}\,F^\#\): a fixed point computed in the abstract over-approximates the concrete, so a property proved upstairs holds of every concrete execution downstairs. Without it, closure in the obligation lattice implies precisely nothing about the program. It is bookkeeping over evidence, and is labelled as such.

A ranking function, not a measure. The CentiColon map \(c: S_{\text{spec}} \to \mathbb{N}\) is bounded, with separately reported denominator and residual, and monotone only for transitions preserving obligation IDs and introducing no penalties, ambiguity, or denominator scope change1. That is a Floyd-style ranking function23: a well-founded descent witness into a finite ordinal, of the kind that proves termination.

It is not a measure. There is no \(\sigma\)-algebra on \(S_{\text{spec}}\); \(c\) is not additive, because obligations overlap and sixteen penalties plus six cap rules make the rollup non-linear in its parts15; and the normaliser is policy rather than a fixed total mass. A bounded ranking function into a finite ordinal therefore induces no probability, and the repository says so outright24. What that forecloses is concrete: two scores may not be combined by Bayes; there is no complement rule, so 89% closed is not an 11% chance of a defect; and no calibration statement is available, because there is no event space on which to calibrate. Shafer25 and Walley26 are cited as the layer a confidence model would have to occupy separately — correctly, since a belief function would at least supply the monotone-capacity structure that \(c\) lacks.

That is a statement about \(c\) as defined, and about nothing more. It does not show that no transformation, extension or added structure could turn part of this model into a measure or another probabilistic object — the citation of belief functions is precisely an admission that such a layer is available to be built. What is claimed is that \(c\) does not already carry that structure, so probabilistic language applied to today's scores is unlicensed. Building the layer that would license it is open work, and the repository files it as such.

shortcoming The shell scorer now delegates arithmetic to the obligation model43, but still supplies weights over CI check names27. A passing check earns its weight at the positive-test bar; it is not runtime observation or bundled evidence. This wiring does not implement the full methodology table of multipliers, evidence credits, caps and penalties15. The committed dashboard remains a historical 890/990 snapshot28, not a measurement of this release.

path to green The framework specification still names a separate scoring implementation29. The shipped path centralises the arithmetic; completing and evidencing the richer scoring contract remains distinct work.

shortcoming The methodology's own complexity constraint — methodology-to-codebase ratio below 0.15, with a red flag at 5000 lines of CI validators30 — has no enforcing check identified in this audit, while the script that computes the score dispatches a large shell validator corpus.

path to green The rule names two measurement procedures30; no enforcing implementation was found in the scripts searched.

CRDTs as algebra

The plan ledger is a genuine convergent replicated data type. State is \(\text{base} \oplus \mathrm{fold}(\text{fragments})\); the carriers are two join-semilattices31: grow-only sets of packets and of events keyed by \((\text{packet id}, \text{event identity})\), with \(x \sqcup y = x \cup y\); and last-writer-wins registers per \((\text{packet id}, \text{field})\), the winner chosen by the total order \((\text{timestamp}, \text{host})\).

\[\text{state} = \Big(\bigcup_i P_i,\; \bigcup_i E_i,\; \textstyle\bigsqcup_i R_i\Big), \qquad \bigsqcup \text{ componentwise}\]

The algebra: a commutative, associative, idempotent \(\sqcup\) induces a partial order \(x \le y \iff x \sqcup y = y\) under which \(x \sqcup y = \sup\{x,y\}\) — the join is the least upper bound, which is why the three properties are a definition rather than a checklist. Convergence follows because the fold is then a function of the set of delivered updates, not their sequence: idempotence kills duplicates, commutativity and associativity kill order.

agent A — offline agent B — offline a new factanother fact a third facta fourth fold — order does not matter set union preserves facts; registers choose a winning value
With the same delivered facts and valid merge rules, replicas agree regardless of arrival order. This illustration shows set union, not every field in the ledger.

State exactly what that buys, because it is strictly less than the folklore suggests32. It gives: replicas having delivered the same update set hold equal state. It does not give delivery — liveness is someone else's problem; it does not give correctness of the converged value; and it does not preserve concurrent intent, which is where LWW does its damage. Applying LWW to a list silently discards the loser's entries, which is why events are a set and not a register — a distinction the file calls the whole correctness argument31. Determinism is pinned by folding in \((\text{timestamp}, \text{filename})\) order rather than directory order33, and two of the three properties — commutativity and idempotence — are tested by name34.

The status field is not a plain register: it composes a monotone join over a closure rank with LWW as tiebreak at equal rank, treats terminal-but-lateral values as non-ladder moves, and permits descent only under an explicit falsification flag35 — a lexicographic join with a deliberate non-monotone escape hatch, so that monotonicity cannot force retention of a certainty later shown false.

verified The repository retracts its own CRDT claim where it fails, and names the property that failed: a deduplication routine's header states plainly that it is a seen-set dedup, not a CRDT, because first-wins keeps whichever duplicate arrives first, so commutativity is false36. Most projects keep the word.

verified Componentwise maximum gives version tuples a join-semilattice. The document uses that algebra to motivate mergeable version coordinates37. Its claim that SemVer has no natural total order is too broad: version precedence and preservation of branch causality are different properties.

verified Semantic merges are typed honestly as "semantic cache with CRDT preconditions" rather than as CRDTs, with the anti-pattern named: calling a lossy cache a CRDT manufactures false convergence claims32. The claim registry holds the lineage at strength "external analogy" until generative property tests exist38.

shortcoming That same versioning document defines its two leading components as a contract version and a feature phase39. The live scheme is a temporal anchor throughout — years since epoch, month, day, build. The join algebra survives untouched, since componentwise max does not care what the coordinates mean; the documented semantics of two of four components are false.

path to green The drift was caught once at a release boundary and the shape test corrected; the doctrine file was not.

Verdict

The formal core is a finite product lattice, a finite monotone-inflationary stabilisation under declared validators at an operator-gated bar, a Floyd-style ranking function, and a correctly stated moving-target result whose zero-floor corollary is declined for want of a metric. Contraction, Galois connections and probabilistic readings are named absent rather than assumed40. As a thesis position that is defensible, and I did not expect to write that sentence.

Two boundaries remain. Generative tests cover the committed refinement model, not the semantic correctness of all live validators. And dependent iteration still lacks a convergence theorem, although the methodology now retracts the unsupported strong-law claim. Stable identifiers and centralised score arithmetic make the bookkeeping more concrete; they do not turn closure into probability or a dashboard snapshot into release evidence.

Footnotes

Every link points at release v56.9.12.2 of the source repository, so line numbers match the text above; a link marked with its own release tag points at that newer release instead. A footnote number in the text opens its source in a new tab; hover it for the quoted lines.

  1. 1Formal objects — obligation, the state chain, product lattices, and the ranking function's monotonicity conditions methodology/math-foundations.yaml#L13-L43A named, auditable requirement, invariant, litmus signal, trace signal, provenance binding, or environment assumption with a stable ID.
  2. 2Stated purpose — separating defensible mathematics from analogy methodology/math-foundations.yaml#L4-L11Separate the math the methodology can defend from analogies it must not overclaim.
  3. 3Davey & Priestley, *Introduction to Lattices and Order*, 2nd ed. — product orders, heights, completeness doi.org/10.1017/CBO9780511809088
  4. 4The lattice-model claim and its stated limit methodology/math-foundations.yaml#L46-L59This proves monotonicity of the model, not truth of the modeled requirement or adequacy of the chosen obligation set.
  5. 5The fixed-point claim: idempotence of refine, and its limit methodology/math-foundations.yaml#L61-L74Define the refinement operator over a finite artifact snapshot and check idempotence: refine(refine(state)) == refine(state).
  6. 6Tarski, *A lattice-theoretical fixpoint theorem and its applications*, Pacific J. Math. 5 (1955) — the Knaster–Tarski theorem, generalising Knaster's 1928 powerset case doi.org/10.2140/pjm.1955.5.285
  7. 7Kleene, *Introduction to Metamathematics* (1952), cited for the iterative least-fixed-point construction archive.org/details/introductiontome00klee
  8. 8Bar-raise governance — the bar is fixed within a release, rises only by operator decision, and the automation must not self-escalate methodology/convergence.yaml#L410-L432is a one-off scope expansion that The Tlatoāni MUST approve every time. Recurring automation (the meta-orchestration loop) MUST NOT self-escalate the bar.
  9. 9The staged validation program: the monotonicity property tests and the denominator scope-change check methodology/math-foundations.yaml#L175-L198- allowed_evidence_transitions_are_monotone - tombstone_and_scope_change_transitions_are_explicitly_non_monotone
  10. 10The evidence-credit table containing `requirement_has_stable_id` methodology/proximity.yaml#L88-L97requirement_has_stable_id: 0.10
  11. 11Multi-version convergence: the moving target, the residual floor, and the refusal of the zero-floor claim methodology/philosophy.yaml#L134-L137Release-over-release non-increase of d_v guarantees convergence to some residual floor d_* >= 0; it does not by itself prove d_* = 0. A zero-floor claim would additionally require a validated progress premise that excludes positive residual fixed points
  12. 12Rudin, *Principles of Mathematical Analysis*, 3rd ed., Thm. 3.14 — monotone bounded sequences converge archive.org/details/principlesofmath0000rudi
  13. 13Banach, *Sur les opérations dans les ensembles abstraits*, Fund. Math. 3 (1922) doi.org/10.4064/fm-3-1-133-181
  14. 14Contraction explicitly not claimed, with the metric/operator/constant debt itemised methodology/math-foundations.yaml#L108-L120Without that metric proof, "monotonic convergence" means ordered non-regression plus finite residual descent, not metric contraction.
  15. 15The cap rules, the sixteen penalties, and the rollup that makes the score non-additive in its parts methodology/proximity.yaml#L100-L135requirement_cc = weighted_obligation * earned_credit + penalties bounded to [0, weighted_obligation].
  16. 16The strong-law citation withdrawn for dependent iteration methodology/philosophy.yaml#L29-L47So the almost-sure claim is not available as stated and is withdrawn rather than weakened.
  17. 17Kolmogorov, *Grundbegriffe der Wahrscheinlichkeitsrechnung* (1933) — the strong law and its variance criterion doi.org/10.1007/978-3-642-49888-6
  18. 18Durrett, *Probability: Theory and Examples*, 5th ed. — SLLN, Kolmogorov's three-series theorem, and the ergodic and martingale substitutes doi.org/10.1017/9781108591034
  19. 19Retrieval as a cache; commits as the Lamport clock versioning it — the mechanism that makes iterations dependent methodology/philosophy.yaml#L54-L57That cache is updated on COMMITS — a commit hitting the relevant bits retrains the RAG. Commits are therefore the LAMPORT CLOCK of the RAG models: they causally order and version the cached knowledge against the code
  20. 20Birkhoff, *Proof of the ergodic theorem*, PNAS 17 (1931) doi.org/10.1073/pnas.17.2.656
  21. 21Cousot & Cousot, *Abstract Interpretation*, POPL 1977 doi.org/10.1145/512950.512973
  22. 22The concession: no Galois connection is defined — "an abstraction discipline, not a formal abstract interpreter" methodology/math-foundations.yaml#L76-L89Tillandsias does not yet define a full Galois connection between program semantics and spec obligations. Until then, this is an abstraction discipline, not a formal abstract interpreter.
  23. 23Floyd, *Assigning Meanings to Programs* (1967), cited for ranking-style progress reasoning doi.org/10.1090/psapm/019/0235771
  24. 24Scores are not probabilities; finite coverage is not proof of absence methodology/math-foundations.yaml#L122-L135Evidence bundles and CentiColon scores should not be interpreted as probabilities. If probabilistic or belief-function confidence is later added, it must be a separate layer from obligation closure.
  25. 25Shafer, *A Mathematical Theory of Evidence* (1976), cited as a possible separate confidence layer press.princeton.edu/books/paperback/9780691100425/a-mathematical-theory-of-evidence
  26. 26Walley, *Statistical Reasoning with Imprecise Probabilities* (1991) doi.org/10.1007/978-1-4899-3472-7
  27. 27The shell still supplies weights over CI check names scripts/local-ci.sh#L384-L403check_weight() { case "$1" in spec-cheatsheet-binding) echo 100 ;; spec-code-drift) echo 120 ;; spec-trace-coverage) echo 90 ;; version-monotonicity) echo 40 ;;
  28. 28The committed dashboard's earned and total figures docs/convergence/centicolon-dashboard.json#L75-L76"total_cc": 990, "earned_cc": 890,
  29. 29The framework specification names its intended scoring implementation methodology/litmus-framework.yaml#L88-L96files: - crates/tillandsias-litmus/src/convergence/mod.rs - crates/tillandsias-litmus/src/convergence/centicolon.rs
  30. 30The uninstrumented complexity constraint and its 5000-line red flag methodology/convergence.yaml#L329-L342ratio_constraint: "methodology_complexity / codebase_complexity < 0.15" anti_pattern: > A validation system that requires more code to understand than the code being validated. Red flag: CI validators exceed 5000 lines or require specialized training to understand.
  31. 31The three CRDT primitives, and why each field uses the one it does crates/tillandsias-plan/src/fragments.rs#L28-L39Applying LWW to a LIST would silently discard the loser's entries, which is why events are a set and not a register.
  32. 32Semantic merge typed as a cache with CRDT preconditions, plus the anti-pattern methodology/cheatsheets.yaml#L88-L114Calling a lossy semantic cache a CRDT without stable IDs, tombstones, deterministic merge, and property tests creates false convergence claims.
  33. 33Determinism rules: fold order and idempotence crates/tillandsias-plan/src/fragments.rs#L41-L49Fragments fold in `(ts, filename)` order, never directory order — the filesystem does not promise an order, and two hosts folding differently would compute different states from identical inputs, which presents as corruption rather than as a sorting bug.
  34. 34Commutativity and idempotence of the fold pinned as named tests crates/tillandsias-plan/src/fragments.rs#L2853-L2886fn the_fold_is_commutative_the_defining_crdt_property() { Order of arrival must not change the result.
  35. 35Rank-aware status join with a falsification escape hatch crates/tillandsias-plan/src/fragments.rs#L307-L320The closure ladder implemented<completed<verified<done is a monotone lattice: you climb UP freely and move DOWN only through a `falsified` event.
  36. 36A retracted CRDT claim, with the failed property named crates/tillandsias-plan/lua/collect.lua#L7-L11This is a SEEN-SET DEDUP, not a CRDT — the earlier header's CRDT claim (commutativity in particular) was false: first-wins keeps whichever duplicate arrives first, so order matters.
  37. 37The join-semilattice argument for the version scheme, and the SemVer critique methodology/versioning.yaml#L104-L124SemVer (Major.Minor.Patch) allows arbitrary resets: Patch can go 1→2→1 if someone hotfixes a branch. There is no natural total order.
  38. 38CRDT preconditions filed at claim strength "external analogy" methodology/provenance.yaml#L179-L192claim_strength: external_analogy
  39. 39The documented component semantics the project retired methodology/versioning.yaml#L9-L20Major: meaning: "Contract version — breaking changes only"
  40. 40Thesis defence position: finite ordered convergence under declared validators, with unknown-event intake as the escape hatch methodology/math-foundations.yaml#L200-L206The defensible claim is finite ordered convergence under declared validators: stable obligations form a finite lattice; evidence-improving transitions are checked for monotonicity; CentiColons are a bounded ranking function over that model; fixed points mean validator stability; and unknown-event intake is the escape hatch
  41. 41Generative property tests for monotonicity, inflationarity and idempotence of the refinement operator, over a committed rule set crates/tillandsias-plan/src/obligation_props.rs#L172-L219fn refine_is_monotone_on_the_real_rules((x, y) in comparable_pair()) {
  42. 42The gate that refuses a missing or duplicated requirement identifier, and states what it cannot check scripts/check-requirement-ids.sh#L5-L6Every spec requirement carries a stable identifier, and no two carry the same one.
  43. 43The shell scorer hands its weights to the model instead of summing them itself scripts/local-ci.sh#L542-L544What LEFT is the arithmetic: earned/denominator/residual are now computed by `tillandsias-plan score-checks`, which runs obligation::centicolon_function over a SpecState.
  44. 44The ranking function in code names which side of the monotone band a score is on crates/tillandsias-plan/src/obligation.rs#L675-L708Regime::Broken("denominator scope changed: an obligation was tombstoned")
  45. 45The shell prints non-comparability when the scorer reports a broken regime scripts/local-ci.sh#L591-L599this score is NOT comparable with the previous run

Live progress

Findings recorded by this website, as of this build. These columns show our audit record, not live telemetry or an inventory of every app defect. A finding advances when evidence supports it; a correction records why an earlier claim was wrong. Earlier events remain in the history.

58 findings · 3 done · 1 tracked · 54 waiting · 0 filed upstream

Found, not yet tracked 54

Newly found, undocumented or untracked, and still carrying real uncertainty.

  • 52c8f180tillandsias.orghigh

    Nothing runs the checked build before a push, so a broken footnote can be published

    skills/update-website/scripts/checked-build.sh, .git/hooks · found 2026-09-05

  • 572f7388tillandsiashigh

    The safe-browser launcher defaults to host networking; its spec promises enclave-only egress through the proxy

    scripts/launch-chromium.sh, spec chromium-safe-variant · found 2026-09-05

    not filed upstream

  • 62cacffetillandsiashigh

    The Vault unseal share persists to a plain file that satisfies the "share in keychain" check, contradicting the tmpfs-only invariant, and `--reset-guest` leaves it behind

    crates/tillandsias-headless/src/vault_bootstrap.rs, crates/tillandsias-headless/src/main.rs, spec tillandsias-vault · found 2026-09-05

    not filed upstream

  • 6767e64dtillandsias.orghigh

    The wording rules do not reach footnote labels, and the disagreement remedy prefers the wrong half

    openspec/changes/add-level-page-specs/specs/site/level-2, docs/matrix/level-2-phone.md · found 2026-09-06

  • 6cd2f616tillandsiashigh

    The permissive proxy port grants unfiltered egress to every enclave member with no source restriction

    images/proxy/squid.conf, spec proxy-container · found 2026-09-05

    not filed upstream

  • 6e61831etillandsiashigh

    The mirror relay wires the GitHub credential helper for any `https://` origin, and the helper answers unconditionally

    images/git/relay-refs.sh, images/git/git-credential-tillandsias.sh · found 2026-09-05

    not filed upstream

  • 778a9057tillandsias.orghigh

    The only page build is Python, which the runtime methodology forbids for committed automation

    scripts/build-matrix.py, scripts/figures.py, scripts/issues.py, skills/update-website/scripts · found 2026-09-05

  • 865e6b30tillandsiashigh

    `--publish-local` serves the whole repository, `.git` included, on a route with no auth gate, with none of the hardening flags the spec's builder carries

    crates/tillandsias-headless/src/main.rs, images/web/entrypoint.sh, spec enclave-service-catalog · found 2026-09-05

    not filed upstream

  • 9f823c95tillandsiashigh

    Two scripts create the enclave network without --internal, and the new guard cannot see them

    scripts/orchestrate-enclave.sh, scripts/run-forge-project.sh, scripts/diagnose-proxy.sh · found 2026-09-05

    not filed upstream

  • b28aba62tillandsiashigh

    No default install path verifies a Cosign bundle, and the Linux installer continues when the checksum file is missing

    scripts/install.sh, scripts/install-macos.sh, scripts/install-windows.ps1, spec update-system · found 2026-09-05

    not filed upstream

  • da34e5eetillandsiashigh

    Five production container launches never reach `validate_launch_argv`, and one of them builds an argv the policy forbids

    crates/tillandsias-headless/src/main.rs, crates/tillandsias-podman/src/container_spec.rs · found 2026-09-05

    not filed upstream

  • f664ad4atillandsiashigh

    The inference engine is fetched at first run from an unpinned `latest` release with no integrity check, the specs promise baked models the image does not carry, and the health check tests the runner binary

    images/inference/entrypoint.sh, images/inference/Containerfile, specs inference-container and zen-default-with-ollama-analysis-pool · found 2026-09-05

    not filed upstream

  • 077e5ab2tillandsias.orgmedium

    Level 3's capability inventory has no requirement in its spec

    openspec/changes/add-level-page-specs/specs/site/level-3/spec.md · found 2026-09-05

  • 0d88cfc7tillandsias.orgmedium

    The level specs restate site/level-common's shared rules instead of referencing them

    openspec/changes/add-level-page-specs/specs/site · found 2026-09-06

  • 0fdb0cb3tillandsiasmedium

    The browser allowlist cannot open any window for a project whose directory name contains a dot

    crates/tillandsias-browser-mcp/src/allowlist.rs, crates/tillandsias-headless/src/local_projects.rs · found 2026-09-05

    not filed upstream

  • 1e3f8c3dtillandsias.orgmedium

    The navigation spec describes a progress board the build does not produce

    openspec/changes/add-level-page-specs/specs/site/navigation, scripts/build-matrix.py · found 2026-09-06

  • 2448903ctillandsiasmedium

    The startup context says on-demand installs verify a Sigstore attestation; the shim disables attestation, and the Homebrew prefix is backed by no volume

    images/default/lib-common.sh, images/default/brew-shim-exec.sh, images/default/Containerfile · found 2026-09-05

    not filed upstream

  • 247ed660tillandsias.orgmedium

    Ten of level 5's forty-four footnotes need re-anchoring when its pin moves, and one quote no longer exists at the daily

    docs/matrix/level-5-phd.md · found 2026-09-05

  • 296676d4tillandsias.orgmedium

    Every broken footnote is re-anchored by hand although the search is mechanical

    skills/update-website/scripts · found 2026-09-05

  • 30127aadtillandsiasmedium

    The macOS release lane signs a hardcoded three-asset list and runs no transitive integrity check

    .github/workflows/release.yml · found 2026-09-05

    not filed upstream

  • 3d0ef7e2tillandsias.orgmedium

    The drift report computes no forecast when every level already pins stable

    skills/update-website/scripts/drift-report.sh · found 2026-09-05

  • 3d8dffdetillandsias.orgmedium

    The pages are 48% longer in prose than yesterday, and the length expectation is written down nowhere

    docs/matrix, openspec/changes/add-level-page-specs/specs/site/level-common · found 2026-09-05

  • 3ed0ddf7tillandsias.orgmedium

    Requirement text carries dated findings, counts and rosters that level-common forbids

    openspec/changes/add-level-page-specs/specs/site/level-{2,3,4},navigation · found 2026-09-06

  • 3f11cee0tillandsiasmedium

    `litmus:git-mirror-no-anonymous-daemon-write` asserts the opposite of its own name

    openspec/litmus-tests/litmus-git-mirror-no-anonymous-daemon-write.yaml · found 2026-09-05

    not filed upstream

  • 467f07batillandsiasmedium

    The proxy CA is one persistent RSA-2048 30-day certificate; the spec promises a per-launch two-level EC P-256 chain on tmpfs

    spec proxy-container, crates/tillandsias-headless/src/main.rs, images/default/ca-path.txt · found 2026-09-05

    not filed upstream

  • 56435d54tillandsiasmedium

    `browser.open` cannot resolve a browser binary: the on-demand chromium installer has no consumer, and the chromium images are built but never run

    crates/tillandsias-browser-mcp/src/launcher.rs, scripts/install-chromium.sh, images/chromium/ · found 2026-09-05

    not filed upstream

  • 576200f9tillandsias.orgmedium

    The level-5 deltas are live on the site while their OpenSpec change is unapproved, and openspec/specs/ holds no site spec

    openspec/changes/level-5-stable-pin-deltas, openspec/specs · found 2026-09-05

  • 5e0a299etillandsiasmedium

    `run_provider_login` attaches a container to the enclave without being a documented member, and the membership guard cannot see it

    crates/tillandsias-headless/src/main.rs, scripts/check-enclave-membership-documented.sh, spec enclave-network · found 2026-09-05

    not filed upstream

  • 5e3a98f3tillandsiasmedium

    The enclave-network spec's cleanup-on-exit and lifecycle-telemetry requirements have no implementation

    spec enclave-network, crates/tillandsias-headless/src/main.rs · found 2026-09-05

    not filed upstream

  • 5f323a37tillandsiasmedium

    The Windows tray reads the secure-wire variable directly, the single-reader ratchet has two slots of slack, and the WSL guest unit never receives the flag

    crates/tillandsias-windows-tray/src/hvsocket.rs, scripts/check-secure-wire-single-reader.sh, crates/tillandsias-vm-layer/src/wsl.rs · found 2026-09-05

    not filed upstream

  • 64d733d3tillandsias.orgmedium

    The Home positioning line makes a claim no level page footnotes

    openspec/changes/add-level-page-specs/specs/site/navigation, scripts/build-matrix.py · found 2026-09-06

  • 6efcbb8btillandsias.orgmedium

    The next level-5 delta change is unwritten while two of its REDs are already outrun in the daily channel

    openspec/changes, docs/matrix/level-5-phd.md · found 2026-09-05

    needs 247ed660

  • 764caa09tillandsias.orgmedium

    Requirements freeze today's verdicts, so a pin bump would make the spec mandate a false sentence

    openspec/changes/add-level-page-specs/specs/site/level-{1,4,5} · found 2026-09-06

  • 88d8d509tillandsiasmedium

    The forge startup context tells the agent Vault is at `http://vault:8200`; the listener is TLS-only

    images/default/lib-common.sh, images/vault/vault.hcl · found 2026-09-05

    not filed upstream

  • 8dd0e5c5tillandsias.orgmedium

    Nine requirements contradict their own scenario or a sibling requirement

    openspec/changes/add-level-page-specs/specs/site/level-{1,2,3,4,5} · found 2026-09-06

  • a988d3c4tillandsiasmedium

    The forge startup context hands agents the pre-flip project.service.localhost URL shape

    images/default/lib-common.sh, spec subdomain-naming-flip · found 2026-09-05

    not filed upstream

  • cf6b4dfftillandsias.orgmedium

    Every claim MUST carry a footnote resolving at the level's pin is false of both pages that state it

    openspec/changes/add-level-page-specs/specs/site/level-{1,4} · found 2026-09-06

  • d0c34ed6tillandsias.orgmedium

    Nothing tells this repository that the runtime published a release

    skills/update-website/scripts/latest-release.sh · found 2026-09-05

    needs 3d0ef7e2

  • e2e95e62tillandsias.orgmedium

    Two register MUSTs name no criterion an editor could apply

    openspec/changes/add-level-page-specs/specs/site/level-{1,4} · found 2026-09-06

  • ecbe8175tillandsias.orgmedium

    Universal MUSTs assert of the pages and of the audit record what is not true of them

    openspec/changes/add-level-page-specs/specs/site/level-{3,4,5} · found 2026-09-06

  • 05229912tillandsias.orglow

    The level-5 delta change is assembled by hand every time

    openspec/changes/level-5-stable-pin-deltas · found 2026-09-05

    needs 296676d4

  • 1e141597tillandsias.orglow

    The 2026-09-04 annotation files were left unamended beside pages the verification pass rewrote

    docs/matrix/level-1-five.audit.md and its three 2026-09-04 siblings · found 2026-09-05

    needs a0be2422

  • 34460f90tillandsiaslow

    The hardening litmus header says the argv validator has zero production call sites; it has three

    openspec/litmus-tests/litmus-podman-idiomatic-security-flags.yaml · found 2026-09-05

    needs da34e5ee

    not filed upstream

  • 373585f1tillandsias.orglow

    The published site cannot be reached from a forge, so no claim about it was verified as served HTML

    docs/audit, and the proxy allowlist in the runtime · found 2026-09-05

  • 53650fa1tillandsias.orglow

    Cross-references in the audit records name line ranges that moved during the day

    docs/audit/2026-09-05-issues-tillandsias.org.md, docs/audit/2026-09-05-v56.9.2.1.md · found 2026-09-05

  • 548baf18tillandsias.orglow

    The audit record's Method paragraph still reports the superseded footnote counts

    docs/audit/2026-09-05-v56.9.2.1.md · found 2026-09-05

    needs 67b4a3aa

  • 5811a59dtillandsias.orglow

    The byte-identical rebuild check is recorded as unrun, and it no longer passes now that the page embeds the ledger

    openspec/changes/add-level-page-specs/tasks.md · found 2026-09-05

  • 71e14b04tillandsiaslow

    Denied runtime traffic gets a TCP reset; the proxy spec says HTTP 403 twice

    spec proxy-container, images/proxy/squid.conf · found 2026-09-05

    not filed upstream

  • 7d737704tillandsias.orglow

    A footnote referenced more than once emits a duplicate element identifier

    scripts/build-matrix.py, var/html/index.html · found 2026-09-05

  • 83309118tillandsiaslow

    README's release ledger has no row for v56.9.5.1

    README.md · found 2026-09-05

    not filed upstream

  • 89ab166atillandsias.orglow

    The loop figure is drawn and placed nowhere, and the new capability sections carry no figure

    scripts/figures.py, docs/matrix/level-3-power.md, docs/matrix/level-4-security.md · found 2026-09-05

  • 956f6baatillandsias.orglow

    The drift report over-counts because it ignores per-footnote release overrides

    skills/update-website/scripts/drift-report.sh · found 2026-09-05

  • a0be2422tillandsias.orglow

    The per-level annotation files are superseded and still sit in docs/matrix beside page content

    docs/matrix/level-1-five.audit.md and its four siblings · found 2026-09-05

  • e87c4f45tillandsias.orglow

    Per-level re-verification is unautomated, and is the part that must stay reviewed

    skills/audit-site-claims/SKILL.md · found 2026-09-05

    needs 52c8f1803d0ef7e2296676d4

Tracked, with a plan 1

Documented and being worked or actively followed, uncertainty low enough to act on.

  • 984bf6cctillandsiaslow

    The published `stable` git tag points at a stale commit

    git tags, release promotion · found 2026-09-05

    not filed upstream

Done, and holding 3

Complete, and the thing it was about now meets its own written criteria.

  • 56415013tillandsias.orghigh

    The checked build passed on footnotes whose release had no checkout

    scripts/build-matrix.py, skills/update-website/scripts/checked-build.sh · found 2026-09-05

  • 66b46da4tillandsias.orgmedium

    The runtime issue drafts cited annotated-tag object hashes as commits

    docs/audit/2026-09-05-issues-tillandsias.md, skills/file-findings/SKILL.md · found 2026-09-05

  • 67b4a3aatillandsias.orgmedium

    The audit record's own counts went stale before it was published

    docs/audit/2026-09-05-v56.9.2.1.md · found 2026-09-05

What this progress measures

The bar counts findings recorded here. A resolved finding means its stated remedy was checked; it does not mean the entire application is complete or that every installed copy has been repaired. Adding a newly discovered defect can lower the completed percentage while improving what we know.

The runtime now delegates scoring to an obligation model, but coverage of the methodology’s full scoring rules remains partial. The methodology level explains the implemented model and its limits, with release-pinned evidence. This website has no CentiColon scorer or comparable score history of its own, so no convergence curve is shown.

We preserve findings, append evidence and record retractions. Monotonic improvement means a more accurate, reviewable record; it does not require the number of green flags to rise at every update.

Slides

The workflow, its boundaries and the methodology, for a live talk. One slide at a time — move with the buttons or the arrow keys. Each slide links to the explanation that carries its evidence.

tillandsias · in one line

A small cloud region on your own computer.

Disposable workspaces. Durable work. Boundaries you can inspect.

Tillandsias brings the tools, network services and AI assistants into a local region. Recreating a broken workspace is part of the design; keeping your work means committing and pushing it.

The software is free. Your hardware, electricity and any paid AI service you choose still have costs.

drawing on Like I'm 5 · I barely understand my phone

1 / 10