docs(canon): reconcile workload and tenant grouping semantics
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a02929-244b-7391-b933-c04010e8eedb
This commit is contained in:
tegwick 2026-08-22 14:53:31 +02:00
parent ad2057acff
commit bee22db620
21 changed files with 1118 additions and 59 deletions

View file

@ -6,11 +6,11 @@ domain: netkingdom
status: proposed
version: "0.1"
created: "2026-08-17"
updated: "2026-08-19"
updated: "2026-08-22"
scope: multi-tenancy-security-framework
revision: "draft-9"
revision: "draft-12"
owner: net-kingdom
last_reviewed: "2026-08-19"
last_reviewed: "2026-08-22"
review_interval: 6m
declaration_schema: canon/schemas/tenancy-posture_v0.1.schema.json
adr:
@ -28,7 +28,7 @@ related:
## Status
**Proposed, draft-9; ratification-ready.** Relocated from
**Proposed, draft-12; ratification-ready.** Relocated from
`the-custodian/canon/architecture` on
2026-08-17: multi-tenancy is part of the IT-security framework NetKingdom
provides, so this framework belongs in NetKingdom canon beside the IAM Profile
@ -64,6 +64,24 @@ and the tenant-engine boundary contract, not in the work-factory canon.
`zones:` in `tenancy.yaml` so the estate keeps one declaration surface, and it
records the reef/`P`/`V` reconciliation as an open defect of this document
rather than of the repo that noticed it (Decision 8.4).
- **draft-10** answers `zone-engine`'s `ZONE-WP-0001-T03`. It keeps the
workload as the sole security-zone policy subject, makes operational and
control-plane execution units part of that term, and requires unresolved
workload identity or membership to remain `unknown` without zone inference
(Decision 5.6.1). It also closes the textual reef/`P` boundary while leaving
the substrate-provider declaration and mechanical `V` join as implementation
work (Decision 8.4.2).
- **draft-11** makes that ruling declarable. A `zones:` block now requires an
authoritative `workload_identity` beside it, including for non-`rapp`
operational workloads; multi-service files carry both per service. Missing
identity remains absence rather than a guessed join (Decision 5.6.2).
- **draft-12** reconciles that declaration with RMGR-ADR-004 and the published
`security-zones_v0.1` proposal. Managed deployables use their authoritative
rapp declaration and the exact Repo Manager reference tuple; independently
governed operational execution units that are not managed deployables may
declare locally. Native actions, actors, lanes, patterns, and resources are
explicitly `not-applicable`, while omitted or unresolved workload references
remain `unknown` (Decision 5.6.2).
**Reviewed by all six. The score:** six repos found three live defects in their
own code by reading the ladders — `tenant-engine`'s unfiltered
@ -650,6 +668,106 @@ joined by machine.
repos. It is an *input* to stance selection and should be read by the zone
model, not absorbed into a declaration.
**Decision 5.6.1 — the workload remains the sole zone policy subject, and
absence resolves to `unknown`.** Here **workload** means an independently
governed execution unit that performs application, automation, or operational
control-plane work and can be attributed at enforcement time to both a
responsible party and an authoritative workload identity. The independently
governed executing unit behind operator-driven or automated SSH access, tunnel
operation, credential brokering, policy compilation or enforcement, or
maintenance is a workload when such a unit exists. The observed access, tunnel,
grant, lane, pattern, or action is not itself a workload. A human or agent
identity remains caller context; it does not replace the workload whose
execution is being governed.
A repository, credential lane, grant template, pattern, or software package is
not an alternative **zone** policy subject. A broker runtime is a workload; the
grants and lanes it handles retain native resource identity and may explicitly
be `not-applicable` to workload resolution. Every managed running deployable,
including operational and tooling runtimes, has one authoritative
`rapp-*/declarations/rapp.yaml` under RMGR-ADR-004. A pre-rapp managed runtime
is migration debt and resolves `unknown`. An independently governed operational
execution unit that is not a managed deployable may declare directly in its
responsible repo's `tenancy.yaml`. The declaration surface broadens to cover
real workloads; the subject model does not broaden merely to totalize an
incomplete registry.
When a workload-applicable subject lacks an authoritative workload identity,
when its reference is absent or ambiguous, or when that identity has no
authoritative zone membership, the resolved membership MUST be `unknown`.
It MUST NOT be inferred from a repository owner, credential path, lane type,
actor class, environment, criticality, reef, organization posture, or a default
zone. A control owner MAY define an explicit, reviewable fail-safe treatment for
`unknown` — including denial, escalation, or a build-stage rule — but that
treatment is stance, not membership. `unknown` never silently inherits a
permissive zone or exception.
**Decision 5.6.2 — zone membership requires an authoritative workload binding
in the same declaration entry.** A bare service name is a label, not identity
evidence. Any single-service declaration carrying `zones:` MUST also carry
`workload_identity`; in a layer repo using `services:`, both fields live on the
same service entry. Top-level `zones:` is not valid for a multi-service file,
because it would make membership ambiguous.
The binding states:
- `name` — the stable workload id, exactly equal to the declaration's
`service`;
- `kind` — application, platform service, automation, operational control
plane, or maintenance job;
- `responsible_repo` — the repository accountable for the identity and zone
declaration;
- one or more `identity_bindings`, each naming the identity scheme,
authoritative issuer or registry, exact subject, IAM Profile principal type,
and optional environment and evidence; and
- for a managed deployable, a `declaration_ref` to its authoritative rapp
declaration. Consuming catalogs reference it with the exact Repo Manager v1
tuple `(rapp_id, workload_identity.name)` and optional `deployable`.
An identity binding uses `principal_type: service` or `agent`. A human identity
may still be required as actor or delegation context, but cannot be the sole
workload identity. A managed application, operational runtime, or tooling
runtime references its rapp declaration. Only an independently governed
operational execution unit that is not a managed deployable declares directly
in its responsible repo's `tenancy.yaml`; native actions and resources do not
acquire a fictional workload or rapp merely to enter policy.
The stable workload id is the join key. Credential lanes, grants, controls, and
compiled policy resources reference that id explicitly; compilers MUST NOT
recover it by parsing a credential path or repository name. Multiple runtime
principals may bind to one workload when environments or mechanisms differ, but
the bindings must be unique and remain owner-reviewed. If no authoritative
binding matches the runtime principal, Decision 5.6.1 returns `unknown`.
```yaml
service: ops-bridge-tunnel
role: operational-access-path
workload_identity:
name: ops-bridge-tunnel
kind: operational-control-plane
responsible_repo: ops-bridge
identity_bindings:
- scheme: ssh-certificate
authority: ops-warden
subject: agt-ops-bridge
principal_type: agent
environment: prod
zones:
standard: security-zones_v0.1
membership: z2-continuity
responsible_party: ops-bridge
justification: foundational tunnel path must retain availability under PDP loss
context:
maturity: M2
criticality: high
data_classification: confidential
evidence:
- ref: docs/evidence/ops-bridge-tunnel-zone.md
supports: [M2, continuity-dependency, recovery]
reviewed: "2026-08-22"
review_due: "2026-11-22"
```
Worked examples after applying the evidence rule and minimum-across-paths rule
consistently:
@ -702,7 +820,8 @@ normative schema is
declaration but do not replace it. The schema carries `current`, `implemented`,
`target`, `reviewed`, `review_due`, `gap`, `placement_exceptions`,
`service_class` (§8.3), per-path detail (§5.2), and provider reachability
(§5.5). From the `net-kingdom` repo, owners validate one or more declarations
(§5.5), plus the workload identity prerequisite for zone membership (§5.6.2).
From the `net-kingdom` repo, owners validate one or more declarations
with `uv run tools/tenancy-posture/validate.py <path>...`; the validator applies
the JSON Schema and the evidence, date, implemented/current and provider-range
rules that JSON Schema alone cannot express.
@ -845,6 +964,14 @@ not. `P` grades **tenant data isolation within a datastore**; a reef is a named
rung meaning "single node, shared control plane, risk accepted", and inventing
one would be the fabrication §6 prohibits.
A reef binding therefore satisfies none of the `P` couplings in Decision 3.2
by itself. It records the compute substrate and its accepted residual risk; it
does not establish a database-per-consumer or database-per-tenant boundary, a
per-tenant credential or encryption boundary, or an erasure horizon. Those
remain properties of the workload and data provider declarations. The binding
does participate in `V`, because availability composes across the complete
critical path and the substrate can impose a ceiling.
The live consequence is on `V`, not `P`. §4.6 already warns that "a dedicated
cluster can still be a single instance on a single node", and Decision 4.6.1
makes `V` the minimum across the synchronous path. `reef-railiance` is
@ -1265,12 +1392,10 @@ nothing about this availability fact; the new V axis carries it.
See §8.3. The question asked whether to add a QoS dimension; the answer is
no, and the reason is that we could not enforce one.
**Routed elsewhere, deliberately.** The tenant identifier
`tenant:<grouping>:<name>` embeds headcount bands (`small`, `medium`, `large`)
that change as a tenant grows, contradicting the consensus that identifiers
should not encode mutable attributes. That is a critique of ADR-0013, not of
this framework, and belongs to `tenant-engine` and NetKingdom canon. Folding it
in here would overreach.
**Tenant grouping ambiguity — resolved outside this framework.** ADR-0013
revision 2 makes the identifier segment immutable onboarding-time history and
the `tenant-engine` record authoritative for current grouping. Consumers do not
parse current policy or spend-ceiling inputs from `tenant:<grouping>:<name>`.
## 20. Ratification path