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 v2
ADRs›ADR-0028: Auth cache design
ADRs

ADR-0028: Auth cache design

Accepted ADR — in-process invalid-token bloom + claims LRU for proxy ValidateToken; 30s revoke lag until 2.2.2 pub/sub.

ADR-0028: Auth cache design

  • Status: Accepted
  • Date: 2026-07-23
  • Authors: IBEX Harness team
  • Milestone: 2.2.1 Auth cache bloom + LRU

Context

Every protected proxy request paid a full auth gRPC ValidateToken (Postgres + Argon2). That consumes a large fraction of the <20ms p99 proxy overhead budget. Milestone 2.2.1 inserts a cache decorator on auth.TokenValidator without changing middleware contracts.

Docs previously drifted between RedisBloom, a 5s revoke SLA via TTL alone, and serving stale LRU during auth downtime. This ADR locks the Phase 2 design; ADR-0029 owns active invalidation.

Decision

1) Two tiers, in-process only

TierRole
Invalid-token bloom (bits-and-blooms/bloom/v3)Probabilistic set of hashes rejected by upstream. Test==true → skip LRU, call gRPC. False positive (bloom bad, gRPC good) → FP metric + populate LRU. Valid tokens are never added to the bloom.
Claims LRU (hashicorp/golang-lru/v2)Bounded map of validated claims keyed by SHA-256 hex of the raw token. Hot-path hits skip gRPC.

Redis is not used for bloom or claims in 2.2.1 (rate limit + future 2.2.2 pub/sub only). Distributed RedisBloom remains deferred.

2) Sizing defaults

ParameterDefaultRationale
Bloom expected items10,000Recent invalids per proxy process
Bloom FP rate0.001 (0.1%)Bounded extra gRPC on FP
LRU capacity5,000~1MB claims footprint per instance
LRU max TTL30sHard upper bound on revoke lag without pub/sub

3) TTL formula

ttl = min(LRUMaxTTL, token.expires_at - now - 5s)

If ttl <= 0, skip caching. Keys are token hashes only — raw tokens are never stored in bloom/LRU.

4) Revocation SLA (aligned with ADR-0029)

  • 2.2.1 alone: max stale window = LRUMaxTTL (30 seconds). Goal 2.2 “revoke ≤5s” is not met by TTL alone.
  • 2.2.2: Redis pub/sub calls CachingValidator.InvalidateByTokenID(tokenID) to shrink lag toward the 5s exit gate / ~1s ideal path.

5) Failure mode — fail closed

On upstream transport / timeout when not serving a fresh LRU hit: return unavailable (proxy → 503). Do not serve expired LRU claims when auth is down.

6) Package boundary and wire-up

  • packages/authcache owns Validator, Result, CachingValidator, TokenHash, Invalidate, and InvalidateByTokenID. The validator owns the secondary token-ID index (and revocation tombstones) required for the InvalidateByTokenID path.
  • Invalid-token bloom uses an RWMutex and two-generation rotation when adds reach BloomExpectedItems, keeping the FP rate bounded on long-lived processes. Each generation is sized at half the configured FP rate so the OR of active+previous stays near the documented target.
  • Proxy adapter maps ↔ ValidateResult (including ExpiresAt, FromCache).
  • Metrics: ibex_proxy_auth_cache_* in packages/metrics (no org_id / token labels).
  • Observability header: X-IBEX-Auth-Cached: true on LRU hits.

7) Config

IBEX_AUTH_CACHE_ENABLED (default true), IBEX_AUTH_CACHE_LRU_CAPACITY, IBEX_AUTH_CACHE_LRU_MAX_TTL, IBEX_AUTH_CACHE_BLOOM_EXPECTED_ITEMS, IBEX_AUTH_CACHE_BLOOM_FP_RATE.

Amendment (Wave 4 adversarial hardening): The proxy wraps bloom+LRU only when REDIS_URL is set and Redis responds to Ping at startup. If the flag is enabled but Redis is empty or unreachable, log WARN and leave ValidateToken uncached so revoke is effective on the next gRPC call (no 30s stale allow without the ADR-0029 channel).

Consequences

  • Hot-path LRU hits avoid gRPC; miss path matches Phase 1 semantics.
  • Revoke lag with healthy pub/sub is ~1s (ADR-0029); without Redis the cache does not wrap (immediate revoke via gRPC).
  • If a pub/sub message is missed, worst-case lag remains LRUMaxTTL (30 seconds).
  • Invalidate is synchronous and ready for the 2.2.2 subscriber.
  • Each proxy process has an independent cache (no cross-instance coherence without pub/sub).

Alternatives considered

  1. RedisBloom + Redis claims — extra hop and ops complexity for Phase 2; deferred.
  2. LRU-only — bloom-positive tokens bypass LRU lookup (avoid stale claims) and fall through to gRPC; without the bloom, known-bad hashes can still hit a fresh LRU entry until TTL/Invalidate.
  3. Shorter TTL (1–5s) to meet 5s SLA — spikes auth load; rejected in favor of pub/sub (ADR-0029).
  4. Serve stale LRU when auth is down — violates fail-closed security posture.

Was this page helpful?

Edit on GitHub

Last updated on

PreviousADR-0027: Streaming dual-write strategyNextADR-0029: Token revocation propagation via Redis pub/sub

On this page

  • Context
  • Decision
  • 1) Two tiers, in-process only
  • 2) Sizing defaults
  • 3) TTL formula
  • 4) Revocation SLA (aligned with ADR-0029)
  • 5) Failure mode — fail closed
  • 6) Package boundary and wire-up
  • 7) Config
  • Consequences
  • Alternatives considered
0%