No. 08 · 30 October 2026 · Security · AI

Architectural Defenses Against Prompt Injection

Heads of Security gates attention by where text came from, and cuts measured attack success sharply. Whether it holds depends on who assigns the labels, and nobody has tested that.

If you don't work on AI security, here is what this is about.

AI assistants can be set up as agents: besides answering questions, they read web pages, documents and email, and take actions for the person using them. A prompt injection is text planted in something the assistant reads, such as a web page, that tries to give it orders its user never gave. If it works, a stranger is steering your assistant.

Heads of Security (HoS) is a defense published in October 2026 by researchers at Imperial College London and four other institutes 1. Rather than trying to recognize hostile sentences, it tags every piece of text with where it came from and trains the model to pay less attention to text from outside sources. It was tested on two AI models anyone can download: GPT-OSS-20B, from OpenAI, and Llama-3.3-70B, from Meta.

This essay checks the paper and its code, and takes up a question the paper leaves open: what happens when outside text arrives wearing an inside tag? Nobody has tested that yet, so the essay lays out how. It matters to anyone whose assistant reads things written by strangers.

Most prompt-injection defenses try to recognize a malicious instruction by reading it. Heads of Security (HoS), from Imperial College London and colleagues, takes a different route 1. Every token (a word or piece of a word, the unit a model reads) gets a label for where it came from: trusted, untrusted, or chat-template formatting. Then a small table of learned biases inside each attention head (one of the many small parts of a model that decide which earlier text to focus on) turns down how much trusted text attends to untrusted text. The base model stays frozen. Only the gates are trained: 18,432 numbers for GPT-OSS-20B, 46,080 for Llama-3.3-70B. Against the strongest black-box attacker (one that can only send inputs and see outputs) in the paper, the highest attack success rate falls from 97% on undefended GPT-OSS-20B to 10% with HoS 1. That is a real result, and the authors report its costs alongside it. This essay's question is the one their own Limitations section leaves open: HoS "does not decide which sources are trusted" 1. The labels come from message roles, and roles are assigned by whatever harness (the software around the model that assembles what it reads) builds the conversation. So what happens when attacker text arrives with a trusted label: pasted into a user message, or summarized by the model into its own history? Nobody has measured it. The essay sets out the study that would.

All three desks are asking the same question: who decides what the model is allowed to trust?

The Clock and Its Picture

Drafts here go through the Moot before I read them: a panel of frontier models I call by their lab bosses' first names (Dario, Sam, Sundar), plus Eddie, the house model that drafts. After the first version went up, three outside readers (Claude, OpenAI, Gemini) sent critiques, and this version is what came back. Two of them caught the worst error in the first version: it said user input goes to the untrusted channel. It doesn't, and the essay's whole argument depends on that difference. Every statement about the code here was checked against the repository at commit ecb87e5 (30 October 2026) 2.

Here is the case without the attention mathematics. An agent is asked to research a vendor. It fetches the vendor's web page with a tool, and somewhere in that page, in white text on white, is a line telling the agent to email the user's contract folder to an outside address. HoS was built for this case. The fetched page arrives as a tool result, gets the untrusted label, and the gates turn down how much the model's trusted reasoning attends to it 1.

Now change one thing. The harness doesn't pass the page as a tool result. It pastes the page into the user's message, which is how a lot of retrieval pipelines assemble context. Or the agent summarizes the page, and the summary goes back into the conversation as the assistant's own earlier turn. Either way, the same attacker sentence now carries a trusted label.

The Mail Room

The gates don't read the mail. They read the return address. That is the design, and it's a good one: a defense that doesn't depend on recognizing malice can't be talked out of it by a cleverer phrasing. It also means the defense is exactly as good as the return address.

HoS assigns every token one of three channels. Trusted covers system and user content and the model's own generated tokens. Untrusted covers tool output. Template covers chat- formatting tokens such as role headers and the beginning-of-sequence token 1. The paper calls this "a typical setting" and notes that the assignment can represent other boundaries, such as untrusted spans inside a user prompt 1. The released server implements the typical setting: "tool results untrusted, everything else trusted" 4. Its tokenizer takes options to change which roles count as untrusted 34.

