diff --git a/docs/evidence/2026-09-14-openrouter-live-pdp.json b/docs/evidence/2026-09-14-openrouter-live-pdp.json new file mode 100644 index 0000000..f7c4358 --- /dev/null +++ b/docs/evidence/2026-09-14-openrouter-live-pdp.json @@ -0,0 +1,22 @@ +{ + "image": "sha256:05a03a8790c2210c48ea92391441c77ddf640d0cd32f5ec09838f5393171fcbd", + "pod": "flex-auth-secrets-engine-6467b96c6f-ncl5t", + "scope": "PDP evaluation only; synthetic claim, no approval consume or OpenBao access", + "checks": { + "native_request_preserved": true, + "missing_caller_refused": true, + "wrong_caller_refused": true, + "foreign_owner_representation_refused": true, + "wrong_tenant_denied": true, + "recipient_cannot_be_lifecycle_subject": true, + "producer_approval_digest_pair": true, + "changed_path_cannot_reuse_approval_digest": true, + "changed_mount_cannot_reuse_approval_digest": true, + "changed_repo_cannot_reuse_approval_digest": true, + "other_deployment_specs_unchanged": true + }, + "decision_id": "decision:e8bec2d72f7df007", + "forward_stopped": true, + "caller_token_handling": "10 minute audience-bound tokens held in memory only; expire at issuer, not revoked by dropping local references", + "consumer_validation": "Actual secrets-engine validate_decision_envelope accepted the live response against the exact submission and v2 pin." +} diff --git a/docs/openrouter-native-access.md b/docs/openrouter-native-access.md new file mode 100644 index 0000000..a757b61 --- /dev/null +++ b/docs/openrouter-native-access.md @@ -0,0 +1,61 @@ +# OpenRouter native access contract — 2026-09-14 + +FLEX-WP-0026 answers intelligence-radar message +`a4a4f455-bacd-4172-a45c-2c375a58db12` and ops-warden question +`a90672e4-4f5f-4ab8-ac43-455f4da351a7` (WARDEN-WP-0039-T03). + +The existing caller binding admits representation of a protected system. It +contains no delegated credential-read contract for ops-warden to represent +railiance-platform. The resolution for this use case is the native +secrets-engine lifecycle path. Do not widen ops-warden's binding, rename the +credential owner, or treat its planner's `autonomous` verdict as runtime admission. + +The native CheckRequest addresses `catalog:openrouter-llm-connect`, type +`secret-catalog-lane`, system `secrets-engine`, tenant `tenant:platform`, subject +`secrets-engine` / `service`. This is the lifecycle resource actually enforced by +secrets-engine, not a relabelled railiance-platform credential read. OpenBao +custody remains railiance-platform's; llm-connect remains the existing workload +owner. Radar is a proposed delivery recipient, not a PDP caller or lifecycle subject. + +`context.catalog_target` carries the actual non-secret mount/path, owner repo, +fields, consumers, delivery/auth specification and workload delivery. Exec also +carries the existing exact recipient digest. The evaluator binds this submitted +context through FLEX-DEC-2026-012; it does not independently admit an arbitrary +KV path. The consumer must join the issuer's exact-action approval to +`approval_binding_digest` and CAS-consume before OpenBao. A changed path or owner +can produce a new policy allow, but cannot reuse the old approval. Claim validity +and declared human control remain the issuer/consumer responsibilities. + +## Dedicated pin correction + +Helm release `flex-auth-secrets-engine`, revision 4, now uses the existing +CI-published source `dd8dd517438b876fdadf27770e3f7e7f55ea69cf` image +`sha256:05a03a8790c2210c48ea92391441c77ddf640d0cd32f5ec09838f5393171fcbd`. +The old September 6 image lacked the consumer's current replay contract. +Policy stays `secrets-engine.catalog-lane.lifecycle` / `v2`; caller enforcement +and the existing ServiceAccount binding remain in force. The policy rules did +not change. Loopback forwarding to the named pod authenticates the responder +through the Kubernetes API; plain workstation Service DNS remains unsupported. + +Validation: full Go race suite, image policy validation (28 tests / 32 fixtures), +Helm lint and server dry-run, then 11 live checks. Correct native caller succeeds; +missing/wrong callers, foreign system, wrong tenant and recipient-as-subject +refuse. Submitted context and approval digest pairing survive the real evaluator; +changed path/mount/owner produces a different approval binding. All other PDP +Deployment specs were compared before/after and are identical. Ten-minute +caller tokens stayed in memory; the temporary named-pod forward was stopped. + +Receipt: `docs/evidence/2026-09-14-openrouter-live-pdp.json`. It is evaluation-only +with a synthetic claim, not real approval or OpenBao evidence. If this pin cannot +serve the current contract, stop native execution; rolling back to the prior +image restores the replay incompatibility and cannot unblock credential delivery. + +## Live handoff + +SECRETS-WP-0010-T03 holds the unresolved native admission and delivery work, +linked to SECRETS-WP-0007-T04/T07 and SECRETS-WP-0006-T05/T06. Approval Engine +has no StatefulSet, pod or Service in its declared namespace at inspection. +Its production identity/audit and client-reader gates must be completed before +native apply. No credential read, AppRole/policy write, ESO change or model spend +was performed here. WARDEN-WP-0039-T03 and IR-WP-0004-T02 remain waiting on the +native verification; publishing this contract does not retire the proxy. diff --git a/internal/callerauth/openrouter_test.go b/internal/callerauth/openrouter_test.go new file mode 100644 index 0000000..1cdcea7 --- /dev/null +++ b/internal/callerauth/openrouter_test.go @@ -0,0 +1,43 @@ +package callerauth + +import ( + "context" + "errors" + "testing" +) + +// IR-WP-0004 / WARDEN-WP-0039: a caller binding is representation, +// not delegated authority over another system's credentials. +func TestOpenRouterNativeCallerBoundary(t *testing.T) { + bindings := map[string]string{ + "ops-warden": "system:serviceaccount:ops-warden:ops-warden", + "secrets-engine": "system:serviceaccount:secrets-engine:secrets-engine", + } + for _, tc := range []struct { + name, caller string + systems []string + denied bool + }{ + {"native lifecycle caller", bindings["secrets-engine"], []string{"secrets-engine"}, false}, + {"warden own system", bindings["ops-warden"], []string{"ops-warden"}, false}, + {"warden cannot represent custody owner", bindings["ops-warden"], []string{"railiance-platform"}, true}, + {"warden cannot impersonate native engine", bindings["ops-warden"], []string{"secrets-engine"}, true}, + {"radar is recipient not lifecycle caller", "system:serviceaccount:intelligence-radar:intelligence-radar", []string{"secrets-engine"}, true}, + {"native caller cannot represent custody owner", bindings["secrets-engine"], []string{"railiance-platform"}, true}, + {"batch must bind every owner", bindings["ops-warden"], []string{"ops-warden", "railiance-platform"}, true}, + } { + t.Run(tc.name, func(t *testing.T) { + auth, err := New(ModeEnforce, fakeReviewer{identity: Identity{Username: tc.caller, Audiences: []string{"flex-auth"}}}, "flex-auth", bindings, nil) + if err != nil { + t.Fatal(err) + } + err = auth.Authorize(context.Background(), "Bearer synthetic-caller", tc.systems) + if tc.denied && !errors.Is(err, ErrForbidden) { + t.Fatalf("want forbidden, got %v", err) + } + if !tc.denied && err != nil { + t.Fatal(err) + } + }) + } +} diff --git a/values/secrets-engine.yaml b/values/secrets-engine.yaml index f153b5c..5b819bd 100644 --- a/values/secrets-engine.yaml +++ b/values/secrets-engine.yaml @@ -1,8 +1,8 @@ -# Dedicated secrets-engine policy service; CI build main-d98323b. +# Dedicated secrets-engine policy service; CI build dd8dd517438b876fdadf27770e3f7e7f55ea69cf. name: flex-auth-secrets-engine image: repository: forgejo.coulomb.social/coulomb/flex-auth - digest: sha256:db1c4f7e621c7ea119489a321d7db0e05da09afc17be5f69d873b2b3c7f60cfc + digest: sha256:05a03a8790c2210c48ea92391441c77ddf640d0cd32f5ec09838f5393171fcbd args: - serve - --addr diff --git a/workplans/FLEX-WP-0026-openrouter-native-contract.md b/workplans/FLEX-WP-0026-openrouter-native-contract.md new file mode 100644 index 0000000..f9e59d3 --- /dev/null +++ b/workplans/FLEX-WP-0026-openrouter-native-contract.md @@ -0,0 +1,49 @@ +--- +id: FLEX-WP-0026 +type: workplan +title: "Resolve OpenRouter access contract and update native PDP" +domain: infotech +repo: flex-auth +status: finished +owner: codex +topic_slug: netkingdom +created: "2026-09-14" +updated: "2026-09-14" +related_workplans: + - WARDEN-WP-0039 + - IR-WP-0004 + - SECRETS-WP-0010 +--- + +## Resolve the caller versus credential-owner contract + +```task +id: FLEX-WP-0026-T01 +status: done +priority: high +``` + +`docs/openrouter-native-access.md` resolves the inbox requests using the existing +native lifecycle resource. No admitted delegated-read contract exists in the +caller binding. Added concrete caller-boundary regressions for native success, +warden owner/engine impersonation, radar subject and mixed-owner batch refusal. +Full Go race suite passed. No ops-warden binding or credential-owner rename. + +## Promote and verify the current native PDP contract + +```task +id: FLEX-WP-0026-T02 +status: done +priority: high +``` + +Dedicated release revision 4 now uses published dd8dd517 image +sha256:05a03a8790c2210c48ea92391441c77ddf640d0cd32f5ec09838f5393171fcbd. +Policy package stays v2; enforce binding retained. Image policy tests/fixtures, +Helm lint/server dry-run and 11 live checks passed. Other PDP Deployment specs +unchanged. Named-pod forward removed; bounded tokens held only in memory. +Receipt: docs/evidence/2026-09-14-openrouter-live-pdp.json. + +Live residual handed off before closure: SECRETS-WP-0010-T03 holds approval-service, +client-reader, attended authority and exact recipient admission plus real native +verification. WARDEN-WP-0039-T03 and IR-WP-0004-T02 are not closed by this work.