From b9a7d1b00853cf5bd07e0d409bc85daa957d06a5 Mon Sep 17 00:00:00 2001 From: tegwick Date: Thu, 23 Jul 2026 14:34:40 +0200 Subject: [PATCH] Draft KEY-WP-0004: Binky Hedgehog GmbH as first tenant + qonto-assistant workload identity Provisions the first real NetKingdom tenant (tenant:customer:binky, matching the slug already live in production OpenBao paths) with a human tenant-admin user and a workload-identity OIDC client for qonto-assistant's MCP callers, routed through secrets-engine for secret custody and ops-warden for discoverable, minimal-touch credential access. Closes Gap #1 flagged in qonto-assistant's QONTO-WP-0003 closure (no OIDC issuer existed in the fleet). Co-Authored-By: Claude Sonnet 5 --- ...P-0004-binky-hedgehog-tenant-onboarding.md | 206 ++++++++++++++++++ 1 file changed, 206 insertions(+) create mode 100644 workplans/KEY-WP-0004-binky-hedgehog-tenant-onboarding.md diff --git a/workplans/KEY-WP-0004-binky-hedgehog-tenant-onboarding.md b/workplans/KEY-WP-0004-binky-hedgehog-tenant-onboarding.md new file mode 100644 index 0000000..a881328 --- /dev/null +++ b/workplans/KEY-WP-0004-binky-hedgehog-tenant-onboarding.md @@ -0,0 +1,206 @@ +--- +id: KEY-WP-0004 +type: workplan +title: "Binky Hedgehog GmbH as first NetKingdom tenant + qonto-assistant workload identity" +domain: infotech +repo: key-cape +status: ready +owner: codex +topic_slug: netkingdom +created: "2026-07-23" +updated: "2026-07-23" +--- + +# Binky Hedgehog GmbH as first NetKingdom tenant + qonto-assistant workload identity + +Binky Hedgehog GmbH ("binky") becomes the **first real tenant** provisioned in +the NetKingdom IAM stack, per the NetKingdom IAM Profile v0.2's tenant claim +model (`net-kingdom/canon/standards/iam-profile_v0.2.md` §"Tenant Claim") — +distinct from `tenant:platform` (control plane) and `tenant:coulomb` (the +ecosystem-development tenant this workstation already operates under). + +This closes **Gap #1** flagged in `qonto-assistant`'s +`workplans/QONTO-WP-0003-mcp-surface.md` closure: no OIDC issuer existed +anywhere in the fleet, so `qonto-assistant`'s MCP surface shipped with an +interim shared-secret bearer token instead of real workload identity. +`key-cape` is "Implementation complete (v0.1)" and already implements the +profile's `client_credentials`/workload-identity grant for service accounts +— it's the component that should close this gap, not `user-engine` +(downstream user/profile data layer, not an identity provider) or +`identity-canon` (terminology research only). + +**Depends on:** `key-cape` v0.1 (done, `KEY-WP-0001`), NetKingdom IAM Profile +v0.2 tenant + service-account claim rules (already ratified canon — no new +profile work needed, this is provisioning against an existing contract). + +**Downstream, not in this workplan:** once T03/T04 land, `qonto-assistant` +needs its own follow-up workplan to swap `mcp_auth.BearerTokenAuthMiddleware` +for `FastMCP`'s built-in `TokenVerifier` + `AuthSettings(issuer_url=)` +against the client registered here. Tracked as a pointer only — not created +by this workplan, since it's a different repo's scope and code change. + +## Task: Confirm tenant identifier and provision the tenant plane + +```task +id: KEY-WP-0004-T01 +status: todo +priority: high +``` + +The IAM profile's tenant identifiers (`tenant:platform`, `tenant:coulomb`, +`tenant:sandbox:`, `tenant:customer:`) are documented as +*suggested*, not exhaustive — but `tenant:customer:` is the closest fit +for the first real operating/customer-plane tenant, distinct from the +`tenant:coulomb` ecosystem-development plane. + +**Recommended default, to confirm before implementing:** `tenant:customer:binky` +— matching the slug already live in production OpenBao paths +(`tenants/binky/qonto-api`, used by `qonto-assistant` today) rather than +`tenant:customer:binky-hedgehog`, to avoid a slug mismatch between the new +IAM tenant claim and the existing secret-custody path convention. Flag to +Bernd for explicit confirmation before provisioning — tenant identifiers are +expensive to rename once tokens and downstream config reference them. + +Provision whatever tenant/org-unit boundary key-cape's current backend +(LLDAP/Authelia) needs so tokens issued for subjects under this tenant +correctly carry `tenant:customer:binky` and nothing implies platform-root +authority for it (profile rule: tenant administration for a tenant must +never imply platform-root authority). + +Done when: tenant identifier confirmed and recorded (decision record or +canon note); a test-subject token carries the correct `tenant` claim. + +## Task: Provision bernd.worsch@binky-hedgehog.com as tenant-admin + +```task +id: KEY-WP-0004-T02 +status: todo +priority: high +``` + +Create the user in key-cape's current backend, enroll MFA per profile +requirements (`assurance` claim, `net-kingdom/canon/standards/iam-profile_v0.2.md`), +and assign a tenant-admin role/group scoped to `tenant:customer:binky` only. + +**Prerequisite outside this repo's control:** the `binky-hedgehog.com` mailbox +for `bernd.worsch@` must exist and be reachable for account verification and +MFA enrollment. Flag to Bernd before starting — this task cannot complete +without it. + +Done when: `bernd.worsch@binky-hedgehog.com` completes OIDC/PKCE + MFA login +and receives a token carrying `tenant:customer:binky` and a tenant-admin role +claim; the same token does **not** grant access to `tenant:platform` or any +other tenant. + +## Task: Register a workload-identity OIDC client for qonto-assistant callers + +```task +id: KEY-WP-0004-T03 +status: todo +priority: high +``` + +Register an OIDC client per the profile's service-account/workload-identity +flow (`client_credentials` or the documented workload-token-exchange +equivalent — §"Service Account Flow"), scoped to `tenant:customer:binky`, +with the minimum role/scope `qonto-assistant`'s MCP and REST surfaces +actually need. No interactive login for this client — it's for agent-harness +sessions and other automated callers, not humans. + +The resulting `client_secret` must never be pasted into chat, Git, this +workplan, or a State Hub message or progress event — hand-off happens only +through T04's secrets-engine/OpenBao lane. + +Done when: a `client_credentials` token exchange against key-cape succeeds +for this client and produces a short-lived service token carrying +`tenant:customer:binky`; the `client_id` and granted scopes are documented +here; no secret material appears anywhere in this repo's history. + +## Task: Route client_secret custody through secrets-engine + OpenBao + +```task +id: KEY-WP-0004-T04 +status: todo +priority: high +``` + +Coordinate with `secrets-engine` to create a scoped OpenBao lane for this +`client_secret`, mirroring the existing `tenants/binky/qonto-api` pattern +already in production use for the Qonto bank credential itself — so +`qonto-assistant`'s runtime fetches this the same way it already fetches the +Qonto API key, via its own dedicated runtime role +(`qonto-assistant-runtime`, per `qonto-assistant/specs/ArchitectureBlueprint.md` +§4.7), not a shared or ad hoc role. + +Done when: `qonto-assistant`'s runtime role can fetch the `client_secret` via +`secrets-engine`/OpenBao; Bernd never handles the raw value at any point in +this flow. + +## Task: Register the credential lane with ops-warden + +```task +id: KEY-WP-0004-T05 +status: todo +priority: high +``` + +This is the task that directly minimizes Bernd's ongoing engagement: add an +entry to ops-warden's routing catalog (`registry/routing/catalog.yaml`) +pointing at this new OIDC client / login lane, following the pattern already +documented in `ops-warden/SCOPE.md`'s "Issue vs route" table ("Login / OIDC / +MFA → key-cape / Keycloak → Assist — route; proxy `login` lane when +`exec_capable`"). ops-warden does not mint tokens or take custody here — it +routes/assists, same as every other credential lane it fronts. + +Once the OpenBao lane from T04 exists, run ops-warden's workload security +posture conformance checker +(`ops-warden/scripts/check_secret_posture_conformance.py`) against the new +`qonto-assistant` workload to confirm it meets the expected maturity tier +(dev/test/prod posture, M0–M3 workload maturity) before this goes live with +real credentials, and record the result (pass or documented exception). + +Done when: `warden route show` (or equivalent) resolves this lane correctly; +posture conformance check passes or exceptions are explicitly recorded; from +this point on, discovering and using this credential lane needs `warden +access`, not direct key-cape/OpenBao admin knowledge. + +## Task: automation@binky-hedgehog.com service identity (deferred) + +```task +id: KEY-WP-0004-T06 +status: todo +priority: low +``` + +Forward-looking per Bernd's framing ("I guess it will later be helpful") — +**do not provision until a concrete automation workflow actually needs its +own identity** distinct from the `qonto-assistant` workload client from T03. +Dormant service-account credentials are themselves a posture risk (unused +attack surface, nothing to rotate against), so this stays parked rather than +front-loaded. + +When a real consuming workflow exists: same pattern as T02 but scoped to an +automation-appropriate role (not tenant-admin), likely paired with its own +`client_credentials` registration for whichever agent/service authenticates +as it, following T03/T04/T05's pattern rather than inventing a new one. + +Done when: explicitly triggered by a named consuming workflow — until then +this task stays `todo` and does not block closing the rest of this workplan. + +## Task: Closure review + +```task +id: KEY-WP-0004-T07 +status: todo +priority: low +``` + +Mark workplan finished when T01–T05 are done (T06 may legitimately remain +`todo`/parked by design — see its own task note). Confirm end to end: a +human token (bernd.worsch@binky-hedgehog.com, tenant-admin) and a service +token (qonto-assistant workload client) both resolve to `tenant:customer:binky` +and nothing else; the credential lane is discoverable via `warden access` +without Bernd touching key-cape/OpenBao admin surfaces directly. Note the +`qonto-assistant`-side follow-up workplan (swap bearer token for +`TokenVerifier`) as the next step, out of this workplan's scope. Run +`statehub fix-consistency`.