mail-address-semantics/workplans/MAS-WP-0011-mode-contract-transport-security.md
tegwick bdf200a600 MAS-WP-0011 finished: peer authentication in mode contract v0.1
CONTRACT.md section 7 (founder-reviewed): pairwise HMAC-SHA256 over
TIMESTAMP.BODY in MAS-Modes-* headers, 300 s window; master commands only
from the configured master; statuses only at the handoff's tokenized URL
from its peer; primed webhooks must name a known peer. mas_modes.signing,
signed contract.post, authenticating json_server, EmailMode/stubs/gateway
signing. conformance/auth.json vectors + 7 authorization flows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 5787@bnt-lap001
Assistant-Session: 5db26b82-138b-40cc-9856-4d5eb0f752fd
2026-10-19 11:25:24 +02:00

3.8 KiB

id type title domain repo status owner topic_slug created updated origin origin_ref state_hub_workstream_id
MAS-WP-0011 workplan Mode contract: peer authentication and transport security communication mail-address-semantics finished claude coulomb-social 2026-10-19 2026-10-19 residual MAS-WP-0010 499ad365-c46a-5ec6-84ef-85e52ce623ef

Mode contract: peer authentication and transport security

Risk B in history/2026-10-19T090725Z-scope-vs-intent.md. mail.send, case.assign and case.policy steer real outbound mail, and handoff.status changes what email tells customers. Yet mas-modes-v0.1-spec/CONTRACT.md does not say how email knows that a master or mode is who it claims to be. The reference endpoints are unauthenticated. This must be fixed before anyone integrates.

Design

id: MAS-WP-0011-T01
status: done
priority: high
state_hub_task_id: "b6b866e0-eb9d-511b-85da-7423bc36f7eb"

Add a transport-security section to CONTRACT.md:

  • per-peer authentication: a shared-secret HMAC over the body plus a timestamp (replay window), or mTLS. Pick one as MUST and allow the other;
  • bind each handoff to its mode endpoint: handoff.status is accepted only from the endpoint that got the handoff.request. status_url carries an unguessable per-handoff token;
  • the slave master is configured per resource type, and commands from any other peer are refused;
  • error codes unauthenticated-peer and replayed.

Review with the founder before implementing.

Implement and test

id: MAS-WP-0011-T02
status: done
priority: high
state_hub_task_id: "57f7cb5b-7e32-54a7-925b-3938b4f98572"

Put the chosen scheme into mas_modes.contract (sign and verify), into EmailMode (all three endpoints) and into both stubs. Add fixtures (signed and unsigned messages, a replayed message, a wrong peer) and flows. Update docs/reuse-surface.md, which still says the endpoints are loopback-only.

Outcome

  • Design (CONTRACT.md §7). Reviewed by the founder on 2026-10-19: HMAC-SHA256 is the MUST and mTLS is optional on top; the authorization rules are as drafted; gateway events are signed when not on loopback (optional per deployment).
    • Pairwise keys. Three headers (MAS-Modes-Peer, -Timestamp, -Signature: v1=) over TIMESTAMP.BODY, with a 300 s window.
    • Errors unauthenticated-peer / replayed (401), wrong-peer (403), unknown-peer (400).
    • Master commands are accepted only from the master configured for the resource type.
    • Statuses are accepted only at the handoff's tokenized status_url (192-bit token) and only from the peer the handoff was sent to.
    • A primed webhook must name a peer email has a key for (schema: transfers.webhook requires peer).
  • Code.
    • mas_modes/signing.py (standard library only).
    • contract.post signs. contract.json_server authenticates before parsing, with prefix routes for tokens and unsigned routes only from loopback.
    • EmailMode has keys, event_peer and master_peer, and checks that every configured peer has a key at startup.
    • Both stubs accept only email and sign their own requests.
    • mas-gateway --sign-peer/--sign-key-file.
  • Fixtures and tests.
    • conformance/auth.json: 3 signing vectors, cross-checked against an independent HMAC, and 11 verification cases.
    • 7 authorization flows a-001 to a-007, with a mutation check that disabling the master check, the replay window or the webhook peer check fails them.
    • tests/test_modes_auth.py (6). mas test: mode corpus 194/194.
  • Live MX unaffected: it runs the ticket service directly, and the gateway posts unsigned on loopback.
  • Not covered: key rotation and storage (deployment configuration), and TLS for the reference email side. The reuse surface states both.