{"id":50958243,"url":"https://github.com/szesnasty/contextguard","last_synced_at":"2026-06-18T10:02:26.422Z","repository":{"id":362548001,"uuid":"1253432142","full_name":"Szesnasty/ContextGuard","owner":"Szesnasty","description":"Context firewall for RAG systems: enforce tenant/role policy, redact sensitive chunks, block prompt injection, and prove what reached the LLM.","archived":false,"fork":false,"pushed_at":"2026-06-04T19:00:06.000Z","size":1917,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-04T21:23:18.663Z","etag":null,"topics":["access-control","ai-security","audit-trail","context-engineering","context-firewall","data-loss-prevention","fastapi","llm-security","ollama","pgvector","pii-redaction","policy-as-code","prompt-injection","rag","rag-security","red-team","retrieval-augmented-generation","tenant-isolation","vue","zero-trust"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Szesnasty.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":"ROADMAP.md","authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-29T13:05:03.000Z","updated_at":"2026-06-04T19:00:14.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/Szesnasty/ContextGuard","commit_stats":null,"previous_names":["szesnasty/contextguard"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/Szesnasty/ContextGuard","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Szesnasty%2FContextGuard","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Szesnasty%2FContextGuard/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Szesnasty%2FContextGuard/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Szesnasty%2FContextGuard/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Szesnasty","download_url":"https://codeload.github.com/Szesnasty/ContextGuard/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Szesnasty%2FContextGuard/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34485169,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-18T02:00:06.871Z","response_time":128,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["access-control","ai-security","audit-trail","context-engineering","context-firewall","data-loss-prevention","fastapi","llm-security","ollama","pgvector","pii-redaction","policy-as-code","prompt-injection","rag","rag-security","red-team","retrieval-augmented-generation","tenant-isolation","vue","zero-trust"],"created_at":"2026-06-18T10:02:25.641Z","updated_at":"2026-06-18T10:02:26.414Z","avatar_url":"https://github.com/Szesnasty.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ContextGuard\n\n[![CI](https://github.com/Szesnasty/ContextGuard/actions/workflows/ci.yml/badge.svg)](https://github.com/Szesnasty/ContextGuard/actions/workflows/ci.yml)\n[![Release](https://img.shields.io/github/v/release/Szesnasty/ContextGuard?label=release\u0026sort=semver\u0026cacheSeconds=3600)](https://github.com/Szesnasty/ContextGuard/releases/latest)\n[![License: Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)\n\n**A context firewall for RAG systems.**\n\nContextGuard sits between retrieval and generation. It decides which chunks are\nallowed to reach the model, which must be redacted, which must be blocked, and\nwhy. Every decision becomes evidence you can inspect, replay, and test.\n\nIt is library-first: the core guard can run without Docker, a database, a model,\nor network access.\n\nMost RAG systems ask:\n\n\u003e Did we retrieve relevant context?\n\nContextGuard asks:\n\n\u003e Was this user allowed to retrieve this context, and can we prove it?\n\nUse it when:\n\n- your RAG system uses private, internal, or multi-tenant data,\n- you need per-chunk `allow`, `redact`, and `block` decisions,\n- you need evidence for why context reached the model.\n\n---\n\n## Why It Exists\n\nProduction RAG is no longer only a relevance problem. Internal assistants now\nretrieve contracts, tickets, HR files, roadmap notes, customer data, source code,\nand tenant-specific knowledge. The retrieval layer is often optimized for\nsemantic match, while access control, minimization, and auditability live\nsomewhere else.\n\nThat creates a sharp failure mode: the vector store can retrieve the right chunk\nfor the question, but the wrong chunk for the user.\n\nContextGuard is the control plane for that boundary:\n\n- **Tenant isolation** - an `acme` user should never receive `contoso` context.\n- **Role and classification policy** - sales can use public/internal docs, but\n  not confidential M\u0026A notes.\n- **Data minimization** - PII and secrets can be masked before prompt assembly.\n- **Indirect injection defense** - poisoned documents are treated as risk\n  signals, not trusted instructions.\n- **Evidence over promises** - each query records what was retrieved, allowed,\n  redacted, blocked, and which policy fired.\n\nContextGuard is not trying to be a better retriever or a smarter model. It is a\nsmall, explicit enforcement layer around the context window.\n\n## Usage Modes\n\nContextGuard is designed around two usage modes:\n\n1. **Core library** - a zero-infra Python guard that takes `UserContext`, a\n   query, and candidate chunks, then returns allowed, redacted, and blocked\n   chunks plus an evidence record. This is the primary product surface. Public\n   package publication is planned after the package naming and distribution\n   pass.\n2. **Local demo stack** - a reference RAG system with FastAPI, pgvector, Ollama,\n   and a Vue dashboard, used to demonstrate policy-aware retrieval, evidence,\n   and leak prevention end to end.\n\n## Latest Release\n\nThe latest stable source release is\n[`v1.0.0`](https://github.com/Szesnasty/ContextGuard/releases/tag/v1.0.0).\nThe default branch tracks current development; versioned tags are the stable\nsnapshots.\n\nUntil the Python packages are published to a public package index, install the\n1.0.0 wheels from the GitHub release:\n\n```bash\nuv venv\nuv pip install \\\n  https://github.com/Szesnasty/ContextGuard/releases/download/v1.0.0/contextguard_contracts-1.0.0-py3-none-any.whl \\\n  https://github.com/Szesnasty/ContextGuard/releases/download/v1.0.0/contextguard_policy_dsl-1.0.0-py3-none-any.whl \\\n  https://github.com/Szesnasty/ContextGuard/releases/download/v1.0.0/contextguard-1.0.0-py3-none-any.whl\n```\n\nFor the full local demo, clone the repository and use `make demo`.\n\n## Project Docs\n\n- [Changelog](CHANGELOG.md) - release history and notable changes.\n- [Security policy](SECURITY.md) - vulnerability scope and reporting guidance.\n- [Contributing guide](CONTRIBUTING.md) - development rules and local workflow.\n- [Roadmap](ROADMAP.md) - planned evals, integrations, and production-hardening work.\n- [Benchmark report](BENCHMARK.md) - deterministic leak-prevention checks.\n- [Red-team report](RED-TEAM.md) - adversarial prompt and retrieval cases.\n\n## Product Tour\n\nThe dashboard shows the security boundary in plain sight: retrieval can find a\nchunk, but policy decides whether that chunk may reach the model for this\nidentity.\n\n### RAG Console\n\n![RAG Console showing a support identity, a cross-tenant prompt, and allowed/redacted/blocked source counts.](docs/img/rag-console-cross-tenant.png)\n\nAsk a normal RAG question and see the context firewall summarize what happened:\nhow many sources were allowed, redacted, or blocked before the answer was\ngenerated.\n\n### Evidence Decision Flow\n\n![Evidence Viewer showing retrieved chunks passing through the policy gate into allowed, redacted, blocked, and prompt outcomes.](docs/img/evidence-decision-flow.png)\n\nReplay a query as an evidence record. The flow makes the invariant visible:\nretrieved chunks pass through a policy gate before the prompt is assembled.\n\n### Context Delta And Enforced Decisions\n\n![Evidence Viewer showing context withheld from the model and the policy decisions behind each blocked or redacted chunk.](docs/img/context-delta-enforced-decisions.png)\n\nInspect the exact context delta: withheld chunks are shown separately, redacted\nPII is masked, and every enforced decision points back to a policy rule.\n\n## Core Library Quickstart\n\nThe core is designed to run as a Python library. It works inside this repo\nwithout Docker, a database, a model, or network access. A public package release\nis planned after the package naming and distribution pass.\n\n```python\nfrom contextguard import ContextGuard\nfrom contextguard.core.types import Chunk, Classification, UserContext\n\nguard = ContextGuard.from_policy(\"data/policies/example.yaml\")\n\nuser = UserContext(\n    sub=\"sales@acme\",\n    tenant=\"acme\",\n    role=\"sales\",\n    purpose=\"support\",\n)\n\nchunks = [\n    Chunk(\n        id=\"public-faq\",\n        doc_id=\"faq\",\n        tenant=\"acme\",\n        classification=Classification.PUBLIC,\n        text=\"Refund requests are handled by support.\",\n    ),\n    Chunk(\n        id=\"secret-mna\",\n        doc_id=\"mna-falcon\",\n        tenant=\"acme\",\n        classification=Classification.CONFIDENTIAL,\n        text=\"Project Falcon acquisition target is Initech for 1.2B.\",\n    ),\n]\n\nresult = guard.guard(user, \"Are we acquiring anyone?\", chunks)\n\nprint([chunk.id for chunk in result.allowed_chunks])\nprint(guard.last_evidence())\n```\n\n## Demo Quickstart\n\nPrerequisites:\n\n- Docker Desktop\n- Python 3.12\n- Node 20+\n- `uv`\n- `pnpm`\n\nOptional local configuration:\n\n```bash\ncp .env.example .env\n```\n\nYou can skip this step if the default ports are free. `make demo` reads `.env`\nautomatically when present.\n\nRun the local demo:\n\n```bash\nmake demo\n```\n\nThe command starts the local stack, prepares the database, seeds the planted\ntenant corpus, starts the FastAPI backend, and starts the Vue dashboard.\n\nOpen the dashboard:\n\n```text\nhttp://localhost:5173\n```\n\nIn the dashboard:\n\n1. Generate a dev token for `sales@acme`.\n2. Ask:\n\n```text\nAre we acquiring any company soon, and for how much?\n```\n\n3. Open the sources/evidence drawer.\n4. Look for the confidential acquisition chunk: it may be retrieved, but it is\n   withheld from the model and attributed to the policy rule that blocked it.\n\nRun the fast demo smoke test against a running API:\n\n```bash\nmake e2e\n```\n\nIf local ports collide with another stack, override them:\n\n```bash\nPOSTGRES_PORT=55432 \\\nDATABASE_URL=postgresql://contextguard:contextguard@localhost:55432/contextguard \\\nREDIS_PORT=6380 \\\nREDIS_URL=redis://localhost:6380/0 \\\nLANGFUSE_PORT=3002 \\\nOLLAMA_PORT=11435 \\\nOLLAMA_BASE_URL=http://127.0.0.1:11435 \\\nAPI_PORT=8008 \\\nWEB_PORT=5174 \\\nmake demo\n```\n\nThen run:\n\n```bash\nAPI_BASE=http://127.0.0.1:8008 make e2e\n```\n\n## What The Demo Shows\n\nThe planted demo corpus contains ordinary product/support documents plus\nintentional leak surfaces:\n\n- a confidential `acme` M\u0026A memo,\n- a cross-tenant `contoso` chunk,\n- PII-bearing support text,\n- a secret-bearing runbook chunk,\n- an indirect prompt-injection document.\n\nFor a `sales@acme` identity, ContextGuard demonstrates:\n\n- retrieval provenance,\n- per-chunk `allowed`, `redacted`, and `blocked` decisions,\n- token reduction before vs. after the firewall,\n- policy reasons such as `tenant-isolation` and `sales-no-confidential`,\n- a query-level evidence record.\n\nThe important security invariant:\n\n**blocked chunk text does not reach the model and is not returned by `/v1/query`\nas raw retrieved text.**\n\n## How It Works\n\n```mermaid\nflowchart LR\n    U[\"User query + signed identity\"] --\u003e A[\"FastAPI adapter\"]\n    A --\u003e R[\"Hybrid retriever\u003cbr/\u003epgvector kNN + BM25\"]\n    R --\u003e C[\"Candidate chunks\"]\n    C --\u003e E1\n    E3 --\u003e P[\"Allowed + redacted chunks\"]\n    P --\u003e PB[\"Prompt builder\u003cbr/\u003ecited context only\"]\n    PB --\u003e L[\"LLM gateway\u003cbr/\u003eOllama by default\"]\n    L --\u003e O[\"Grounded answer\"]\n    E3 --\u003e E[\"Evidence record\"]\n\n    subgraph Guard[\"Context firewall\"]\n        E1[\"Enrich\u003cbr/\u003ePII, secrets, injection signals\"]\n        E2[\"Policy\u003cbr/\u003etenant, role, classification\"]\n        E3[\"Redact / block / allow\"]\n        E1 --\u003e E2 --\u003e E3\n    end\n```\n\n### Decision Pipeline\n\n```mermaid\nflowchart TD\n    Q[\"Query\"] --\u003e K[\"Top-k retrieval\"]\n    K --\u003e C1[\"Chunk: public/internal\"]\n    K --\u003e C2[\"Chunk: confidential\"]\n    K --\u003e C3[\"Chunk: cross-tenant\"]\n    K --\u003e C4[\"Chunk: PII or secret\"]\n    K --\u003e C5[\"Chunk: injection risk\"]\n\n    C1 --\u003e A[\"Allowed\"]\n    C2 --\u003e B[\"Blocked\u003cbr/\u003esales-no-confidential\"]\n    C3 --\u003e T[\"Blocked\u003cbr/\u003etenant-isolation\"]\n    C4 --\u003e D[\"Redacted\u003cbr/\u003eredact-pii / redact-secrets\"]\n    C5 --\u003e I[\"Blocked\u003cbr/\u003eblock-injection\"]\n\n    A --\u003e P[\"Prompt context\"]\n    D --\u003e P\n    B --\u003e X[\"Withheld from model\"]\n    T --\u003e X\n    I --\u003e X\n    P --\u003e M[\"Model answer\"]\n\n    A --\u003e EV[\"Evidence\"]\n    B --\u003e EV\n    T --\u003e EV\n    D --\u003e EV\n    I --\u003e EV\n```\n\n### Repository Architecture\n\n```mermaid\nflowchart TB\n    Web[\"apps/web\u003cbr/\u003eVue dashboard\"] --\u003e API[\"contextguard.api\u003cbr/\u003eFastAPI adapter\"]\n    API --\u003e Query[\"/v1/query\u003cbr/\u003eretrieve -\u003e guard -\u003e prompt -\u003e LLM\"]\n    API --\u003e Scan[\"/v1/guard\u003cbr/\u003escan-only verdict\"]\n    Query --\u003e Retrieval[\"retrieval\u003cbr/\u003epgvector kNN + BM25\"]\n    Query --\u003e Core[\"contextguard.core\u003cbr/\u003ezero-infra guard\"]\n    Query --\u003e Prompt[\"prompt builder\u003cbr/\u003eallowed chunks only\"]\n    Prompt --\u003e LLM[\"llm gateway\u003cbr/\u003eOllama by default\"]\n    Scan --\u003e Core\n    API --\u003e Metrics[\"/metrics\u003cbr/\u003ePrometheus format\"]\n\n    Core --\u003e Contracts[\"packages/contracts\u003cbr/\u003ePydantic models + JSON Schema\"]\n    Core --\u003e Policy[\"packages/policy-dsl\u003cbr/\u003eYAML policy evaluator\"]\n    Core --\u003e Evidence[\"EvidenceSink protocol\u003cbr/\u003ein-memory / JSONL default\"]\n    API --\u003e PgEvidence[\"optional Postgres JSONB evidence\u003cbr/\u003eEVIDENCE_SINK=postgres\"]\n    PgEvidence --\u003e Evidence\n\n    Eval[\"packages/eval-harness\u003cbr/\u003ebenchmarks + red-team corpus\"] --\u003e Core\n    Data[\"data/\u003cbr/\u003etenants, users, policies, attacks\"] --\u003e Retrieval\n    Data --\u003e Eval\n\n    Compose[\"compose.yaml\u003cbr/\u003ePostgres, Redis, Ollama, Langfuse\"] --\u003e API\n```\n\n## Policy Examples\n\nPolicies are declarative YAML. Rules are evaluated by priority. The first match\nwins; if no rule matches, the `default_effect` applies.\n\nThe demo policy is permissive by default so the planted examples are easy to\ncompare:\n\n```yaml\nversion: 1\ndefault_effect: allow\n\nroles:\n  manager:\n    inherits: [sales]\n\nrules:\n  - id: tenant-isolation\n    effect: deny\n    priority: 100\n    when:\n      - { field: chunk.tenant, op: neq, ref: user.tenant }\n\n  - id: sales-no-confidential\n    effect: deny\n    priority: 50\n    when:\n      - { field: user.roles, op: contains, value: sales }\n      - { field: chunk.classification, op: gte, value: confidential }\n\n  - id: redact-pii\n    effect: redact\n    priority: 30\n    when:\n      - { field: chunk.pii_count, op: gte, value: 1 }\n```\n\nFor a production-style posture, start deny-by-default and add explicit allow\nrules after stricter deny/redact rules:\n\n```yaml\nversion: 1\ndefault_effect: deny\n\nrules:\n  - id: tenant-isolation\n    effect: deny\n    priority: 100\n    when:\n      - { field: chunk.tenant, op: neq, ref: user.tenant }\n\n  - id: allow-safe-context\n    effect: allow\n    priority: 10\n    when:\n      - { field: chunk.tenant, op: eq, ref: user.tenant }\n```\n\nSee [`data/policies/example.yaml`](data/policies/example.yaml) and\n[`data/policies/strict.yaml`](data/policies/strict.yaml).\n\n## Proof Points\n\nThe current benchmark and red-team reports are deterministic. They do not use an\nLLM judge.\n\n| Artifact | Result |\n|---|---|\n| `make test` | 336 Python tests + 18 frontend tests passed |\n| `make lint` | Python and frontend lint clean |\n| `make types` | mypy + Vue typecheck passed |\n| `make benchmark` | on the planted demo corpus: policy off 100% leak rate, policy on 0% leak rate |\n| `make red-team` | on the committed adversarial corpus: 6/6 cases passed, 0% leak rate, 0 replay mismatches |\n| `make e2e` | fast local demo smoke test |\n\nSee:\n\n- [BENCHMARK.md](BENCHMARK.md)\n- [RED-TEAM.md](RED-TEAM.md)\n\n## What Makes It Different\n\nContextGuard is not a generic LLM firewall. It focuses on the context boundary:\n\n| Category | Typical focus | ContextGuard focus |\n|---|---|---|\n| LLM firewalls | prompt/output content filtering | what context is allowed before generation |\n| RAG retrieval tools | relevance and ranking | policy-aware retrieval and minimization |\n| Observability tools | traces, cost, evals | evidence as an auditable product contract |\n| Data governance tools | data at rest | RAG query hot path |\n\nThe product thesis is narrow on purpose:\n\n**Was this context allowed to be here, and can we prove it?**\n\n## What ContextGuard Is Not\n\nContextGuard is not:\n\n- a replacement for your retriever,\n- a replacement for identity or ACL mapping,\n- a replacement for secure source-system permissions,\n- a generic LLM firewall,\n- a compliance certification,\n- a guarantee that no data can ever leak.\n\n## Project Layout\n\n```text\n.\n├── apps/\n│   └── web/                     # Vue dashboard + frontend README\n├── packages/\n│   ├── contextguard/            # Python core, API adapter, retrieval, evidence\n│   ├── contracts/               # Pydantic contracts + JSON Schemas + OpenAPI\n│   ├── policy-dsl/              # YAML policy schema and evaluator\n│   └── eval-harness/            # benchmarks and red-team runner\n├── data/\n│   ├── tenants/                 # planted demo corpus\n│   ├── policies/                # demo + strict policies\n│   └── red-team-corpora/        # golden adversarial cases\n├── docs/\n│   └── img/                     # README screenshots\n├── compose.yaml                 # local stack\n├── CHANGELOG.md                 # release notes\n├── CONTRIBUTING.md              # contribution guide\n├── ROADMAP.md                   # next milestones\n├── SECURITY.md                  # vulnerability reporting scope\n├── Makefile                     # canonical dev/demo commands\n└── README.md\n```\n\n## Status\n\nContextGuard 1.0.0 is the first official MVP release, built in the open. It is\nready to demonstrate the product thesis locally:\n\n- policy-aware context filtering,\n- redaction and blocking,\n- evidence records,\n- red-team and benchmark artifacts,\n- a dashboard for query, sources, policy, and evidence review.\n\nIt is not a compliance certification, legal opinion, or production deployment\ntemplate. Real production use would still need hardened identity, connector-level\nACL mapping, retention policy, deployment hardening, and a fuller security\nreview.\n\n## License\n\nContextGuard is licensed under the Apache License 2.0. See [LICENSE](LICENSE).\n\n## Legal\n\nContextGuard is not legal advice and does not by itself make a system compliant\nwith GDPR, the EU AI Act, DORA, or any other regime. It is an engineering control\nand evidence layer intended to support stronger AI data-flow governance.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fszesnasty%2Fcontextguard","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fszesnasty%2Fcontextguard","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fszesnasty%2Fcontextguard/lists"}