Delegate tenant lifecycle to the tenant authority
Some checks are pending
CI Smoke / container-smoke (push) Waiting to run
CI Smoke / host-smoke (push) Successful in 0s

TEN-WP-0005 landed the authoritative metadata update and reversible
retirement contract, so USER-WP-0021-T01's deferred tenant operations are
now implementable without user-engine inventing lifecycle semantics.

TenantManagementPort gains read, update, retire, and reactivate. The HTTP
adapter echoes the record version as an If-Match ETag (never `*`), sends an
Idempotency-Key plus actor/reason/correlation_id, and surfaces
Idempotent-Replay. Authority failures map to redacted domain errors carrying
only the contract's stable error_code; its detail text never crosses the
boundary.

Platform operators get the matching API routes and a CSRF-protected browser
screen that reads the record before mutating it and hides the metadata form
for a retired tenant. Portal OpenAPI moves to 0.3.0 with TenantRecord,
UpdateTenant, and TenantLifecycleChange.

Full suite: 145 tests, 3 external-provider skips.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
tegwick 2026-08-16 01:28:02 +02:00
parent db8769cc8c
commit 667ea694c2
8 changed files with 952 additions and 11 deletions

View file

@ -8,7 +8,7 @@ status: blocked
owner: codex
topic_slug: netkingdom
created: "2026-08-09"
updated: "2026-08-10"
updated: "2026-08-16"
depends_on:
- USER-WP-0021
- NK-WP-0025
@ -110,6 +110,20 @@ creating another session or requesting another email; reuse with different
inputs returns 409. Raw keys and applicant fields are absent from idempotency
evidence. The full suite passes 134 tests with three external skips.
2026-08-16 gate review: every implementation item listed for this task is
built and covered — accessible start/verify/resume/cancel entry points,
allow-listed anonymous routes, opaque intent handles, CSRF, per-peer rate
limiting, required idempotency keys, and non-enumerating responses. The
application-level registration limit is configured in the runtime manifest on
top of the Traefik-wide limit, closing the ingress defense-in-depth item.
What remains is not user-engine code. Public registration stays disabled until
the OpenBao verification and delivery tokens plus the transactional SMTP lane
are delivered through governed channels. The service already fails closed when
they are absent, and no placeholder or reused credential will be added to
unblock it. This task stays in `wait` because the remaining gate is a
credential-custody handoff, not an outstanding change here.
## T02 - Orchestrate provider identity creation
```task
@ -221,6 +235,13 @@ the identity-link uniqueness boundary, leaves the original link unchanged, and
never transfers that identity to the later registration. The full suite passes
131 tests with three environment-dependent skips.
2026-08-16 authorization evidence: flex-auth reports FLEX-WP-0009 finished and
handed back deployed-policy evidence for the user-engine surface, including the
three registration-applicant fixtures (applicant allow, membership deny, wrong
issuer deny) replayed against the live `POST /v1/check`. The fail-closed
`FlexAuthHTTPAdapter` maps a missing or refused service to deny. This removes
authorization from this task's gate; only the credential handoff remains.
The latest tested image is published at
`forgejo.coulomb.social/coulomb/user-engine@sha256:a49a0a105d392e374f6da958f8ad1bff36f2cfa8779ac5cafa479b7f34b6efc1`;
the matching email-connect image is