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 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: sonnet
Assistant-Process: 6969@bnt-lap001
Assistant-Session: e8ae73f2-8385-4c9c-b714-94c3c52cdbc0
This commit is contained in:
tegwick 2026-09-29 22:59:23 +02:00
parent f45fe7afa7
commit cf0bd0da20
2 changed files with 106 additions and 0 deletions

View file

@ -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`.