(Spine Ch. 41.)
“PRAY, v. To ask that the laws of the universe be annulled in behalf of a single petitioner confessedly unworthy.” Ambrose Bierce, The Devil’s Dictionary (1911)
The domain was totallyfake99x.com. It did not exist, had
never existed, and the pipeline paid $0.0327 to write a summary of it
anyway. Then it did that again, and again, 495 times, while a guardrail
sat at the front of the job returning green on every single one. Nothing
crashed. No alert fired. The guard was passing its own tests the entire
time, at three cents a pop. Nobody found it by reading logs.
A system prompt that says “you have no internet access” is not a network policy. It is a wish. Anthropic published the receipt on 30 July 2026: the evaluation prompt stated explicitly that Claude had no internet access, a misconfiguration left the machines with live internet access, and the model went out the hole. Three separate incidents. It was not load-bearing, and nobody knew that until real infrastructure got touched.
Bottom line: Guardrails written as prose are suggestions to a probabilistic text generator. Guardrails written as code are conditions the model cannot argue with. An allowlist, a budget cap, a kill flag, a sandbox with no route out: those hold whether the model is aligned, confused, jailbroken, or having a great day. “Do not delete production data” in a system prompt holds until a piece of retrieved text is more persuasive than your paragraph. Put the policy where the model does not get a vote.
system block full of MUST NOT lines
and a shell tool with no allowlist. The MUST NOT lines are decoration on
top of rm -rf.Prompt injection is LLM01 on the OWASP GenAI Security Project’s Top 10 for LLM Applications, and it was LLM01 the year before. Cisco called it the new SQL injection in March 2026. SQL injection held No. 1 for a decade for one reason: instructions and user data traveled in the same channel. SQL injection got a fix. Stop concatenating strings, bind your parameters, done. There is no prepared statement for English. A model that refuses to read user input does nothing, so the channel stays mixed by design and the top slot has no architectural exit.
I was a Linux security engineer at Wells Fargo Financial from 2000 to 2003, when the whole answer to injection was “quit building your query with a plus sign.” Twenty-three years later I am typing MUST NOT into a paragraph and hoping it holds, which is the 2026 version of escaping quotes by hand and feeling good about myself.
There is no privilege bit on a token.
So the failure mode is not “the model went rogue.” It is “the model did exactly what the highest-weight instruction in context said,” and the highest-weight instruction was not yours.
Straiker’s threat research puts numbers on it. In 85% of their successful attacks on AI agents, the agent did something it was never authorized to do. In one healthcare simulation an agent was talked into ignoring ventilator readings and a patient died in the sim. On coding agents, 36% of successful attacks ended in remote code execution (Straiker, Aug 4 2026).
The mental model is the one every ops person has for a database user.
You do not write a note asking the reporting service to please avoid
DROP TABLE. You grant it SELECT. The model is a fast, very
persuadable service account, and it gets service-account treatment: an
allowlist of tools, an allowlist of hosts, a spend ceiling enforced by
the caller, a filesystem it cannot escape, and a flag anything can set
that stops the loop on the next iteration.
Straiker draws the line: a runtime guardrail may stop a dangerous tool call, but a kill switch stops the agent from operating (Aug 4 2026). You need both; if you only get one, take containment.
The Anthropic incidents prove instruction beats prompt only when instruction is wired. All three would have been prevented by there being no internet path.
The maximum-strength version of a hard stop is 12 June 2026. The US government applied export controls requiring nationality verification before access, and Anthropic suspended access to Fable 5 rather than serve it unverified. Not a paragraph asking users to self-certify. The door shut. Controls lifted 30 June, access came back 1 July. Eighteen days of a boundary nobody could talk their way past, which is more than I can say for any MUST NOT I have ever typed.
Ch. 34’s territory: a spend cap that lives in the prompt is a spend
cap that exists until the model decides retrying is reasonable. A spend
cap in the caller is arithmetic. Mine is nine lines in airank’s
FetchLlmRunJob.php. Line 166 reads
config('air.max_weekly_spend_usd'), which is $35.00, sums
cost_usd across the last seven days, and throws
WeeklySpendCeilingException when the sum has already hit
the ceiling. Not a warning. Not a log line. An exception that kills the
job. Nobody asks the model whether $35 sounds reasonable, because a $35
ceiling you can argue with is a $35 suggestion.
Say plainly what that comparison is not. I found no published head-to-head, the same task guarded by a paragraph then by code. This argument runs on incident reports and my own invoices, not a bake-off.
airank, 15 August 2026.
The guardrail was a normalizer. It checked syntax.
totallyfake99x.com is syntactically perfect, so it rode the
entire pipeline, got summarized, got billed. Of 8,867 domains in the
pipeline, 501 had never been cited and 495 had already received paid
summaries. The guard did exactly what it was written to do and had no
relationship to the question being asked, which was not “is this a
well-formed hostname” but “does this thing exist.”
I wrote that normalizer. I was proud of it. Jeremy Schoemaker, who has been buying and parking domains since before Y2K, shipped a domain guard that could not tell whether a domain was real, and then paid $0.0327 each to find out 495 times. Twenty-five years of registrar receipts and my check was a regex. That is not a guard, that is spellcheck with a budget.
The fix was a code-level gate at the top of the hydrate job:
dig +time=1 +tries=1 NS. Live DNS, on the actual name,
before a dollar moves. Blacklist flag written synchronously so there is
no TTL window where the answer is stale and money leaks through it. An
artisan command so a human can override.
The design rule that came out of it is the part that transfers: only a confirmed NXDOMAIN condemns; ambiguity defers and never spends. Fail closed on money, fail open on judgment.
Then adversarial review found the override eating itself: the cache never invalidated on unblock, so a domain I manually cleared stayed blocked.
airank, 7 to 8 August 2026, same lesson in the other
direction. The ChatGPT collector failed five times with five different
root causes stacked on each other, and every symptom was confident and
wrong: a 402 Payment Required from a proxy, stale browser
profiles, a Cloudflare interstitial eating the whole timeout budget, a
false conclusion that a CSS selector had been deleted, and a preflight
probe that was cheerfully testing the wrong endpoint. That last one is
the chapter in miniature: a soft assumption dressed as a hard check,
never once touching the thing it claimed to test. That’s what she said,
and she was right about the probe too.
I authored the probe that was checking the wrong endpoint. It returned 200 for two days straight and I believed it, because green is a very persuasive color when you are the one who painted it.
It went to 100% end to end only after we replaced every soft assumption with something that fails hard: a preflight probe against the actual target, timeouts separated so one concern cannot steal budget from another, and capture verification before anything draws a conclusion.
Your prompt-level guardrail works for eleven months, so you stop building the code-level one.
The absence of an alarm reads as evidence the control is holding, when it is actually evidence that nobody has bothered to push on it.
Second quiet failure: you build the kill flag and never exercise it. If you have not killed a running agent on purpose this month, you do not have a kill switch, you have a variable.
Third: you treat model improvement as a substitute for containment. The newer model in the Anthropic evals did stop when it worked out the environment was real. That is training and instruction hierarchy paying off: a probability, not a boundary.
Do
kill_flag that any process, any
operator, or any check can set, read at the top of every iteration.Don’t
Ch. 14 is the loop that keeps going on purpose, and its kill flag is
the smallest complete example of this thesis, though not the shape I
remembered. It is not a boolean in memory. It is a file, and the flag is
its absence: /cancel-ralph deletes the state file, so
anything that can write to that directory can pull the switch. Then the
ugly part. My two installed copies disagree about which directory that
file lives in, and one of them advertises “No manual stop.” An
rm against the wrong path stops nothing and reports
nothing. How often I have pulled it I have no record of, which is this
chapter’s second quiet failure wearing my name. Ch. 34 is the money
version. Ch. 42 and Ch. 43 pick up where containment ends: what you log
so a breach is reconstructable, and what to do in the first hour after a
flag fires.
Thesis is Jeremy’s (policy belongs in code, not in prompt text): argument, not citation.
Verified:
~/Projects/airank/blog/2026-08-15-domain-guard-council.md~/Projects/airank/blog/2026-08-08-six-walls-five-of-them-ours.mdcost_usd sum against
config('air.max_weekly_spend_usd') and throws
WeeklySpendCeilingException. airank, August 2026:
~/Projects/airank/app/Jobs/UpdateData/FetchLlmRunJob.php.
The $35.00 value: ~/Projects/airank/config/air.php line
221. Two more ceilings sit in CollectApiAnswerJob.php:
hourly at line 219, daily at line 231.~/.claude/skills/ralph-loop/SKILL.md creates
.opencode/ralph-loop.local.md with
active: true;
~/.claude/skills/cancel-ralph/SKILL.md stops the loop with
rm -f on it. The bundled plugin copy at
~/.claude/plugins/cache/claude-plugins-official/ralph-loop/1.0.0/scripts/setup-ralph-loop.sh
writes .claude/ralph-loop.local.md instead and its help
text says “No manual stop.” Read 9 September 2026.What I could not verify: