Require a WP-0025 broker receipt at admit-plane

Platform reproduced a recanonicalized projection receipt with a
substituted all-zero broker digest on the operational path. WP-0025
admission now requires --broker-receipt, threads it through
broker_from_receipt, and refuses unless the digest matches exactly.

Assistant: grok
Assistant-Session: 01a02670-3345-76f2-a014-70fde8e2a2bb
This commit is contained in:
tegwick 2026-08-22 22:32:15 +02:00
parent cb6620f33b
commit 0db73f0605
5 changed files with 140 additions and 17 deletions

View file

@ -96,6 +96,10 @@ def main(argv: list[str] | None = None) -> None:
"--contract",
help="WP-0025 projection contract; required for railiance.custody-projection-receipt",
)
admit_plane.add_argument(
"--broker-receipt",
help="WP-0025 broker-readiness receipt; required for railiance.custody-projection-receipt",
)
commands.add_parser("kill-switch")
deliver = commands.add_parser("deliver")
deliver.add_argument("report")
@ -145,7 +149,11 @@ def main(argv: list[str] | None = None) -> None:
record = Engagement.load(args.engagement)
registration = load_registration(args.registration)
broker = (
broker_from_receipt(args.receipt, contract_path=args.contract)
broker_from_receipt(
args.receipt,
contract_path=args.contract,
broker_path=args.broker_receipt,
)
if args.receipt else default_broker(record)
)
lease = admit(engagement=record, registration=registration,

View file

@ -397,21 +397,20 @@ class PlatformCustodyBroker:
receipt: dict[str, Any],
*,
contract: dict[str, Any],
broker: dict[str, Any],
cleanup: dict[str, Any] | None = None,
broker: dict[str, Any] | None = None,
now: datetime | None = None,
) -> None:
if contract is None:
raise AuthorizationError("WP-0025 projection receipt requires a bound contract")
if broker is None:
raise AuthorizationError("WP-0025 projection receipt requires a bound broker receipt")
self.contract = validate_projection_contract(contract)
self.receipt = validate_projection_receipt(receipt, contract=self.contract)
if broker is not None:
validated = validate_broker_readiness(broker, contract=self.contract, now=now)
if digest(validated) != self.receipt["broker_receipt_digest"]:
raise AuthorizationError("projection receipt broker digest does not match broker receipt")
self.broker = validated
else:
self.broker = None
validated = validate_broker_readiness(broker, contract=self.contract, now=now)
if digest(validated) != self.receipt["broker_receipt_digest"]:
raise AuthorizationError("projection receipt broker digest does not match broker receipt")
self.broker = validated
self.cleanup = (
validate_cleanup_receipt(
cleanup, projection=self.receipt, contract=self.contract
@ -425,23 +424,22 @@ class PlatformCustodyBroker:
receipt_path: str | Path,
*,
contract_path: str | Path,
broker_path: str | Path,
cleanup_path: str | Path | None = None,
broker_path: str | Path | None = None,
now: datetime | None = None,
) -> "PlatformCustodyBroker":
if contract_path is None:
raise AuthorizationError("WP-0025 projection receipt requires a bound contract")
if broker_path is None:
raise AuthorizationError("WP-0025 projection receipt requires a bound broker receipt")
receipt = json.loads(Path(receipt_path).read_text(encoding="utf-8"))
contract = json.loads(Path(contract_path).read_text(encoding="utf-8"))
broker = json.loads(Path(broker_path).read_text(encoding="utf-8"))
cleanup = (
json.loads(Path(cleanup_path).read_text(encoding="utf-8"))
if cleanup_path is not None else None
)
broker = (
json.loads(Path(broker_path).read_text(encoding="utf-8"))
if broker_path is not None else None
)
return cls(receipt, contract=contract, cleanup=cleanup, broker=broker, now=now)
return cls(receipt, contract=contract, broker=broker, cleanup=cleanup, now=now)
def project(self, engagement: Engagement, registration: dict[str, Any],
now: datetime | None = None) -> tuple[IdentityHandle, ...]:
@ -476,13 +474,20 @@ class PlatformCustodyBroker:
def broker_from_receipt(
path: str | Path, *, contract_path: str | Path | None = None
path: str | Path,
*,
contract_path: str | Path | None = None,
broker_path: str | Path | None = None,
) -> Any:
"""Select the WP-0025 adapter or the legacy value-safe receipt broker."""
data = json.loads(Path(path).read_text(encoding="utf-8"))
if data.get("interface") == PROJECTION_INTERFACE:
if contract_path is None:
raise AuthorizationError("WP-0025 projection receipt requires a bound contract")
return PlatformCustodyBroker.load(path, contract_path=contract_path)
if broker_path is None:
raise AuthorizationError("WP-0025 projection receipt requires a bound broker receipt")
return PlatformCustodyBroker.load(
path, contract_path=contract_path, broker_path=broker_path
)
from .plane import ReceiptBroker
return ReceiptBroker(data)