docs(canon): reconcile workload and tenant grouping semantics
Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a02929-244b-7391-b933-c04010e8eedb
This commit is contained in:
parent
ad2057acff
commit
bee22db620
21 changed files with 1118 additions and 59 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -83,6 +83,7 @@ instance/
|
||||||
|
|
||||||
# Sphinx documentation
|
# Sphinx documentation
|
||||||
docs/_build/
|
docs/_build/
|
||||||
|
docs/architecture/.ast_cache/
|
||||||
|
|
||||||
# PyBuilder
|
# PyBuilder
|
||||||
.pybuilder/
|
.pybuilder/
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,8 @@
|
||||||
| workplan | NK-WP-0024 | finished | — | workplans/NK-WP-0024-user-engine-portal-integration-expansion.md |
|
| workplan | NK-WP-0024 | finished | — | workplans/NK-WP-0024-user-engine-portal-integration-expansion.md |
|
||||||
| workplan | NK-WP-0025 | finished | — | workplans/NK-WP-0025-public-self-registration-and-application-jit.md |
|
| workplan | NK-WP-0025 | finished | — | workplans/NK-WP-0025-public-self-registration-and-application-jit.md |
|
||||||
| workplan | NK-WP-0026 | finished | — | workplans/NK-WP-0026-flex-auth-caller-identity-rollout.md |
|
| workplan | NK-WP-0026 | finished | — | workplans/NK-WP-0026-flex-auth-caller-identity-rollout.md |
|
||||||
|
| workplan | NK-WP-0027 | blocked | — | workplans/NK-WP-0027-reef-placement-reconciliation.md |
|
||||||
|
| workplan | NK-WP-0028 | finished | — | workplans/NK-WP-0028-canon-publication-and-grouping-semantics.md |
|
||||||
| task | ADHOC-2026-07-02-T01 | done | — | workplans/ADHOC-2026-07-02.md |
|
| task | ADHOC-2026-07-02-T01 | done | — | workplans/ADHOC-2026-07-02.md |
|
||||||
| task | ADHOC-2026-07-02-T02 | done | — | workplans/ADHOC-2026-07-02.md |
|
| task | ADHOC-2026-07-02-T02 | done | — | workplans/ADHOC-2026-07-02.md |
|
||||||
| task | ADHOC-2026-08-14-T01 | done | — | workplans/ADHOC-2026-08-14.md |
|
| task | ADHOC-2026-08-14-T01 | done | — | workplans/ADHOC-2026-08-14.md |
|
||||||
|
|
@ -29,20 +31,20 @@
|
||||||
| task | NET-WP-0020-T03 | done | — | workplans/NET-WP-0020-openbao-unseal-custody-and-ssh-automation.md |
|
| task | NET-WP-0020-T03 | done | — | workplans/NET-WP-0020-openbao-unseal-custody-and-ssh-automation.md |
|
||||||
| task | NET-WP-0020-T04 | done | — | workplans/NET-WP-0020-openbao-unseal-custody-and-ssh-automation.md |
|
| task | NET-WP-0020-T04 | done | — | workplans/NET-WP-0020-openbao-unseal-custody-and-ssh-automation.md |
|
||||||
| task | NET-WP-0020-T05 | done | — | workplans/NET-WP-0020-openbao-unseal-custody-and-ssh-automation.md |
|
| task | NET-WP-0020-T05 | done | — | workplans/NET-WP-0020-openbao-unseal-custody-and-ssh-automation.md |
|
||||||
| task | NK-WP-0009-T1 | todo | — | workplans/NK-WP-0009-netkingdom-security-pattern-tutorials.md |
|
| task | NK-WP-0009-T01 | todo | — | workplans/NK-WP-0009-netkingdom-security-pattern-tutorials.md |
|
||||||
| task | NK-WP-0009-T2 | todo | — | workplans/NK-WP-0009-netkingdom-security-pattern-tutorials.md |
|
| task | NK-WP-0009-T02 | todo | — | workplans/NK-WP-0009-netkingdom-security-pattern-tutorials.md |
|
||||||
| task | NK-WP-0009-T3 | todo | — | workplans/NK-WP-0009-netkingdom-security-pattern-tutorials.md |
|
| task | NK-WP-0009-T03 | todo | — | workplans/NK-WP-0009-netkingdom-security-pattern-tutorials.md |
|
||||||
| task | NK-WP-0009-T4 | todo | — | workplans/NK-WP-0009-netkingdom-security-pattern-tutorials.md |
|
| task | NK-WP-0009-T04 | todo | — | workplans/NK-WP-0009-netkingdom-security-pattern-tutorials.md |
|
||||||
| task | NK-WP-0009-T5 | todo | — | workplans/NK-WP-0009-netkingdom-security-pattern-tutorials.md |
|
| task | NK-WP-0009-T05 | todo | — | workplans/NK-WP-0009-netkingdom-security-pattern-tutorials.md |
|
||||||
| task | NK-WP-0009-T6 | todo | — | workplans/NK-WP-0009-netkingdom-security-pattern-tutorials.md |
|
| task | NK-WP-0009-T06 | todo | — | workplans/NK-WP-0009-netkingdom-security-pattern-tutorials.md |
|
||||||
| task | NK-WP-0011-T1 | todo | — | workplans/NK-WP-0011-enterprise-federation-saml.md |
|
| task | NK-WP-0011-T01 | todo | — | workplans/NK-WP-0011-enterprise-federation-saml.md |
|
||||||
| task | NK-WP-0011-T2 | todo | — | workplans/NK-WP-0011-enterprise-federation-saml.md |
|
| task | NK-WP-0011-T02 | todo | — | workplans/NK-WP-0011-enterprise-federation-saml.md |
|
||||||
| task | NK-WP-0011-T3 | todo | — | workplans/NK-WP-0011-enterprise-federation-saml.md |
|
| task | NK-WP-0011-T03 | todo | — | workplans/NK-WP-0011-enterprise-federation-saml.md |
|
||||||
| task | NK-WP-0011-T4 | todo | — | workplans/NK-WP-0011-enterprise-federation-saml.md |
|
| task | NK-WP-0011-T04 | todo | — | workplans/NK-WP-0011-enterprise-federation-saml.md |
|
||||||
| task | NK-WP-0011-T5 | todo | — | workplans/NK-WP-0011-enterprise-federation-saml.md |
|
| task | NK-WP-0011-T05 | todo | — | workplans/NK-WP-0011-enterprise-federation-saml.md |
|
||||||
| task | NK-WP-0011-T6 | todo | — | workplans/NK-WP-0011-enterprise-federation-saml.md |
|
| task | NK-WP-0011-T06 | todo | — | workplans/NK-WP-0011-enterprise-federation-saml.md |
|
||||||
| task | NK-WP-0011-T7 | todo | — | workplans/NK-WP-0011-enterprise-federation-saml.md |
|
| task | NK-WP-0011-T07 | todo | — | workplans/NK-WP-0011-enterprise-federation-saml.md |
|
||||||
| task | NK-WP-0011-T8 | todo | — | workplans/NK-WP-0011-enterprise-federation-saml.md |
|
| task | NK-WP-0011-T08 | todo | — | workplans/NK-WP-0011-enterprise-federation-saml.md |
|
||||||
| task | NK-WP-0021-T01 | done | — | workplans/NK-WP-0021-activity-core-ops-sso-operators.md |
|
| task | NK-WP-0021-T01 | done | — | workplans/NK-WP-0021-activity-core-ops-sso-operators.md |
|
||||||
| task | NK-WP-0021-T02 | done | — | workplans/NK-WP-0021-activity-core-ops-sso-operators.md |
|
| task | NK-WP-0021-T02 | done | — | workplans/NK-WP-0021-activity-core-ops-sso-operators.md |
|
||||||
| task | NK-WP-0021-T03 | done | — | workplans/NK-WP-0021-activity-core-ops-sso-operators.md |
|
| task | NK-WP-0021-T03 | done | — | workplans/NK-WP-0021-activity-core-ops-sso-operators.md |
|
||||||
|
|
@ -75,5 +77,15 @@
|
||||||
| task | NK-WP-0025-T05 | done | — | workplans/NK-WP-0025-public-self-registration-and-application-jit.md |
|
| task | NK-WP-0025-T05 | done | — | workplans/NK-WP-0025-public-self-registration-and-application-jit.md |
|
||||||
| task | NK-WP-0026-T01 | done | — | workplans/NK-WP-0026-flex-auth-caller-identity-rollout.md |
|
| task | NK-WP-0026-T01 | done | — | workplans/NK-WP-0026-flex-auth-caller-identity-rollout.md |
|
||||||
| task | NK-WP-0026-T02 | done | — | workplans/NK-WP-0026-flex-auth-caller-identity-rollout.md |
|
| task | NK-WP-0026-T02 | done | — | workplans/NK-WP-0026-flex-auth-caller-identity-rollout.md |
|
||||||
|
| task | NK-WP-0027-T01 | done | — | workplans/NK-WP-0027-reef-placement-reconciliation.md |
|
||||||
|
| task | NK-WP-0027-T02 | wait | — | workplans/NK-WP-0027-reef-placement-reconciliation.md |
|
||||||
|
| task | NK-WP-0027-T03 | wait | — | workplans/NK-WP-0027-reef-placement-reconciliation.md |
|
||||||
|
| task | NK-WP-0027-T04 | done | — | workplans/NK-WP-0027-reef-placement-reconciliation.md |
|
||||||
|
| task | NK-WP-0027-T05 | done | — | workplans/NK-WP-0027-reef-placement-reconciliation.md |
|
||||||
|
| task | NK-WP-0027-T06 | wait | — | workplans/NK-WP-0027-reef-placement-reconciliation.md |
|
||||||
|
| task | NK-WP-0028-T01 | done | — | workplans/NK-WP-0028-canon-publication-and-grouping-semantics.md |
|
||||||
|
| task | NK-WP-0028-T02 | done | — | workplans/NK-WP-0028-canon-publication-and-grouping-semantics.md |
|
||||||
|
| task | NK-WP-0028-T03 | done | — | workplans/NK-WP-0028-canon-publication-and-grouping-semantics.md |
|
||||||
|
| task | NK-WP-0028-T04 | done | — | workplans/NK-WP-0028-canon-publication-and-grouping-semantics.md |
|
||||||
| intake | NK-IN-0001 | closed | blue | docs/intakes/activity-core-ops-sso-operators.md |
|
| intake | NK-IN-0001 | closed | blue | docs/intakes/activity-core-ops-sso-operators.md |
|
||||||
| intake | NK-IN-0002 | closed | blue | docs/intakes/activity-core-ops-sso-operators.md |
|
| intake | NK-IN-0002 | closed | blue | docs/intakes/activity-core-ops-sso-operators.md |
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,9 @@
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"minLength": 1
|
"minLength": 1
|
||||||
},
|
},
|
||||||
|
"workload_identity": {
|
||||||
|
"$ref": "#/$defs/workloadIdentity"
|
||||||
|
},
|
||||||
"tenancy": {
|
"tenancy": {
|
||||||
"$ref": "#/$defs/tenancy"
|
"$ref": "#/$defs/tenancy"
|
||||||
},
|
},
|
||||||
|
|
@ -28,7 +31,7 @@
|
||||||
"$ref": "#/$defs/provider"
|
"$ref": "#/$defs/provider"
|
||||||
},
|
},
|
||||||
"zones": {
|
"zones": {
|
||||||
"description": "Reserved for security-zone membership (tenancy-posture_v0.1 Decision 5.6). Its internal shape is defined by the NetKingdom security-zone standard drafted by zone-engine, not by this schema, and is deliberately unconstrained here until that standard lands. Present so a conformant combined declaration is not rejected by this validator."
|
"$ref": "#/$defs/zoneDeclaration"
|
||||||
},
|
},
|
||||||
"evidence": {
|
"evidence": {
|
||||||
"$ref": "#/$defs/evidence"
|
"$ref": "#/$defs/evidence"
|
||||||
|
|
@ -83,6 +86,16 @@
|
||||||
"provider"
|
"provider"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"required": [
|
||||||
|
"workload_identity"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"required": [
|
||||||
|
"zones"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"required": [
|
"required": [
|
||||||
"evidence"
|
"evidence"
|
||||||
|
|
@ -92,12 +105,30 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"if": {
|
||||||
|
"required": [
|
||||||
|
"zones"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"then": {
|
||||||
|
"required": [
|
||||||
|
"workload_identity"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"$defs": {
|
"$defs": {
|
||||||
"serviceName": {
|
"serviceName": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"pattern": "^[a-z0-9][a-z0-9._-]*$"
|
"pattern": "^[a-z0-9][a-z0-9._-]*$"
|
||||||
},
|
},
|
||||||
|
"repoName": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^[a-z0-9]+(-[a-z0-9]+)*$"
|
||||||
|
},
|
||||||
"axisName": {
|
"axisName": {
|
||||||
"enum": [
|
"enum": [
|
||||||
"I",
|
"I",
|
||||||
|
|
@ -209,6 +240,183 @@
|
||||||
"minLength": 1
|
"minLength": 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"identityBinding": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"scheme",
|
||||||
|
"authority",
|
||||||
|
"subject",
|
||||||
|
"principal_type"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"scheme": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^[a-z0-9]+(-[a-z0-9]+)*$",
|
||||||
|
"description": "Identity mechanism, for example iam-profile, kubernetes-service-account, ssh-certificate, or openbao-auth-role."
|
||||||
|
},
|
||||||
|
"authority": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1,
|
||||||
|
"description": "Authoritative issuer or registry for this principal."
|
||||||
|
},
|
||||||
|
"subject": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1,
|
||||||
|
"description": "Exact principal value asserted by the authority."
|
||||||
|
},
|
||||||
|
"principal_type": {
|
||||||
|
"enum": [
|
||||||
|
"service",
|
||||||
|
"agent"
|
||||||
|
],
|
||||||
|
"description": "IAM Profile principal type. A human identity is caller context and cannot be the sole workload identity."
|
||||||
|
},
|
||||||
|
"environment": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
},
|
||||||
|
"evidence": {
|
||||||
|
"$ref": "#/$defs/stringList"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"workloadIdentity": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"name",
|
||||||
|
"kind",
|
||||||
|
"responsible_repo",
|
||||||
|
"identity_bindings"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"$ref": "#/$defs/serviceName",
|
||||||
|
"description": "Stable workload id. It must equal the containing service field."
|
||||||
|
},
|
||||||
|
"kind": {
|
||||||
|
"enum": [
|
||||||
|
"application",
|
||||||
|
"platform-service",
|
||||||
|
"automation",
|
||||||
|
"operational-control-plane",
|
||||||
|
"maintenance-job"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"responsible_repo": {
|
||||||
|
"$ref": "#/$defs/repoName",
|
||||||
|
"description": "Repository accountable for the workload identity and zone declaration."
|
||||||
|
},
|
||||||
|
"declaration_ref": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1,
|
||||||
|
"description": "Authoritative owner declaration. Required by RMGR-ADR-004 for a managed deployable, for example rapp-user-engine/declarations/rapp.yaml."
|
||||||
|
},
|
||||||
|
"identity_bindings": {
|
||||||
|
"type": "array",
|
||||||
|
"minItems": 1,
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/$defs/identityBinding"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"zoneEvidence": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"ref",
|
||||||
|
"supports"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"ref": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
},
|
||||||
|
"supports": {
|
||||||
|
"type": "array",
|
||||||
|
"minItems": 1,
|
||||||
|
"uniqueItems": true,
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"zoneDeclaration": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"standard",
|
||||||
|
"membership",
|
||||||
|
"responsible_party",
|
||||||
|
"justification",
|
||||||
|
"context",
|
||||||
|
"evidence",
|
||||||
|
"reviewed",
|
||||||
|
"review_due"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"standard": {
|
||||||
|
"const": "security-zones_v0.1"
|
||||||
|
},
|
||||||
|
"membership": {
|
||||||
|
"enum": [
|
||||||
|
"z0-experimental",
|
||||||
|
"z1-operational",
|
||||||
|
"z2-protected",
|
||||||
|
"z3-critical",
|
||||||
|
"z2-continuity"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"responsible_party": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
},
|
||||||
|
"justification": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
},
|
||||||
|
"context": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"maturity",
|
||||||
|
"criticality",
|
||||||
|
"data_classification"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"maturity": {
|
||||||
|
"enum": ["M0", "M1", "M2", "M3"]
|
||||||
|
},
|
||||||
|
"criticality": {
|
||||||
|
"enum": ["low", "medium", "high", "critical", "n/a"]
|
||||||
|
},
|
||||||
|
"data_classification": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"evidence": {
|
||||||
|
"type": "array",
|
||||||
|
"minItems": 1,
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/$defs/zoneEvidence"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"reviewed": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "date"
|
||||||
|
},
|
||||||
|
"review_due": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "date"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
"paths": {
|
"paths": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"propertyNames": {
|
"propertyNames": {
|
||||||
|
|
@ -408,6 +616,9 @@
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"minLength": 1
|
"minLength": 1
|
||||||
},
|
},
|
||||||
|
"workload_identity": {
|
||||||
|
"$ref": "#/$defs/workloadIdentity"
|
||||||
|
},
|
||||||
"tenancy": {
|
"tenancy": {
|
||||||
"$ref": "#/$defs/tenancy"
|
"$ref": "#/$defs/tenancy"
|
||||||
},
|
},
|
||||||
|
|
@ -419,8 +630,25 @@
|
||||||
},
|
},
|
||||||
"notes": {
|
"notes": {
|
||||||
"$ref": "#/$defs/stringList"
|
"$ref": "#/$defs/stringList"
|
||||||
|
},
|
||||||
|
"zones": {
|
||||||
|
"$ref": "#/$defs/zoneDeclaration"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"if": {
|
||||||
|
"required": [
|
||||||
|
"zones"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"then": {
|
||||||
|
"required": [
|
||||||
|
"workload_identity"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,11 @@ title: "NetKingdom IAM Profile v0.3"
|
||||||
domain: netkingdom
|
domain: netkingdom
|
||||||
status: accepted
|
status: accepted
|
||||||
owner: net-kingdom
|
owner: net-kingdom
|
||||||
last_reviewed: "2026-07-23"
|
last_reviewed: "2026-08-22"
|
||||||
review_interval: 6m
|
review_interval: 6m
|
||||||
version: "0.3"
|
version: "0.3"
|
||||||
created: "2026-07-23"
|
created: "2026-07-23"
|
||||||
updated: "2026-07-23"
|
updated: "2026-08-22"
|
||||||
scope: core-platform
|
scope: core-platform
|
||||||
supersedes:
|
supersedes:
|
||||||
- canon/standards/iam-profile_v0.2.md
|
- canon/standards/iam-profile_v0.2.md
|
||||||
|
|
@ -236,7 +236,7 @@ flex-auth resource/action semantics.
|
||||||
`tenant` is required for every token accepted by profile consumers.
|
`tenant` is required for every token accepted by profile consumers.
|
||||||
|
|
||||||
Tenant identifiers follow `tenant:<grouping>:<name>`, where `<grouping>` is
|
Tenant identifiers follow `tenant:<grouping>:<name>`, where `<grouping>` is
|
||||||
one of the taxonomy ratified by ADR-0013:
|
one of the taxonomy ratified by ADR-0013 at identifier creation:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
trial - test/trial/showcase tenants only
|
trial - test/trial/showcase tenants only
|
||||||
|
|
@ -265,9 +265,14 @@ request MUST identify the tenant context for that request. If a client
|
||||||
needs to switch tenant context, it obtains a new token or uses an
|
needs to switch tenant context, it obtains a new token or uses an
|
||||||
approved token-exchange flow that records the target tenant.
|
approved token-exchange flow that records the target tenant.
|
||||||
|
|
||||||
The grouping segment is **onboarding-risk / entity-shape** classification
|
The grouping segment is an immutable record of the tenant's
|
||||||
only. It does not gate which capability roles (below) a tenant may hold —
|
**onboarding-time** onboarding-risk / entity-shape classification. It remains
|
||||||
see Tenant Roles.
|
vocabulary-valid but becomes historical if the tenant's classification later
|
||||||
|
changes. The authoritative current grouping is the `grouping` field held by
|
||||||
|
`tenant-engine`; consumers MUST NOT split `tenant` and treat its middle segment
|
||||||
|
as current policy input. Changing current grouping never renames the tenant.
|
||||||
|
Neither the historical segment nor current grouping gates which capability
|
||||||
|
roles (below) a tenant may hold — see Tenant Roles.
|
||||||
|
|
||||||
## Tenant Roles
|
## Tenant Roles
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,21 @@
|
||||||
---
|
---
|
||||||
id: security-zones_v0.1
|
id: netkingdom-security-zones-v0.1
|
||||||
type: standard
|
type: standard
|
||||||
|
title: "NetKingdom Security Zones v0.1"
|
||||||
|
domain: netkingdom
|
||||||
status: proposed
|
status: proposed
|
||||||
|
version: "0.1"
|
||||||
owner: zone-engine
|
owner: zone-engine
|
||||||
publication_owner: net-kingdom
|
publication_owner: net-kingdom
|
||||||
date: "2026-08-22"
|
created: "2026-08-22"
|
||||||
|
updated: "2026-08-22"
|
||||||
|
last_reviewed: "2026-08-22"
|
||||||
|
review_interval: 3m
|
||||||
|
source_revision: "zone-engine@a510393"
|
||||||
|
standard_token: security-zones_v0.1
|
||||||
|
related:
|
||||||
|
- canon/standards/tenancy-posture_v0.1.md
|
||||||
|
- repo-manager/docs/adr-004-authoritative-workload-declarations.md
|
||||||
---
|
---
|
||||||
|
|
||||||
# NetKingdom Security Zones v0.1
|
# NetKingdom Security Zones v0.1
|
||||||
|
|
@ -313,8 +324,8 @@ conformant.
|
||||||
|
|
||||||
## 9. Time-boxed exceptions
|
## 9. Time-boxed exceptions
|
||||||
|
|
||||||
The normative lifecycle is the T04 decision in
|
The normative lifecycle is the ZONE-WP-0001-T04 decision in
|
||||||
`docs/exception-lifecycle-2026-08-22.md`: only the control owner's designated
|
`zone-engine/docs/exception-lifecycle-2026-08-22.md`: only the control owner's designated
|
||||||
authority grants a named-workload, named-zone, named-control relaxation within a
|
authority grants a named-workload, named-zone, named-control relaxation within a
|
||||||
declared maximum duration. Enforcement applies it only for
|
declared maximum duration. Enforcement applies it only for
|
||||||
`not_before <= now < not_after`; invalid or unevaluable records are inactive,
|
`not_before <= now < not_after`; invalid or unevaluable records are inactive,
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,11 @@ domain: netkingdom
|
||||||
status: proposed
|
status: proposed
|
||||||
version: "0.1"
|
version: "0.1"
|
||||||
created: "2026-08-17"
|
created: "2026-08-17"
|
||||||
updated: "2026-08-19"
|
updated: "2026-08-22"
|
||||||
scope: multi-tenancy-security-framework
|
scope: multi-tenancy-security-framework
|
||||||
revision: "draft-9"
|
revision: "draft-12"
|
||||||
owner: net-kingdom
|
owner: net-kingdom
|
||||||
last_reviewed: "2026-08-19"
|
last_reviewed: "2026-08-22"
|
||||||
review_interval: 6m
|
review_interval: 6m
|
||||||
declaration_schema: canon/schemas/tenancy-posture_v0.1.schema.json
|
declaration_schema: canon/schemas/tenancy-posture_v0.1.schema.json
|
||||||
adr:
|
adr:
|
||||||
|
|
@ -28,7 +28,7 @@ related:
|
||||||
|
|
||||||
## Status
|
## Status
|
||||||
|
|
||||||
**Proposed, draft-9; ratification-ready.** Relocated from
|
**Proposed, draft-12; ratification-ready.** Relocated from
|
||||||
`the-custodian/canon/architecture` on
|
`the-custodian/canon/architecture` on
|
||||||
2026-08-17: multi-tenancy is part of the IT-security framework NetKingdom
|
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
|
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
|
`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
|
records the reef/`P`/`V` reconciliation as an open defect of this document
|
||||||
rather than of the repo that noticed it (Decision 8.4).
|
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
|
**Reviewed by all six. The score:** six repos found three live defects in their
|
||||||
own code by reading the ladders — `tenant-engine`'s unfiltered
|
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
|
repos. It is an *input* to stance selection and should be read by the zone
|
||||||
model, not absorbed into a declaration.
|
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
|
Worked examples after applying the evidence rule and minimum-across-paths rule
|
||||||
consistently:
|
consistently:
|
||||||
|
|
||||||
|
|
@ -702,7 +820,8 @@ normative schema is
|
||||||
declaration but do not replace it. The schema carries `current`, `implemented`,
|
declaration but do not replace it. The schema carries `current`, `implemented`,
|
||||||
`target`, `reviewed`, `review_due`, `gap`, `placement_exceptions`,
|
`target`, `reviewed`, `review_due`, `gap`, `placement_exceptions`,
|
||||||
`service_class` (§8.3), per-path detail (§5.2), and provider reachability
|
`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
|
with `uv run tools/tenancy-posture/validate.py <path>...`; the validator applies
|
||||||
the JSON Schema and the evidence, date, implemented/current and provider-range
|
the JSON Schema and the evidence, date, implemented/current and provider-range
|
||||||
rules that JSON Schema alone cannot express.
|
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
|
rung meaning "single node, shared control plane, risk accepted", and inventing
|
||||||
one would be the fabrication §6 prohibits.
|
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
|
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
|
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
|
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
|
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.
|
no, and the reason is that we could not enforce one.
|
||||||
|
|
||||||
**Routed elsewhere, deliberately.** The tenant identifier
|
**Tenant grouping ambiguity — resolved outside this framework.** ADR-0013
|
||||||
`tenant:<grouping>:<name>` embeds headcount bands (`small`, `medium`, `large`)
|
revision 2 makes the identifier segment immutable onboarding-time history and
|
||||||
that change as a tenant grows, contradicting the consensus that identifiers
|
the `tenant-engine` record authoritative for current grouping. Consumers do not
|
||||||
should not encode mutable attributes. That is a critique of ADR-0013, not of
|
parse current policy or spend-ceiling inputs from `tenant:<grouping>:<name>`.
|
||||||
this framework, and belongs to `tenant-engine` and NetKingdom canon. Folding it
|
|
||||||
in here would overreach.
|
|
||||||
|
|
||||||
## 20. Ratification path
|
## 20. Ratification path
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ domain: netkingdom
|
||||||
status: accepted
|
status: accepted
|
||||||
version: "0.1"
|
version: "0.1"
|
||||||
created: "2026-07-23"
|
created: "2026-07-23"
|
||||||
updated: "2026-07-23"
|
updated: "2026-08-22"
|
||||||
scope: tenant-domain-boundaries
|
scope: tenant-domain-boundaries
|
||||||
adr:
|
adr:
|
||||||
- docs/adr/ADR-0006-recursive-multi-tenant-identity-authorization.md
|
- docs/adr/ADR-0006-recursive-multi-tenant-identity-authorization.md
|
||||||
|
|
@ -55,16 +55,22 @@ deployments.
|
||||||
|
|
||||||
| Resource kind | Source of truth | tenant-engine relation | Boundary rule |
|
| Resource kind | Source of truth | tenant-engine relation | Boundary rule |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| Tenant record (existence, grouping, name/slug) | `tenant-engine` | Canonical owner | Grouping value must be one of ADR-0013's taxonomy, or the reserved ungrouped `platform`/`coulomb` identifiers |
|
| Tenant record (existence, current grouping, name/slug) | `tenant-engine` | Canonical owner | Current grouping must be one of ADR-0013's taxonomy, or the reserved ungrouped `platform`/`coulomb` classifications; the identifier segment is historical and must not be used as current grouping |
|
||||||
| Tenant capability roles | `tenant-engine` | Canonical owner | Non-exclusive; grant/revoke are audited mutations (see Grant Contract below), never free-form field updates |
|
| Tenant capability roles | `tenant-engine` | Canonical owner | Non-exclusive; grant/revoke are audited mutations (see Grant Contract below), never free-form field updates |
|
||||||
| Plan/subscription assignment | `tenant-engine` | Canonical owner | References an `adaptive-pricing` plan id; does not define plan terms |
|
| Plan/subscription assignment | `tenant-engine` | Canonical owner | References an `adaptive-pricing` plan id; does not define plan terms |
|
||||||
| Pricing-model / plan definitions | `adaptive-pricing` | Consume by reference only | `tenant-engine` must not cache plan terms beyond what's needed to resolve which roles a plan currently grants |
|
| Pricing-model / plan definitions | `adaptive-pricing` | Consume by reference only | `tenant-engine` must not cache plan terms beyond what's needed to resolve which roles a plan currently grants |
|
||||||
| Guardrail/quota policy (spend limits, entity/action counts) | `tenant-engine` (reserved) | Canonical owner once designed | Not implemented by this contract version — reserved namespace only, see Guardrail Policy section |
|
| Guardrail/quota policy (spend limits, entity/action counts) | `tenant-engine` (reserved) | Canonical owner once designed | Not implemented by this contract version — reserved namespace only, see Guardrail Policy section |
|
||||||
| `tenant_roles` token claim | `tenant-engine` (live) / `key-cape` (cached copy) | `tenant-engine` is authoritative; the token claim is a point-in-time cache | Privileged/high-stakes decisions MUST re-query `tenant-engine` live — see IAM Profile v0.3, "Tenant Roles" section — never trust the cached claim alone |
|
| `tenant_roles` token claim | `tenant-engine` (live) / `key-cape` (cached copy) | `tenant-engine` is authoritative; the token claim is a point-in-time cache | Privileged/high-stakes decisions MUST re-query `tenant-engine` live — see IAM Profile v0.3, "Tenant Roles" section — never trust the cached claim alone |
|
||||||
| Tenant identifier claim shape (`tenant`) | NetKingdom / IAM Profile contract | `tenant-engine` validates against it; does not mint the claim format | The wire format stays owned by the profile; `tenant-engine` owns which concrete tenant values currently exist |
|
| Tenant identifier claim shape (`tenant`) | NetKingdom / IAM Profile contract | `tenant-engine` validates against it; does not mint the claim format | The wire format stays owned by the profile; the identifier is immutable, including its historical grouping segment; `tenant-engine` owns which concrete tenant values currently exist |
|
||||||
| User/membership records scoped by a tenant | `user-engine` | No relation | `tenant_id` is the only key shared between the two services; `tenant-engine` never stores or reads user data |
|
| User/membership records scoped by a tenant | `user-engine` | No relation | `tenant_id` is the only key shared between the two services; `tenant-engine` never stores or reads user data |
|
||||||
| Authorization decisions | `flex-auth` | Data source only | `tenant-engine` never enforces access itself; it answers queries `flex-auth`'s policy packages issue |
|
| Authorization decisions | `flex-auth` | Data source only | `tenant-engine` never enforces access itself; it answers queries `flex-auth`'s policy packages issue |
|
||||||
|
|
||||||
|
The tenant identifier and current grouping are intentionally independent after
|
||||||
|
creation. `tenant-engine` MAY change the record's `grouping` through an audited
|
||||||
|
mutation without renaming `tenant_id`. Reads and domain events expose current
|
||||||
|
grouping explicitly. No API, guardrail, pricing, or authorization consumer may
|
||||||
|
recover current grouping by parsing `tenant_id`.
|
||||||
|
|
||||||
## Tenant Role & Plan Grant Contract
|
## Tenant Role & Plan Grant Contract
|
||||||
|
|
||||||
Every role grant or revocation is an audited mutation, not a direct field
|
Every role grant or revocation is an audited mutation, not a direct field
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,15 @@
|
||||||
|
---
|
||||||
|
id: NK-ADR-0006
|
||||||
|
type: architecture-decision-record
|
||||||
|
title: "Recursive Multi-Tenant Identity and Authorization Architecture"
|
||||||
|
status: accepted
|
||||||
|
owner: net-kingdom
|
||||||
|
revision: "1"
|
||||||
|
decided: "2026-05-17"
|
||||||
|
last_reviewed: "2026-08-22"
|
||||||
|
review_interval: 12m
|
||||||
|
---
|
||||||
|
|
||||||
# ADR-0006 - Recursive Multi-Tenant Identity and Authorization Architecture
|
# ADR-0006 - Recursive Multi-Tenant Identity and Authorization Architecture
|
||||||
|
|
||||||
**Status:** Accepted
|
**Status:** Accepted
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,15 @@
|
||||||
|
---
|
||||||
|
id: NK-ADR-0007
|
||||||
|
type: architecture-decision-record
|
||||||
|
title: "Security Orchestration Boundary"
|
||||||
|
status: accepted
|
||||||
|
owner: net-kingdom
|
||||||
|
revision: "1"
|
||||||
|
decided: "2026-05-18"
|
||||||
|
last_reviewed: "2026-08-22"
|
||||||
|
review_interval: 12m
|
||||||
|
---
|
||||||
|
|
||||||
# ADR-0007 - Security Orchestration Boundary
|
# ADR-0007 - Security Orchestration Boundary
|
||||||
|
|
||||||
**Status:** Accepted
|
**Status:** Accepted
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,15 @@
|
||||||
|
---
|
||||||
|
id: NK-ADR-0008
|
||||||
|
type: architecture-decision-record
|
||||||
|
title: "Object Storage STS Credential Vending Boundary"
|
||||||
|
status: accepted
|
||||||
|
owner: net-kingdom
|
||||||
|
revision: "1"
|
||||||
|
decided: "2026-05-18"
|
||||||
|
last_reviewed: "2026-08-22"
|
||||||
|
review_interval: 12m
|
||||||
|
---
|
||||||
|
|
||||||
# ADR-0008 - Object Storage STS Credential Vending Boundary
|
# ADR-0008 - Object Storage STS Credential Vending Boundary
|
||||||
|
|
||||||
**Status:** Accepted
|
**Status:** Accepted
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,15 @@
|
||||||
|
---
|
||||||
|
id: NK-ADR-0010
|
||||||
|
type: architecture-decision-record
|
||||||
|
title: "Orchestration vs Dependency, and Self-Coherent Intent"
|
||||||
|
status: accepted
|
||||||
|
owner: net-kingdom
|
||||||
|
revision: "1"
|
||||||
|
decided: "2026-05-21"
|
||||||
|
last_reviewed: "2026-08-22"
|
||||||
|
review_interval: 12m
|
||||||
|
---
|
||||||
|
|
||||||
# ADR-0010 - Orchestration vs Dependency, and Self-Coherent Intent
|
# ADR-0010 - Orchestration vs Dependency, and Self-Coherent Intent
|
||||||
|
|
||||||
**Status:** Accepted (repo classification subject to ongoing refinement)
|
**Status:** Accepted (repo classification subject to ongoing refinement)
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,15 @@
|
||||||
|
---
|
||||||
|
id: NK-ADR-0011
|
||||||
|
type: architecture-decision-record
|
||||||
|
title: "NetKingdom IAM Profile Ownership And Version Governance"
|
||||||
|
status: accepted
|
||||||
|
owner: net-kingdom
|
||||||
|
revision: "1"
|
||||||
|
decided: "2026-05-22"
|
||||||
|
last_reviewed: "2026-08-22"
|
||||||
|
review_interval: 12m
|
||||||
|
---
|
||||||
|
|
||||||
# ADR-0011 - NetKingdom IAM Profile Ownership And Version Governance
|
# ADR-0011 - NetKingdom IAM Profile Ownership And Version Governance
|
||||||
|
|
||||||
**Status:** Accepted
|
**Status:** Accepted
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,15 @@
|
||||||
|
---
|
||||||
|
id: NK-ADR-0012
|
||||||
|
type: architecture-decision-record
|
||||||
|
title: "Playbook Capability Contract Ownership"
|
||||||
|
status: accepted
|
||||||
|
owner: net-kingdom
|
||||||
|
revision: "1"
|
||||||
|
decided: "2026-05-22"
|
||||||
|
last_reviewed: "2026-08-22"
|
||||||
|
review_interval: 12m
|
||||||
|
---
|
||||||
|
|
||||||
# ADR-0012 - Playbook Capability Contract Ownership
|
# ADR-0012 - Playbook Capability Contract Ownership
|
||||||
|
|
||||||
**Status:** Accepted
|
**Status:** Accepted
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,20 @@
|
||||||
|
---
|
||||||
|
id: NK-ADR-0013
|
||||||
|
type: architecture-decision-record
|
||||||
|
title: "Tenant Onboarding Grouping Taxonomy"
|
||||||
|
status: accepted
|
||||||
|
owner: net-kingdom
|
||||||
|
revision: "2"
|
||||||
|
decided: "2026-07-23"
|
||||||
|
last_reviewed: "2026-08-22"
|
||||||
|
review_interval: 12m
|
||||||
|
---
|
||||||
|
|
||||||
# ADR-0013 - Tenant Onboarding Grouping Taxonomy
|
# ADR-0013 - Tenant Onboarding Grouping Taxonomy
|
||||||
|
|
||||||
**Status:** Accepted
|
**Status:** Accepted
|
||||||
**Date:** 2026-07-23
|
**Date:** 2026-07-23
|
||||||
|
**Amended:** 2026-08-22 (current classification versus historical identifier segment)
|
||||||
**Deciders:** Bernd Worsch, Codex
|
**Deciders:** Bernd Worsch, Codex
|
||||||
|
|
||||||
## Context
|
## Context
|
||||||
|
|
@ -54,12 +67,26 @@ association - a legal association of people
|
||||||
agentic - financially enabled AI entities
|
agentic - financially enabled AI entities
|
||||||
```
|
```
|
||||||
|
|
||||||
This grouping is deliberately **orthogonal to capability role**
|
The taxonomy has two deliberately different uses:
|
||||||
(`PLTF`/`IAM`/`VEN`/`CUS`, still unratified): the grouping describes *what
|
|
||||||
kind of entity a tenant is and how it was onboarded*; role describes *what
|
- At creation, the identifier's grouping segment records the tenant's
|
||||||
it does on the platform*. Both may need to be carried as tenant metadata,
|
onboarding-time classification. The complete identifier is immutable, so
|
||||||
but never conflated into one identifier segment — that conflation is exactly
|
this segment is historical after creation.
|
||||||
what this ADR avoids.
|
- The tenant record's `grouping` field records current classification. It may
|
||||||
|
change as the entity changes and is authoritative for present-day policy,
|
||||||
|
including guardrails and spend ceilings.
|
||||||
|
|
||||||
|
No consumer may parse the identifier's middle segment and treat it as current
|
||||||
|
grouping. Consumers needing current grouping MUST read it from `tenant-engine`.
|
||||||
|
Identifier creation still validates the segment against this vocabulary;
|
||||||
|
historical does not mean free-form or optional.
|
||||||
|
|
||||||
|
Grouping is deliberately **orthogonal to capability role**
|
||||||
|
(`PLTF`/`IAM`/`VEN`/`CUS`, subsequently ratified by ADR-0014): grouping
|
||||||
|
describes *what kind of entity a tenant is and its current onboarding-risk
|
||||||
|
classification*; role describes *what it does on the platform*. Both are
|
||||||
|
carried as tenant metadata, but never conflated into the immutable identifier
|
||||||
|
segment — that conflation is exactly what this ADR avoids.
|
||||||
|
|
||||||
`tenant:platform` and `tenant:coulomb` remain **reserved, ungrouped
|
`tenant:platform` and `tenant:coulomb` remain **reserved, ungrouped
|
||||||
identifiers outside this taxonomy**: `tenant:platform` is the control-plane
|
identifiers outside this taxonomy**: `tenant:platform` is the control-plane
|
||||||
|
|
@ -101,10 +128,15 @@ section — not a new versioned profile document.
|
||||||
already reflects this decision (`tenant:friendly:binky`).
|
already reflects this decision (`tenant:friendly:binky`).
|
||||||
- Future tenant onboarding work should classify a tenant against this list
|
- Future tenant onboarding work should classify a tenant against this list
|
||||||
before minting an identifier, rather than reaching for a role word.
|
before minting an identifier, rather than reaching for a role word.
|
||||||
- The capability-role model (`PLTF`/`IAM`/`VEN`/`CUS`) remains a separate,
|
- Tenant identifiers never change when current grouping changes. The middle
|
||||||
still-unratified dimension; this ADR does not ratify that model, only
|
segment is creation-time history; `tenant-engine` is authoritative for the
|
||||||
avoids colliding with it. If/when it is ratified, role metadata should be
|
current grouping value.
|
||||||
carried alongside — not instead of — the grouping segment decided here.
|
- Policy and commercial consumers, including spend-ceiling resolution, MUST
|
||||||
|
query `tenant-engine` and MUST NOT derive current grouping by splitting a
|
||||||
|
tenant identifier.
|
||||||
|
- The capability-role model (`PLTF`/`IAM`/`VEN`/`CUS`) remains a separate
|
||||||
|
dimension, now ratified by ADR-0014. Role metadata is carried alongside —
|
||||||
|
not instead of — current grouping and the historical identifier segment.
|
||||||
- `tenant:platform` and `tenant:coulomb` are reserved outside the taxonomy,
|
- `tenant:platform` and `tenant:coulomb` are reserved outside the taxonomy,
|
||||||
pending Bernd's explicit confirmation (see Decision).
|
pending Bernd's explicit confirmation (see Decision).
|
||||||
|
|
||||||
|
|
@ -138,6 +170,8 @@ resolved before the first non-Coulomb tenant goes live, not after.
|
||||||
reviewable change).
|
reviewable change).
|
||||||
- Confirm the `tenant:platform`/`tenant:coulomb` reserved/ungrouped
|
- Confirm the `tenant:platform`/`tenant:coulomb` reserved/ungrouped
|
||||||
treatment explicitly.
|
treatment explicitly.
|
||||||
- If/when the `PLTF`/`IAM`/`VEN`/`CUS` capability-role model is ratified,
|
- ADR-0014 and the Tenant Engine Boundary Contract define how capability-role
|
||||||
define how role metadata is carried alongside the grouping segment
|
metadata is carried alongside grouping.
|
||||||
decided here.
|
- Keep `tenant-engine`'s identifier parser vocabulary-validating for creation
|
||||||
|
and lookup compatibility, but do not expose parsed grouping as current
|
||||||
|
classification.
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,15 @@
|
||||||
|
---
|
||||||
|
id: NK-ADR-0014
|
||||||
|
type: architecture-decision-record
|
||||||
|
title: "Tenant Capability Roles, Carrying Mechanism, and Tenant-Engine Ownership"
|
||||||
|
status: accepted
|
||||||
|
owner: net-kingdom
|
||||||
|
revision: "1"
|
||||||
|
decided: "2026-07-23"
|
||||||
|
last_reviewed: "2026-08-22"
|
||||||
|
review_interval: 12m
|
||||||
|
---
|
||||||
|
|
||||||
# ADR-0014 - Tenant Capability Roles, Carrying Mechanism, and Tenant-Engine Ownership
|
# ADR-0014 - Tenant Capability Roles, Carrying Mechanism, and Tenant-Engine Ownership
|
||||||
|
|
||||||
**Status:** Accepted
|
**Status:** Accepted
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,15 @@
|
||||||
|
---
|
||||||
|
id: NK-ADR-0015
|
||||||
|
type: architecture-decision-record
|
||||||
|
title: "NetKingdom Railiance Workload Packaging and Relational Platform"
|
||||||
|
status: accepted
|
||||||
|
owner: net-kingdom
|
||||||
|
revision: "1"
|
||||||
|
decided: "2026-08-11"
|
||||||
|
last_reviewed: "2026-08-22"
|
||||||
|
review_interval: 12m
|
||||||
|
---
|
||||||
|
|
||||||
# ADR-0015 - NetKingdom Railiance Workload Packaging and Relational Platform
|
# ADR-0015 - NetKingdom Railiance Workload Packaging and Relational Platform
|
||||||
|
|
||||||
**Status:** Accepted
|
**Status:** Accepted
|
||||||
|
|
|
||||||
109
docs/reef-posture-provider-contract.md
Normal file
109
docs/reef-posture-provider-contract.md
Normal file
|
|
@ -0,0 +1,109 @@
|
||||||
|
# Reef posture-provider contract
|
||||||
|
|
||||||
|
Status: proposal for `repo-manager` agreement under `NK-WP-0027-T02`
|
||||||
|
|
||||||
|
Date: 2026-08-22
|
||||||
|
|
||||||
|
Canon owner: `net-kingdom`
|
||||||
|
|
||||||
|
Reef vocabulary and declaration owner: `repo-manager` / `railiance-master`
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
A reef states where workloads may be placed and which residual substrate risk
|
||||||
|
has been accepted. It is not a `P` level. It can nevertheless bound a posture
|
||||||
|
axis: the current single-member `reef-railiance` can never make V2 failover
|
||||||
|
reachable, regardless of how many replicas a workload declares.
|
||||||
|
|
||||||
|
This proposal extends Tenancy Posture Decision 5.5's provider shape to reefs
|
||||||
|
without moving reef vocabulary into NetKingdom. The block belongs in the
|
||||||
|
authoritative `declarations/reef.yaml`; it does not create a second reef file or
|
||||||
|
duplicate the derived `bound_rapps` projection.
|
||||||
|
|
||||||
|
## Proposed declaration block
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
posture_provider:
|
||||||
|
framework: netkingdom-tenancy-posture
|
||||||
|
reviewed: "2026-08-22"
|
||||||
|
review_due: "2027-02-22"
|
||||||
|
axes:
|
||||||
|
V:
|
||||||
|
available: 0
|
||||||
|
maximum: 1
|
||||||
|
conditions:
|
||||||
|
- A bound workload documents and exercises restart or recreation recovery in this failure domain.
|
||||||
|
- The workload names every synchronous dependency used by the claimed operation.
|
||||||
|
evidence:
|
||||||
|
- evidence/verification/rail-runtime-2026-08-21.json
|
||||||
|
- evidence/admission/rail-kubernetes-baseline.json
|
||||||
|
reason: One observed member carries the control plane, etcd, and workloads; no automated failover is reachable.
|
||||||
|
```
|
||||||
|
|
||||||
|
The `reef-railiance` values are deliberately conservative:
|
||||||
|
|
||||||
|
- `available: 0` means the reef offers no unconditional, reusable recovery
|
||||||
|
guarantee to every bound workload today. A Ready node is not recovery
|
||||||
|
evidence.
|
||||||
|
- `maximum: 1` means a named workload can reach V1 by satisfying and evidencing
|
||||||
|
the restart/recreate conditions. One failure domain makes V2 unreachable.
|
||||||
|
|
||||||
|
The owner may raise `available` when the reef publishes a reusable recovery
|
||||||
|
guarantee that consumers can cite. Adding independent members and an exercised
|
||||||
|
failover path may raise `maximum`; topology alone does not.
|
||||||
|
|
||||||
|
## Semantics
|
||||||
|
|
||||||
|
Each declared axis contains:
|
||||||
|
|
||||||
|
| Field | Meaning |
|
||||||
|
| --- | --- |
|
||||||
|
| `available` | Highest level the provider guarantees unconditionally to every binding in scope, with current evidence. |
|
||||||
|
| `maximum` | Highest level a named consumer can reach after satisfying the listed conditions. |
|
||||||
|
| `conditions` | Consumer or binding work required above `available`; empty only when `available == maximum`. |
|
||||||
|
| `evidence` | Source-linked, current artifacts supporting the provider facts. |
|
||||||
|
| `reason` | Why the ceiling exists, especially when structural or accepted. |
|
||||||
|
|
||||||
|
A reef declares only axes it materially bounds. A compute reef normally
|
||||||
|
declares `V`; it does not declare `P` merely because data-bearing workloads sit
|
||||||
|
on it. A provider-delegated storage reef may bound `P`, `R`, or `V`, but only
|
||||||
|
where the substrate contract actually makes those properties reachable.
|
||||||
|
|
||||||
|
`available` and `maximum` use the axis vocabulary from
|
||||||
|
`tenancy-posture_v0.1`. `available` MUST NOT exceed `maximum`. Review dates and
|
||||||
|
evidence discipline follow Decisions 5.4, 5.5, and §13.
|
||||||
|
|
||||||
|
## Mechanical reconciliation
|
||||||
|
|
||||||
|
For each workload operation and each bound reef:
|
||||||
|
|
||||||
|
1. Resolve the reef from the workload's authoritative placement declaration.
|
||||||
|
Missing or conflicting placement is `unknown`; do not select a default reef.
|
||||||
|
2. Load the reef's `posture_provider` block from `declarations/reef.yaml`.
|
||||||
|
3. Reject a workload claim above the reef's `maximum`.
|
||||||
|
4. For a claim above `available`, require machine-readable evidence that every
|
||||||
|
listed condition is satisfied for that workload and operation.
|
||||||
|
5. Compose `V` with every synchronous provider using Decision 4.6.1's minimum
|
||||||
|
rule. A reef ceiling is one input, not the whole availability claim.
|
||||||
|
6. Emit the reef declaration revision and evidence references in the
|
||||||
|
reconciliation result so the decision is reproducible.
|
||||||
|
|
||||||
|
The result is one of `satisfied`, `unsatisfied`, or `unknown`. `unknown` covers
|
||||||
|
an absent provider block, unresolved reef binding, stale review, missing
|
||||||
|
evidence, or an unrecognised axis value. It is never converted to a permissive
|
||||||
|
ceiling.
|
||||||
|
|
||||||
|
## Ownership boundary
|
||||||
|
|
||||||
|
- `net-kingdom` owns the meaning of provider reachability and the `P`/`V`
|
||||||
|
composition rules.
|
||||||
|
- `repo-manager` / `railiance-master` owns whether and how this block is added
|
||||||
|
to the reef schema.
|
||||||
|
- Each `reef-*` repo owns its values, evidence, and review.
|
||||||
|
- The workload owner owns its claim and evidence that provider conditions are
|
||||||
|
met.
|
||||||
|
- The reconciler reports; it does not manufacture acceptance or posture.
|
||||||
|
|
||||||
|
T03 may implement the join only after `repo-manager` agrees the authoritative
|
||||||
|
field name and carrier. The semantics above are the canon-side acceptance
|
||||||
|
criteria; the exact reef-schema spelling remains the reef owner's decision.
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import importlib.util
|
import importlib.util
|
||||||
|
import json
|
||||||
import pathlib
|
import pathlib
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
|
@ -10,6 +11,7 @@ SPEC = importlib.util.spec_from_file_location("tenancy_posture_validate", MODULE
|
||||||
assert SPEC and SPEC.loader
|
assert SPEC and SPEC.loader
|
||||||
VALIDATE = importlib.util.module_from_spec(SPEC)
|
VALIDATE = importlib.util.module_from_spec(SPEC)
|
||||||
SPEC.loader.exec_module(VALIDATE)
|
SPEC.loader.exec_module(VALIDATE)
|
||||||
|
SCHEMA = json.loads(VALIDATE.SCHEMA.read_text(encoding="utf-8"))
|
||||||
|
|
||||||
|
|
||||||
def declaration() -> dict:
|
def declaration() -> dict:
|
||||||
|
|
@ -30,10 +32,38 @@ def declaration() -> dict:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def zone_declaration() -> dict:
|
||||||
|
return {
|
||||||
|
"standard": "security-zones_v0.1",
|
||||||
|
"membership": "z2-continuity",
|
||||||
|
"responsible_party": "ops-warden",
|
||||||
|
"justification": "foundational access path",
|
||||||
|
"context": {
|
||||||
|
"maturity": "M2",
|
||||||
|
"criticality": "high",
|
||||||
|
"data_classification": "confidential",
|
||||||
|
},
|
||||||
|
"evidence": [
|
||||||
|
{
|
||||||
|
"ref": "docs/evidence/example-zone.md",
|
||||||
|
"supports": ["M2", "continuity-dependency"],
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"reviewed": "2026-08-22",
|
||||||
|
"review_due": "2026-11-22",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class SemanticValidationTests(unittest.TestCase):
|
class SemanticValidationTests(unittest.TestCase):
|
||||||
def validate(self, document: dict) -> list[str]:
|
def validate(self, document: dict) -> list[str]:
|
||||||
return VALIDATE.validate_semantics(document, pathlib.Path("tenancy.yaml"))
|
return VALIDATE.validate_semantics(document, pathlib.Path("tenancy.yaml"))
|
||||||
|
|
||||||
|
def validate_full(self, document: dict, tmp_path: pathlib.Path) -> list[str]:
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
tmp_path.write_text(yaml.safe_dump(document), encoding="utf-8")
|
||||||
|
return VALIDATE.validate(tmp_path, SCHEMA)
|
||||||
|
|
||||||
def test_floor_vector_with_reasons_is_valid(self) -> None:
|
def test_floor_vector_with_reasons_is_valid(self) -> None:
|
||||||
self.assertEqual([], self.validate(declaration()))
|
self.assertEqual([], self.validate(declaration()))
|
||||||
|
|
||||||
|
|
@ -68,6 +98,140 @@ class SemanticValidationTests(unittest.TestCase):
|
||||||
}
|
}
|
||||||
self.assertIn("service names must be unique", self.validate(document)[0])
|
self.assertIn("service names must be unique", self.validate(document)[0])
|
||||||
|
|
||||||
|
def test_workload_identity_name_must_match_service(self) -> None:
|
||||||
|
document = declaration()
|
||||||
|
document["workload_identity"] = {
|
||||||
|
"name": "different",
|
||||||
|
"kind": "operational-control-plane",
|
||||||
|
"responsible_repo": "example",
|
||||||
|
"identity_bindings": [
|
||||||
|
{
|
||||||
|
"scheme": "iam-profile",
|
||||||
|
"authority": "key-cape",
|
||||||
|
"subject": "example-prod",
|
||||||
|
"principal_type": "service",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
self.assertIn(
|
||||||
|
"workload_identity.name must equal service", self.validate(document)[0]
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_workload_identity_bindings_are_unique(self) -> None:
|
||||||
|
document = declaration()
|
||||||
|
binding = {
|
||||||
|
"scheme": "iam-profile",
|
||||||
|
"authority": "key-cape",
|
||||||
|
"subject": "example-prod",
|
||||||
|
"principal_type": "service",
|
||||||
|
}
|
||||||
|
document["workload_identity"] = {
|
||||||
|
"name": "example",
|
||||||
|
"kind": "platform-service",
|
||||||
|
"responsible_repo": "example",
|
||||||
|
"identity_bindings": [binding, binding],
|
||||||
|
}
|
||||||
|
self.assertIn(
|
||||||
|
"workload identity bindings must be unique", self.validate(document)[0]
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_zones_require_authoritative_workload_identity(self) -> None:
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
document = declaration()
|
||||||
|
document["zones"] = zone_declaration()
|
||||||
|
with tempfile.TemporaryDirectory() as directory:
|
||||||
|
path = pathlib.Path(directory) / "tenancy.yaml"
|
||||||
|
errors = self.validate_full(document, path)
|
||||||
|
self.assertTrue(any("workload_identity" in error for error in errors))
|
||||||
|
|
||||||
|
def test_operational_workload_may_declare_zones(self) -> None:
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
document = declaration()
|
||||||
|
document["workload_identity"] = {
|
||||||
|
"name": "example",
|
||||||
|
"kind": "operational-control-plane",
|
||||||
|
"responsible_repo": "ops-warden",
|
||||||
|
"identity_bindings": [
|
||||||
|
{
|
||||||
|
"scheme": "ssh-certificate",
|
||||||
|
"authority": "ops-warden",
|
||||||
|
"subject": "agt-example",
|
||||||
|
"principal_type": "agent",
|
||||||
|
"environment": "prod",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
document["zones"] = zone_declaration()
|
||||||
|
with tempfile.TemporaryDirectory() as directory:
|
||||||
|
path = pathlib.Path(directory) / "tenancy.yaml"
|
||||||
|
self.assertEqual([], self.validate_full(document, path))
|
||||||
|
|
||||||
|
def test_zone_membership_uses_canonical_catalog(self) -> None:
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
document = declaration()
|
||||||
|
document["workload_identity"] = {
|
||||||
|
"name": "example",
|
||||||
|
"kind": "application",
|
||||||
|
"responsible_repo": "example",
|
||||||
|
"declaration_ref": "rapp-example/declarations/rapp.yaml",
|
||||||
|
"identity_bindings": [
|
||||||
|
{
|
||||||
|
"scheme": "iam-profile",
|
||||||
|
"authority": "key-cape",
|
||||||
|
"subject": "example-prod",
|
||||||
|
"principal_type": "service",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
document["zones"] = zone_declaration()
|
||||||
|
document["zones"]["membership"] = "permissive-default"
|
||||||
|
with tempfile.TemporaryDirectory() as directory:
|
||||||
|
path = pathlib.Path(directory) / "tenancy.yaml"
|
||||||
|
errors = self.validate_full(document, path)
|
||||||
|
self.assertTrue(any("membership" in error for error in errors))
|
||||||
|
|
||||||
|
def test_zone_review_due_must_be_after_reviewed(self) -> None:
|
||||||
|
document = declaration()
|
||||||
|
document["zones"] = zone_declaration()
|
||||||
|
document["zones"]["review_due"] = document["zones"]["reviewed"]
|
||||||
|
self.assertIn(
|
||||||
|
"zones.review_due must be after zones.reviewed",
|
||||||
|
self.validate(document)[0],
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_multi_service_zones_must_not_be_top_level(self) -> None:
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
entry = declaration()
|
||||||
|
document = {
|
||||||
|
"schema_version": "0.1",
|
||||||
|
"framework": "netkingdom-tenancy-posture",
|
||||||
|
"services": [entry],
|
||||||
|
"zones": zone_declaration(),
|
||||||
|
}
|
||||||
|
with tempfile.TemporaryDirectory() as directory:
|
||||||
|
path = pathlib.Path(directory) / "tenancy.yaml"
|
||||||
|
errors = self.validate_full(document, path)
|
||||||
|
self.assertTrue(errors)
|
||||||
|
|
||||||
|
def test_multi_service_zone_requires_identity_on_same_entry(self) -> None:
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
entry = declaration()
|
||||||
|
entry["zones"] = zone_declaration()
|
||||||
|
document = {
|
||||||
|
"schema_version": "0.1",
|
||||||
|
"framework": "netkingdom-tenancy-posture",
|
||||||
|
"services": [entry],
|
||||||
|
}
|
||||||
|
with tempfile.TemporaryDirectory() as directory:
|
||||||
|
path = pathlib.Path(directory) / "tenancy.yaml"
|
||||||
|
errors = self.validate_full(document, path)
|
||||||
|
self.assertTrue(any("workload_identity" in error for error in errors))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,30 @@ def validate_semantics(document: dict[str, Any], path: pathlib.Path) -> list[str
|
||||||
|
|
||||||
for entry in entries:
|
for entry in entries:
|
||||||
name = entry["service"]
|
name = entry["service"]
|
||||||
|
workload_identity = entry.get("workload_identity")
|
||||||
|
if workload_identity:
|
||||||
|
if workload_identity["name"] != name:
|
||||||
|
errors.append(
|
||||||
|
f"{name}: workload_identity.name must equal service"
|
||||||
|
)
|
||||||
|
bindings = workload_identity["identity_bindings"]
|
||||||
|
binding_keys = [
|
||||||
|
(
|
||||||
|
binding["scheme"],
|
||||||
|
binding["authority"],
|
||||||
|
binding["subject"],
|
||||||
|
binding.get("environment"),
|
||||||
|
)
|
||||||
|
for binding in bindings
|
||||||
|
]
|
||||||
|
if len(binding_keys) != len(set(binding_keys)):
|
||||||
|
errors.append(f"{name}: workload identity bindings must be unique")
|
||||||
|
zones = entry.get("zones")
|
||||||
|
if zones:
|
||||||
|
zone_reviewed = dt.date.fromisoformat(zones["reviewed"])
|
||||||
|
zone_review_due = dt.date.fromisoformat(zones["review_due"])
|
||||||
|
if zone_review_due <= zone_reviewed:
|
||||||
|
errors.append(f"{name}: zones.review_due must be after zones.reviewed")
|
||||||
posture = entry["tenancy"]
|
posture = entry["tenancy"]
|
||||||
current = posture["current"]
|
current = posture["current"]
|
||||||
reason = posture.get("reason", {})
|
reason = posture.get("reason", {})
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,19 @@
|
||||||
---
|
---
|
||||||
id: NK-WP-0027
|
id: NK-WP-0027
|
||||||
type: workplan
|
type: workplan
|
||||||
title: "Reconcile the reef taxonomy with the P and V ladders"
|
title: "Reconcile reef placement and security-zone canon dependencies"
|
||||||
domain: infotech
|
domain: infotech
|
||||||
repo: net-kingdom
|
repo: net-kingdom
|
||||||
status: proposed
|
status: blocked
|
||||||
owner: net-kingdom
|
owner: net-kingdom
|
||||||
topic_slug: netkingdom
|
topic_slug: netkingdom
|
||||||
planning_priority: P2
|
planning_priority: P1
|
||||||
created: "2026-08-19"
|
created: "2026-08-19"
|
||||||
updated: "2026-08-19"
|
updated: "2026-08-22"
|
||||||
|
state_hub_workstream_id: "a30a33e4-f980-4934-9efb-d61a75e6ae81"
|
||||||
---
|
---
|
||||||
|
|
||||||
# NK-WP-0027 — Reefs, placement, and what a substrate makes reachable
|
# NK-WP-0027 — Reefs, placement, and security-zone canon dependencies
|
||||||
|
|
||||||
Raised by answering `zone-engine`'s `ZONE-WP-0001-T01`. `zone-engine` asked
|
Raised by answering `zone-engine`'s `ZONE-WP-0001-T01`. `zone-engine` asked
|
||||||
whether canon should reconcile security zones with reefs, expecting the answer
|
whether canon should reconcile security zones with reefs, expecting the answer
|
||||||
|
|
@ -37,20 +38,61 @@ accurately by its own reading and be wrong by the standard's own composition
|
||||||
rule. This is Decision 5.5's provider-declaration finding one layer down: a
|
rule. This is Decision 5.5's provider-declaration finding one layer down: a
|
||||||
reef is a provider with nowhere to say what it makes reachable.
|
reef is a provider with nowhere to say what it makes reachable.
|
||||||
|
|
||||||
|
## Activation review — 2026-08-22
|
||||||
|
|
||||||
|
The original reef finding remains valid and correctly owned. Review against
|
||||||
|
draft-9 found T01 substantially written already in Decision 8.4.2, but the text
|
||||||
|
did not explicitly say which Decision 3.2 couplings a reef binding fails to
|
||||||
|
satisfy. Draft-10 now closes that textual gap. T02 and T03 remain real: the reef
|
||||||
|
provider declaration and its mechanical join to consumer `V` do not yet exist.
|
||||||
|
|
||||||
|
The review also took in a second canon question handed over by
|
||||||
|
`ZONE-WP-0001-T03`. Measured coverage is nine declared `rapp` workloads, one of
|
||||||
|
27 credential lanes joinable, 13 plausible but undeclared consumers, and 13
|
||||||
|
operational controls with no workload-shaped path. The ruling is recorded as
|
||||||
|
Tenancy Posture Decision 5.6.1:
|
||||||
|
|
||||||
|
- the **workload remains the sole policy subject**;
|
||||||
|
- workload includes independently governed application, automation, and
|
||||||
|
operational/control-plane execution units behind SSH, tunnels, brokers,
|
||||||
|
credential flows, policy machinery, and maintenance activity;
|
||||||
|
- lanes, grants, patterns, repositories, actors, and packages do not become
|
||||||
|
substitute policy subjects;
|
||||||
|
- every managed deployable, including operational/tooling runtimes, resolves
|
||||||
|
through its authoritative rapp declaration; a real operational execution
|
||||||
|
unit that is not a managed deployable may declare locally; and
|
||||||
|
- absent authoritative identity or membership resolves to `unknown`, never an
|
||||||
|
inferred or silently permissive zone.
|
||||||
|
|
||||||
|
An explicit control rule may decide how to treat `unknown`; that is stance and
|
||||||
|
does not manufacture membership. This preserves the build-stage flexibility
|
||||||
|
already accepted by `ADR-0006` without encoding it as a false zone fact.
|
||||||
|
|
||||||
|
## T01 — Establish the reef and `P` boundary
|
||||||
|
|
||||||
```task
|
```task
|
||||||
id: NK-WP-0027-T01
|
id: NK-WP-0027-T01
|
||||||
status: todo
|
status: done
|
||||||
priority: medium
|
priority: medium
|
||||||
|
state_hub_task_id: "a1dfa4ec-f946-425e-8183-782eef7763c4"
|
||||||
```
|
```
|
||||||
|
|
||||||
**Establish the boundary between a reef and the `P` ladder in canon text.** Say
|
**Establish the boundary between a reef and the `P` ladder in canon text.** Say
|
||||||
what each answers, why a reef is not a `P` level, and which of `P`'s couplings
|
what each answers, why a reef is not a `P` level, and which of `P`'s couplings
|
||||||
(§3.2) a reef binding does and does not satisfy. Do not extend the `P` ladder.
|
(§3.2) a reef binding does and does not satisfy. Do not extend the `P` ladder.
|
||||||
|
|
||||||
|
**Done 2026-08-22.** Tenancy Posture Decision 8.4.2 now says explicitly that a
|
||||||
|
reef binding records compute substrate and accepted residual risk, satisfies no
|
||||||
|
`P` coupling by itself, and participates only as a possible `V` ceiling across
|
||||||
|
the critical path.
|
||||||
|
|
||||||
|
## T02 — Extend provider declarations to reefs
|
||||||
|
|
||||||
```task
|
```task
|
||||||
id: NK-WP-0027-T02
|
id: NK-WP-0027-T02
|
||||||
status: todo
|
status: wait
|
||||||
priority: medium
|
priority: medium
|
||||||
|
state_hub_task_id: "601a0c5f-3f77-419f-9731-25e247424b31"
|
||||||
```
|
```
|
||||||
|
|
||||||
**Extend Decision 5.5's provider declaration to substrate providers, and agree
|
**Extend Decision 5.5's provider declaration to substrate providers, and agree
|
||||||
|
|
@ -60,10 +102,30 @@ sentence `apps-pg` now owes its consumers. `reef-railiance`'s first line is
|
||||||
almost certainly a `V` ceiling. This is a proposal to `repo-manager`, not a
|
almost certainly a `V` ceiling. This is a proposal to `repo-manager`, not a
|
||||||
canon fiat: it owns the reef vocabulary and the acceptance record.
|
canon fiat: it owns the reef vocabulary and the acceptance record.
|
||||||
|
|
||||||
|
**In progress 2026-08-22.** The canon-side boundary and proposed provider shape
|
||||||
|
are ready. Agreement and the authoritative reef declaration surface remain with
|
||||||
|
`repo-manager`.
|
||||||
|
|
||||||
|
**Contract proposal 2026-08-22.**
|
||||||
|
`docs/reef-posture-provider-contract.md` now proposes a `posture_provider`
|
||||||
|
block inside authoritative `declarations/reef.yaml`, reusing Decision 5.5's
|
||||||
|
`available`/`maximum`/conditions/evidence semantics. For `reef-railiance` it
|
||||||
|
proposes V0 available and V1 maximum: no unconditional reusable recovery
|
||||||
|
guarantee exists, while a named workload can evidence restart/recreate recovery
|
||||||
|
inside the one failure domain. The field name and reef-schema adoption remain
|
||||||
|
subject to `repo-manager` agreement.
|
||||||
|
|
||||||
|
**Waiting:** `repo-manager` must confirm or amend the `reef.yaml` carrier,
|
||||||
|
field name, and conservative V0/V1 values. NetKingdom cannot make that
|
||||||
|
reef-vocabulary decision on its behalf.
|
||||||
|
|
||||||
|
## T03 — Reconcile reef ceilings mechanically
|
||||||
|
|
||||||
```task
|
```task
|
||||||
id: NK-WP-0027-T03
|
id: NK-WP-0027-T03
|
||||||
status: wait
|
status: wait
|
||||||
priority: low
|
priority: low
|
||||||
|
state_hub_task_id: "b53bfb83-1df9-4548-a62f-628cb55ece4d"
|
||||||
```
|
```
|
||||||
|
|
||||||
**Join reef ceilings to consumer `V` declarations mechanically.** Waits on T02.
|
**Join reef ceilings to consumer `V` declarations mechanically.** Waits on T02.
|
||||||
|
|
@ -72,6 +134,104 @@ a machine reconciles. Until then a consumer bound to a reef declares `V` with
|
||||||
the reef named as a synchronous dependency, which is already required by
|
the reef named as a synchronous dependency, which is already required by
|
||||||
Decision 4.6.1 and is not being done.
|
Decision 4.6.1 and is not being done.
|
||||||
|
|
||||||
|
**Design advanced 2026-08-22.** The proposal defines a three-valued join:
|
||||||
|
`satisfied`, `unsatisfied`, or `unknown`. It rejects claims above the reef
|
||||||
|
maximum, requires condition evidence above its unconditional available level,
|
||||||
|
and composes V across synchronous providers. Implementation still waits on the
|
||||||
|
authoritative reef field name from T02.
|
||||||
|
|
||||||
|
## T04 — Rule on zone policy subject and absence
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: NK-WP-0027-T04
|
||||||
|
status: done
|
||||||
|
priority: high
|
||||||
|
```
|
||||||
|
|
||||||
|
**Rule on the security-zone policy subject and absence semantics for
|
||||||
|
`ZONE-WP-0001-T03`.** Keep workload as the sole subject, define it broadly
|
||||||
|
enough to cover operational/control-plane execution, and state whether missing
|
||||||
|
identity or membership may inherit a zone.
|
||||||
|
|
||||||
|
**Done 2026-08-22.** Decision 5.6.1 defines operational execution units as
|
||||||
|
workloads, rejects lanes/actors/patterns as substitute subjects, and requires
|
||||||
|
unresolved membership to return `unknown` without inference. A control may
|
||||||
|
apply an explicit fail-safe stance to `unknown`; it may not relabel it.
|
||||||
|
|
||||||
|
## T05 — Broaden workload declaration coverage
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: NK-WP-0027-T05
|
||||||
|
status: done
|
||||||
|
priority: high
|
||||||
|
```
|
||||||
|
|
||||||
|
**Broaden authoritative workload declaration coverage beyond managed `rapp`
|
||||||
|
applications.** In the `security-zones_v0.1` publication review, require a
|
||||||
|
stable workload identity and responsible party for operational/control-plane
|
||||||
|
units, and a machine-readable join from credential/control resources to the
|
||||||
|
workload they serve. Do not require `rapp` packaging merely to gain identity,
|
||||||
|
and do not infer the join from path strings or repository ownership. Coordinate
|
||||||
|
the declaration boundary with `zone-engine`, `repo-manager`, and the affected
|
||||||
|
control owners.
|
||||||
|
|
||||||
|
**Done 2026-08-22.** Tenancy Posture draft-12 Decision 5.6.2 and its schema now
|
||||||
|
require `workload_identity` whenever a `zones:` block is present. The binding
|
||||||
|
names the stable workload id, kind, responsible repo, and one or more exact
|
||||||
|
authority/subject/principal-type tuples. Multi-service declarations carry both
|
||||||
|
fields per service; top-level multi-service membership is rejected. Following
|
||||||
|
RMGR-ADR-004, every managed deployable uses its authoritative rapp declaration
|
||||||
|
and consumer references use `(rapp_id, workload_identity.name)` plus optional
|
||||||
|
`deployable`. Only a non-managed operational execution unit declares locally;
|
||||||
|
native actions, actors, lanes, patterns, and resources are explicitly
|
||||||
|
`not-applicable`, while omissions stay `unknown`. The published
|
||||||
|
`canon/standards/security-zones_v0.1.md` proposal now has a closed schema shape
|
||||||
|
for its five memberships, admission context, evidence, and review dates. The
|
||||||
|
validator rejects service/id mismatch, duplicate bindings, and invalid review
|
||||||
|
windows; tests cover missing identity and a valid operational workload.
|
||||||
|
|
||||||
|
## T06 — Resolve the `DataClassification` mismatch
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: NK-WP-0027-T06
|
||||||
|
status: wait
|
||||||
|
priority: high
|
||||||
|
```
|
||||||
|
|
||||||
|
**Resolve the `DataClassification` vocabulary mismatch surfaced by
|
||||||
|
`ZONE-WP-0001-T03`.** `rapp-policy-nexus` declares `public`, while ops-warden's
|
||||||
|
`dataclass_floor` maps only `synthetic`, `internal`, `confidential`, and
|
||||||
|
`restricted`. Do not alias `public` to `synthetic`: public describes disclosure
|
||||||
|
policy, while synthetic describes data origin and whether values are real.
|
||||||
|
Route the vocabulary decision to `info-tech-canon`, which owns
|
||||||
|
`DataClassification`, and then update the workload-maturity mapping with its
|
||||||
|
ruling. Until the mapping is authoritative, a compiler must report the maturity
|
||||||
|
floor as unresolved rather than guess.
|
||||||
|
|
||||||
|
**Routed 2026-08-22.** The mismatch and non-equivalence are confirmed; owner
|
||||||
|
consultation is sent and the downstream mapping is pending.
|
||||||
|
|
||||||
|
**Waiting:** `info-tech-canon` owns `DataClassification` and must rule its
|
||||||
|
ordering relative to synthetic-data provenance. The proposed downstream
|
||||||
|
`public -> M1` mapping remains explicitly non-authoritative until that answer.
|
||||||
|
|
||||||
|
## Current gate
|
||||||
|
|
||||||
|
The zone-engine publication candidate at `a510393` has been reviewed and
|
||||||
|
published as the proposed NetKingdom `security-zones_v0.1` standard. All
|
||||||
|
locally actionable canon and schema work is complete. The remaining chain
|
||||||
|
is externally owned:
|
||||||
|
|
||||||
|
1. `repo-manager` agrees or amends the `reef.yaml` provider carrier (T02).
|
||||||
|
2. NetKingdom implements the mechanical reef ceiling join against that accepted
|
||||||
|
carrier (T03).
|
||||||
|
3. `info-tech-canon` rules the `public`/synthetic relationship, after which
|
||||||
|
ops-warden can update `dataclass_floor` (T06).
|
||||||
|
|
||||||
|
The workplan is `blocked` rather than left nominally active with every open task
|
||||||
|
at `wait`. No human intervention is required yet; owner responses are the
|
||||||
|
ordinary next input.
|
||||||
|
|
||||||
## Related
|
## Related
|
||||||
|
|
||||||
- `canon/standards/tenancy-posture_v0.1.md` Decisions 4.6.1, 5.5, 8.4.1, 8.4.2
|
- `canon/standards/tenancy-posture_v0.1.md` Decisions 4.6.1, 5.5, 8.4.1, 8.4.2
|
||||||
|
|
@ -80,3 +240,7 @@ Decision 4.6.1 and is not being done.
|
||||||
is not readiness
|
is not readiness
|
||||||
- `zone-engine/workplans/ZONE-WP-0001-security-zone-model.md` — where the
|
- `zone-engine/workplans/ZONE-WP-0001-security-zone-model.md` — where the
|
||||||
question came from, and why it is not answered there
|
question came from, and why it is not answered there
|
||||||
|
- `zone-engine/docs/exception-lifecycle-2026-08-22.md` — already requires
|
||||||
|
authoritative workload ids and fail-safe exception evaluation
|
||||||
|
- `docs/reef-posture-provider-contract.md` — concrete T02/T03 carrier and join
|
||||||
|
proposal awaiting reef-owner agreement
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
---
|
||||||
|
id: NK-WP-0028
|
||||||
|
type: workplan
|
||||||
|
title: "Publish zone canon and clarify tenant grouping semantics"
|
||||||
|
domain: infotech
|
||||||
|
repo: net-kingdom
|
||||||
|
status: finished
|
||||||
|
owner: codex
|
||||||
|
topic_slug: netkingdom
|
||||||
|
planning_priority: P1
|
||||||
|
created: "2026-08-22"
|
||||||
|
updated: "2026-08-22"
|
||||||
|
---
|
||||||
|
|
||||||
|
# NK-WP-0028 — Canon publication and grouping semantics
|
||||||
|
|
||||||
|
Authorized by the operator on 2026-08-22 after repository triage. This work
|
||||||
|
publishes the zone-engine owner draft, reconciles its authoritative workload
|
||||||
|
reference semantics with Tenancy Posture, resolves ADR-0013's now-load-bearing
|
||||||
|
grouping ambiguity, and makes the existing ADR set publication-addressable.
|
||||||
|
|
||||||
|
## Publish and integrate Security Zones v0.1
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: NK-WP-0028-T01
|
||||||
|
status: done
|
||||||
|
priority: high
|
||||||
|
```
|
||||||
|
|
||||||
|
Reviewed zone-engine revision `a510393` against Tenancy Posture Decisions 5.6.1
|
||||||
|
and 5.6.2 and RMGR-ADR-004. Published it as proposed canon at
|
||||||
|
`canon/standards/security-zones_v0.1.md`, retaining workload-only zone
|
||||||
|
membership, explicit `not-applicable` for native non-workload subjects, and
|
||||||
|
`unknown` for missing or ambiguous workload references. Replaced the tenancy
|
||||||
|
schema's placeholder `zones:` object with the standard's five memberships,
|
||||||
|
admission context, evidence, and review fields.
|
||||||
|
|
||||||
|
## Clarify current grouping versus historical identifier segment
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: NK-WP-0028-T02
|
||||||
|
status: done
|
||||||
|
priority: high
|
||||||
|
```
|
||||||
|
|
||||||
|
Amended NK-ADR-0013 so the immutable tenant identifier retains its
|
||||||
|
onboarding-time grouping segment as history while `tenant-engine.grouping` is
|
||||||
|
the mutable, authoritative current classification. Consumers may not derive
|
||||||
|
current policy, guardrail, or spend-ceiling inputs by splitting a tenant id.
|
||||||
|
Propagated the ruling to IAM Profile v0.3 and the Tenant Engine Boundary
|
||||||
|
Contract.
|
||||||
|
|
||||||
|
## Add publication metadata and ignore generated architecture cache
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: NK-WP-0028-T03
|
||||||
|
status: done
|
||||||
|
priority: medium
|
||||||
|
```
|
||||||
|
|
||||||
|
Added unique `NK-ADR-*` identifiers plus owner, revision, review date, and
|
||||||
|
review interval metadata to ADR-0006 through ADR-0015 that exist in this repo.
|
||||||
|
Recorded the security-zone source revision and added the generated architecture
|
||||||
|
AST cache to `.gitignore`.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: NK-WP-0028-T04
|
||||||
|
status: done
|
||||||
|
priority: medium
|
||||||
|
```
|
||||||
|
|
||||||
|
Verified the JSON schema parses, all 14 tenancy-posture unit tests pass, and the
|
||||||
|
repository diff has no whitespace errors. Seven estate declarations were also
|
||||||
|
checked: five validate; the previously-routed flex-auth `implemented A2` drift
|
||||||
|
and railiance-platform apps-pg R2/V1 evidence drift remain owner work and are
|
||||||
|
not regressions from this change. Workplan state was reconciled with State Hub.
|
||||||
|
Remaining external decisions stay tracked as waits in NK-WP-0027 and
|
||||||
|
NK-WP-0022.
|
||||||
Loading…
Add table
Add a link
Reference in a new issue