From cf0bd0da20921ec5f427406748a261708a596fcf Mon Sep 17 00:00:00 2001 From: tegwick Date: Tue, 29 Sep 2026 22:59:23 +0200 Subject: [PATCH 1/2] Propose ADR-0017: root-grant custody, tenant:platform mapping, observation evidence Answers user-engine's USER-WP-0037-T02 request; custody doc points to it. Co-Authored-By: Claude Sonnet 5.5 Assistant: claude-code Assistant-Model: sonnet Assistant-Process: 6969@bnt-lap001 Assistant-Session: e8ae73f2-8385-4c9c-b714-94c3c52cdbc0 --- ...oot-grant-custody-and-account-authority.md | 91 +++++++++++++++++++ docs/platform-root-custody.md | 15 +++ 2 files changed, 106 insertions(+) create mode 100644 docs/adr/ADR-0017-root-grant-custody-and-account-authority.md diff --git a/docs/adr/ADR-0017-root-grant-custody-and-account-authority.md b/docs/adr/ADR-0017-root-grant-custody-and-account-authority.md new file mode 100644 index 0000000..0d62e64 --- /dev/null +++ b/docs/adr/ADR-0017-root-grant-custody-and-account-authority.md @@ -0,0 +1,91 @@ +--- +id: NK-ADR-0017 +type: architecture-decision-record +title: "The Root Grant Is Derived From the Custody Record, Scoped to tenant:platform, and Observed as Non-Secret Evidence" +status: proposed +owner: net-kingdom +revision: "1" +decided: "2026-09-29" +last_reviewed: "2026-09-29" +review_interval: 12m +--- + +# ADR-0017 - Root-Grant Custody and Account Authority + +**Status:** Proposed (drafted on the defaults agreed with the operator; accept after user-engine review) +**Date:** 2026-09-29 +**Deciders:** Bernd Worsch, Claude + +## Context + +user-engine implemented `account.authority.read` (USER-WP-0037-T01) and a +workload-authenticated lookup endpoint. It will not derive +`AccountAuthority.root-grant` from JWT roles, a username, or a consumer's +configured root subject, and USER-WP-0037-T02 is blocked on NetKingdom +specifying the authority. `docs/platform-root-custody.md` covers custody of the +platform-root credential and OpenBao material, but not how a workload-facing +"subject X holds the root grant" fact is derived, revoked, or evidenced. + +## Decision + +### 1. Authority: the custody record; directory membership is a projection + +The **platform-root custody record** (the non-secret ceremony record: custodian +assignment, quorum plan, root-token disposition, signoff) is the single +authority for who holds the root grant. + +- A root grant exists for a subject only while a custody record names that + `(issuer, subject)` as king/platform-root custodian and no later record + revokes it. +- LLDAP `platform-root` membership and IAM claims (`groups: platform-root`, + `roles: platform-root-custodian`) are **projections** of the record. They are + authentication facts and MUST NOT be used alone to derive a root grant. +- **Revocation** is a recorded custody action: a new record entry that + supersedes the grant, with actor, reason, and time. Revocation must not + remove recovery authority (break-glass under king custody stays intact). +- If the record and a projection disagree, the record wins and the mismatch is + a posture finding, not a grant. + +### 2. Mapping: `platform:root` grants `tenant:platform` only + +- A root grant is expressed as relation `platform-root-custodian` in tenant + context `tenant:platform`. It is scoped to the platform control plane. +- It is never inherited by, or implied for, any other tenant, and tenant admin + authority never implies it (consistent with the user-engine boundary + contract and IAM Profile v0.3). +- The grant is a distinct grant type in the membership envelope + (`scope_type: tenant`, `scope_id: platform`, `relation: + platform-root-custodian`, `owner_system: net-kingdom-custody`). Consumers + such as the Hub read it from user-engine; they do not mint it. + +### 3. Evidence: non-secret observations with explicit freshness + +An account/entitlement observation retained by a consumer contains only: +`subject` (`issuer`, `subject`), `grant`, `source` (custody record id and +revision), `observed_at`, and `revoked_at` or `expires_at` when known. It never +contains tokens, keys, unseal shares, recovery codes, or reset links. + +- Observations are read models: they carry `observed_at` and are re-derived from + the custody record rather than treated as durable truth. +- A consumer MAY cache an observation for at most **15 minutes**; sensitive + platform actions (root-grant-dependent writes) MUST re-check and fail closed + when the record cannot be read or the observation is stale. +- Revocation takes effect at the next re-check; the cache bound is the maximum + revocation latency. + +## Consequences + +- user-engine can represent `AccountAuthority.root-grant` deterministically from + the custody record id and revision, and unblocks USER-WP-0037-T02. +- The custody record needs a machine-readable, non-secret form that a workload + can read. Its shape and location are an open follow-up (below); until it + exists, user-engine returns "no root grant" rather than inferring one. +- The 15-minute bound and the projection-versus-record rule are decisions of + this ADR and may be tightened at review. + +## Open follow-ups + +- Define the machine-readable custody-record schema and read surface (owner: + net-kingdom; delivery of the read path may involve railiance-platform). +- Confirm with user-engine that the membership-envelope mapping above is + sufficient for `AccountAuthority`. diff --git a/docs/platform-root-custody.md b/docs/platform-root-custody.md index b86d984..1aea1ed 100644 --- a/docs/platform-root-custody.md +++ b/docs/platform-root-custody.md @@ -305,6 +305,21 @@ admin for `tenant:coulomb` or another tenant must not imply OpenBao root, NetKingdom identity-admin, flex-auth platform-policy, or Railiance platform-service authority. +## Root Grant as a Workload-Facing Fact + +Consumers such as the Hub and user-engine need to know whether a subject holds +the root grant. That fact is derived from the custody record, not from claims. +See [ADR-0017](adr/ADR-0017-root-grant-custody-and-account-authority.md): + +- the custody record is the authority; LLDAP membership and the claims above are + projections and never sufficient alone; +- revocation is a recorded custody action that does not remove recovery + authority; +- the grant is `platform-root-custodian` in `tenant:platform` only, and never + implied by tenant admin; and +- retained observations are non-secret, carry `observed_at`, may be cached for at + most 15 minutes, and fail closed for root-dependent writes when stale. + ## OpenBao Bootstrap Sequence The Railiance OpenBao runbook owns the live commands. NetKingdom owns the From df2873ce54f37e03bb662275e5bedfb51dc0e63f Mon Sep 17 00:00:00 2001 From: tegwick Date: Tue, 29 Sep 2026 23:00:04 +0200 Subject: [PATCH 2/2] Add NK-WP-0045: machine-readable custody record for the root grant Co-Authored-By: Claude Sonnet 5.5 Assistant: claude-code Assistant-Model: sonnet Assistant-Process: 6969@bnt-lap001 Assistant-Session: e8ae73f2-8385-4c9c-b714-94c3c52cdbc0 --- .../NK-WP-0045-root-grant-custody-record.md | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 workplans/NK-WP-0045-root-grant-custody-record.md diff --git a/workplans/NK-WP-0045-root-grant-custody-record.md b/workplans/NK-WP-0045-root-grant-custody-record.md new file mode 100644 index 0000000..cf68097 --- /dev/null +++ b/workplans/NK-WP-0045-root-grant-custody-record.md @@ -0,0 +1,107 @@ +--- +id: NK-WP-0045 +type: workplan +title: "Make the platform-root custody record readable as a non-secret root-grant fact" +domain: infotech +repo: net-kingdom +status: proposed +flavor: implementation +owner: claude-code +topic_slug: netkingdom +created: "2026-09-29" +updated: "2026-09-29" +related: [NK-ADR-0017, NK-WP-0042] +--- + +NK-ADR-0017 makes the platform-root custody record the authority for the root +grant, but the record has no machine-readable, non-secret form a workload can +read. Until it does, user-engine returns "no root grant" and USER-WP-0037-T02 +stays blocked. This workplan defines that form and its read surface. It does +not change custody itself, and no secret material enters the record. + +## Accept ADR-0017 + +```task +id: NK-WP-0045-T01 +status: wait +needs_human: true +blocking_reason: "Bernd accepts or amends NK-ADR-0017 (proposed) after user-engine confirms the AccountAuthority mapping." +priority: high +``` + +Resolve the open points (15-minute cache bound, envelope mapping) and move the +ADR to `accepted`. + +## Define the custody-record schema + +```task +id: NK-WP-0045-T02 +status: todo +depends_on: [NK-WP-0045-T01] +priority: high +``` + +Add `canon/schemas/custody-record_v0.1.schema.json`: record id and revision, +`(issuer, subject)`, relation `platform-root-custodian`, `tenant:platform`, +granted-at, superseded-by, revoked-at with actor and reason. Non-secret only; +the schema rejects token, key, share and recovery-code fields. + +## Add a validator and fixtures + +```task +id: NK-WP-0045-T03 +status: todo +depends_on: [NK-WP-0045-T02] +priority: medium +``` + +Validator with grant, revocation and supersession fixtures, plus negative cases: +projection-only grants, tenant-admin implication, secret-bearing fields. +Follow the existing tool layout under `tools/`. + +## Decide the read surface + +```task +id: NK-WP-0045-T04 +status: wait +depends_on: [NK-WP-0045-T02] +needs_human: true +blocking_reason: "Needs a decision on where a workload reads the record (OpenBao-held, platform service, or repository artifact) and which owner delivers it." +priority: high +``` + +Options and the owner (net-kingdom versus railiance-platform) go to Bernd; the +answer fixes T05. + +## Deliver the read path with the owner + +```task +id: NK-WP-0045-T05 +status: wait +depends_on: [NK-WP-0045-T04] +blocking_reason: "Depends on the read-surface decision and on the delivering owner." +priority: medium +``` + +Hand off the schema and read contract to the owner; NetKingdom does not absorb +the runtime. Include the fail-closed and staleness rules from ADR-0017. + +## Close the loop with user-engine + +```task +id: NK-WP-0045-T06 +status: wait +depends_on: [NK-WP-0045-T05] +blocking_reason: "Needs the delivered read path." +priority: medium +``` + +Tell user-engine the read path is live so USER-WP-0037-T02 can derive +`AccountAuthority.root-grant` from it, and record the evidence. + +## Acceptance Criteria + +- The custody-record schema and validator exist and reject secret-bearing and + projection-only inputs. +- A workload can read the root-grant fact from an owner-delivered surface. +- user-engine derives the root grant without inferring it from claims.