IBEX Harness
DocsBenchmarksBlogChangelogRoadmap
GitHub
IBEX Harness

Documentation

Architecture Decision RecordsADR-0002: Repository foundation bootstrapADR-0003: Branch protection and merge policyADR-0004: Protobuf and code generation policyADR-0005: Postgres migration strategyADR-0006: Auth protobuf contract (`ibex.auth.v1`)ADR-0007: Auth token validation implementationADR-0008: Security scanning and CI quality gatesADR-0009: Permission bitmap layoutADR-0010: Cryptography policyADR-0011: Proxy auth gRPC client and middlewareADR-0012: Proxy request normalization (OpenAI chat)ADR-0013: Proxy input validation and stable error envelopeADR-0014: Core domain migration sequencingADR-0015: Proxy rate limit skeleton (Phase 1)ADR-0016: Proxy agent identity verification (Phase 1)ADR-0017: Request ID and trace context strategy (Phase 1)ADR-0018: Graceful shutdown contract (Phase 1)ADR-0019: OpenTelemetry provider configuration (Phase 1)ADR-0020: Shared package boundaries — `packages/config` and `packages/apierror`ADR-0021: Prometheus Metric Catalog (Phase 1)ADR-0022: Health check contract (Phase 1)ADR-0023: Docs site architecture (Phase 1.5)ADR-0024: Benchmark data publishing modelADR-0025: LLM provider abstractionADR-0026: OpenAI client designADR-0027: Streaming dual-write strategyADR-0028: Auth cache designADR-0029: Token revocation propagation via Redis pub/subADR-0030: Directive versioning strategyADR-0031: System prompt injection strategyADR-0032: Session data model and retentionADR-0033: ClickHouse llm_traces schema and retentionADR-0034: Proxy overhead performance measurement methodologyADR-0035: Chat Idempotency-Key Redis dedupeADR-0038: Context assembly service design and gRPC contractADR-0039: Proxy Postgres ownership for session and directive storesADR-0040: Anthropic provider adapterADR-0041: Model capability registryADR-0042: Self-hosted OpenAI-compatible LLM adapterADR-0043: Tokenizer registry architectureADR-0044: Non-streaming response pipelineADR-0045: Streaming response transformationADR-0046: Embedder interface and profile registryADR-0047: Memory temporal validity foundationADR-0048: Memory multi-label categoriesADR-0049: Memory relationship graph readinessADR-0050: MCP server skeleton (transport, auth, audit)ADR-0051: Local LGTM observability stack (Phase 2.5 exit pull-forward)ADR-0052: Memory schema v2 expand (HNSW, quality columns)ADR-0053: Vector store abstraction and composite scoring v2ADR-0054: In-process Presidio PII detection in memoryADR-0055: Memory write-path exact and near-duplicate dedupADR-0056: Temporal-interval-aware conflict detectionADR-0057: Memory write-path persistence and orchestrationADR-0058: Semantic search FTS fallback triggerADR-0059: Hot cache read pathADR-0060: Memory service tenant isolation architectureADR-0061: Retrieval-quality benchmark methodologyADR-0062: Worker task observability and dead-letter handlingADR-0063: Extraction output schema v2ADR-0064: Extraction provider registry and batch executionADR-0065: Extraction batch idempotency and turn pointer orderingADR-0066: Extraction quality evaluation harnessADR-0067: Context capability catalog generate-and-diff for token budgetsADR-0068: Composite scoring-time relevance gateADR-0069: Context packer bounded DP knapsackADR-0070: Context formatting locked ordering and session nonceADR-0071: Context assembly gRPC degradation ladder and retrieval deadlineADR-0072: Extraction enqueue via worker HTTP (not Celery Redis wire format)ADR-0073: Organization Lifecycle and Cascading SuspensionADR-0074 Provider Credential StorageADR-0075 Per-Org Model Routing PolicyADR-0076: Circuit Breaker per ProviderADR-0077: Provider Fallback RoutingADR-0078: Operator runtime topology & environment contract (4.P.0)ADR-0079: Operator session RS256 dual-verify and TOTP step-upIBEX Console service rename and boundaryADR-0081: Fail-closed Proxy runtime controlsD2 metadata-only trace read contractD2 publication, identity, query, and lifecycle contract
ADRs›ADR-0075 Per-Org Model Routing Policy
ADRs

