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
This commit is contained in:
tegwick 2026-10-19 11:25:24 +02:00
parent 2bc41a3318
commit bdf200a600
25 changed files with 1603 additions and 151 deletions

View file

@ -4,7 +4,7 @@ type: workplan
title: "Mode contract: peer authentication and transport security"
domain: communication
repo: mail-address-semantics
status: proposed
status: finished
owner: claude
topic_slug: coulomb-social
created: "2026-10-19"
@ -27,7 +27,7 @@ unauthenticated. This must be fixed before anyone integrates.
```task
id: MAS-WP-0011-T01
status: todo
status: done
priority: high
state_hub_task_id: "b6b866e0-eb9d-511b-85da-7423bc36f7eb"
```
@ -48,7 +48,7 @@ Review with the founder before implementing.
```task
id: MAS-WP-0011-T02
status: todo
status: done
priority: high
state_hub_task_id: "57f7cb5b-7e32-54a7-925b-3938b4f98572"
```
@ -58,3 +58,40 @@ Put the chosen scheme into `mas_modes.contract` (sign and verify), into
(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.