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-0039: Proxy Postgres ownership for session and directive stores
ADRs

ADR-0039: Proxy Postgres ownership for session and directive stores

Accept proxy-owned database/sql for session and directive hot paths only; identity remains on auth gRPC. Enforce with depguard.

ADR-0039: Proxy Postgres ownership for session and directive stores

  • Status: Accepted
  • Date: 2026-07-30
  • Authors: IBEX Harness team
  • Milestone: Phase 2 quality closeout (MF-001 / issue #417)
  • Supersedes forward reference in: ADR-0025 §9

Context

Phase 1 required the proxy to avoid owning Postgres for identity — token and agent validation stay on the auth service gRPC API. That rule remains.

Phase 2 milestones 2.3.2 and 2.4.x intentionally placed directive and session stores on the proxy request path (packages/directive, packages/session), wired from services/proxy/internal/bootstrap. Today the proxy opens a database/sql pool via sql.Open("postgres", …) in services/proxy/internal/bootstrap/postgres.go for:

  • Directive Postgres fallback behind Redis cache
  • Session create / checkpoint / complete / abandon-idle (+ sweeper)
  • Advisory Postgres readiness when POSTGRES_DSN is set
  • Coordinated pool close on shutdown

There is no session or directive service under services/ to migrate into. ADR-0025 §9 foresaw a dedicated ADR and incorrectly described a read-only pgxpool with writes only on auth/api — reality is database/sql and session writes from the proxy.

Decision

Keep the Phase 2 exception and make it explicit:

  1. The proxy may own a database/sql (lib/pq) pool opened only in services/proxy/internal/bootstrap for session store, directive loader/cache wiring, health, and shutdown.
  2. The proxy must not use that pool (or any other DB) for token/agent identity validation — identity remains auth gRPC only.
  3. RLS and org_id checks stay in packages/session and packages/directive SQL; bootstrap must not introduce identity queries.
  4. Lint enforces the boundary: database/sql is denied under services/proxy/** except services/proxy/internal/bootstrap/** (and test files). See .golangci.depguard.yml rule proxy-no-database-sql.
  5. Moving session/directive ownership behind a dedicated service requires a new ADR, a real service boundary, and a latency plan that preserves the <20ms proxy overhead budget.

Consequences

Positive:

  • Architectural honesty: exception is decided, not tribal knowledge
  • Depguard prevents accidental database/sql in HTTP handlers / middleware
  • Hot-path session and directive latency stay in-process

Negative / follow-ons:

  • Proxy remains stateful w.r.t. Postgres until a future service extraction
  • Docs that still say “Phase 1 proxy has no Postgres” must cite this ADR for Phase 2+

Alternatives considered

  1. Migrate session/directive to a new service now — rejected: no existing service/proto/client; multi-milestone product work; risks proxy SLA.
  2. Remove Postgres from proxy — rejected: breaks directive miss path and session lifecycle / sweeper.
  3. Leave undocumented exception — rejected: MF-001 High finding and stale main.go / pgxpool wording.

References

  • Issue #417
  • GOLANGCI_POLICY.md § Proxy Postgres
  • ADR-0025: LLM provider abstraction §9 (updated to point here)
  • ADR-0032: Session data model
  • ADR-0030: Directive versioning

Was this page helpful?

Edit on GitHub

Last updated on

PreviousADR-0038: Context assembly service design and gRPC contractNextADR-0040: Anthropic provider adapter

On this page

  • Context
  • Decision
  • Consequences
  • Alternatives considered
  • References
0%