# ops-warden secret-read example (`flex-auth-ops-warden-fetch`) Policy, registry and fixtures for ops-warden's **secret-fetch** policy check (`check_fetch_policy` in ops-warden `src/warden/policy.py`): before `warden access --fetch/--exec` proxies a non-SSH credential read, it asks whether `read` on `secret:[/]` owned by `` is allowed. Opened by the-custodian bug `b792c5d0` (CUST-WP-0075): those checks were sent to the SSH signing pin `flex-auth-ops-warden`, whose package admits only `resource.system == "ops-warden"` and whose caller bindings know only that system, so every other owner got 403. | File | What it is | | --- | --- | | `policy_package.md` | `ops-warden.secret.read` v1, `allow_ttl: 5m`; the `catalog_lanes` block is generated | | `registry_snapshot.json` | generated: one system per owner, one `secret:` resource per lane | | `policy_fixtures.yaml` | 15 fixtures: 4 allows, 11 denial branches | | `check_request_*.json` | standalone requests for `POST /v1/check` | | `../../values/ops-warden-fetch.yaml` | the pin; caller bindings generated, image digest set by hand after CI | ## Why a separate pin One decision class per pin, independently rollable (FLEX-WP-0016). The server serves one policy package per process. SSH signing (`sign` on `ssh-certificate`, system `ops-warden`) and secret reads (`read` on `secret`, system = owner repo) differ in action, resource type, systems and caller bindings. Folding reads into the signing pin would redeploy the lane the ops-bridge tunnels depend on for every catalog change. ## The rule Allow when all hold, otherwise deny with the first failing reason: `action == read` (`wrong_action`), `resource.type == secret` (`wrong_resource_type`), tenant `tenant:platform` (`wrong_tenant`), `subject.type == operator` (`wrong_subject_type`), non-empty subject id (`missing_subject`), `context.need_id` present (`missing_need_id`) and in the catalog set (`unknown_need`), `resource.system` a catalog owner (`unknown_system`) and the owner of that need (`wrong_system`), `resource.id == secret:` or `secret:/` (`resource_need_mismatch`), `context.owner_repo == resource.system` (`owner_repo_mismatch`). Caller authentication is `enforce`: every owner system is bound to `system:serviceaccount:ops-warden:ops-warden`. Another caller, an anonymous call, or an owner system missing from the bindings is rejected with 401/403 before the package runs. For a registered resource the registry's `type` wins over the caller's (FLEX-DEC-2026-012), so a non-secret type only reaches the package on an unregistered id; the package denies it either way. ## Regenerate when the catalog changes The readable set is every ops-warden catalog entry with `status: active`, `exec_capable: true` and `lane: secret`. After a lane is added or retired: ```bash python3 tools/gen_ops_warden_fetch.py # rewrites the three generated files python3 tools/gen_ops_warden_fetch.py --check # exit 1 on drift (tests/stage1.sh runs it) go run ./cmd/access-engine test-policy --file examples/ops-warden-fetch/policy_package.md ``` `--catalog PATH` overrides the default `~/ops-warden/registry/routing/catalog.yaml`. A new **owner system** adds a caller binding, so it needs a pin redeploy. A new lane under an existing owner changes only the package and registry, which are baked into the image, so it needs a new image too (`deploy/README.md`). ## Verify ```bash go run ./cmd/access-engine test-policy --file examples/ops-warden-fetch/policy_package.md # 6 Rego tests, 15 fixtures go run ./cmd/access-engine load-registry -file examples/ops-warden-fetch/registry_snapshot.json go test ./cmd/access-engine -run OpsWardenFetch ``` ## Not yet deployed `values/ops-warden-fetch.yaml` carries no image digest until CI builds an image containing this directory. The deploy steps (founder go required) are in `workplans/ADHOC-2026-10-03.md`. Residual (build mode, close-down checklist row 1): `subject.id` is whatever ops-warden sends (its subject env or `operator`); it is not bound to an attended human identity. The caller binding proves that ops-warden asked, not which operator did.