ADR-0075 Per-Org Model Routing Policy

Org allow/deny model policies gated in front of Registry.For, with proxy Postgres + LRU+TTL cache

ADR-0075 — Per-Org Model Routing Policy

Status

Accepted (Milestone 4.C.2)

Context

Registry.For(model) is an exact platform map with no org awareness. Exit criteria require per-org allow/deny over requested models. Credentials use Auth gRPC because they are secrets (ADR-0074); org model policy is non-secret config closer to directives and rate-limit overrides. Auth's ValidateAgent does not return default_model (4.A.3 columns exist only in Postgres).

Decision

  1. OrgAwareRegistry wraps *provider.Registry; do not change For(model) signature or exact-match semantics.
  2. Precedence: trimmed request-body model if non-empty; else proxy Postgres agents.default_model for the verified agent; else 400 model is required. Org policy is then a veto (priority ASC, first filepath.Match wins). No match → allow any registered model. Then Registry.For. default_provider is not used for registry lookup.
  3. Deny → 403 MODEL_NOT_ALLOWED. Unknown model after allow → existing 501 PROVIDER_NOT_CONFIGURED.
  4. Direct Postgres from the proxy (packages/modelpolicy store + RLS). No Auth gRPC hop.
  5. Cache: process-local LRU + TTL in packages/modelpolicy (not packages/authcache), 60s TTL, Redis pub/sub model_policy_updates:{org_id} via packages/redissub.Loop. Invalidate advances a per-org generation under the same lock used for cache install so in-flight loads cannot repopulate a stale snapshot. A positive bloom filter is not used: false negatives would fail open on deny policies. Policy load errors fail closed and return 503 SERVICE_DEGRADED; true policy denials remain 403. Agent default_model reads use a separate short-TTL LRU. Management publish-after-commit is best-effort: if Redis publish fails, proxies may serve a stale allow/deny snapshot until the ~60s TTL expires (monitor publish failures; do not treat this as fail-open on the hot path). When POSTGRES_DSN or a required model-policy dependency is unavailable, the proxy selects no provider, records ibex_proxy_model_policy_enabled=0, and emits a warning. The removed IBEX_MODEL_POLICY_ALLOW_PASSTHROUGH control cannot enable an ungoverned route.
  6. No fallback_chain column in 4.C.2 — deferred to 4.C.4 when a reader exists.
  7. Admin CRUD under /v1/organizations/{org_id}/model-policies with RequireOrgSettings on writes. Write-side model_pattern validation strips whitespace, enforces length ≤256, rejects the shared golden reject corpus, and runs a full Go filepath.Match grammar scan (character classes, escapes) so the API refuses patterns the proxy would fail-closed on at load time.
  8. Middleware change is required: ProviderRoutingMiddleware reads auth.FromContext org ID and calls ForOrg — handlers remain registry-agnostic.

Consequences

  • Chat parse allows empty model so agent-default fill can run before the policy gate.
  • Management API publishes invalidation events; proxy subscribers drop LRU entries for that org.
  • Glob dialect is Go filepath.Match (shell-style *, ?, and character classes including ranges/^ negation; malformed classes such as [] are rejected by ValidatePattern / management API golden reject list).
  • filepath.Match * does not cross /. A sole deny pattern * does not match slashy catalog IDs (e.g. openai/gpt-4). Catch-alls for those IDs need */* (and/or explicit provider/*) in addition to *.

Was this page helpful?

Edit on GitHub

Last updated on

PreviousADR-0074 Provider Credential StorageNextADR-0076: Circuit Breaker per Provider

On this page

  • Status
  • Context
  • Decision
  • Consequences
0%