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-0035: Chat Idempotency-Key Redis dedupe
ADRs

ADR-0035: Chat Idempotency-Key Redis dedupe

Accepted — Optional Idempotency-Key on non-streaming chat completions; Redis claim/commit with fail-open; fingerprint conflict and in-progress 409s.

ADR-0035: Chat Idempotency-Key Redis dedupe

  • Status: Accepted
  • Date: 2026-07-26
  • Authors: IBEX Harness team
  • Milestone: 2.1.6 Idempotency-Key

Context

Clients that time out after the proxy has already started (or finished) an upstream completion often retry. Without dedupe, each retry bills a second OpenAI completion. Stripe and OpenAI solve this with Idempotency-Key. OpenAI client retries inside the proxy (ADR-0026) are orthogonal — this ADR covers client → proxy retries.

Decision

  1. Header: Optional Idempotency-Key (opaque, max 256 characters). Empty/whitespace is treated as absent.
  2. Scope: Non-streaming POST /v1/chat/completions only. stream=true + header → 400 VALIDATION_ERROR.
  3. Redis key: idempotency:{org_id}:{key} with org_id from verified auth context only.
  4. TTLs: In-flight pending claims use a short TTL (default 9m, covering OpenAI RequestTimeout×(MaxRetries+1) plus backoff). Completed records use 24h (IBEX_IDEMPOTENCY_TTL). Orphaned pending keys self-heal instead of blocking retries for a day.
  5. Protocol: SETNX pending on first claim; on terminal success / deterministic 4xx (not 429), CAS-commit completed only while the key is still pending with the same fingerprint (stale commit is a no-op). Transient 429 and 5xx CAS-release the pending claim so the same key can retry. Same key + same fingerprint → replay. Same key + different fingerprint → 409 IDEMPOTENCY_KEY_REUSE. Concurrent pending → 409 IDEMPOTENCY_IN_PROGRESS (no long-poll).
  6. Fingerprint: SHA-256 over normalized parsed fields (model, messages, stream, temperature, max_tokens). Unknown JSON keys ignored per ADR-0012.
  7. Fail-open: Redis errors or claim/commit timeout (IBEX_IDEMPOTENCY_REDIS_TIMEOUT, default 50ms) proceed without dedupe (WARN + redis_error metric). Availability beats perfect billing protection when Redis is down — same class as rate limiting.
  8. Replay side effects: Hits do not call the provider and do not re-enqueue session checkpoint / ClickHouse trace.
  9. Upstream: Do not forward Idempotency-Key to OpenAI; proxy owns dedupe.
  10. Schema: Redis JSON includes v (record version); unknown versions are rejected.

Consequences

  • Clients can safely retry non-streaming chat after timeouts when they send a stable key.
  • Streaming idempotency remains a follow-up.
  • Redis outages can allow double-billing; operators should alert on ibex_proxy_idempotency_total{result="redis_error"}.
  • Package: packages/idempotency (shared, no services/ imports).

Was this page helpful?

Edit on GitHub

Last updated on

PreviousADR-0034: Proxy overhead performance measurement methodologyNextADR-0038: Context assembly service design and gRPC contract

On this page

  • Context
  • Decision
  • Consequences
0%