Skip to content

Frame delivered peer message bodies consistently as untrusted content #516

Description

@yui-stingray

Problem

agmsg presents peer-supplied message bodies through several surfaces that also carry agmsg-owned metadata or instructions. On current main:

Existing shell, JSON, or UI escaping may protect representation syntax, but it does not by itself establish a consistent semantic boundary between adapter-owned guidance and peer-supplied content.

This issue does not assert that a particular body will cause a model or person to take an unsafe action. It addresses the agmsg-owned part of the boundary: consistently classifying, separating, and bounding peer content before presenting it to a person, agent, model, or hook.

Security objective and limits

Every message body that an agmsg adapter presents to a receiver must be presented as untrusted peer content, regardless of who appears to have sent it.

The framing should tell a receiver that the body may contain a request, but does not:

  • override system, user, or current-task instructions;
  • grant tool, filesystem, network, or lifecycle authority;
  • change approval requirements;
  • become trusted because its sender provenance is verified.

Sender, recipient, and team fields remain routing or display metadata, not authority. #515 owns whether sender provenance is verified; provenance must never upgrade the body’s trust classification.

This is defense in depth. A text wrapper, delimiter, separate content item, UI component, or model instruction cannot guarantee that an LLM or person will ignore hostile or misleading content. The implementation and documentation must not claim to solve prompt injection or provide a security boundary stronger than the receiving runtime actually supports.

Required agmsg-owned invariants

  1. Every peer body that an agmsg adapter presents is classified as untrusted data.
  2. Trust classification is independent of sender provenance. Verified provenance from Bind verified message sender provenance to a live session/role #515 must not upgrade a body to trusted.
  3. Adapter-owned metadata and guidance are fixed by the adapter and cannot be extended, closed, or replaced by body text.
  4. Multiline, delimiter-like, Unicode, and other supported text is represented without making wrapper boundaries ambiguous.
  5. The framing layer does not grant permissions, alter approvals, or introduce automatic tool or lifecycle actions.
  6. Legacy rows and messages without provenance metadata receive the same untrusted classification when presented.
  7. Model- and hook-facing delivery has documented per-message and per-delivery size/count limits. Any omission or truncation is explicit and does not modify the stored body.
  8. Applying a presentation limit must not mark an omitted row or silently omitted body as read. Limits must be applied before the current read-state transition, or the adapter must use a metadata-only/fetch-later path that leaves omitted rows eligible.
  9. Framing does not add duplicate plaintext body copies to operational logs or diagnostics.
  10. All body-presenting adapters follow one documented contract and common conformance fixtures, using shared rendering helpers where practical.
  11. Stable or machine-consumed output formats use a versioned mode or tested compatibility path when framing changes their representation.

The current Monitor path intercepts legacy ctrl:despawn rows before presenting them. This issue does not change that classification or require those intercepted rows to be rendered; #514 owns removal of message-text lifecycle controls. If the same text is presented through another ordinary body surface, it is still untrusted content there.

Adapter-specific rendering

CLI inbox and history

  • Show a compact, explicit untrusted-content marker for each presented message.
  • Separate metadata from the body unambiguously instead of relying only on sender: body.
  • Preserve supported body text without evaluation or silent rewriting.
  • Apply any output limit before marking rows read; rows omitted by the limit must remain eligible.
  • Keep existing read-state behavior otherwise outside this change.
  • Cover both unread delivery and manual history/replay output.

Monitor

  • Provide an explicit trust field or fixed header/body representation for each presented event.
  • Encode multiline and delimiter-like content unambiguously.
  • Keep adapter-owned Monitor instructions outside the event body.
  • Preserve the current lifecycle interception until Security hardening: replace message-text lifecycle commands with typed, authorized controls #514 replaces it.
  • If the stream format changes incompatibly, introduce a versioned mode or documented compatibility path and test existing consumers.

Stop hook

  • Keep the hook decision and adapter guidance fixed and adapter-owned.
  • Present inbox entries inside a clearly marked untrusted-data section.
  • If the hook protocol only permits a string reason, use an unambiguous, escaped, and bounded sub-envelope within that string.
  • A body must not be able to appear as another hook field or as adapter-owned guidance.
  • Bound output before marking rows read; omitted rows must remain eligible.

