{"slug": "show-hn-warden-authorization-gateway-for-agentic-rag", "title": "Show HN: Warden – authorization gateway for agentic RAG", "summary": "A developer released Warden, an open-source authorization gateway for agentic RAG systems that enforces relationship-based, deny-aware, cross-tenant document permissions. The tool uses a three-gate architecture combining a permissive pre-filter with a fail-closed authoritative check to prevent data leaks from prompt injection or stale caches. Warden is built with Python, FastAPI, Postgres with pgvector, and Redis.", "body_md": "**Permission-aware retrieval and agent-authorization gateway for AI systems.**\n\nWarden enforces relationship-based, deny-aware, cross-tenant document permissions\n\ninsidethe retrieval path of agentic RAG systems — behind a fail-closed security boundary, with agent-context revalidation and a tamper-evident audit trail.\n\nEvery existing tool answers *\"can user U read document D?\"*\nNone of them answer *\"what should this agent be allowed to retrieve, keep in context, and cite — right now?\"*\n\nThe two obvious designs both fail.\n\n**Post-filter**(retrieve top-K, then check permissions) is safe but** recall collapses under selectivity**. If a principal is authorized on 1% of the corpus, top-50 by pure similarity contains ~0.5 authorized docs in expectation.**Pre-filter by expanded ID list**(`SpiceDB.LookupResources`\n\n,`OpenFGA.ListObjects`\n\n) preserves recall but is**O(|Authorized(u)|)**. A firm-wide partner is authorized on 10⁷+ documents. You cannot ship a ten-million-element`IN`\n\nclause into an ANN query.\n\nWarden flattens the authorization graph into small **capability-label** sets that push down into the vector index as an `int[]`\n\noverlap predicate — **O(1) in corpus size** — and then treats that filter as a *performance optimization only*. The security boundary is a separate fail-closed check against the graph itself, on the small candidate set, right before anything enters the model's context.\n\n```\n    agent tool call: retrieve(query)         ← principal is bound server-side to the session\n              │                                (NEVER a model-supplied argument)\n              ▼\n   ┌─ GATE 1 · PRE-FILTER ────────────────────────────────────┐  performance\n   │  L(u), B(u) from Redis  [may be stale — permissive only] │\n   │  ANN search inside org partition, predicate pushed down: │\n   │      acl_labels && L(u)  AND NOT (barrier_tags && B(u))  │\n   │  over-fetch K' = 1.5·K                                   │\n   └───────────────────────────┬──────────────────────────────┘\n                               ▼ K' candidates\n   ┌─ GATE 2 · AUTHORITATIVE CHECK ───────────────────────────┐  ← THE SECURITY BOUNDARY\n   │  check(u, read, d) against the tuple graph. FAIL-CLOSED. │\n   │  deny > allow. expiry enforced. audit row + reason_path. │\n   └───────────────────────────┬──────────────────────────────┘\n                               ▼ authorized top-K → LLM context\n   ┌─ GATE 3 · SESSION REVALIDATION ──────────────────────────┐  temporal\n   │  before EVERY model call: re-check all context doc refs  │\n   │  evict revoked → inject \"[N sources removed]\" → replan   │\n   │  before final answer: re-check every citation            │\n   └──────────────────────────────────────────────────────────┘\n```\n\n**Gate 1 is an optimization; Gate 2 is the boundary.** A stale cache, a bad label, a pgvector quirk — none can leak, because Gate 2 re-checks the graph. Gate 1 exists so Gate 2 is cheap. Gate 2 exists so Gate 1 is allowed to be wrong.— the pre-filter must be a`LabelFilter(u) ⊇ Authorized(u)`\n\n*permissive superset*. Therefore**stale revocations are safe**(Gate 2 catches them) and** stale grants silently destroy recall**(nothing catches them). Grants propagate eagerly; revocations may propagate lazily. This inverts the naive intuition.\n\n**Why prompt injection fails against this:** the model can influence the *query string*. It cannot influence *who is asking*. The principal is bound to the session server-side and is never a tool parameter. Authorization is enforced at the tool boundary, not by the model's good behavior — because model alignment is not a security control.\n\n| Layer | Choice | Why |\n|---|---|---|\n| Language | Python 3.11+ | FastAPI ecosystem, Hypothesis, LLM SDKs |\n| API | FastAPI | Typed, async, OpenAPI-native |\n| Storage | Postgres 16 + pgvector 0.8+ | ReBAC tuples, vectors, audit — one transaction boundary, no distributed consistency to hand-wave |\n| Cache | Redis | `labels:{principal}:{epoch}` with write-through invalidation |\n| Vector index | pgvector HNSW + GIN on `int[]` |\nFiltered ANN with iterative index scan |\n| Testing | Hypothesis + pytest | Property-based differential tests vs. a reference oracle |\n| Packaging | Docker Compose | One-command setup |\n| CI | GitHub Actions | Property tests + 10-scenario suite + `make bench` on every PR |\n\nPluggable `AuthzBackend`\n\nprotocol ships with two implementations: our native Postgres engine, and an **OpenFGA** adapter for teams that already run one.\n\n```\nwarden/\n├── core/\n│   ├── algebra.py         # formal permission semantics\n│   ├── oracle.py          # brute-force ground truth — DO NOT OPTIMIZE\n│   ├── rebac.py           # check() / expand() / ReasonPath\n│   ├── barriers.py        # deny layer + tag encoding\n│   └── labels.py          # materialization, epochs, Redis cache\n├── retrieval/\n│   ├── index.py           # pgvector, partitioning\n│   └── strategies.py      # exact | iterative | partitioned\n├── gateway/\n│   ├── gates.py           # 1, 2, 3\n│   ├── session.py         # typed context refs\n│   ├── audit.py           # hash chain + verify\n│   └── api.py             # FastAPI\n├── backends/\n│   ├── protocol.py        # AuthzBackend\n│   ├── postgres.py\n│   └── openfga.py\n├── agent/                 # minimal real agent loop\n├── evals/\n│   ├── generators.py      # Hypothesis strategies\n│   ├── differential.py    # vs. oracle\n│   ├── scenarios/         # the 10 adversarial cases\n│   ├── baseline.py        # naive RAG\n│   └── bench/             # the 4 tables\n├── profiles/legal.yaml\n├── demo/\n└── docker-compose.yml\n```\n\nShip in order. Each milestone has open issues linked to it.\n\n| # | Milestone | What ships |\n|---|---|---|\n|\n\n[W1](/geminimir/warden/milestone/2)`check()`\n\nwith reason paths, information barriers[W2](/geminimir/warden/milestone/3)[W3](/geminimir/warden/milestone/4)[W4](/geminimir/warden/milestone/5)[W5](/geminimir/warden/milestone/6)Stated explicitly because naming what we *didn't* build is a seniority signal.\n\n**Not an authorization engine** competing with SpiceDB or OpenFGA. Warden is the*gateway*that composes an authz model with a retrieval path and an agent lifecycle. Native engine so it works standalone; adapter so it doesn't have to.**Not a vector database.** pgvector is an implementation detail.**Not production-hardened.** Single-node, synthetic benchmarks. Numbers will say so plainly.**Not a hallucination checker.** Citation stripping here is an*access-control*mechanism, not a correctness one.\n\n```\ndocker-compose up          # Postgres + pgvector + Redis + API + seeded demo corpus\nmake test                  # property tests + 10 scenarios\nmake bench                 # four benchmark tables (smoke scale)\nmake demo                  # split-screen UI at http://localhost:3000\n```\n\nTBD.", "url": "https://wpnews.pro/news/show-hn-warden-authorization-gateway-for-agentic-rag", "canonical_source": "https://github.com/geminimir/warden", "published_at": "2026-07-18 17:06:54+00:00", "updated_at": "2026-07-18 17:21:18.697721+00:00", "lang": "en", "topics": ["artificial-intelligence", "ai-safety", "ai-infrastructure", "ai-tools", "generative-ai"], "entities": ["Warden", "Redis", "Postgres", "pgvector", "FastAPI", "SpiceDB", "OpenFGA"], "alternates": {"html": "https://wpnews.pro/news/show-hn-warden-authorization-gateway-for-agentic-rag", "markdown": "https://wpnews.pro/news/show-hn-warden-authorization-gateway-for-agentic-rag.md", "text": "https://wpnews.pro/news/show-hn-warden-authorization-gateway-for-agentic-rag.txt", "jsonld": "https://wpnews.pro/news/show-hn-warden-authorization-gateway-for-agentic-rag.jsonld"}}