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
97 lines
3.8 KiB
Markdown
97 lines
3.8 KiB
Markdown
---
|
|
id: MAS-WP-0011
|
|
type: workplan
|
|
title: "Mode contract: peer authentication and transport security"
|
|
domain: communication
|
|
repo: mail-address-semantics
|
|
status: finished
|
|
owner: claude
|
|
topic_slug: coulomb-social
|
|
created: "2026-10-19"
|
|
updated: "2026-10-19"
|
|
origin: residual
|
|
origin_ref: MAS-WP-0010
|
|
state_hub_workstream_id: "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
|
|
|
|
```task
|
|
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
|
|
|
|
```task
|
|
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.
|