Vigie

Sovereign, auditable RAG for confidential documents

Role
Design, development, security, deployment, documentation
Tools
  • Python
  • FastAPI
  • BM25 + embeddings
  • OIDC / JWKS
  • AES-256-GCM
  • AWS KMS
  • PostgreSQL 16
  • MCP
  • Ollama
  • Docker
  • Helm
  • Terraform
Status
Functional
The same question asked by two people with different permissions.
paraphrased injections detected (0 / 56 with patterns alone), 0 false positives on 54 texts
34 / 56
leaks: evaluation 32 / 32 on SQLite and PostgreSQL alike
0
of statements verified on the sample dataset
100%
automated tests (55 before), including 12 contract tests on real PostgreSQL
102
The problem
AI pilots on confidential documents stall on security: where data goes, who sees what, can answers be checked and proven.
For whom
Legal, HR, security and admin teams in a regulated company; shown with a sample dataset (“Lémanis SA”, a Swiss company).
The outcome
A self-hostable RAG on SQLite or PostgreSQL 16: evaluation 32 / 32 and zero leaks on both, 34 of 56 paraphrased injections detected with no false positives, every figure checked against its source, offline mode.

Context

AI pilots rarely stall on the model. They stall on five questions from security and legal: where does the data go, who sees what, can the answer be checked, what if a document attacks the model, can we prove who asked what.

Fictional client: “Lémanis SA”, a Swiss company, with a synthetic dataset. Scoping, build, tests, documentation and acceptance follow the course of a client project.

What I built

  • Attribute-based permissions read from the client’s identity provider (OIDC/JWKS), filtering before ranking, BM25 statistics computed on the visible slice only.
  • Deterministic verification: every sentence and every figure must appear in the cited passage, highlighted in the UI.
  • Injection scanning at ingestion, by patterns and semantic detection: 34 of 56 paraphrased injections detected, 0 false positives on a 110-text FR/EN held-out set. Swiss and French PII validated by checksum, then pseudonymised (HMAC).
  • Two storage drivers, SQLite and PostgreSQL 16, with row-level tenant isolation: 12 / 12 contract tests on real PostgreSQL, evaluation 32 / 32 and 0 leaks on both.
  • Per-tenant envelope encryption (local key or KMS), rotation, crypto-shredding; chained, signed audit log.
  • MCP server for agents, hardened Docker, Helm with NetworkPolicy, Terraform for AWS, offline mode with Ollama.

Architecture 7 blocks

  1. 01 · InputIngestionPII pseudonymised (HMAC), injections caught by patterns and semantics, quarantined
  2. 02 · DataEncrypted storeSQLite or PostgreSQL 16 (row-level tenant isolation), per-tenant AES-256-GCM, local key or AWS KMS
  3. 03 · ToolPermission maskOIDC/JWKS token: filtering before any ranking
  4. 04 · ToolHybrid retrievalBM25 and dense on the visible slice, RRF fusion
  5. 05 · ModelGenerationOffline, Ollama/vLLM or API; sources treated as data
  6. 06 · ToolVerificationEvery sentence and figure found in the cited passage
  7. 07 · OutputAudited answerHighlighted evidence, chained and signed log

If the visible passages do not cover the question, Vigie declines without calling the model.

Pipeline 6 steps

  1. TokenTenant, groups and clearance read from the token.
  2. Permission maskWhat the user cannot see never enters the computation.
  3. Hybrid retrievalBM25 and dense, RRF fusion, reranking.
  4. GenerationSources wrapped and neutralised as data.
  5. VerificationCitations, figures, links and PII checked.
  6. AuditEvery request and citation logged in a signed chain.

Decisions the choice, the reason, the alternative ruled out

  1. D1
    Filter by permissions before ranking and compute BM25 statistics on the visible slice only.

    Post-filtering leaks through IDF and score distributions: a forbidden document must not even weigh on the score.

    Ruled outFiltering results after ranking.

  2. D2
    Deterministic verification of citations and figures.

    It catches the most damaging errors in legal and financial text without a second model call, and cannot be talked out of it.

    Ruled outAn LLM judge alone (fine on top, not as a replacement).

  3. D3
    Decline rather than guess: without enough visible evidence, no model call.

    Cheaper, faster, and no made-up answer when the evidence is missing.

    Ruled outLetting the model answer as best it can.

  4. D4
    Separate duties: the administrator manages documents without reading beyond their clearance.

    Running the system does not grant access to its contents.

    Ruled outA super-admin who sees everything.

  5. D5
    Minimal dependencies: SQLite, NumPy, FastAPI, cryptography; BM25, PII and audit in readable code.

    A security reviewer can read all the critical code in an afternoon.

Results

Sample dataset scenarios

ScenarioOutcome
Chloé, intern, asks for the Acme contract capDeclined, without revealing the document exists
Alice, legal counsel, asks the same questionCited answer, passage highlighted, 100% verified
A supplier FAQ carries an injectionPassage quarantined, never sent to the model
The same injection, rephrased without keywordsCaught by the semantic layer (34 / 56 on the held-out set)
An audit record is editedBroken chain caught by “audit verify”

Deliverables 6

  • Full application: REST API, web console, CLI and a 4-tool MCP server.
  • 102 automated tests, including 12 contract tests on real PostgreSQL 16, and an FR/EN evaluation suite with security gates in CI (32 / 32, 0 leaks on both drivers).
  • Semantic anti-injection layer and a 110-text FR/EN held-out set: 34 / 56 paraphrased injections detected, 0 / 54 false positives.
  • Hardened Dockerfile, docker-compose, Helm chart with NetworkPolicy, Terraform for AWS.
  • Deployment pack on the “Lémanis SA” sample dataset: brief, acceptance, handover, 90-day plan.
  • Architecture, threat model with residual risks, deployment guide.

Environment

Hardware and measurement setup

  • SQLite or PostgreSQL 16 depending on configuration; in-memory search after the permission mask, vectors encrypted like the text.
  • The sample dataset runs with no API key, no GPU and no internet connection; offline generation with Ollama.

Reproduce README commands

Private code: read access under NDA, or a 30-minute live demo. Request access →

pip install -e . && vigie demo        # then open http://127.0.0.1:8000
vigie ingest examples/corpus
vigie ask "When are supplier invoices paid?" --as chloe
vigie eval --gate
vigie audit verify
  • No API key, GPU or internet connection needed for the sample dataset.
  • Six test identities from the “Lémanis SA” sample dataset: switching identity shows permissions at work.

Gallery 10

Outside her permissions: the intern is declined.
Verified answer, highlighted evidence.
Injection quarantined.
Personal data pseudonymised.
Chained audit log.
Admin overview.
CI evaluation with security gates.
Tampered audit: detected.
Architecture.
Who can read what.

What this project means for you

The same know-how, on your problem.

01

Deployed on your premises

On your servers or an isolated network with a self-hosted model: your data stays in, and permissions come from your existing directory.

02

A pilot run as an engagement

Brief, measurable acceptance criteria, a question set written with your teams, handover: the structure is ready to be measured on your documents.

03

Audit and measurement

Every question and citation logged in a tamper-evident trail, and continuous evaluation where a single leak fails the release.