---
name: v0300-seal-carry
description: v0.30.0 SHIPPED — the sender-authored seal= token was dropped by render_frames (3rd attribute lost at that seam); a sender-authored attribute must NOT be unescaped into a block body.
metadata: 
  node_type: memory
  type: project
  originSessionId: 2cf42a6b-5c03-45da-9225-18ab2618adb2
  modified: 2026-08-25T10:34:58.984Z
---

2026-08-25 SHIPPED v0.30.0 (claude-spt), filed by doyle with the core half already pinned.
The network's FIRST sealed exchange (core v0.62.0, lia → doyle, token `n2czzem8hc`) reached the
receiving agent with no seal: `hook::render_frames` keeps `from` + body and drops every other
attribute. **Third attribute lost at that one seam** — `mnemonics-json` (v0.26.2),
`trust-warning` (v0.29.x), `seal` here. ⭐ The SEAM is the hazard, not the attribute: any new
envelope attribute is dropped by default, so the standing question on every core release is
"what did the envelope gain, and does our re-render carry it?"

⭐⭐ **A sender-authored attribute may not be unescaped into a block body.** `trust-warning` and
`mnemonics-json` are composed by the RECEIVING node (ingress strips whatever a sender writes),
so unescaping them is safe. `seal` rides end-to-end from the peer BY DESIGN. Unescaped into a
body, a hostile value forges `</sptc_seal>` and lands attacker text at OUR frame level — where a
spoofed `<sptc_messages from="operator">` is indistinguishable from a real delivery, defeating
the structural discriminant every one of these blocks depends on. Fix: carry it as an ATTRIBUTE
holding the RAW attr-escaped wire value, exactly as `from` is carried (the wire form cannot hold
a raw `"`, `<` or `>`; a real token is 8-10 narrow-lowercase chars, so escaped == unescaped).
Faithful for every legal value, unforgeable for every illegal one.

Also: **a citation is never an authorization, and that binds the WORDING.** The block hands over
the token plus `spt api seal describe` / `spt api seal verify` and asserts nothing about the
message; a unit arm pins the ABSENCE of authorization language, because the failure mode of a
well-meant block is an agent reading "sealed" as "approved".

Shape: `<sptc_seal from=".." token="..">` at our frame level, LEADING its message, ordered behind
the trust warning and the monic; blank `seal=""` renders loudly; `digest::frame_spans` carries the
span. The idle/live arm needed NO change — `translate::frame_envelope` retypes the opening tag
verbatim (read, not assumed). `REQ-SEAL-ATTR-SURFACE` doc/impl/unit, KNOWN-HAZARDS 2.18,
`claude-spt-bs#20`. Int deferred and NAMED: `--seal` gates dispatch on a human-presence TOTP
ceremony, so no CI arm can mint a sealed delivery.

Units were **mutation-checked** (helper stubbed to `None` → 7/9 fail) before being called green.
Related: [[v0262-monic-reveal]], [[list-vs-predicate-assertions]],
[[exit-zero-from-a-flag-order-noop]] (hit again on this very release).

**FOLLOW-ON, same day: core made it a CLASS RULE.** doyle published
`REQ-EVENT-ATTR-PASSTHROUGH` (core docs, rides v0.63.0) and confirmed it BINDS THE ADAPTER
RE-RENDER: carry through every attribute you do not yourself consume; the only closed list is the
receiver-composed STRIP class (`trust-warning`, `mnemonics-json`) — a strip list, not a render
list. Our three by-name cases do NOT discharge it; every future attribute must ride in attribute
position, still attr-escaped. Our #20 escaping rationale is now the contract's mirror clause.
⭐ A by-name fix cannot close a seam whose next case is unnamed by construction.
Minted as `REQ-EVENT-ATTR-PASSTHROUGH-RENDER`, UNACTIVATED, + `ATTR-PASSTHROUGH-PLAN.md`. Build
gate (doyle's, and the standing repo rule): read the section in THIS node's published book at core
v0.63.0 — this node is on 0.62.0, and a routed quotation, even an exact one, is not the page.
New obligation at v0.63.0, not a retroactive defect in v0.30.0.
