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
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.statusis accepted only from the endpoint that got thehandoff.request.status_urlcarries an unguessable per-handoff token; - the slave master is configured per resource type, and commands from any other peer are refused;
- error codes
unauthenticated-peerandreplayed.
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=) overTIMESTAMP.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.webhookrequirespeer).
- Pairwise keys. Three headers (
- Code.
mas_modes/signing.py(standard library only).contract.postsigns.contract.json_serverauthenticates before parsing, with prefix routes for tokens and unsigned routes only from loopback.EmailModehas keys,event_peerandmaster_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-001toa-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.