net-kingdom/sso-mfa/k8s/keycape/handover-adapter.md
tegwick cbadd359ba NK-WP-0048 r5 adapters: OW-1.3-C1 attended change lane, RAPPS-1.3-C1 human actor, RAPPS-1.3-C2 handover observations
Lane key-cape-oidc-login -> keycape-attended-case-change in the register-client,
ensure-groups and bind-env descriptors; deliver-consumer-secret actor agent ->
human; verify_consumer reports a value-free, exactly-once probe record list bound
into the observation digest. Composition artifacts, type.yaml and admit/compose
tests are left for the orchestrator to regenerate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 903097@bnt-lap001
Assistant-Session: aa320c8f-c3fa-46e7-8541-0b58e1e45d23
2026-10-17 11:59:14 +02:00

3.1 KiB

Apps hand-over adapter (source candidate, NK-WP-0048-T29)

handover-adapter.py is an unexercised, injection-only seam for Warden's v1 kernel, modelled on the T13 registration adapter. It covers step (f), Apps-owned consumer verification and the hand-over receipt.

  • Descriptor: descriptor(owner) — step hand-over, owner is a parameter (type 1.2 declares {consumer_owner_repo}; the T30 composer passes it, e.g. railiance-apps), actor human, lane apps-consumer-handover, target client_id, effect change, allowed created object handover-receipt, not retry-safe, revocation scope parent-session (the Warden v1 kernel refuses an empty scope set). build() derives the descriptor from admitted.owner; a 1.2 case must carry a bounded consumer_owner_repo equal to it. The lane name is still a candidate value for Apps owner review.
  • Case binding: canonical oidc-browser-relying-party@1.1 is the default; @1.2 is reached only through an explicit version_root='1.2' selection, and successor @1.3 (1.2 plus execution_descriptors) only through version_root='1.3', with the same consumer_owner_repo owner handling. No --allow-unimplemented admission exists. Case, descriptor and acceptance pins are checked; access_groups must be non-empty, because 1.1 hand-over requires the consumer to enforce them (consumer-enforces-groups).
  • Injected capabilities (required, never strings or case parameters): verify_consumer(client_id, groups) must return exactly client_id, enforces_groups=True, the case's groups, login_verified=True and observations (RAPPS-1.3-C2): a value-free list of {probe, evidence} records, one each for member-login, non-member-refused, membership-add-grants and membership-remove-revokes, evidence an owner evidence identifier (IDENTIFIER pattern). Missing, extra, duplicate or unknown probes and any other key are refused; the observation digest covers the records in canonical probe order; record_handover(client_id, observation_digest) -> (identifier, created); remove_handover(identifier) -> True; publish(bytes) -> identifier and authenticate(identifier, bytes) -> True for owner evidence; revoke_session(invocation) -> identifier, called in a finally block on every run and rollback (even failed ones) so the parent session always ends. A missing or malformed revocation identifier refuses before publication; the identifier is recorded in the evidence payload and returned as session_revocation_evidence with revoked=True.
  • Rollback is created-only: only a receipt this process created and whose evidence the owner authenticates may be removed. An existing receipt is no_change and never rolled back; unconfirmed removal refuses and keeps the creation. Process loss and ambiguity stay for owner reconciliation (T31/T15).

Tests: python3 -m pytest sso-mfa/k8s/keycape/test_handover_adapter.py (synthetic fixtures only; no consumer, store or credential is touched).

Not supplied here: Apps owner source review before pinning, actual consumer verifier and receipt store, admission, and any native action.