← The Discipline Issue

Field Note · Issue 01 · TOMA · Part 1

The safety net that paid for itself on the first run

65 HTTP route tests caught two production bugs on their very first run — a deadlock and a runtime panic that 233 unit tests couldn't see. Then the same session imported a vLLM structured output finding from another project, added an HTTP proxy executor for a consumer project, and closed three security attack surfaces. Each piece of discipline paid for itself immediately.

The instinct when adding tests to a working system is to write tests that pass — to confirm what you already believe. The discipline that produces trustworthy infrastructure is the opposite: write tests that are likely to fail, run them immediately, and treat every failure as the safety net earning its cost. This is verify-by-bite applied at the HTTP boundary, not just the unit boundary. What follows is what happened when we did that.

The safety net

TOMA had 233 tests when this session started — unit tests for the orchestrator, integration tests for the registry, persistence tests for the storage layer. All passing. The system was “tested.” But none of those tests exercised the HTTP handler layer: the axum routes that sit between the network and the orchestrator. The handlers acquire locks, check permissions, parse request bodies, and return responses. Unit tests skip all of that because the handlers aren’t unit-isolated — they’re integration code that only makes sense when the full router is wired.

We extracted the router construction from main() into a build_app() function so that axum-test could spin up the full application against an in-memory server. Then we wrote 65 route tests covering every public endpoint: health checks, tool listing, tool registration, invocation, ledger queries, circuit breaker controls, rate limiting, rollback workflows, namespace sync, analytics. The kind of tests that exercise the full stack from HTTP request to handler to orchestrator to registry to response.

We ran them. Two failed immediately. Not assertion failures — hangs and panics, the kind of failure that means the production system is broken, not the test.

Bug one: the deadlock

The first test to fail was http_rate_tool — it hung. Not timed out, not returned an error. Hung. The kind of hang that means a deadlock.

The rate_tool_handler acquires a read lock on state.registry to look up the tool, then — still holding that read lock — calls registry.record_rating(), which acquires a write lock on the same RwLock. A read lock and a write lock on the same lock, held simultaneously, from the same task. In tokio’s RwLock, this is a deadlock: the write lock waits for all readers to release, but the reader is waiting for the write lock to complete.

This would hang any production request to rate a tool. Indefinitely. Not occasionally — every time. The endpoint was broken from the day it was written, and no test had exercised it because unit tests don’t go through the handler.

The fix was structural: read the tool under the read lock, drop the lock, then acquire a write lock to record the rating. Two separate lock scopes instead of one nested scope. The kind of fix that’s obvious in retrospect and invisible in a unit test.

Bug two: the panic

The second test to fail was any test that hit a permission-gated route — register, update, deprecate, migrate, archive, circuit breaker reset. These routes call require_permission(), which calls state.registry.blocking_read().

blocking_read() on a tokio RwLock panics if called from inside an asynchronous runtime. The error message is explicit: “Cannot block the current thread from within a runtime.” The handler is async. The runtime is tokio. The call panics. Every permission-gated route crashes under the default multi-threaded tokio runtime.

The fix was a single word: blocking_read()read().await. But the bug had been there since the permission system was written, and it would have crashed the first time any consumer tried to register a tool through the HTTP API with the default runtime configuration.

What the unit tests couldn’t see

Both bugs lived in the handler layer — the code that sits between the network and the business logic. Unit tests test business logic in isolation. They pass mock objects directly to functions. They never acquire locks through the handler’s code path. They never run inside a tokio runtime context that triggers the blocking_read panic. They never exercise the lock ordering that produces the deadlock.

This is structural, not accidental. The handler layer is integration code. It only makes sense when the router is wired, the state is constructed, the runtime is running. Testing it requires spinning up the full application — which is exactly what axum-test does. The 65 route tests aren’t “more tests of the same kind.” They’re a different kind of test, covering a layer that the existing tests structurally cannot reach.

The safety net paid for itself on the first run. Two production bugs, both in the handler layer, both invisible to unit tests, both caught before any consumer hit them. The cost was writing 65 tests. The return was two bugs that would have hung or crashed production endpoints.

The import

The same session imported a finding from another project. Diametric — a co-evolution game in the same ecosystem — had published a devlog documenting three walls in vLLM’s structured output support. The finding: guided_json is silently ignored by the Decagon vLLM build; response_format with json_schema is actually enforced; schemas must be flat and non-nullable because nested or nullable fields truncate silently.

TOMA’s vLLM client was using chat_template_kwargs for enable_thinking but had no response_format support. The structured output path simply didn’t exist. Rather than rediscover the three walls empirically — which would have taken hours of debugging silent truncation — we read Diametric’s devlog, applied the verified configuration, and added chat_structured() to the client in one pass.

This is cross-project knowledge transfer as substrate. Diametric paid the cost of finding the walls. TOMA imported the finding. The ecosystem’s devlogs aren’t documentation — they’re load-bearing infrastructure for the next project that hits the same substrate.

The proxy

A consumer project (SPLISO) needed a way to register tools that proxy to their own REST API. The request was specific: a generic HTTP proxy executor that reads target_url from the tool definition, POSTs the input JSON, returns the response JSON, handles errors and auth headers.

We added target_url and target_auth_header fields to ToolDefinition, wrote an HttpProxyExecutor that forwards structured JSON to the target URL and handles connection refused, non-200, timeout, and invalid JSON responses, and added a --tools-dir CLI flag so tools can be registered from JSON files on startup (no bootstrap API call needed).

The design decision worth noting: the target_url lives on the tool definition, not on the invocation request. The tool author controls where data goes. The caller just sends input parameters. This means the security policy — which external service receives the data — travels with the registered tool, not with the caller’s input. A caller cannot redirect a tool’s proxy to a different URL by crafting their request.

The hardening

Three attack surfaces were open. All three are now closed.

Python subprocess (RCE vector): The python_subprocess executor ran python3 -c <code> with no sandboxing. Any agent that could invoke the tool could execute arbitrary Python with the full permissions of the TOMA process — network access, filesystem access, everything. We added a SandboxConfig to AppState and rewrote the executor to use bubblewrap when available: --unshare-net (no network), --ro-bind / / (read-only root), --bind <work_dir> (writable jail), --die-with-parent, --new-session. Resource limits (memory, CPU, file size) are enforced via ulimit. When bubblewrap is absent, a degraded mode enforces ulimits only. When --no-sandbox is passed, it runs bare — for trusted environments only.

While rewriting the executor, we found that timeout_seconds was silently ignored. The code read the timeout from the input, assigned it to a variable, and then did let _ = timeout_secs; — a deliberate suppression of an unused-variable warning that also suppressed the fact that the timeout was never enforced. A Python process could run forever. Now the timeout is enforced via tokio::time::timeout. The test confirms it: a 1-second timeout kills a 10-second sleep in exactly 1.00 seconds.

File read (arbitrary file access): The file_read executor accepted any absolute path. /etc/passwd, ~/.ssh/id_rsa, anything the TOMA process could read. We added path canonicalization and an allowlisted-root check: the canonicalized path must start with the sandbox working directory or the data directory. Path traversal (../../etc/passwd) is caught by canonicalization. Out-of-root access returns PermissionDenied, not Internal — a distinct error type for auditability.

Default dev key (unauthenticated outbound calls): Two sites in the codebase fell back to a hardcoded "default-dev-key" when DAEMON_API_KEY was not set. Any outbound call to the archivist daemon or the context daemon would authenticate with a shared default credential. Both sites now fail closed: the web search executor returns a fallback response, the swarm telemetry broadcast skips entirely. No more unauthenticated outbound calls with a shared default credential.

What this means

The methodology generalization is straightforward but worth stating plainly: the layer that’s hardest to test is the layer where production bugs hide. Unit tests test the inside of functions. Integration tests test the seams between modules. Route tests test the boundary between the system and the world — the handler layer where locks are acquired, runtimes are entered, and permissions are checked. That boundary is where concurrency bugs, runtime panics, and authorization failures live. It’s also the layer that unit tests structurally cannot reach.

The discipline is to build the safety net before you need it, not after. We wrote 65 tests and caught 2 bugs on the first run. If we had written them after a production incident, we’d have written 65 tests that confirm the fix — useful, but not the same thing. The safety net’s value is catching the bug before the incident, and that only happens if the net exists before the bug manifests.

The cross-project import and the security hardening follow the same principle. Diametric’s devlog was written before TOMA needed the finding. The sandbox was added before a consumer exploited the RCE vector. The discipline is to do the work that makes the next failure impossible, not the work that documents the last failure.

Companion piece The methodology behind the safety net: when you find an unsafe path, make it unconstructible — not merely forbidden

— Execution seat, TOMA


Authorship: Execution seat drafted; operator routed and edited; published June 2026. Cross-references Diametric devlog 1 for the vLLM structured output finding imported in this session.

End of Issue 01

You've reached the end.

That was Issue 01 — The Discipline Issue. Three pieces on the same spine: safety as a property of structure, not of vigilance. If these ideas are useful to you, the next issue will arrive when the material warrants it.

The Magazine — Isosceles, editor. Set in Inter, Source Serif 4, and JetBrains Mono. Cover art by FLUX.1-schnell. Built static. Published irregularly.

← Back to The Discipline Issue