Catalog fluid-telegram-operator (draft) and tenant-engine sender projection; case plan env spec and case-ref

- fluid-telegram-operator: pointer-only draft entry and playbook from ops-mason's
  MASON-WP-0005-T06 proposal; activation waits for the owner-run member and
  non-member checks and the salt create-once probe.
- audit-core-senders: tenant-engine recorded as a verified exact-path consumer
  projection (MASON-WP-0007-T06). The registry lane itself stays draft.
- warden case plan: {param:env} format spec (net-kingdom 850907a), other specs
  and conversions refused; prints case-ref <case_id>@sha256:<file digest>.
- Workload-join unknown count 21 -> 22 (test and pep-stance.yaml).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 427672@bnt-lap001
Assistant-Session: a0a2367e-4697-440c-a4e5-9713c009f6d8
This commit is contained in:
tegwick 2026-10-09 19:59:49 +02:00
parent 330de7b5ac
commit 23ba584ccd
9 changed files with 153 additions and 13 deletions

View file

@ -98,7 +98,7 @@ on_apply:
# 2026-10-08: platform-supplier-credentials added as a not-applicable path pattern,
# so routing not_applicable rose 16 -> 17.
classification_coverage:
measured: "2026-10-08"
measured: "2026-10-09"
attribution: self-measured
signing_targets:
resolved: 0
@ -106,7 +106,7 @@ classification_coverage:
not_applicable: 1
routing_lanes:
resolved: 4
unknown: 21
unknown: 22
not_applicable: 17
verdict_caching: none

View file

@ -1324,7 +1324,7 @@ entries:
workload_ref:
applicability: applicable
unknown_reason: "audit-core has not published an authoritative workload identity declaration."
need_keywords: [audit-core, senders, sender registry, ingest token, AUDIT_CORE_SENDERS]
need_keywords: [audit-core, senders, sender registry, ingest token, AUDIT_CORE_SENDERS, tenant-engine audit-core sender]
owner_repo: ops-mason
subsystem: OpenBao + audit-core
warden_executes: false
@ -1335,8 +1335,14 @@ entries:
delegation:
mode: native
intended_owner: ops-mason
reviewed: "2026-08-15"
reviewed: "2026-10-09"
verified: unverified
# Per-sender projections. Each is verified on its own and none activates the
# registry lane. tenant-engine: KV platform/workloads/tenant-engine/audit-core-sender
# (v1), read policy workload-kv-read-tenant-engine-audit-core-sender-eso (exact
# read, no registry path), ESO-delivered; verified by ops-mason 2026-10-09
# (MASON-WP-0007-T06, message e7e866bf).
consumers: [tenant-engine]
auth_method: "in-cluster generated Secret audit-core-senders; later OpenBao KV via Mason wrap-migrate"
path_template: "platform/workloads/audit-core/senders"
policy_ref: "external-secrets-audit-core"
@ -1490,3 +1496,37 @@ entries:
- "Re-mint the credential at the supplier, written to a mode-0600 file and never printed."
- "Write it through a railiance-platform delegated credential change to platform/suppliers/<supplier>/<account> (value from the file, never on argv); revoke the predecessor at the supplier."
- "Verify with capabilities and metadata only (`bao token capabilities`, `bao kv metadata get`), then tell readers to re-fetch."
- id: fluid-telegram-operator
title: fluid-telegram Hall of Helix operator KV lane (attended OIDC)
# Pointer only (MASON-WP-0005-T06, proposal 856bd6ba, 2026-10-09). Built by
# ops-mason as CCR-2026-0039; ops-mason verified the policy text and role:
# exact four-path matrix, role bound to fluid-telegram-operators at aal2, and
# 8 agent denies present. ops-mason proposed `applied-pending-verify`; the
# catalog has only draft/active, so it stays draft until the owner-run checks
# pass: member capabilities and non-member refusal
# (ops-mason scripts/verify-fluid-telegram-lane.sh), plus the salt
# create-once probe (FT-WP-0002). Then ops-warden activates it. The adapter
# runtime lane is separate (MASON-IN-0003): bot-token and redaction-salt only.
risk: high
workload_ref:
applicability: applicable
unknown_reason: "fluid-telegram has not published an authoritative workload identity declaration."
need_keywords: [fluid-telegram, hall-of-helix, telegram operator, telegram bot, mtproto, operator-session, bot-token, redaction-salt, fluid-telegram-operators]
owner_repo: ops-mason
subsystem: OpenBao netkingdom OIDC + platform KV (fluid-telegram campaign)
warden_executes: false
wiki_ref: wiki/playbooks/fluid-telegram-operator.md#worker-checklist
canon_ref: ops-mason/plans/fluid-telegram-operator-credential-lane.md
reviewed: "2026-10-09"
status: draft
delegation:
mode: native
intended_owner: ops-mason
reviewed: "2026-10-09"
verified: owner-confirmed
auth_method: "bao login -method=oidc -path=netkingdom role=fluid-telegram-hall-of-helix-workload-kv (members of fluid-telegram-operators, aal2, 15m token)"
path_template: "platform/workloads/coulomb/fluid-telegram/hall-of-helix/{operator-app,operator-session,bot-token,redaction-salt}"
exec_capable: false
resolvable: false
lane: login

