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:
parent
2bc41a3318
commit
bdf200a600
25 changed files with 1603 additions and 151 deletions
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue