Repository navigation
fix(OMN-20533): dev-lane runtime-effects reads tenant BYOK keys with its own read-only Infisical identity - #4561
Conversation
…its own read-only Infisical identity The contract declares the tenant_inference_credentials namespace rule on the dev profile's effects process only, rendered as DEV_RUNTIME_EFFECTS_STORE_SECRET_RESOLVER_CONFIG_JSON. docker-compose.dev-lane.yml binds it on runtime-effects with RUNTIME_EFFECTS_INFISICAL_* from the host env file; the dev-105, dev-200, dev-202 and pre-PR overlays rebind the house-only config so a lane without the identity never demands one. Onex-Lane: byok-infisical Onex-Session: ef2d8921f964462697ccdcd0395cc080
…12832) * evidence(OMN-20533): author OCC companion for OmniNode-ai/omnibase_infra#4561 OCC companion by node_pr_lifecycle_fix_effect (OMN-13317 F1 / OMN-13990 / OMN-14285). Product PR head 641c37be6525a65f5e440535f9e70a955e1702ea. * evidence(OMN-20533): self-bind OCC#12832 + rebind contract_sha256 --------- Co-authored-by: omnimarket-bot <bot@omninode.ai>
There was a problem hiding this comment.
Hostile Reviewer — adversarial findings (OMN-17492)
Models succeeded: qwen3-review, gpt-oss-review
Models failed: none
New finding threads: 0
Deduped (already posted on this PR): 0
Below quorum (one model only, reported not threaded): 11
Nit-level findings suppressed: 0
The model is the FINDER, never the gate: merge is gated only by the
deterministic Hostile Review Thread Gate, which blocks while
hostile-reviewer threads are unresolved. Resolve each thread after
addressing (or rejecting, with a reply) its finding.
Below quorum: 11 finding(s) raised by one model only (OMN-18479)
These are reported and NOT dropped, but they get no thread and do not block: a single model's finding no other model reproduced is not evidence enough to stop a merge. Read them; act on them if they are right.
- [MAJOR]
contracts/services/runtime_policy.contract.yaml(qwen3-review) — Regex allows tenant refs to reference arbitrary paths in Infisical | The ref_pattern^cred_[A-Za-z0-9._:-]+_[A-Za-z0-9_-]+_[0-9a-f]{32}$permits the tenant and provider segments to contain dots, col - [MAJOR]
docker/docker-compose.dev-lane.yml, docker/docker-compose.dev-105.yml, docker/docker-compose.dev-200.yml, docker/docker-compose.dev-202.yml, docker/docker-compose.prepr.yml(qwen3-review) — Infisical client secret exposed in docker-compose environment | The dev-lane and all borrower overlays setINFISICAL_CLIENT_SECRETas a plain environment variable in the docker-compose file. In dev- - [MAJOR]
docker/docker-compose.dev-105.yml, docker/docker-compose.dev-200.yml, docker/docker-compose.dev-202.yml, docker/docker-compose.prepr.yml(qwen3-review) — Borrower overlays use house config but still set INFISICAL_* env vars | The borrower overlays (dev-105, dev-200, dev-202, prepr) rebindONEX_SECRET_RESOLVER_CONFIG_JSONto the house-only config (`DE - [MAJOR]
scripts/render_runtime_policy_env.py(gpt-oss-review) — Empty SECRET_RESOLVER_CONFIG_JSON emitted when no mappings or namespaces | render_runtime_policy_env.render_env sets secret_resolver_config_json to an empty string when a profile has neither secret_re - [MINOR]
tests/integration/infra/test_runtime_effects_tenant_credential_read_omn20533.py(qwen3-review) — No test for the case where a borrower overlay is missing the rebind | The testtest_every_other_dev_lane_layer_rebinds_the_house_configasserts that each borrower overlay sets `ONEX_SECRET_RESOLVER_ - [MINOR]
scripts/render_runtime_policy_env.py(qwen3-review) — Resolver config JSON is re-serialized on every render | The_secret_resolver_config_jsonfunction is called for each process that hassecret_resolver_namespaces. In the current contract, only the - [MINOR]
tests/integration/infra/test_runtime_effects_tenant_credential_read_omn20533.py(qwen3-review) — Test does not verify that the house config is not modified | The testtest_the_rendered_store_config_is_the_house_config_plus_the_ruleasserts that the store config's mappings equal the house config - [MINOR]
src/omnibase_infra/runtime/models/model_runtime_process_policy.py(qwen3-review) — Process-level namespaces are a new concept that may be confusing | The introduction ofsecret_resolver_namespaceson the process policy is a new architectural concept. The comment explains that it i - [MINOR]
src/omnibase_infra/runtime/models/model_runtime_profile_policy.py(gpt-oss-review) — Assumption that secret_resolver_config_path is always a string | ModelRuntimeProfilePolicy._requires_the_runtime_family_and_allows_optional_carriers validates `if not self.secret_resolver_config_path. - [MINOR]
docker/runtime-policy.env(gpt-oss-review) — Potential information disclosure via env‑exposed secret resolver config | The newDEV_RUNTIME_EFFECTS_STORE_SECRET_RESOLVER_CONFIG_JSONenvironment variable contains the full secret‑resolver configu - [MINOR]
docker/docker-compose.dev-105.yml(gpt-oss-review) — Default‑empty INFISICAL_* variables may hide missing identity configuration | Docker compose files for dev‑105, dev‑200, dev‑202, and prepr setINFISICAL_ADDR,INFISICAL_PROJECT_ID, `INFISICAL_CLI
✅ Hostile Reviewer — REVIEWEDCritical findings: 0
Semantics (OMN-17492 — the model finds, thread resolution gates)
Powered by omniintelligence.review_pairing.cli_review — multi-model adversarial review: qwen3-review (Qwen3.8-27B), gpt-oss-review (gpt-oss-120b) (OMN-8468/OMN-8524/OMN-17492) |
What
Closes the BYOK read half on the .201 dev lane (OMN-20533, child of OMN-18829; OMN-20439 landed the write half in #4539).
onex-api writes a tenant's provider key to Infisical env
dev, folder/tenant-inference-credentials, under a minted ref, and the tenant overlay routes the lab tenant to it. The provider call runs in runtime-effects, whose resolver config was house-only (env mappings, no namespace rule, blank identity), so every tenant ref failed closed: 4 of 4 lab-tenant runs on 2026-10-04 09:33-09:50Z ended provider_error.contracts/services/runtime_policy.contract.yaml: thetenant_inference_credentialsnamespace rule on the dev profile's effects process only (same rule and folder as the onex-dev runtime ConfigMap).ModelRuntimeProcessPolicy.secret_resolver_namespaces(new, process-level) plus profile validation (needs the config path, names unique across process and profile).scripts/render_runtime_policy_env.py: rendersDEV_RUNTIME_EFFECTS_STORE_SECRET_RESOLVER_CONFIG_JSON(house mappings plus the rule). Every profile-level*_SECRET_RESOLVER_CONFIG_JSONis unchanged.docker-compose.dev-lane.ymlruntime-effects: binds that config,INFISICAL_ADDR/PROJECT_ID/CLIENT_ID/CLIENT_SECRETfromRUNTIME_EFFECTS_INFISICAL_*host names, slug pinned todev.docker-compose.dev-105/200/202.ymlanddocker-compose.prepr.yml: rebind runtime-effects to the house config and the base identity names. An Infisical rule makes a process build its store handler at wiring time and refuse when the identity is blank, and those lanes hold none.Why not the profile, and why
:-A profile-level rule would reach main, worker and tenant-projection, and each would then demand an identity at boot. The identity is
:-, because compose interpolates every file before it merges them, so:?in the shared dev-lane file would fail the borrower lanes' render. On .201 a blank identity is still loud: effects refuses at wiring and names every blank variable (proven below).Identity (already provisioned, consent ledger OPERATOR-CONSENT 2026-10-04T18:27:06Z lane=byok-infisical)
Instance D, project 872e00f4, identity
dev-lane-runtime-effects-tenant-credentials(8940ae51), org role no-access, project roledev-lane-runtime-effects-tenant-cred-read= secrets read on env dev/tenant-inference-credentialsonly. As the identity, the tenant folder lists 22 names (admin control: 22);/shared/envlists 0 (admin: 6);/lab-provider-keyslists 0 (admin: 1); prod and staging list 0; a create on the tenant path returns 403. The fourRUNTIME_EFFECTS_INFISICAL_*names are in the .201 operator env file. Nothing reads them until this merges.Lab proof (no dev-lane container touched)
An isolated throwaway container ran on .202 (image
omnibase-infra-dev-202-runtime-effectssha256:04b92f1c5842, bridge network, removed afterwards) against the real instance D, using this branch's rendered store config. The probe ran at 2026-10-04T18:50:43Z:build_topology_secret_resolverresolve_api_key_with_source_asynctenant_inference_credentials; lab-tenant ref resolves non-empty (len 73); unregistered ref raises RESOURCE_NOT_FOUNDlane_secret_storeINFISICAL_*The compose render (
docker compose config, non-mutating) shows dev runtime-effects carrying the rule andRUNTIME_EFFECTS_INFISICAL_*, with main and worker unchanged. dev-202 and prepr runtime-effects render house-only, with no client id and a blank slug.RLS and tenant-form paths: not touched. This reads a credential at the effect boundary. It changes no RLS policy and no reader authorization through onex-api.
Tests
tests/integration/infra/test_runtime_effects_tenant_credential_read_omn20533.py: 14 pass. Against the dev pre-image, 12 of the 14 fail.render_runtime_policy_env.py --check-env-file docker/runtime-policy.env: match.Deploy note
Runtime-affecting for the .201 dev lane's runtime-effects only. The ledger HOLD keeps it unmerged until 2026-10-05T05:45Z, because a merge triggers a deploy-agent dev-lane recreate and the dev lane is in live demo use until then. The scheduled warm refresh is at 2026-10-05T06:00Z.
Evidence-Ticket: OMN-20533
Evidence-Source: OCC#12832