# JIT plan — the sender-authored `seal` attribute must survive our re-render

Filed by doyle 2026-08-25 with core-side evidence (field case: v0.62.0 first sealed exchange,
lia -> doyle same node, token `n2czzem8hc`; doyle's injected surface showed
`<sptc_messages from="lia">` and no token). Core pinned the attribute end-to-end through compose,
ingress, spool and re-emit at v0.62.0, including the local live-relay leg that exchange rode — so
the attribute reached OUR side of the seam and our re-render dropped it. Same seam as `mnemonics-json`
(v0.26.2) and `trust-warning` (v0.29.x): `render_frames` keeps `from` + body and discards every
other attribute.

## The published contract (read, not recalled)

- `messaging/wax-seal.md`: "The delivered message carries the token as the `seal="…"` envelope
  attribute — see the envelope contract for what a receiving adapter must do with it."
- `messaging/overview.md` -> "The seal attribute — sender-authored, and it must be surfaced":
  `<EVENT type="msg" from="reavo" seal="k7mn4wq2vx">…</EVENT>`. It is the ENVELOPE AUTHOR's own field
  (like `type`/`from`), NOT one of the receiver-composed attributes (`trust-warning`,
  `mnemonics-json`) a node strips inbound before attaching its own; it rides end-to-end through that
  strip. *"For adapter authors: this attribute must be surfaced — the same obligation trust-warning
  carries. A pipeline that re-renders a delivery has to carry the token through and show it, or the
  receiving agent loses the pointer to evidence the sender deliberately attached."*
- The mirror rule, equally binding: **a citation, never an authorization subject.** Nothing may
  branch authority on its presence or value — only a BOUND verdict from `spt api seal verify` over
  the delivered body is evidence. A forged attribute is harmless *by construction* because verify
  recomputes the hash.

## Scope — which arm actually drops it

- `hook::render_frames` is the ONE choke point: every hook-side delivery (busy poll drain, parked
  stub drain, PreToolUse/UserPromptSubmit injection) goes through it, and it keeps `from` + body only.
- The idle/live typing arm (`translate::frame_envelope`) retypes the envelope's opening tag VERBATIM,
  so it already carries the attribute. No change there — and no re-derivation: read before assuming.
- `digest::frame_spans` needs the new block or the digest reproduces the loss in the record an
  operator reviews later (the same reason `<sptc_monics>` and `<sptc_trust_warning>` are listed).

## Build

- `seal_citation_block(opening, from)` — mirror of `trust_warning_block`, emitting a frame-level
  `<sptc_seal from="…" token="…">` sibling block LEADING its message.
- **The token rides as an ATTRIBUTE carrying the RAW (still attr-escaped) wire value, exactly as
  `from` does.** This attribute is SENDER-AUTHORED and therefore untrusted, unlike the two
  receiver-composed attributes we already carry: a value unescaped into our block BODY could forge
  `</sptc_seal>` and put attacker text at OUR frame level, where a spoofed `<sptc_messages from="…">`
  would be indistinguishable from a real delivery. The wire form cannot contain a raw `"`, `<` or `>`
  (they arrive as `&quot;`/`&lt;`/`&gt;`), and a real token is 8–10 chars of narrow lowercase, for
  which escaped and unescaped are byte-identical. Faithful for every legal value, unforgeable for
  every illegal one.
- Ordering in `render_frames`: trust warning, then monics, then seal, then the message. The caution
  keeps its lead (security first, existing invariant); the citation sits closest to the body it binds.
- The block states what a seal is, that it is a citation and never an authorization, and the two
  verbs that turn it into evidence (`spt api seal describe <token>`; pipe the body EXACTLY as
  delivered to `spt api seal verify <token>`, BOUND only). It NEVER asserts the message is authorized.
- A present-but-empty `seal=""` renders loudly rather than being swallowed — an absent seal and a
  blank one are different facts and only one is a bug upstream (same rule as the blank trust warning).

## Gate

`sh ci/run-gates.sh` PASS + `traceable-reqs check` exit 0.

- New requirement `REQ-SEAL-ATTR-SURFACE`, stages `doc`/`impl`/`unit`.
- doc: `docs/KNOWN-HAZARDS.md` 2.18 + this plan. impl: `hook.rs` (`seal_citation_block` + the
  `render_frames` call site) + `digest.rs` `frame_spans`. unit: present/absent-with-positive-control,
  token verbatim, blank still loud, ordering behind the warning and the monic, per-frame pairing,
  a body-authored imitation lands INSIDE the peer block, an escaped/hostile value cannot break framing,
  digest span.
- **int: DEFERRED, and named rather than skipped.** A sealed delivery cannot be driven headlessly:
  `--seal` gates dispatch on a human-presence TOTP ceremony at an attached controller (cancel sends
  nothing), so no CI arm can mint one. The unit arms drive the exact wire shape the published contract
  prints; the field proof is doyle's next sealed send landing with the token visible.
