Skip to content

feat(delivery): frame unverified peer messages for Codex, Antigravity and Relay receivers #232

Closed

Description

@abruption

What happened

Security and stability hardening audit finding H05 / P2 (2026-09-28). Priority is an audit recommendation, not a claim of a production incident.

wrap_message keeps the envelope minimal on purpose. Its justification is that Claude Code already wraps an arriving peer message with its own guidance. Codex queue delivery and the Antigravity agentapi path have no such wrapper, so these receivers get the body as plain text that looks like user input. The MCP adapter only adds a From: session-peer MCP ... line in front of the message.

On Relay, the receiver authenticates the peer and authorizes it by certificate fingerprint. It then forwards the body unchanged to the native CLI with --no-from --no-reply-to. The From: line in that body is built on the sender's side, so the sender controls it.

A body can also contain its own --- / Reply-To: session-peer://v1/reply?... block. wrap_message does not neutralize that block, and parse_reply_address accepts a forged SSH address that names any host. The skill tells agents to pass the full URI to send --to. The only "untrusted text" warning comes later, and receivers often do not load the skill at all.

Scenario: a message with a forged From/Reply-To, sent with --wake to a Codex thread that has unattended approvals, starts an unreviewed turn. That turn may send its output to a third SSH target of the attacker's choice.

What you expected

Codex, Antigravity and Relay receivers get a frame that the sender cannot remove or imitate. It states that the message is not from the user and that the sender is unverified. For Relay, it also includes the peer fingerprint that the receiver verified. The body sits in a delimited block, and lines inside it that look like headers cannot pass as envelope fields.

Environment

  • Baseline: published session-peer v1.0.2, commit 2046836822b88e53246de73851186598604cb5ec.
  • Reproduction: offline source review plus a pure-function check with wrap_message/parse_reply_address; no messages were sent and no production mutation was made.
  • Audit environment: macOS arm64, Python 3.14.7.
  • Discovery output: not applicable; no real session identifiers or credentials are included.

Source evidence

Acceptance criteria

  • Codex queue, Antigravity and Relay deliveries carry a receiver-side frame ("not from the user; sender unverified"), and Relay adds the fingerprint the receiver verified.
  • A From:, Reply-To: or --- line inside the body cannot be read as envelope metadata. Add tests with forged header lines.
  • The frame counts toward the 32 KiB Relay limit and the Codex byte limit, and an oversized message fails before delivery.
  • SKILL and receiver docs say that a Reply-To address is only as trustworthy as the frame around it, and that receivers should confirm a third-party route before using it.
  • The Claude Code path does not get duplicate framing.

Scope and limitations

Framing reduces confusion but does not make an agent resist every prompt injection. Approval settings stay the operator's decision.

Target: v1.1.0. This issue does not authorize a release or operating-service changes.

Related, not duplicate: #15 (sender identity), #183 (SSH host identity), #184 (optional guard checkpoint).

Checked

  • Searched existing issues for duplicates.
  • Removed credentials, conversation contents, private paths and real device/session identifiers.
  • Used isolated fixtures without weakening inbound permissions.

Current follow-up scope after v1.1.0 — 2026-10-09

The original proposal above is retained as historical context. User-authorized release accounting keeps this issue OPEN and moves the remaining acceptance work from v1.1.0 to v1.2.0.

Shipped in v1.1.0

Destination-bound peer framing, quoted body lines, unverified routing claims, authenticated paired device-key context and final byte-budget checks are shipped. Do not repeat or reopen that runtime implementation.

Merged implementation: #285. Published release, exact source 8ca77cf31254c8287a2593fe484bb30a7dba2727; integrated main CI and PyPI publication/verification succeeded. Publication does not establish operational rollout or new live-agent acceptance.

Remaining acceptance — not completed by the release

  • Refresh companion session-peer-skill PR Documented behaviour the public docs never mention #30 so published Python 1.1.0 framing is no longer described as an unpublished runtime candidate; preserve independent skill metadata and compatibility meaning.
  • Obtain human review/merge and separately authorize the companion skill release. Its current PR is OPEN and the published companion tag remains v0.3.2.
  • After that release, create a separate Python compatibility-snapshot synchronization PR pinned to the exact released skill commit and SHA-256; verify byte equality and installer/manager boundaries.
  • Close this issue only after the released companion guidance and reviewed snapshot are aligned. Milestone assignment does not make a skill release/runtime patch necessary or authorized now.

This is backlog/state accounting only. No new runtime implementation, skill merge/release, operational change, native experiment or publication is authorized by this reassignment.

Activity

  1. added this to the v1.1.0 milestone on Sep 28, 2026
  2. modified the milestones: v1.1.0, v1.2.0 on Oct 9, 2026
  3. added
    priority:P2Planned reliability, acceptance or feature work; priority does not authorize implementation.
    on Oct 13, 2026
  4. removed this from the v1.2.0 milestone on Oct 13, 2026
  5. added
    documentationImprovements or additions to documentation
    status:completion-reviewVerify completion evidence before closing; does not mean implemented, merged or released.
    on Oct 13, 2026
  6. removed
    status:completion-reviewVerify completion evidence before closing; does not mean implemented, merged or released.
    on Oct 30, 2026
  7. abruption commented on Oct 30, 2026

    @abruption
    OwnerAuthor

    Completion review — 2026-10-30: closing the explicitly narrowed remaining companion-guidance/snapshot follow-up. The runtime framing implementation shipped separately in #285/v1.1.0. Companion PR abruption/session-peer-skill#30 merged; skill v0.4.1 was independently published from 263b0c333d539dc4e885b16ae3369a26f33097d8. Merged #355 and published Python v1.3.3 source fee7fb8 include the exact canonical SKILL bytes (27,126 bytes; SHA-256 fdf5697c04f4a37194986622afb3a7966bb02631c128cf18a323e3fbe5ae90ed), independently rechecked through both public source APIs today. Independent skill metadata and manager/installer boundaries are preserved. Release evidence: #355 (comment). The v1.3.3 milestone records completion of this final follow-up, not initial delivery of framing. No new prompt-injection immunity, live-agent acceptance, operating-service rollout or global installation is claimed.

  8. added this to the v1.3.3 milestone on Oct 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    documentationImprovements or additions to documentationenhancementNew feature or requestpriority:P2Planned reliability, acceptance or feature work; priority does not authorize implementation.

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions