Skip to content

feat(routing): fence intelligent_route candidates via entitlement claim - #1283

Open
hexfusion wants to merge 1 commit into
praxis-proxy:mainfrom
hexfusion:feat/candidate-claim-gating
Open

hexfusion wants to merge 1 commit into
praxis-proxy:mainfrom
hexfusion:feat/candidate-claim-gating

Conversation

@hexfusion

@hexfusion hexfusion commented Sep 22, 2026

Copy link
Copy Markdown

Summary

Adds a generic, config-driven fence to intelligent_route. Candidates carry a labels map, and match_claims keeps only candidates whose label equals a claim on the authenticated identity, before the model-name pick. Adding an entitlement dimension (residency, tier, sovereignty) is configuration, not code.

It fails closed. When a request carries no authenticated identity, is missing the gated claim, or matches no in-scope candidate, the gateway denies it rather than routing. A fenced-out capability returns the same 404 as an unknown one, so a caller cannot enumerate what it is fenced out of. Session-affinity reuse and weighted selection both respect the fence. The ungated path (no match_claims) is unchanged and allocates nothing new. It reuses the AuthenticatedIdentity extension token_rate_limit already reads, so it adds no new plumbing.

filters:
  - filter: intelligent_route
    model_header: X-Model
    match_claims:
      - { claim: grid_region, label: region }   # residency fence; a missing claim denies
    candidates:
      - { kind: inference_model, name: qwen, cluster: site-us, labels: { region: us-east-1 } }
      - { kind: inference_model, name: qwen, cluster: site-eu, labels: { region: eu-west-1 } }

Matching is exact-equality only. Richer operators (range, hierarchy, set membership) are out of scope and can be added later without breaking this config.

Granting a user an entitlement

The fence holds no per-user state. The entitlement rides in the caller's token as a claim, sourced from the IdP (for example a Keycloak user attribute mapped into the JWT). alice's token carries:

{ "sub": "alice", "grid_region": "eu-west-1", "tier": "gold" }

The gateway validates the token, lands grid_region on the authenticated identity, and match_claims keeps only candidates whose region label equals it, so alice routes to the eu-west-1 site. Granting or changing alice's residency is an IdP change (set the attribute), never a gateway change. A second dimension is the same move: the IdP adds grid_tier, and the config adds { claim: grid_tier, label: tier } plus a tier label on candidates.

Related issue

Closes #1282

Validation

  • cargo test -p praxis-ai-filters --lib routing:: gives 405 passed. Covers matching, unlabeled-candidate exclusion, no-eligible-member, weighted-draws-stay-in-scope, validation bounds, fail-closed-without-identity, and the ungated-unchanged path.
  • Example config intelligent-route-claim-gating.yaml plus a fail-closed integration test.
  • make lint clean. Commits are signed with a Signed-off-by trailer.

Breaking changes

None. match_claims and labels default empty, so existing configs route exactly as before.

…laim

Add generic claim-gated candidate selection. Candidates carry a `labels`
map; the filter carries `match_claims: [{claim, label}]`. Per request the
filter reads the claim off the authenticated identity and keeps only
candidates whose matching label equals it, so a request is never routed
outside the caller's entitlement (data residency, tier, and so on).

The gate reuses the AuthenticatedIdentity extension the policy engine
already publishes, so no new plumbing is added. It fails closed: a gate
with no identity or a missing claim denies. A fenced-out capability
returns the same 404 as an unknown one, so a caller cannot enumerate the
capabilities it is fenced out of. The ungated path is unchanged and
allocates nothing new; a gate also fences session-affinity reuse and
weighted selection, so no draw lands out of scope.

Both static and overlay candidate modes carry the label, so a fleet
registry can drive the fence through the hot-reloaded overlay.

Signed-off-by: Sam Batschelet <sbatsche@redhat.com>
@hexfusion
hexfusion requested review from a team and crstrn13 September 22, 2026 03:25
@hexfusion hexfusion changed the title feat(routing): fence intelligent_route candidates by an entitlement claim feat(routing): fence intelligent_route candidates via entitlement claim Sep 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(routing): configurable entitlement fence for intelligent_route candidates

1 participant