oficina / home Public

Specify explicit pending-consumer standing verification amendment

Paul Campbell pushed by paul@keymail.dev 42529b58fe3424b240e97e2cee612fdb24823161 parent 3945a34
1 file changed, +95 −0
  • implementation/pending-consumer-verification.md +95 −0

CI passed — run details

diff --git a/implementation/pending-consumer-verification.md b/implementation/pending-consumer-verification.md
new file mode 100644
index 0000000..ddf0ee8
--- /dev/null
+++ b/implementation/pending-consumer-verification.md
@@ -0,0 +1,95 @@
+# Pending consumer verification window
+
+Status: proposed focused amendment, 2026-09-08. Home37's core/provisioner design
+was approved at898fe21; this document seeks explicit approval for the change to
+the producer's live-only rule before any code. Producer32fb49c and roster contract
+4e7b3ff remain the source contracts. No live consumer activation is authorized.
+
+A pending Docs instance cannot complete actual TeamMode acceptance while call8
+refuses pending bindings. Add an audited, temporary standing window for precisely
+one pending consumer. During that window, current team members can gain actual app
+standing, subject to the existing item-permission checks and60-second roster lease.
+The chooser still shows only live bindings. This is a bounded authorization change,
+not a read-only probe or a cosmetic navigation flag.
+
+Use new additive directory tables: immutable verification_attempts keyed by operation
+ID, and verification_active keyed by consumer ID with a foreign key to its attempt.
+An attempt binds consumer/team/app/origin/generation, exact provisioning record
+sequence/digest, expected current roster epoch and owner person_ref, issued_at,
+expires_at and the signed authorization digest. Completion/rejection/activation
+history uses an additional append-only outcome table. Preserve the existing
+roster_operator_receipts CHECK enum unchanged. No schema boot creates a window.
+
+`directory.verify-consumer` uses the approved signed-private-input verifier and
+fresh console-owner maintenance approval. The exact signed payload sets absolute
+issued_at/expires_at, with positive lifetime at most600 seconds. The app never
+adds600 seconds at execution/retry time. The transaction checks positive clock,
+no future issuance, unexpired request, generation equality, pending exact registry
+binding, enabled consumer, matching record sequence/digest and epoch, and current
+owner membership matching the signed expected owner. It requires no active attempt;
+an expired attempt may be removed only from verification_active while its immutable
+attempt/outcomes remain. Never overwrite/delete its history. Write attempt, active
+pointer and sealed idempotent result with the operator receipt/sequence advancement
+in the same immediate transaction on a verified-FK-ON connection.
+
+Exact operation replay returns its prior result without extending any time. Different
+payload under the same operation ID refuses. A new deliberately approved attempt
+needs a new signed operation and actual fresh console approval; opening while the
+previous attempt is active refuses. Failure, timeout, consumer requests, hint, browser
+assertion, GET, polling and retry cannot open or renew a window. No sibling instance
+can select this consumer's window or supply its own expiry.
+
+The producer's existing one-read-transaction authorization first resolves the exact
+current/approved-overlap bearer hash, enabled consumer and matching registry. Keep
+all challenge/cursor/epoch/generation/compiled-route checks. For state=live the
+behavior remains unchanged. For state=pending it additionally joins the active
+attempt and requires exact consumer/team/app/origin/generation/provisioning record
+sequence/digest, valid positive stored times, issued_at<=now<expires_at, and matching
+still-current owner membership. Refuse disabled or any other state. Pending without
+that exact valid attempt returns the same existing fixed503 as unsupported pending.
+Wrong or foreign bearer/team retain existing401/404 behavior. A window grants no
+other pending binding, account, alias, consumer or route access. Browser chooser
+queries remain live-only.
+
+Every page, including cursor pages, rechecks the window and consumer state under the
+same read transaction. Neither a signature nor a cursor bypasses expiry. Epoch
+changes retain the ordinary signed409 pagination behavior; a completed new snapshot
+can observe current membership during the window. Activation requires new evidence
+if its expected epoch changed. Owner removal stops further pending reads immediately
+at the next source check. Producer sign/HTTP/privacy requirements remain those of the
+approved shared roster and HTTP integration; no new wire fields, cache-age shortcut
+or unsigned response is added.
+
+Expiry, disable or owner removal prevents subsequent successful fetches. Existing
+consumer leases retain at most the approved60 seconds from their last successful
+fetch start, including the fetch duration; this also applies to pending instances.
+Measure this bound in actual Docs request, commit and quiet-SSE tests. The window's
+absolute end is therefore not an instantaneous revocation guarantee: residual
+standing may last until that established lease ceiling. The producer has no timer
+or request path that renews the window. An accepted generation change still fences
+consumer standing according to the approved roster contract.
+
+`directory.activate-consumer` accepts the exact verification attempt ID and signed
+bounded activation evidence from the provisioner collectors: expected epoch/owner,
+record digest, generation, app release/artifact, checked protected environment binding,
+credential consumer manifest digest and materialized canonical alias. It requires
+that attempt to be active and unexpired and all current bindings/owner/epoch to match
+under its transaction lock. Mark only that binding live, remove its active pointer,
+append completion history, and commit its sealed result/sequence together. Exact
+replay returns the same result. Changed evidence refuses. New verification for an
+already-live binding refuses. A failed activation leaves pending unchanged and does
+not extend the active attempt. Expired evidence requires a distinct newly approved
+attempt; no automatic activation exists.
+
+Tests use actual additive schemas and real signed operator inputs plus shared roster
+signing/verifier. Cover 0/599/600-second boundaries, future/negative/overflow times,
+partial/corrupt rows, wrong token/team/generation/record/owner, current and overlap
+bearers, owner removal, disable, epoch changes between pages, cursor crossing expiry,
+no-other-pending-instance access, unchanged live path and hidden chooser. Exercise
+simultaneous open/activate, changed-payload replay, expired retry and lost-result
+recovery with exact byte-identical result. Mutations removing exact scope, expiry,
+owner or active-pointer checks must fail. Actual TeamMode canary proves member
+standing and item isolation during the window,60-second residual revocation under
+failed refresh, and final two-team chooser isolation after activation. Full gates,
+CI and Fable code review precede integration; approved/shipped platform117 and real
+fresh-owner approval precede any live operation.