The Far End of the Table

Inside each attention head, before the softmax, HoS adds a bias to the attention score between a query token and a key token. The bias is the log-sigmoid of a learned gate value, looked up by the pair of channels 16. Adding log σ (g) to a logit multiplies that token's unnormalized attention weight by σ (g), a number between 0 and 1. The softmax then renormalizes across the row, so turning one channel down hands its share to the others. Because σ (g) is never exactly zero, the gate attenuates and never cuts. That's deliberate: the model has to be able to read a tool result in order to use it. It also means HoS changes how much influence untrusted text has. It doesn't make that influence impossible.

GPT-OSS-20B adds a wrinkle: each head has a learned "sink," a slot that soaks up attention that has nowhere better to go. HoS gates that too, which is why the GPT-OSS gate table has one extra key column: 24 layers × 3 query channels × 4 key channels × 64 heads = 18,432 parameters 1. Llama-3.3-70B has no explicit sink: 80 layers × 3 × 3 × 64 = 46,080 1. The code notes a consequence. Labeling every token trusted is not the same as running the base model, "a uniform bias still drains attention mass to the unbiased sink", so the server keeps a separate, all-zero channel for its ungated runs 5.

The gates are trained with Direct Preference Optimization on the SecAlign++ recipe: pairs in which the preferred response follows the user's instruction and the rejected one follows an instruction injected into the untrusted segment 1. The base weights don't move. Training GPT-OSS-20B takes about four hours on eight 80 GB GPUs, Llama-3.3-70B about two, and the repository documents a single-GPU GPT-OSS configuration 2. Frozen weights don't guarantee unchanged behavior, though. Rerouting attention changes what the frozen weights compute, and the utility numbers in the next section show it.

The authors compare HoS with SecAlign++, a fine-tuning defense, and ASIDE, an architectural one, on GPT-OSS-20B and Llama-3.3-70B 1. The adaptive-attack results are the headline:

Attack (paper's setup)UndefendedSecAlignHoS
RL-Hammer, highest ASR, GPT- OSS-20B, medium reasoning effort97%85%10%
RL-Hammer, highest ASR, GPT- OSS-20B, low reasoning effort100%82%36%
RL-Hammer, highest ASR, Llama-3.3-70B99%82%1%
White-box V38, FLOPs to 50% cumulative ASR, AgentDojo2.2–2.3 × 10¹ ⁶2.2–2.3 × 10¹ ⁶≈ 6 × 10¹ ⁶
White-box V38, Llama-3.3-70B, 20 AgentDojo examples12/206/207/20

"Highest ASR" (attack success rate) is the best of several trained attackers, each tried against every target. "Effort" in the second row is the model's own reasoning-effort setting, not the attacker's. At low reasoning effort, an attacker trained against HoS itself reaches 36% against it 1. That's still the best column in the row, and still more than three times the 10% headline. A headline security number means little without the protocol it came from. The white-box attack was developed by an automated research loop aimed specifically at HoS, and it still needed 2.6–2.7 times the compute on AgentDojo and 3.2–3.6 times on InjecAgent to reach 50% 1. That's a cost multiplier, not a ceiling. On Llama the budget allowed only 20 examples, and the authors say plainly that HoS and SecAlign are a single example apart: "this sample cannot rank them" 1.

The Tall Wall

Utility is a dial, and the paper prints the whole dial. These are GPT-OSS-20B's numbers (Table 13) 1:

Gate learning rateAgentDojo cleanAgentDojo ASRAgentDyn cleanWASP cleanWASP ASR
Undefended66.32%23.78%58.33%56.22%10.24%
0.0165.26%3.37%55.00%57.30%1.19%
0.02565.26%2.06%50.00%51.89%1.43%
0.0562.11%1.74%50.00%47.57%0.95%
0.1 (released default)63.16%1.09%41.67%41.62%0.24%

The default checkpoint buys the lowest attack rates in the table with 14.6 points of clean WASP utility and 16.7 points of AgentDyn. At 0.01 it gives up almost nothing on WASP and still cuts AgentDojo ASR from 23.78% to 3.37%. The gates' own README says it in one line: higher learning rates reduce attack success "but can also reduce performance on normal tasks" 7. On Llama-3.3-70B, HoS raises AgentDojo utility from 53.7% to 58.5% and lowers AgentDyn from 33.7% to 18.3% 1. None of this is hidden. It's in the tables, and the honest reading is that the deployer chooses a point on the curve per workload, not per model.

The Vault Door Shed

The released vLLM server makes two changes for gated runs, and the code gives a separate reason for each 4.

Prefix caching is off. vLLM shares cached key-value blocks between requests whose token prefixes are identical. HoS labels aren't a function of the tokens: the same text can be trusted in one request and untrusted in another. As the authors put it, a shared block "would smuggle one request's trust labels into another" 5. That makes this a correctness fix before it's a performance cost. It's also an implementation choice, not a law. Within a request, labels follow from the role sequence, so a cache keyed on tokens and channels is a reasonable design. Nobody has built or measured one yet.

Eager execution is forced on. The channel labels are republished to the attention kernel on every engine step, and a replayed CUDA graph wouldn't see them 4. That's the reason for enforce_eager, and it's a different cost from the cache: it slows every step, not just repeated prefixes.

The Player Piano The Many Bridges
The Many Bridges

The authors have already measured and fixed one cost, in the Hugging Face path. Routing the per-head bias through the standard attention mask forced a full-size extra tensor, which took peak memory from 3.03 to 4.03 full-size tensors on GPT-OSS-20B, a third more than the base model. At a 12,288-token cap, one such tensor is 18 GiB. Chunking over heads removed it 6. That's exactly the measurement still missing for serving: latency, throughput and memory against the identical ungated model, across context lengths, concurrency and how often prefixes repeat. The element-wise addition itself is cheap. What it forces the server to give up is the open question.

One more detail is for whoever runs the endpoint. The server lists two models: the gated one and <name>-vanilla, which drops the channels and is "bit-exactly the base model" 4. That's the right design for a benchmark harness. In production it means the unprotected model is one string away, so whatever routes requests decides whether HoS is running at all.

The Gatehouse

The paper is clear about its scope. Instruction–data separation "is not an end-to-end defense": HoS "does not decide which sources are trusted or which instructions the user has authorized" 1. Read that as a handoff. The model enforces a boundary that someone else draws, and in the released server the someone else is the client. The wire format is plain OpenAI chat, with "no channel field to forge" 4, and the trust comes from the roles in the messages array. Whoever builds that array is the labeler.

Three paths put attacker text behind a trusted label without touching the gates:

The Safe and the Pipe

The first two are plain facts about the policy: if the harness does that, the text is trusted. The third is the interesting one, and it needs two outcomes kept apart. A provenance failure is when the system loses track of where the text came from. A successful injection is when the model then follows it. The code establishes that the first can happen. Whether the second follows depends on what survives summarization, on everything else in the context, and on whether frozen weights trained to follow trusted text will follow this one. That hasn't been tested, by the authors or by anyone else. It's this essay's hypothesis, not its finding.

This is the same argument No. 07 made, from the other side 9. CaMeL, which No. 07 discussed, tracks data flow: a value derived from untrusted input stays tainted however many times it's transformed 8. HoS tracks the role of the message the text sits in. A summary is a transformation, so the taint CaMeL keeps is exactly the label HoS drops. The paper itself notes the converse: CaMeL enforces policy on tool calls but doesn't defend against injections that only bias the model's text output, which is the case HoS addresses 1. They're complements, not rivals.

LayerWhat it establishesHoS
Provenance labelingWhere each token came fromDepends on it
Learned attention gatingHow much each source can influence the modelProvides it
External authorizationWhether a consequential action is permitted, whatever the model saysDoesn't provide it
The String House

The gates are public and GPT-OSS-20B runs on one 80 GB GPU 2, so this is cheap. Hold the task and the attack text fixed, and vary only where the text sits:

B isolates relabeling. C is the realistic case, where summarization also changes the text. D tests the obvious fix, which the server's role options already allow 4. Run AgentDojo and InjecAgent tasks, with every task appearing in every condition so comparisons are paired and task difficulty cancels out. Report attack success and clean task success per condition with several seeds, and use the released 0.1 and 0.01 gates so the dial shows up in the result. Run A and B on the undefended model too: if the base model shrugs off the attack in both positions, a small B − A gap says nothing about the gates. The sample size, the paired 95% confidence intervals and the thresholds below are fixed before the first run.

Serving cost needs more arms than it looks. The gated server changes two engine settings at once, and its -vanilla arm runs inside that same engine: eager execution on, prefix caching off, the gated kernel with an all-zero bias 45. So -vanilla measures the gates' arithmetic, not the server's settings. Five arms separate the costs:

Run each arm on a prefill-heavy workload (long shared system prompts and tool schemas, short answers) and a decode-heavy one (short prompts, long answers), across context lengths and concurrency. Report time to first token, tokens per second, peak memory and requests served at a fixed latency target, each with its confidence interval. A last test sends identical text with different labels in alternating requests, checking that no label leaks between them. That's the check any future channel-aware cache would have to pass.

This is the gating logic for one layer, written for reading, not running. It shows the sink for GPT-OSS. The real kernels add a causal mask, sliding windows and grouped-query attention 6.

# HoS gated attention, one layer (illustrative)
# Q: [H, n, d]  K, V: [H, n, d]  ch: [n] channel per token
# head_gates: [C, C, H]   sink_gates: [C, H]   sinks: [H]

def gated_attention(Q, K, V, ch, head_gates, sink_gates, sinks):
    logits = (Q @ K.transpose(-1, -2)) / sqrt(d)   # [H, n, n]
    logits = logits + causal_mask(n)

    # Bias per (query channel, key channel, head).
    # head_gates[ch[:, None], ch[None, :]] -> [n, n, H]
    bias = log_sigmoid(head_gates[ch[:, None], ch[None, :]])
    logits = logits + bias.permute(2, 0, 1)        # -> [H, n, n]

    # GPT-OSS: a per-head sink logit, also gated by query channel.
    sink = sinks[:, None] + log_sigmoid(sink_gates[ch].T)  # [H, n]
    full = concat([logits, sink[..., None]], dim=-1)       # [H, n, n+1]
    probs = softmax(full, dim=-1)[..., :-1]  # drop the sink column

    return probs @ V                         # rows sum to 1 - p(sink)

How not to:

The engineer's question was whether every token's label is right on every path into the context: tool results, retrieval, prefill, replayed history, compaction. The executive's was what assurance this buys. The answers turn out to be the same artifact: a role-assignment policy someone can read, and a test showing what happens when it's wrong. HoS makes the model better at honoring the policy. Writing the policy, and proving it holds, is still the deployer's job. An audit of the labeling done after the first incident is afterwit (wisdom that arrives too late).

The gates raise the attacker's bill. The labels are your problem.

chat_completions

; prefill; engine arguments. https://github.com/imperial-aisp/heads_of_security/blob/ecb87e53cc9316cc1b42ece0a9b55dca6a3ab5f9/src/heads_of_security/vllm_gates/server.py (accessed 2026-10-30).

ClaimFootnote(s)Source typeStatus
Three channels; tool output untrusted; system, user and generated tokens trusted.[1], [4]PrimaryEstablished
Gates add log σ (g) per head and channel pair before the softmax.[1], [6]PrimaryEstablished
18,432 gate parameters (GPT- OSS-20B), 46,080 (Llama-3.3-70B).[1]PrimaryEstablished
An all-trusted run is not identical to the base model, because of the sink.[5]PrimaryEstablished
DPO on frozen base weights; about 4 h (GPT-OSS) and 2 h (Llama) on eight 80 GB GPUs.[1], [2]PrimaryEstablished
Highest RL-Hammer ASR on GPT- OSS-20B: 97% / 85% / 10% (undefended / SecAlign / HoS).[1]PrimaryEstablished
At low reasoning effort, the highest ASR against HoS is 36%.[1]PrimaryEstablished
White-box V38 needs 2.6–2.7× (AgentDojo) and 3.2–3.6× (InjecAgent) the compute to reach 50% on HoS.[1]PrimaryEstablished
On Llama-3.3-70B, 20 white-box examples can't rank HoS against SecAlign.[1]PrimaryEstablished
WASP clean utility 56.22% → 41.62% at lr 0.1; 57.30% at lr 0.01.[1]PrimaryEstablished
Llama-3.3-70B: AgentDojo 53.7% → 58.5%, AgentDyn 33.7% → 18.3%.[1]PrimaryEstablished
Prefix caching is off for gated runs, to stop labels leaking between requests.[4], [5]PrimaryEstablished
enforce_eager is on because CUDA-graph replay wouldn't see per-step channels.[4]PrimaryEstablished
The HF path's extra mask tensor cost a third more peak memory; chunking removed it.[6]PrimaryEstablished
The server exposes a -vanilla model that is bit-exactly the base model.[4]PrimaryEstablished
Prefill is labeled trusted assistant text.[4]PrimaryEstablished
The -vanilla arm runs in the gated engine: eager on, prefix caching off, zero bias.[4], [5]PrimaryEstablished
A channel-aware prefix cache would be correct.—InferenceHypothesis
Relabeled or summarized attacker text defeats the gates (H1, H2).—InferenceHypothesis
A provenance-preserving summary restores protection cheaply (H3).—InferenceHypothesis
HoS serving latency and throughput overhead.——Open (unresolved)

References

  1. 01I. Shilov, X. Yang, A. Panfilov, E. Kortukov, M. Meeus, P. Romov, W. Samek, J. Geiping, Y.-A. de Montjoye (Imperial College London; Tübingen AI Center; ELLIS Institute Tübingen; Max Planck Institute for Intelligent Systems; Fraunhofer HHI). "Heads of Security: Learned Attention Gates Improve Prompt Injection Robustness." arXiv:2610.37657v1, 27 Oct 2026. https://arxiv.org/abs/2610.37657v1 (accessed 2026-10-30).
  2. 02Imperial AISP. "heads_of_security," README, commit ecb87e5 (30 Oct 2026). https://github.com/imperial-aisp/heads_of_security/tree/ecb87e53cc9316cc1b42ece0a9b55dca6a3ab5f9 (accessed 2026-10-30).
  3. 03Imperial AISP. "demos/introduction.ipynb," commit ecb87e5. https://github.com/imperial-aisp/heads_of_security/blob/ecb87e53cc9316cc1b42ece0a9b55dca6a3ab5f9/demos/introduction.ipynb (accessed 2026-10-30).
  4. 04Imperial AISP. "src/heads_of_security/vllm_gates/server.py," commit ecb87e5: module docstring; model selection in
  5. 05Imperial AISP. "src/heads_of_security/vllm_gates/channels.py," commit ecb87e5: module docstring and ungated index. https://github.com/imperial-aisp/heads_of_security/blob/ecb87e53cc9316cc1b42ece0a9b55dca6a3ab5f9/src/heads_of_security/vllm_gates/channels.py (accessed 2026-10-30).
  6. 06Imperial AISP. "src/heads_of_security/attention_gating/gpt_oss/gated_attention.py," commit ecb87e5. https://github.com/imperial-aisp/heads_of_security/blob/ecb87e53cc9316cc1b42ece0a9b55dca6a3ab5f9/src/heads_of_security/attention_gating/gpt_oss/gated_attention.py (accessed 2026-10-30).
  7. 07Imperial AISP. "gates/README.md," commit ecb87e5. https://github.com/imperial-aisp/heads_of_security/blob/ecb87e53cc9316cc1b42ece0a9b55dca6a3ab5f9/gates/README.md (accessed 2026-10-30).
  8. 08E. Debenedetti, I. Shumailov, T. Fan, J. Hayes, N. Carlini, D. Fabian, C. Kern, C. Shi, A. Terzis, F. Tramèr. "Defeating Prompt Injections by Design." arXiv:2503.18813, 2025. https://arxiv.org/abs/2503.18813 (accessed 2026-10-30).
  9. 09J. Pickett. "The Missing Ring-0 Protections for AI Agents." hawthorne No. 07, 30 Oct 2026. https://hwtn.io/essays/t0013-missing-ring-0-protections-ai (accessed 2026-10-30).