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

@ -5,8 +5,11 @@ calls anyone. Scenarios are scripted and deterministic: nothing happens
until `step()` (or POST /step) emits the next status. A scenario is chosen
by the request's `details.scenario`, or else by the stub's default.
stub = CallbackStub(default="no-answer-retry")
base = serve(stub.server()) # POST /handoffs, /step
stub = CallbackStub({"email": key}, default="no-answer-retry")
base = serve(stub.server()) # POST /handoffs (signed), /step (loopback)
Requests are accepted only when signed by email's peer id, and statuses
are signed with the key shared with email (CONTRACT.md section 7).
"""
from __future__ import annotations
@ -55,9 +58,11 @@ class Handoff:
class CallbackStub:
def __init__(self, default: str = "completed"):
def __init__(self, keys: dict[str, bytes], default: str = "completed",
peer_id: str = "callback-1", email_peer: str = "email"):
if default not in SCENARIOS:
raise ValueError(f"unknown scenario {default!r}")
self.keys, self.peer_id, self.email_peer = keys, peer_id, email_peer
self.default = default
self.handoffs: dict[str, Handoff] = {}
self.received: list[dict] = []
@ -66,7 +71,9 @@ class CallbackStub:
# -- contract endpoint ---------------------------------------------------------
def receive(self, msg: dict) -> tuple[int, dict]:
def receive(self, msg: dict, peer: Optional[str] = None, rest: str = "") -> tuple[int, dict]:
if peer != self.email_peer:
return refuse("wrong-peer", "only email may hand off", 403)
return self._once(msg, self._receive)
def _receive(self, msg):
@ -127,7 +134,7 @@ class CallbackStub:
seq=h.seq, status=status, **extra)
h.sent.append(msg)
url = h.request["status_url"]
post(url, msg)
post(url, msg, (self.peer_id, self.keys[self.email_peer]))
return msg
def run(self, handoff_id: Optional[str] = None, limit: int = 20) -> list[dict]:
@ -149,6 +156,6 @@ class CallbackStub:
return None
def server(self, host="127.0.0.1", port=0):
return json_server({"/handoffs": self.receive,
"/step": lambda body: (200, {"ok": True, "sent": self.step(body.get("handoff_id"))})},
host, port)
step = lambda body, peer, rest: (200, {"ok": True, "sent": self.step(body.get("handoff_id"))}) # noqa: E731
return json_server({"/handoffs": self.receive, "/step": step}, self.keys,
frozenset({"/step"}), host, port)