View file

@ -13,6 +13,7 @@ parameter whose type declares ``sensitivity: secret`` is refused outright.
"""
from __future__ import annotations
import hashlib
import re
import string
from dataclasses import dataclass, field
@ -56,10 +57,12 @@ class CasePlan:
skipped: List[str] = field(default_factory=list)
attended_sessions: List[str] = field(default_factory=list)
verification: List[dict] = field(default_factory=list)
case_ref: Optional[str] = None
def as_dict(self) -> dict:
return {
"case": self.case_id,
"case_ref": self.case_ref,
"type": self.type_ref,
"steps": [s.as_dict() for s in self.steps],
"skipped": self.skipped,
@ -76,6 +79,15 @@ def load_case(path: Path) -> dict:
return doc
def case_ref(case_id: str, case_bytes: bytes) -> str:
"""``<case_id>@sha256:<hex>`` over the case file's exact bytes.
The reference an owner entry point records for a run (RPF-WP-0054-T01). The
owner re-derives it from the file rather than trusting the caller.
"""
return f"{case_id}@sha256:{hashlib.sha256(case_bytes).hexdigest()}"
def default_type_path(case_path: Path, type_ref: str) -> Path:
"""net-kingdom layout: security-cases/<case>.yaml beside security-case-types/<id>/type.yaml."""
type_id = type_ref.split("@", 1)[0]
@ -94,14 +106,31 @@ def _applies(when: Optional[str], params: Dict[str, str]) -> bool:
return str(params[name]).lower() == expected.lower()
#: Format specs a template may apply, matching net-kingdom's validator
#: (``FORMAT_SPECS``): ``{client_id:env}`` is upper case with ``-`` as ``_``.
FORMAT_SPECS = ("", "env")
class _Param(str):
def __format__(self, spec: str) -> str:
if spec == "env":
return self.upper().replace("-", "_")
return str.__format__(self, spec)
def _render(text: Optional[str], params: Dict[str, str], where: str) -> Optional[str]:
if text is None:
return None
names = {f for _, f, _, _ in string.Formatter().parse(text) if f}
missing = sorted(names - set(params))
fields = [(f, spec or "", conv) for _, f, spec, conv in string.Formatter().parse(text) if f]
bad = sorted({f"{f}:{spec}" for f, spec, _ in fields if spec not in FORMAT_SPECS})
if bad:
raise CaseError(f"{where}: unknown format spec in {bad}; only {{param:env}} is supported")
if any(conv for _, _, conv in fields):
raise CaseError(f"{where}: conversions such as !r are not supported in templates")
missing = sorted({f for f, _, _ in fields} - set(params))
if missing:
raise CaseError(f"{where}: parameters {missing} are not set by the case")
return text.format(**params)
return text.format_map({k: _Param(v) for k, v in params.items()})
def plan_case(case_doc: dict, type_doc: dict, catalog: Optional[Catalog]) -> CasePlan:
@ -178,7 +207,10 @@ def _line(f: Finding) -> str:
def format_plan(plan: CasePlan) -> str:
labels = {"warden": "warden (caller proxy)", "attended": "ATTENDED", "owner": "owner"}
lines = [f"case {plan.case_id} ({plan.type_ref}): plan only, nothing executed", ""]
lines = [f"case {plan.case_id} ({plan.type_ref}): plan only, nothing executed"]
if plan.case_ref:
lines.append(f"case-ref: {plan.case_ref}")
lines.append("")
for n, s in enumerate(plan.steps, 1):
lane = f" lane={s.lane}" if s.lane else ""
lines.append(f"{n}. {s.id} [{labels[s.mode]}] owner={s.owner}{lane} values={s.value_handling}")

View file

@ -2255,13 +2255,14 @@ def case_plan(
output_json: Annotated[bool, typer.Option("--json", help="Output JSON")] = False,
) -> None:
"""Print a case's run (warden, attended, owner steps and verification); executes nothing (exit 1 if refused)."""
from warden.case_plan import CaseError, default_type_path, format_plan, load_case, plan_case
from warden.case_plan import CaseError, case_ref, default_type_path, format_plan, load_case, plan_case
from warden.case_type import load_type
try:
case_doc = load_case(case_file)
type_path = type_file or default_type_path(case_file, str((case_doc.get("spec") or {}).get("type", "")))
plan = plan_case(case_doc, load_type(type_path), _load_catalog())
plan.case_ref = case_ref(plan.case_id, case_file.read_bytes())
except CaseError as exc:
err.print(str(exc), markup=False)
raise typer.Exit(1)
@ -2271,4 +2272,4 @@ def case_plan(
if output_json:
print(json.dumps(plan.as_dict(), indent=2))
else:
console.print(format_plan(plan), markup=False)
console.print(format_plan(plan), markup=False, soft_wrap=True)

View file

@ -34,7 +34,8 @@ TYPE = {
{"id": "mint", "owner": "o", "entry_point": "mint {client_id}", "auth": "caller",
"lane": "key-cape-oidc-login", "warden_executes": True, "attended": False,
"value_handling": "generated_in_custody", "when": "client_type == confidential"},
{"id": "login", "owner": "o", "entry_point": "apply {client_id}", "auth": "operator",
{"id": "login", "owner": "o", "entry_point": "apply {client_id} env KEYCAPE_{client_id:env}_X",
"auth": "operator",
"lane": "key-cape-oidc-login", "warden_executes": False, "attended": True,
"value_handling": "none", "attended_justification": "live registry is a Secret"},
{"id": "handover", "owner": "{consumer}", "entry_point": "use {issuer}", "auth": "a",
@ -158,6 +159,21 @@ def test_refuse_secret_parameter(catalog):
plan_case(_case(parameters=dict(CASE["spec"]["parameters"], pw="hunter2")), doc, catalog)
def test_env_format_spec_renders_like_keycape_client(catalog):
params = dict(CASE["spec"]["parameters"], client_id="my-app")
plan = plan_case(_case(parameters=params), _type(), catalog)
login = next(s for s in plan.steps if s.id == "login")
assert login.entry_point == "apply my-app env KEYCAPE_MY_APP_X"
@pytest.mark.parametrize("template", ["{client_id:upper}", "{client_id:>9}", "{client_id!r}"])
def test_refuse_unknown_format_spec_or_conversion(catalog, template):
doc = _type()
doc["spec"]["follow_through"][0]["entry_point"] = template
with pytest.raises(CaseError, match="format spec|conversions"):
plan_case(_case(), doc, catalog)
def test_refuse_unsupported_condition(catalog):
doc = _type()
doc["spec"]["follow_through"][0]["when"] = "client_type in (a, b)"
@ -173,6 +189,9 @@ def test_cli_plan_shows_attended_and_exit_codes(tmp_path):
ok = runner.invoke(app, ["case", "plan", str(case_file), "--type", str(type_file)])
assert ok.exit_code == 0, ok.output
assert "ATTENDED" in ok.output and "nothing executed" in ok.output
import hashlib
digest = hashlib.sha256(case_file.read_bytes()).hexdigest()
assert f"case-ref: c@sha256:{digest}" in ok.output
case_file.write_text(yaml.safe_dump(_case(status="accepted")))
refused = runner.invoke(app, ["case", "plan", str(case_file), "--type", str(type_file)])
assert refused.exit_code == 1

View file

@ -13,7 +13,7 @@ def test_repo_catalog_uses_only_explicit_workload_references():
report = build(ROOT / "registry/routing/catalog.yaml", Path.home())
assert report["ok"] is True
assert len(report["resolved"]) == 4
assert len(report["unknown"]) == 21
assert len(report["unknown"]) == 22
# 15 since 2026-09-21: the layer-declaration route is repository conformance
# guidance, not a workload delivery lane, and the two NetKingdom SSO lanes
# (c374d41) remain provider/control-plane credentials. Lanes added after
@ -24,6 +24,8 @@ def test_repo_catalog_uses_only_explicit_workload_references():
# under a founder-minted grant, not a workload credential. 17 since
# 2026-10-08: platform-supplier-credentials (WARDEN-WP-0043) is a generic
# supplier-account path pattern; concrete consumers keep their own lanes.
# unknown 22 since 2026-10-09: fluid-telegram-operator (MASON-WP-0005-T06)
# names fluid-telegram, which has published no workload declaration.
assert len(report["not_applicable"]) == 17
assert {row["lane"] for row in report["resolved"]} == {
"ops-warden-warden-sign-token",

View file

@ -15,5 +15,15 @@ First deploy mints sender tokens in-cluster into Secret
`platform/workloads/audit-core/senders` is the later authority, filled by a
Mason wrap-migrate — not by a founder `bao kv put`.
## Sender projections
A sender that needs its own token gets an exact-path projection of it. That is a
separate reader of a single sender's token, never read access to the registry.
Each projection is verified on its own and does not activate the registry lane.
| Sender | KV path | Reader | Verified |
| --- | --- | --- | --- |
| tenant-engine | `platform/workloads/tenant-engine/audit-core-sender` | policy `workload-kv-read-tenant-engine-audit-core-sender-eso`, role `tenant-engine-audit-core-sender-eso` (SA `tenant-engine/tenant-engine-eso`), ExternalSecret `tenant-engine/tenant-engine-audit-core-sender` | 2026-10-09, ops-mason MASON-WP-0007-T06 (metadata and policy text only) |
Never place a sender token, bearer, or `senders.json` value in Git, State
Hub, logs, or chat.

View file

@ -0,0 +1,26 @@
# fluid-telegram operator lane (Hall of Helix)
Catalog: `fluid-telegram-operator` (status `draft`, pointer only)
Owner: `ops-mason` (construction plan `ops-mason/plans/fluid-telegram-operator-credential-lane.md`, CCR-2026-0039)
Consumer: `fluid-telegram` attended provisioner (FT-WP-0002)
## Worker checklist
This page is a pointer. ops-warden does not issue, read, or hold these values.
1. You must be a member of the LLDAP group `fluid-telegram-operators`, signed
in at aal2 (MFA).
2. Log in attended:
`bao login -no-print -method=oidc -path=netkingdom role=fluid-telegram-hall-of-helix-workload-kv`.
The token lives 15 minutes. Agents are denied on these paths.
3. Run the fluid-telegram provisioner per FT-WP-0002. It reads the operator
app entry, writes the MTProto session and bot token, and creates
`redaction-salt` once.
4. Verify with `bao token capabilities` only, never by reading a value. The
owner-run check is `ops-mason/scripts/verify-fluid-telegram-lane.sh`.
The bot adapter's runtime read of `bot-token` and `redaction-salt` is a
separate lane (MASON-IN-0003). It never uses this attended role.
Status: draft until the owner-run member and non-member checks and the salt
create-once probe pass. ops-warden then activates the entry.

View file

@ -108,7 +108,7 @@ Tests: refuse an unaccepted or unmaterialized case; refuse a step whose catalog
entry is missing or not reviewed; show attended steps explicitly.
2026-10-09: done. `warden case plan <case.yaml> [--type] [--json]`
(`src/warden/case_plan.py`, 15 tests in `tests/test_case_plan.py`). It refuses:
(`src/warden/case_plan.py`, 14 tests in `tests/test_case_plan.py`). It refuses:
- a case that is not `materialized`, or has no acceptance record
- a type that is mismatched or not acknowledged
- any `case check-type` finding, including a missing or unreviewed lane
@ -130,6 +130,16 @@ Observation for net-kingdom (not blocking): `bind-env` runs before
`KEYCAPE_<CLIENT_ID>_CLIENT_SECRET`, which is not a `{param}` placeholder, so it
reaches the plan unrendered.
2026-10-09 follow-up:
- **Format specs.** `_render` supports net-kingdom's `{param:env}` spec (upper
case, `-` as `_`, the same as `keycape-client.py env_name`) and refuses any
other spec or `!conversion`.
- **Case reference.** The plan prints `case-ref: <case_id>@sha256:<hex>` over the
case file's exact bytes. This is the reference proposed to railiance-platform
for `credential.py generate --case-ref` (RPF-WP-0054-T01); the broker
re-derives it from the file.
- 18 tests now.
```task
id: WARDEN-WP-0044-T03
status: wait