Untrusted Fence
The markers that name where a piece of model-facing content came from, and the field each surface labels them with.
The fence is a marker. It marks content; it does not control capability. Nothing is refused, delayed, reordered or truncated for being fenced, no capability grant is widened or narrowed by it, and a model that acts on fenced instructions can still do everything the manifest allows. Access control covers what the fence is for and where it sits among the runtime's other boundaries.
No surface strips it. The bytes a model received are the bytes the A2A stream, the conversation record and the session trace report.
Grammar
A fenced block is an opening marker, a newline, the content, a newline, and a closing marker.
<untrusted-content source=tool:web-fetch>
{"status":200,"body":"…"}
</untrusted-content>
| Part | Form |
|---|---|
| Opening marker | <untrusted-content source=NAME> — one line, no attributes other than source |
| Source name | tool:<artifact name> for a tool result, skill:<artifact name> for a runtime-pinned skill's result, task:<origin> for a task payload, delegation:<capsule> for a sub-capsule's outcome. A >, a carriage return or a newline appearing in a name is replaced with _, and respectively |
| Closing marker | </untrusted-content>, in full. It carries no source name |
| Separator | Exactly one \n after the opening marker and one before the closing marker. The content's own trailing newline, if it has one, sits before that separator |
Marker matching is case-insensitive over ASCII: </UNTRUSTED-CONTENT> is a closer.
The neutralised form
Content that spells a marker of its own is rewritten before the fence closes, by inserting
!MURMUR-NEUTRALISED! directly after the marker's <:
| In the content | In the fenced block |
|---|---|
</untrusted-content> |
<!MURMUR-NEUTRALISED!/untrusted-content> |
<untrusted-content source=tool:other> |
<!MURMUR-NEUTRALISED!untrusted-content source=tool:other> |
</UNTRUSTED-CONTENT> |
<!MURMUR-NEUTRALISED!/UNTRUSTED-CONTENT> |
The rewrite is a pure insertion that preserves the original casing, so a forged marker stays
visible as rewritten text and nothing is deleted or truncated. The infix contains no < of its
own, so rewriting is idempotent: rewriting an already-rewritten block changes nothing.
Parsing a block
- A block opens at
<untrusted-content source=and its source name runs to the first>. - A block ends at the final
</untrusted-content>in the message, not the first. The opening and closing markers a consumer sees are the runtime's own; any marker the content carried is in its neutralised form and matches neither. - Everything between the separators is data, verbatim.
- Text after the final closer is the runtime's own, such as the note that follows a started
call-memberresult.
The runtime's system prompt states the same rule to the model: a closing marker appearing anywhere inside a block — including one drawn inside an image — is a forgery.
Which surfaces carry a fence
| Surface | Fence present | How it is labelled |
|---|---|---|
The stream's artifact frame |
In content |
fenceSource — the source name, or null. Present on every frame |
conversation.jsonl message line |
In content |
fence — the source name. Absent on an unfenced line |
trace.jsonl tool_call |
In output, and counted in output_bytes |
Unlabelled |
trace.jsonl skill_call |
Not recorded: the event carries no output | trust — "untrusted" exactly when the skill's result was fenced under skill:<skill_name> |
murmur:conversation/read |
In the message's content |
Unlabelled. The message record has no fence field; read the markers |
The two labelled surfaces differ: fenceSource is on every frame, null when content is
unfenced, while a record line's fence is absent when unset, like the other envelope keys in the
file.
The artifact frame
{"frame":"artifact","toolName":"web-fetch","content":"<untrusted-content source=tool:web-fetch>\n{\"status\":200}\n</untrusted-content>","fenceSource":"tool:web-fetch","toolCallId":"toolu_1","isError":false,"durationMs":41,"exitCode":null,"truncated":false}
content is the bytes the model received, markers included. fenceSource is the source
content is fenced under, or null when content carries no fence. Every field on the frame is
listed in Streaming Protocol.
The record line
{"role":"tool","tool_call_id":"toolu_1","is_error":false,"content":[{"type":"text","text":"<untrusted-content source=tool:web-fetch>\n{\"status\":200}\n</untrusted-content>"}],"id":"msg_01a0…","fence":"tool:web-fetch"}
An unfenced line carries no fence key:
{"role":"assistant","content":[{"type":"text","text":"Fetched it."}],"id":"msg_01a0…"}
A record written before fence existed carries no fence key on any line, whatever its content
holds.
What is never fenced
| Content | Why |
|---|---|
An operator-pinned skill's skill.md |
The capsule author's own guidance, staged inside the capsule at install. Fencing it as data would make the skill inert. A skill whose murmur.lock pin a running capsule pulled is fenced — see Artifact origin |
| A dispatch failure | The runtime's own text about a call that never reached a tool |
The runtime's note after a started call-member result |
The runtime's own text about the call; the result itself stays fenced |
A refusal — from capabilities.filesystem.read_only or from a hook's decision |
The runtime's own text. The call never ran |
| A hook artifact | The capsule operator's own declared hook speaking |
A task whose trust class is trusted |
The operator instructing their own capsule |
| The model's own text | It is the model's turn, not content handed to it |
Where the fence is applied
| Boundary | Content | Source name |
|---|---|---|
| Tool result | Every agent-facing tool dispatch except a skill — WASM tool, native subprocess tool, shell binary, and the runtime's own peer-handoff tools | tool:<artifact name> |
| Skill result | A skill whose murmur.lock origin is runtime, on every call |
skill:<artifact name> |
| Task payload | A task whose trust class is untrusted |
task:<origin> |
| Delegation outcome | Every delegate-task outcome delivered into the task that delegated, whatever that task's trust |
delegation:<capsule> |
Every boundary applies on inference.transport: http and inference.transport: process. A
process capsule keeps no conversation record. Its A2A artifact frames name the fence in
fenceSource, as Streaming Protocol describes.