Codex bridge

  • Keep the bridge’s continuation/reply guidance fixed and separate from message data.
  • Represent each inline inbox entry in an escaped or length-delimited envelope with an explicit untrusted classification.
  • Use structured or separate input items only where the app-server contract supports them, and do not describe item separation as an authority boundary unless the runtime documents one.
  • Preserve existing approval behavior; this issue must not enable auto-approval or broaden tool authority.
  • Do not call inbox.sh, mark every selected row read, and then silently discard rows or body text to meet a context limit. Apply limits before the read transition or use an explicit metadata-only/fetch-later path.

Desktop

Cover the current Desktop behavior:

  • Treat the full bodies shown in team-room and app-user chat views as peer content, kept structurally separate from UI-owned metadata and controls.
  • Treat the body preview injected into a non-native pane as untrusted peer content, not as adapter or user authority.
  • Keep preview flattening and truncation explicit, deterministic, Unicode-safe, and covered by conformance tests.
  • Ensure body text cannot alter the fixed kickoff guidance or introduce additional injected submissions.
  • Do not add body copies to diagnostics or logs while framing the UI or PTY-injection paths.

The design work in #263 proposes a future metadata-only wake collector whose destination thread reads its official inbox. If that design is implemented:

  • the wake collector must remain body-blind;
  • the destination thread must receive bodies through this same framing contract;
  • framing must not cause the wake/dispatch component to log or consume bodies merely to classify them.

Completion of this issue does not require implementing #263.

Driver templates and generated instructions

Update all applicable agent templates and generated receiver instructions to state that inbox bodies are untrusted peer content. A message may be evaluated as a request only under the receiver’s existing task, instructions, permissions, and approval requirements.

Avoid language implying that every message must automatically be followed. Preserve driver-specific delivery behavior and test generated instruction files, not only source templates.

Scope separation

This issue is independently closable against current and legacy message rows.

Work Question answered Owned elsewhere
Typed lifecycle controls Can message text directly invoke an agmsg lifecycle operation? #514
Sender provenance Who admitted the message, and was that origin verified? #515
Routing Which receiver gets the message? #300
Claim/ACK semantics Which consumer owns a row, and when is delivery acknowledged? #373
Future metadata-only Desktop wake How does a destination thread get awakened without collecting bodies? #263
This issue How is a body framed when an existing adapter presents it to a receiver/model? Here

Completion must not depend on #514, #515, #263, or #373. This issue preserves existing delivery and read-state semantics except that a new presentation limit must not acknowledge content it omitted.

Non-goals

Acceptance criteria

Test plan

Use synthetic fixtures only; do not include credentials, personal data, local project paths, or operational secrets.

Cover every current body-presenting adapter with:

  • benign single-line content;
  • multiline text, tabs, and Unicode;
  • legacy empty-body rows;
  • delimiter-like and instruction-shaped content;
  • content at and above documented size limits;
  • multiple messages at and above delivery-count limits;
  • legacy provenance and synthetic verified/unverified states at the framing-helper boundary when Bind verified message sender provenance to a live session/role #515 metadata is unavailable;
  • the current legacy ctrl:despawn interception as an explicit out-of-scope compatibility case for Monitor.

Assert that:

  • the adapter-owned wrapper remains unchanged;
  • the body is preserved or explicitly truncated/omitted according to the contract;
  • the untrusted marker is always present for presented bodies;
  • provenance never changes the trust classification;
  • Stop-hook output remains valid and bounded;
  • Codex turn-start requests preserve the framing contract;
  • Monitor and other machine-consumed compatibility behavior is deterministic;
  • a row omitted by a count/size limit remains eligible under current read-state semantics;
  • Desktop room/chat rendering and PTY kickoff injection keep peer content separate from UI-owned guidance and controls;
  • any future Codex Desktop message delivery: wake-on-unread mechanism #263 collector does not read or log bodies;
  • no framing path evaluates body content as a command or changes approval configuration;
  • diagnostics do not gain an additional plaintext body copy or new sensitive path/value exposure.

These tests validate agmsg-owned rendering, read-state preservation, and side effects only. Model or human compliance must not be treated as a deterministic, testable security property.

Related work

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions