[proxy,management] Bound model discovery to the caller's own policies (#7239)

[proxy,management] Bound model discovery to the caller's own policies

GET /v1/models was bounded by the provider record's enumerated models, which is
the right bound only while a single policy reaches a provider. Where two teams
share one provider under different allowlists, every caller was offered the
union — each model outside their own policy being a request the guardrail
refuses a moment later. A gateway record enumerating nothing was worse: it
offered the upstream's entire catalogue however narrow the policy was.

Each route now carries one rule per authorising policy — its source groups and
the models it permits — instead of a single flattened list. At request time the
router keeps the rules whose groups intersect the caller's, unions their
models, and intersects that with what the provider serves.

nil and [] stay distinct end to end: a policy setting no allowlist reaches the
router as nil and lifts the restriction for the groups it binds, while an
enabled allowlist with no models arrives as [] and permits nothing. Collapsing
them would let a listing that should offer nothing fall open to everything.

The guardrail's own per-provider allowlist is untouched. It is a fail-closed
backstop that cannot tell who is asking, so discovery is now narrower than the
backstop rather than wider.
This commit is contained in:
Maycon Santos
2026-08-23 20:13:35 +02:00
committed by GitHub
parent 766fcae3f8
commit 7be45c2dd8
9 changed files with 993 additions and 25 deletions
+6 -1
View File
@@ -174,7 +174,12 @@ func chatOnce(t *testing.T, ctx context.Context, env pricedEnv, model, sessionID
// accessLogIngestWindow is how long a single request's access-log row is given
// to appear before the caller gives up on it.
const accessLogIngestWindow = 30 * time.Second
// accessLogIngestWindow bounds how long a row may take to appear after its
// request returned. The proxy streams each entry to management with a 10s send
// timeout of its own, so a request whose send hits one full timeout and is
// retried has not yet missed anything real — 30s left barely three send
// attempts of headroom and lost the race on a loaded runner.
const accessLogIngestWindow = 60 * time.Second
// accessLogPollInterval is how long the lookup waits between pages. Ingest is
// asynchronous, so the row lands somewhere inside the window rather than on