Implement §5.3 machine-readably — layer.yaml, checker, conformance tests
The security layer model moved v0.1 -> v0.4 (accepted) after ops-warden's
assent. Both §5 asks from ADR-0010 were adopted: §5.2 now sanctions the conduit
shape on the supplied-authority property, and §5.3 is the declared engine gap
amendment, carrying the four fields verbatim and crediting ops-warden's
delegation machinery as prior art.
Which creates an obligation. §5.3 requires those fields MACHINE-READABLY, and
§11 makes "every direct Tooling client maps to a declared §5.1/§5.2/§5.3 entry"
a mechanical check. ops-warden's declaration was prose in INTENT.md — the repo
that proposed the shape was not implementing it.
layer.yaml is the map: 5 contacts (2 declared gaps, 1 read-only observation,
2 conduits) plus the non-Tooling clients recorded explicitly so the check is
total rather than silently selective.
scripts/check_layer_conformance.py enforces it and found three undeclared
modules on its first run — all false positives (help text, a docstring, and the
doubles library that SIMULATES bao rather than calling it), which is why the
scan now matches invocation shapes instead of the word: an httpx call built
against the configured OpenBao address, or an argv whose first element is the
bao binary.
tests/test_layer_conformance.py adds the §5.2 test the standard says SHOULD
exist: _caller_env() returns the caller's environment unchanged, and proxy.py
is asserted not to reference X-Vault-Token, approle login, or token create — a
conduit that presents its own token is not a conduit.
No assertion on review dates, deliberately: a date-triggered failure breaks the
build on a calendar day with no code change, the same reasoning WP-0033-T05
recorded for blocker staleness.
398 tests pass, ruff clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YWBMovyFoy9RRrfL7zKvPJ
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 4014535@bnt-lap001
Assistant-Session: d0036016-73e8-4da1-8e47-563e3ab39a3c
2026-08-29 02:45:29 +02:00
|
|
|
# ops-warden — NetKingdom security layer declaration
|
|
|
|
|
#
|
2026-09-05 01:19:48 +02:00
|
|
|
# Framework: net-kingdom/canon/standards/security-layer-model_v0.7.md
|
Implement §5.3 machine-readably — layer.yaml, checker, conformance tests
The security layer model moved v0.1 -> v0.4 (accepted) after ops-warden's
assent. Both §5 asks from ADR-0010 were adopted: §5.2 now sanctions the conduit
shape on the supplied-authority property, and §5.3 is the declared engine gap
amendment, carrying the four fields verbatim and crediting ops-warden's
delegation machinery as prior art.
Which creates an obligation. §5.3 requires those fields MACHINE-READABLY, and
§11 makes "every direct Tooling client maps to a declared §5.1/§5.2/§5.3 entry"
a mechanical check. ops-warden's declaration was prose in INTENT.md — the repo
that proposed the shape was not implementing it.
layer.yaml is the map: 5 contacts (2 declared gaps, 1 read-only observation,
2 conduits) plus the non-Tooling clients recorded explicitly so the check is
total rather than silently selective.
scripts/check_layer_conformance.py enforces it and found three undeclared
modules on its first run — all false positives (help text, a docstring, and the
doubles library that SIMULATES bao rather than calling it), which is why the
scan now matches invocation shapes instead of the word: an httpx call built
against the configured OpenBao address, or an argv whose first element is the
bao binary.
tests/test_layer_conformance.py adds the §5.2 test the standard says SHOULD
exist: _caller_env() returns the caller's environment unchanged, and proxy.py
is asserted not to reference X-Vault-Token, approle login, or token create — a
conduit that presents its own token is not a conduit.
No assertion on review dates, deliberately: a date-triggered failure breaks the
build on a calendar day with no code change, the same reasoning WP-0033-T05
recorded for blocker staleness.
398 tests pass, ruff clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YWBMovyFoy9RRrfL7zKvPJ
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 4014535@bnt-lap001
Assistant-Session: d0036016-73e8-4da1-8e47-563e3ab39a3c
2026-08-29 02:45:29 +02:00
|
|
|
# Assent: docs/adr/ADR-0010 (ops-warden's own voice, per §11 "who must declare")
|
|
|
|
|
# Validate: python3 scripts/check_layer_conformance.py
|
|
|
|
|
#
|
|
|
|
|
# §11 makes one check mechanical: "every direct Tooling client in a Staff
|
|
|
|
|
# repository maps to a declared §5.1, §5.2, or §5.3 entry". This file is that
|
|
|
|
|
# map. It is machine-readable because §5.3 requires it to be — ops-warden
|
|
|
|
|
# proposed that shape and is implementing it rather than declaring in prose.
|
|
|
|
|
#
|
|
|
|
|
# Conformance rule inherited from tenancy.yaml: accuracy, not altitude. A
|
|
|
|
|
# declared gap is TRACKED NON-CONFORMANCE (§11), never a claim of conformance.
|
|
|
|
|
|
|
|
|
|
schema_version: "0.1"
|
|
|
|
|
framework: netkingdom-security-layer-model
|
2026-09-05 01:19:48 +02:00
|
|
|
standard_version: "0.7"
|
Implement §5.3 machine-readably — layer.yaml, checker, conformance tests
The security layer model moved v0.1 -> v0.4 (accepted) after ops-warden's
assent. Both §5 asks from ADR-0010 were adopted: §5.2 now sanctions the conduit
shape on the supplied-authority property, and §5.3 is the declared engine gap
amendment, carrying the four fields verbatim and crediting ops-warden's
delegation machinery as prior art.
Which creates an obligation. §5.3 requires those fields MACHINE-READABLY, and
§11 makes "every direct Tooling client maps to a declared §5.1/§5.2/§5.3 entry"
a mechanical check. ops-warden's declaration was prose in INTENT.md — the repo
that proposed the shape was not implementing it.
layer.yaml is the map: 5 contacts (2 declared gaps, 1 read-only observation,
2 conduits) plus the non-Tooling clients recorded explicitly so the check is
total rather than silently selective.
scripts/check_layer_conformance.py enforces it and found three undeclared
modules on its first run — all false positives (help text, a docstring, and the
doubles library that SIMULATES bao rather than calling it), which is why the
scan now matches invocation shapes instead of the word: an httpx call built
against the configured OpenBao address, or an argv whose first element is the
bao binary.
tests/test_layer_conformance.py adds the §5.2 test the standard says SHOULD
exist: _caller_env() returns the caller's environment unchanged, and proxy.py
is asserted not to reference X-Vault-Token, approle login, or token create — a
conduit that presents its own token is not a conduit.
No assertion on review dates, deliberately: a date-triggered failure breaks the
build on a calendar day with no code change, the same reasoning WP-0033-T05
recorded for blocker staleness.
398 tests pass, ruff clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YWBMovyFoy9RRrfL7zKvPJ
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 4014535@bnt-lap001
Assistant-Session: d0036016-73e8-4da1-8e47-563e3ab39a3c
2026-08-29 02:45:29 +02:00
|
|
|
repository: ops-warden
|
Revert the npm field, re-measure coverage, and hold the layer divergence
Five inbox items worked, none of which changed a credential value or moved a
secret.
whynot-design-npm-publish: field reverted npm_token -> NPM_AUTH_TOKEN and the
path confirmed, on railiance-platform's attended, read-only, no-value field
enumeration (their docs/evidence/2026-09-10-npm-lane-field-resolution.json).
Exactly one field is present at the governed path. The 2026-09-09 change was
adopted from a coordination message and would have failed at the WP-0037-T03
rotation. The ungoverned second location is recorded as an explicit non-lane,
not deleted and not tidied away.
pep-stance coverage: published figures were stale by eight lanes (unknown
18->20, not_applicable 12->15) while resolved stayed at 3 — the denominator
moved, the classification did not. Caught by the test that asserts the published
block equals what report_coverage.py measures. tests/test_workload_join.py held
the same stale counts; both now measure the same populations.
rapp-qonto-keycape-client: blocker character updated — authority exists and is
unexercised by owner decision ("not yet", offer open), which is not the same as
no authority existing. Reopen triggers are events, never elapsed time.
flex-auth -> access-engine rename (WARDEN-IN-0003): access-engine added to the
policy-check lane's keywords so routing resolves under both names from today.
owner_repo deliberately not flipped — policy.py sends it as resource.system on
every /v1/check, and FLEX-DEC-2026-013 keeps runtime names as flex-auth.
layer declaration: INTENT.md says Staff, layer.yaml says staff, section 11 does
not say which governs. Neither changed; gate-house holds the ruling. Position in
docs/layer-declaration-precedence.md, wait in WARDEN-WP-0034-T06, and a comment
in layer.yaml telling the next session not to "fix" it — the divergence is the
evidence the ruling is made against.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 63291@bnt-lap001
Assistant-Session: 8bd77868-ca68-4f49-bb1e-d539ecc0d703
2026-09-21 02:16:33 +02:00
|
|
|
# UNRULED, 2026-09-21: INTENT.md frontmatter declares `Staff` and this file
|
|
|
|
|
# declares `staff`. §11 permits both forms and does not say which governs when
|
|
|
|
|
# both exist and disagree; gate-house holds that ruling and the case-sensitivity
|
|
|
|
|
# one. NEITHER VALUE IS BEING CHANGED until it rules — see
|
|
|
|
|
# docs/layer-declaration-precedence.md and WARDEN-WP-0034-T06. Do not "fix" this
|
|
|
|
|
# line to match INTENT.md, or the reverse: the divergence is the evidence.
|
Implement §5.3 machine-readably — layer.yaml, checker, conformance tests
The security layer model moved v0.1 -> v0.4 (accepted) after ops-warden's
assent. Both §5 asks from ADR-0010 were adopted: §5.2 now sanctions the conduit
shape on the supplied-authority property, and §5.3 is the declared engine gap
amendment, carrying the four fields verbatim and crediting ops-warden's
delegation machinery as prior art.
Which creates an obligation. §5.3 requires those fields MACHINE-READABLY, and
§11 makes "every direct Tooling client maps to a declared §5.1/§5.2/§5.3 entry"
a mechanical check. ops-warden's declaration was prose in INTENT.md — the repo
that proposed the shape was not implementing it.
layer.yaml is the map: 5 contacts (2 declared gaps, 1 read-only observation,
2 conduits) plus the non-Tooling clients recorded explicitly so the check is
total rather than silently selective.
scripts/check_layer_conformance.py enforces it and found three undeclared
modules on its first run — all false positives (help text, a docstring, and the
doubles library that SIMULATES bao rather than calling it), which is why the
scan now matches invocation shapes instead of the word: an httpx call built
against the configured OpenBao address, or an argv whose first element is the
bao binary.
tests/test_layer_conformance.py adds the §5.2 test the standard says SHOULD
exist: _caller_env() returns the caller's environment unchanged, and proxy.py
is asserted not to reference X-Vault-Token, approle login, or token create — a
conduit that presents its own token is not a conduit.
No assertion on review dates, deliberately: a date-triggered failure breaks the
build on a calendar day with no code change, the same reasoning WP-0033-T05
recorded for blocker staleness.
398 tests pass, ruff clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YWBMovyFoy9RRrfL7zKvPJ
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 4014535@bnt-lap001
Assistant-Session: d0036016-73e8-4da1-8e47-563e3ab39a3c
2026-08-29 02:45:29 +02:00
|
|
|
layer: staff
|
|
|
|
|
declared_by: docs/adr/ADR-0010
|
|
|
|
|
declared_at: "2026-08-29"
|
|
|
|
|
|
Review layer model v0.6; publish the PEP stance map §6.4 requires
All three v0.4 findings were acted on — §9.1 split into pending/declared-gap and
§5's scope rule adopted as recommended and credited, and §9.6 ruled via the
load-bearing/attributive distinction with ops-warden's `# audit must not block
signing` named as the estate's live example.
Checked the favourable ruling rather than accepting it. §9.6's test is "no
control branches on its presence": the only consumer of audit.jsonl is `warden
activity`, which displays. Nothing gates on a signing record, so the lane is
genuinely attributive. AuditTrail.md now records the ruling instead of the open
question, and states that the trade must be revisited if a control ever gates on
the trail.
CONFORMANCE ACTION. §6.4 obligation 3 requires a stance map "published rather
than held in code", and requires every PEP-shaped consumer to publish one so the
maps can be inventoried — naming ADR-0009 as the reference shape. ops-warden was
not doing it: the map lived in PolicyConfig.failure_modes, a dataclass default.
Not a code comment, but not published either.
pep-stance.yaml publishes it, and the test asserts the published map EQUALS the
shipped default. A published map that may drift from the code is worse than no
map, because it invites reliance it cannot support.
Two findings sent to gate-house, in history/2026-08-29-layer-model-v06-review.md:
§6.4 obligation 1 (no side effect without a decision record) contradicts
obligation 3 and §9.3, with ops-warden's blessed fail-open stance as the
instance; and §6.4 mandates a stance-map inventory in §13 that §13 does not
implement — where ops-warden is currently the only PEP to have published one.
402 tests pass, ruff clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YWBMovyFoy9RRrfL7zKvPJ
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 4014535@bnt-lap001
Assistant-Session: d0036016-73e8-4da1-8e47-563e3ab39a3c
2026-08-29 10:20:49 +02:00
|
|
|
# §6.4 — ops-warden is PEP-shaped (it causes a protected side effect: issuing a
|
|
|
|
|
# certificate). Its unreachable-engine stance map is published separately, and
|
|
|
|
|
# asserted equal to shipped behaviour by tests/test_layer_conformance.py.
|
|
|
|
|
pep_stance: pep-stance.yaml
|
|
|
|
|
|
Implement §5.3 machine-readably — layer.yaml, checker, conformance tests
The security layer model moved v0.1 -> v0.4 (accepted) after ops-warden's
assent. Both §5 asks from ADR-0010 were adopted: §5.2 now sanctions the conduit
shape on the supplied-authority property, and §5.3 is the declared engine gap
amendment, carrying the four fields verbatim and crediting ops-warden's
delegation machinery as prior art.
Which creates an obligation. §5.3 requires those fields MACHINE-READABLY, and
§11 makes "every direct Tooling client maps to a declared §5.1/§5.2/§5.3 entry"
a mechanical check. ops-warden's declaration was prose in INTENT.md — the repo
that proposed the shape was not implementing it.
layer.yaml is the map: 5 contacts (2 declared gaps, 1 read-only observation,
2 conduits) plus the non-Tooling clients recorded explicitly so the check is
total rather than silently selective.
scripts/check_layer_conformance.py enforces it and found three undeclared
modules on its first run — all false positives (help text, a docstring, and the
doubles library that SIMULATES bao rather than calling it), which is why the
scan now matches invocation shapes instead of the word: an httpx call built
against the configured OpenBao address, or an argv whose first element is the
bao binary.
tests/test_layer_conformance.py adds the §5.2 test the standard says SHOULD
exist: _caller_env() returns the caller's environment unchanged, and proxy.py
is asserted not to reference X-Vault-Token, approle login, or token create — a
conduit that presents its own token is not a conduit.
No assertion on review dates, deliberately: a date-triggered failure breaks the
build on a calendar day with no code change, the same reasoning WP-0033-T05
recorded for blocker staleness.
398 tests pass, ruff clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YWBMovyFoy9RRrfL7zKvPJ
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 4014535@bnt-lap001
Assistant-Session: d0036016-73e8-4da1-8e47-563e3ab39a3c
2026-08-29 02:45:29 +02:00
|
|
|
# Every direct contact with a Tooling-layer system (§4), one entry each.
|
|
|
|
|
tooling_contacts:
|
|
|
|
|
|
|
|
|
|
- id: ssh-ca-signing-write
|
|
|
|
|
shape: "5.3" # declared engine gap
|
|
|
|
|
module: src/warden/vault.py
|
|
|
|
|
symbol: VaultCA.sign
|
|
|
|
|
tooling: OpenBao
|
|
|
|
|
operation: "HTTP POST <addr>/v1/<mount>/sign/<role> with X-Vault-Token"
|
|
|
|
|
write: true
|
|
|
|
|
capability: "Sign a short-lived SSH certificate for an adm/agt/atm actor"
|
|
|
|
|
intended_owner: secrets-engine
|
|
|
|
|
blocked_on: >-
|
|
|
|
|
No engine exposes an SSH certificate signing surface. secrets-engine owns
|
|
|
|
|
credential abstraction, custody and lifecycle, which is the layer this
|
|
|
|
|
belongs in, but fronts no SSH-CA API today.
|
|
|
|
|
review: "2026-11-28"
|
|
|
|
|
note: >-
|
|
|
|
|
Production-verified and in daily use. This is the one lane ops-warden owns
|
|
|
|
|
permanently (§4). Signing continues while the gap is open: refusing would
|
|
|
|
|
remove production host access to close a documentation gap.
|
|
|
|
|
|
|
|
|
|
- id: desk-paste-once-provision
|
|
|
|
|
shape: "5.3"
|
|
|
|
|
module: src/warden/desk.py
|
|
|
|
|
symbol: _provision_to_openbao
|
|
|
|
|
tooling: OpenBao
|
|
|
|
|
operation: "bao kv put <path> <field>=- (value on stdin, never argv)"
|
|
|
|
|
write: true
|
|
|
|
|
capability: "Founder paste-once provisioning of a secret straight into OpenBao"
|
|
|
|
|
intended_owner: secrets-engine
|
|
|
|
|
blocked_on: >-
|
|
|
|
|
No engine exposes an attended provisioning surface for a value the founder
|
|
|
|
|
holds and no automated path can produce.
|
|
|
|
|
review: "2026-11-28"
|
|
|
|
|
note: >-
|
|
|
|
|
Attended and founder-operated (WP-0029). The value reaches OpenBao without
|
|
|
|
|
passing through a terminal, an argv, or the audit log.
|
|
|
|
|
|
|
|
|
|
- id: taint-metadata-read
|
|
|
|
|
shape: "5.1" # read-only diagnostic observation
|
|
|
|
|
module: src/warden/taint.py
|
|
|
|
|
symbol: fetch_taint_status
|
|
|
|
|
tooling: OpenBao
|
|
|
|
|
operation: "bao kv metadata get -format=json <path>"
|
|
|
|
|
write: false
|
|
|
|
|
capability: "Report EXPOSED taint (custom_metadata) without reading secret data"
|
|
|
|
|
intended_owner: secrets-engine
|
|
|
|
|
blocked_on: >-
|
|
|
|
|
No engine exposes a disclosure-taint query. Metadata-only by construction —
|
|
|
|
|
reading the data would be the 2026-07-16 vector this exists to avoid.
|
|
|
|
|
review: "2026-11-28"
|
|
|
|
|
|
|
|
|
|
- id: access-proxy-conduit
|
|
|
|
|
shape: "5.2" # conduit
|
|
|
|
|
module: src/warden/proxy.py
|
|
|
|
|
symbol: proxy_fetch, proxy_attended_login_exec
|
|
|
|
|
tooling: OpenBao, key-cape
|
|
|
|
|
operation: "Runs the catalog-declared owner fetch_command as a child process"
|
|
|
|
|
write: false
|
|
|
|
|
capability: "warden access --fetch/--exec/--out/--wrap for exec_capable lanes"
|
|
|
|
|
supplied_authority: none
|
|
|
|
|
evidence:
|
|
|
|
|
no_own_credential: src/warden/proxy.py::_caller_env
|
|
|
|
|
test: tests/test_proxy.py::test_conduit_supplies_no_authority_of_its_own
|
|
|
|
|
audit: "audit.jsonl records the caller, the lane, and the outcome; never a value"
|
|
|
|
|
note: >-
|
|
|
|
|
The §5.2 test is the supplied-authority property: ops-warden presents no
|
|
|
|
|
credential of its own, cannot widen what the caller could already do, and
|
|
|
|
|
the action reconstructs as the caller's. Governed by ADR-0002.
|
|
|
|
|
|
|
|
|
|
- id: caller-identity-token
|
|
|
|
|
shape: "5.2"
|
|
|
|
|
module: src/warden/caller_identity.py
|
|
|
|
|
symbol: resolve_caller_token
|
|
|
|
|
tooling: OpenBao
|
|
|
|
|
operation: "Runs the operator-configured caller_auth command, or reads token env"
|
|
|
|
|
write: false
|
|
|
|
|
capability: "Establish the caller's own identity for the pre-sign policy gate"
|
|
|
|
|
supplied_authority: none
|
|
|
|
|
detection: voluntary # runs an operator-configured command, so no fixed
|
|
|
|
|
# argv shape to scan for; declared rather than omitted
|
|
|
|
|
note: >-
|
|
|
|
|
Obtains the CALLER's credential by the operator's configured means; adds no
|
|
|
|
|
authority. Never mints, and never persists what it resolves.
|
|
|
|
|
|
|
|
|
|
# Contacts that are deliberately NOT Tooling contacts, recorded so the check is
|
|
|
|
|
# total rather than silently selective.
|
|
|
|
|
non_tooling_clients:
|
|
|
|
|
|
|
|
|
|
- module: src/warden/policy.py
|
|
|
|
|
target: access-engine (flex-auth)
|
|
|
|
|
rationale: "Engine API — §5 permits it; this is the shape §5 prescribes."
|
|
|
|
|
|
|
|
|
|
- module: src/warden/worker.py
|
|
|
|
|
target: state-hub, llm-connect
|
|
|
|
|
rationale: >-
|
|
|
|
|
Not catalogued in §4. The layer catalog scopes the security estate, and
|
|
|
|
|
neither the State Hub nor llm-connect appears in it, so no §5 shape applies
|
|
|
|
|
on the standard's own terms. Raised with gate-house 2026-08-29 as a scope
|
|
|
|
|
question rather than resolved unilaterally — see the assessment note.
|