2026-06-27 18:07:42 +02:00
# Workload Security Posture — NetKingdom standard (draft)
> **Status:** ops-warden-authored draft, WARDEN-WP-0015 T1. **Pending promotion to
> canon** along two homes (see *Canon layering*). Until landed, this file is the
> authoritative working draft; the canon copies supersede it once merged.
>
> **ops-warden's role:** *author + conformance*. ops-warden does **not** enforce this
> standard at runtime (flex-auth) and does **not** hold the secrets (OpenBao). It
> authors the ops-security slice and ships conformance checks + dev-tier doubles.
NetKingdom IT-security posture is defined along **two orthogonal axes** . A workload's
right to receive a secret depends on **both** , unified by a secret-flow lattice.
---
## Axis A — Environment posture (how the secret store is secured)
The lifecycle tier of the *secret store backing a workload* . Contracts are identical at
every tier (so automation and the `warden access` proxy run unchanged); only the
backend's security posture changes.
**R1 — Contract parity, posture divergence.** Identical interface at every tier; only
2026-06-27 19:30:30 +02:00
posture changes. This is why dev-tier contract doubles ("fake bao") work. ops-warden
ships the sanctioned `dev` backend as a library: `warden.doubles.materialize_doubles()`
writes hermetic stand-ins for the routed subsystems (OpenBao, key-cape login) that honor
each contract (argv/stdout/exit) and emit **synthetic values only** (every value is
`synthetic-` prefixed), so access flows run fully offline in dev/test.
2026-06-27 18:07:42 +02:00
**R2 — Promote topology, regenerate material.** Secret *values* are never promoted up
the ladder; only *structure* (paths, policy shape, names). Values are generated fresh
per tier. Test conveniences (reuse, single-unseal) stay quarantined in test.
**R3 — Dev touches no real data, ever.** An insecure personal mock store in dev is
sanctioned *iff* dev uses only synthetic data. Absolute invariant.
**R4 — Phase-changes are ceremonies, not copies.** `test → prod` is a gated checklist
(regenerate secrets, switch unseal model, enable break-glass, human sign-off),
referencing the existing net-kingdom `security-bootstrap-*` and unseal-custody docs —
not duplicating them.
| | dev | test | prod |
| --- | --- | --- | --- |
| backend | mock / contract double | OpenBao `-dev` (single-unseal) | OpenBao sealed (Shamir 3-of-5) |
| real values | forbidden (synthetic) | generated, reuse allowed | generated fresh, reuse forbidden |
| unseal | n/a | single key / auto | 3-of-5 + break-glass |
| real user/business data | never | never | allowed |
| audit | optional | on | full, tamper-evident |
---
## Axis B — Workload maturity (how trusted a workload is)
**Production is a posture, not a maturity.** A workload can run in prod posture yet be
low maturity (alpha with friendly customers). Maturity gates *which secrets and data
classes* a prod workload may touch. Levels are a total order `M0 < M1 < M2 < M3` .
| Level | Phase | Max `DataClassification` it may handle | Promotion gate (into this level) |
| --- | --- | --- | --- |
| **M0** | Experimental / PoC | synthetic only | — (entry level) |
| **M1** | Alpha / early-access | low-criticality, loss-acceptable; **no** `confidential` /`restricted` | friendly-customer scope agreed, basic SLO, data-handling note |
| **M2** | Beta / GA | up to `confidential` ; SLOs; audited | security review, SLO history, on-call, incident runbooks |
| **M3** | Critical / regulated | `restricted` ; break-glass; compliance | pen-test, 3-of-5 custody, human-in-loop ops, compliance audit |
`DataClassification` (`confidential` , `restricted` , …) is **reused** from the
info-tech-canon Data Model — not redefined here. Promotion gates **reuse** the
info-tech-canon DevSecOps Model's quality/policy gates and `DeploymentVerification`
(SLOs / smoke / canary / operator confirmation), applied to maturity advancement.
---
## The combined rule — secret-flow lattice
A secret carries a `required_maturity` (and implicitly the `required_maturity` of its
`DataClassification` ). Delivery is **no-write-down** :
```
deliver(secret → workload) is permitted only if
workload.env_posture == prod # Axis A
AND workload.maturity >= secret.required_maturity # Axis B
AND workload.maturity >= required_maturity(dataclass(secret)) # data class floor
```
**"Critical-infrastructure secrets must not be transferred to workloads below maturity
M"** is exactly the second clause. The lattice is **checkable** by ops-warden
(conformance) and **enforceable** at runtime by flex-auth. Access *semantics* (who, on
behalf of whom) remain governed by the CARING Access Governance Standard.
Worked example: an `NPM_AUTH_TOKEN` used only by a build pipeline → `required_maturity:
M1`, dataclass ` internal`. A production database password for regulated user data →
`required_maturity: M3` , dataclass `restricted` ; it may be delivered only to a
prod-posture, M3 workload.
---
2026-06-27 18:22:09 +02:00
## Using this to refine blockers
2026-07-08 16:41:15 +02:00
When a workplan says "blocked on security", classify it before escalating. The
2026-06-27 18:22:09 +02:00
classification decides whether the blocker is real, belongs to an owning subsystem, or
can be removed by a dev/test double.
| Question | Result |
| --- | --- |
| Is the work **dev** or **test** posture only? | Use synthetic contract doubles or generated test values. Do not wait on real production secrets. |
| Is the work **prod** posture with real values? | Require owner custody (usually OpenBao), flex-auth policy where applicable, and non-secret evidence only. |
| Is workload maturity below the secret's `required_maturity` or data-class floor? | This is a real IT-security blocker until the workload advances, the secret is reclassified, or the design avoids the secret. |
| Does a route exist and the lane is `exec_capable` ? | `warden access --fetch/--exec` may remove operator copy/paste as a blocker by proxying the owner's tool as the caller. |
| Is unseal, break-glass, or issuer custody unresolved? | Keep it as an operator ceremony/design blocker; do not paper it over with agent-visible values. |
The evidence to record is route id, owner, env posture, workload maturity,
`required_maturity` , policy decision id, OpenBao path/version, populated-key count,
smoke id, or token accessor. Never record the secret value.
This is the practical bridge from WARDEN-WP-0014 (`warden access` ) to WP-0015: access
assist can remove manual secret handling friction, while posture/maturity decides
whether the secret may flow at all.
2026-08-22 15:36:37 +02:00
## Security-zone consumer contract
Security zones are now the prescriptive sibling of this descriptive posture
model. `zone-engine` owns `security-zones_v0.1` ; ops-warden follows it as a
consumer under `ADR-0009` .
- Workload maturity and data classification are admission evidence, not zone
names and not control stance.
- The workload owner declares authoritative `workload_identity` and `zones:` in
the same `tenancy.yaml` service entry.
- Credential lanes and actor resources reference that workload explicitly.
Managed deployables use Repo Manager's exact `(rapp_id, name, deployable?)`
tuple; independent operational workloads reference their owner declaration.
- Missing identity, membership, or evidence resolves `unknown` . No compiler may
infer it from a path, repository owner, actor class, or environment.
- flex-auth owns pre-sign stance; ops-warden owns PEP dependency failure behavior,
the agent read boundary, and `warden plan` escalation behavior.
Ops-warden declares `z1-operational` with M1/internal/medium evidence. That is an
accuracy statement, not a target: M2 remains unavailable until SLO history,
on-call, and incident/recovery evidence exist.
2026-06-27 18:22:09 +02:00
---
2026-06-27 18:07:42 +02:00
## Canon layering (where each part lands)
| Part | Canonical home | ops-warden role |
| --- | --- | --- |
| Generic `WorkloadMaturityLevel` concept + the secret-flow lattice | **info-tech-canon** (DevSecOps / Landscape; reuses Data Model `DataClassification` , Security Model criticality) | Contribute; do not fork |
| NetKingdom M0– M3 security **requirements** + env-posture ceremonies | **net-kingdom canon** (beside `openbao-unseal-custody-models.md` , `responsibility-map.md` ) | Author the ops-security slice |
2026-06-27 19:30:30 +02:00
| Machine-readable descriptors (`registry/policy/security-posture.yaml` , `warden policy` ) + read-only conformance checker (`scripts/check_secret_posture_conformance.py` ) + dev doubles (`warden.doubles` ) | **ops-warden** | Own (WP-0015 T2– T4) |
2026-06-27 18:07:42 +02:00
| Runtime enforcement of the lattice | **flex-auth** | Route; do not enforce here |
---
## Boundaries preserved
- **OpenBao** holds secret values. ops-warden never custodies them.
- **flex-auth** decides allow/deny (incl. enforcing this lattice at runtime).
- **CARING / Access Control** governs access semantics and delegation.
- **key-cape** establishes identity. ops-warden authors the standard and *checks
conformance* — it does not become a broker, PDP, or IdP (responsibility-map).
---
## See also
- `wiki/OperatorAccessAssist.md` — the posture-aware `warden access` fetch surface
- `net-kingdom/docs/openbao-unseal-custody-models.md` , `responsibility-map.md` ,
`platform-root-custody.md` , `security-bootstrap-*`
- info-tech-canon: Security Model, DevSecOps Model, Data Model, CARING Access Governance
- `workplans/WARDEN-WP-0015-secret-lifecycle-tiering.md`