Metadata-Version: 2.4
Name: bivelio-privacy-gateway
Version: 0.1.3
Summary: BV-PRGA — fail-closed DLP frontier between BiVelio services and LLM providers
Author: BiVelio Inc.
License: Proprietary — BiVelio Inc. All Rights Reserved
Project-URL: Homepage, https://privacy.bivelio.com
Project-URL: Repository, https://github.com/BiVelio/bivelio-privacy-gateway
Project-URL: Documentation, https://github.com/BiVelio/bivelio-privacy-gateway/tree/main/docs
Keywords: dlp,pii,privacy,llm,gateway,presidio,bv-prga
Classifier: License :: Other/Proprietary License
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Intended Audience :: Developers
Classifier: Topic :: Security
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
License-File: NOTICE
Provides-Extra: server
Requires-Dist: fastapi>=0.110; extra == "server"
Requires-Dist: uvicorn[standard]>=0.29; extra == "server"
Requires-Dist: httpx>=0.27; extra == "server"
Provides-Extra: nlp
Requires-Dist: presidio-analyzer>=2.2; extra == "nlp"
Requires-Dist: presidio-anonymizer>=2.2; extra == "nlp"
Requires-Dist: spacy>=3.7; extra == "nlp"
Provides-Extra: gliner
Requires-Dist: gliner>=0.2; extra == "gliner"
Provides-Extra: yaml
Requires-Dist: pyyaml>=6.0; extra == "yaml"
Provides-Extra: crypto
Requires-Dist: cryptography>=42.0; extra == "crypto"
Provides-Extra: redis
Requires-Dist: redis>=5.0; extra == "redis"
Provides-Extra: license
Requires-Dist: cryptography>=42.0; extra == "license"
Provides-Extra: control-plane
Requires-Dist: fastapi>=0.110; extra == "control-plane"
Requires-Dist: uvicorn[standard]>=0.29; extra == "control-plane"
Requires-Dist: httpx>=0.27; extra == "control-plane"
Requires-Dist: cryptography>=42.0; extra == "control-plane"
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pyyaml>=6.0; extra == "dev"
Requires-Dist: cryptography>=42.0; extra == "dev"
Dynamic: license-file

# bivelio-privacy-gateway (BV-PRGA)

> **PROPRIETARY AND CONFIDENTIAL — © 2026 BiVelio Inc. All Rights Reserved.**
> This software is the exclusive intellectual property of BiVelio Inc. No license
> is granted. Copying, distribution, modification, or use without the prior
> written authorization of BiVelio Inc. is strictly prohibited. BiVelio reserves
> all patent rights. See [LICENSE](./LICENSE).

**BV-PRGA** (*BiVelio Privacy Redaction & Gating Algorithm*) is a **fail-closed
data-loss-prevention (DLP) frontier** that sits between BiVelio's internal
services (Brain, document ingestion, RAG, agents, automations) and any external
LLM provider (OpenAI, Anthropic, …). No prompt, tool call, document chunk or
embedding reaches a third-party model without passing through it.

It is built to the principle that **privacy cannot depend on every developer
remembering to call a library**: the gateway is the only egress path, it
inspects *every* text-bearing field of the request (including data buried inside
tool-call arguments), and anything it cannot inspect is denied — never forwarded.

---

## Why this exists

Off-the-shelf proxies inspect `messages[].content` and little else. Sensitive
data slips through the gaps: a DNI inside a tool call's JSON arguments, an API
key in `tool_use.input`, an IBAN in an embeddings request, a secret split across
streaming chunks, an image nobody redacted. BV-PRGA is designed to close those
gaps explicitly and to **fail closed** when it can't.

| Design rule | How BV-PRGA enforces it |
| --- | --- |
| No silent passthrough | Unknown endpoint → **deny** (no catch-all). New endpoint = new adapter + tests. |
| Cover every field | Recursive walk of content, **tool-call arguments** (parsed as JSON), `tool_use.input`, embeddings input, metadata, message names. |
| Low false positives | Every structured ID is **checksum-gated** (DNI mod-23, IBAN mod-97, Luhn, JWT header decode). |
| Secrets ≠ PII | Credentials are **blocked and treated as compromised**, never pseudonymised. |
| Fail closed | Detector error/timeout → block or route-local, never forward the original. |
| Reversible only inside the perimeter | Pseudonym tokens are per-tenant + per-session, TTL-bounded, restored only on the way back. |
| Auditable, not leaky | Audit events carry entity **counts** and decisions — never values, tokens or prompts. |

---

## Benchmarks (reproduced from code)

Measured on the **deterministic layer** (structured identifiers + secrets) over
a seeded, labelled, multilingual corpus with hard negatives. Full methodology
and the honesty caveats (what is *not* measured) are in
[`docs/BENCHMARKS.md`](./docs/BENCHMARKS.md).

| Metric | Value |
| --- | --- |
| Micro precision / recall / F1 | **1.000 / 1.000 / 1.000** (4522 entities, 1000 docs) |
| Residual leak rate (values still present after sanitisation) | **0.0000 %** |
| Pseudonymisation round-trip fidelity | **100 %** (1163/1163) |
| Irreversible-redaction leaks | **0** |
| Throughput (single-thread, pure-Python core) | **~3.0 M chars/s** (~6.9 k docs/s) |

Reproduce: `make benchmark` (JSON) or `make benchmark-doc` (regenerates the doc).

> These are deterministic-layer numbers by design — checksum gating makes near
> perfect precision/recall achievable for IDs and secrets. Free-text PII
> (names/addresses) is delegated to the optional NLP layer and is **not** claimed
> at 100 %. We do not overclaim.

---

## Architecture

```
┌─────────────────────────────────────────────────────────────┐
│                     BiVelio services                        │
│  Brain · document ingestion · RAG · agents · automations    │
└──────────────────────────────┬──────────────────────────────┘
                               │  normalised internal contract
┌──────────────────────────────▼──────────────────────────────┐
│              BIVELIO PRIVACY GATEWAY  (BV-PRGA)              │
│  1. endpoint + schema validator   (unknown → deny)          │
│  2. protocol adapter (OpenAI Chat / Responses / Anthropic)  │
│  3. recursive field walk (tool args, tool_use.input, ...)   │
│  4. deterministic detectors  (regex + checksum)             │
│  5. optional NLP/NER layer   (Presidio, local models)       │
│  6. secret / credential detector                            │
│  7. per-tenant confidential dictionaries                    │
│  8. policy engine   (block / redact / pseudonymize / local) │
│  9. token vault     (per tenant+session, TTL, encrypted)    │
│ 10. value-free audit + synthetic canaries                   │
└──────────────┬────────────────┬────────────────┬────────────┘
        FORWARD│         ROUTE_LOCAL│           DENY│
        ┌──────▼──────┐   ┌────────▼─────┐   ┌─────▼──────┐
        │   LiteLLM   │   │ local model  │   │ 403 + audit│
        │  (router)   │   └──────────────┘   │  security  │
        └──────┬──────┘                       │   event    │
   OpenAI / Anthropic / …                     └────────────┘
```

LiteLLM is used **after** BV-PRGA, purely as a provider router — never as the
privacy boundary. See [`docs/ARCHITECTURE.md`](./docs/ARCHITECTURE.md).

### The four detection layers

1. **Structured identifiers** — regex gated on checksums: Spanish DNI/NIE/NIF/CIF,
   IBAN (ES/AD/FR…, ISO 7064 mod-97), credit cards (Luhn), phones (ES/AD),
   email, IPv4.
2. **Contextual PII** *(optional `[nlp]`)* — Presidio + local spaCy/GLiNER for
   names, addresses, locations in es/ca/fr/en.
3. **Secrets & credentials** — provider keys (OpenAI/Anthropic), AWS, GCP,
   GitHub, Slack, Stripe, JWT (header-validated), PEM private keys, bearer
   tokens, connection strings, basic-auth URLs.
4. **Tenant confidential** — per-tenant dictionaries for codenames, unannounced
   clients, internal labels — the sensitive data no generic model knows about.

---

## Field coverage matrix

| Endpoint | Inspected surfaces |
| --- | --- |
| `POST /v1/chat/completions` | `messages[].content` (string + vision blocks), `messages[].name`, **`tool_calls[].function.arguments`** (JSON, recursive), `function_call.arguments`, `tools[]` descriptions, `user`, `metadata` |
| `POST /v1/responses` | `instructions`, `input[]` (recursive: content blocks, function args, tool outputs), `user`, `metadata`, `tools[]` |
| `POST /v1/messages` (Anthropic) | `system` (string + blocks), `text`, `thinking`/`redacted_thinking`, **`tool_use.input`** (recursive), `tool_result.content`, `tools[]`, `metadata` |
| `POST /v1/embeddings` | `input` (string or list), `user` |
| any other path | **denied** — add an adapter + tests to support it |

Image / audio / file blocks are reported as *unsupported* and denied under the
fail-closed policy (they must be processed locally first).

---

## Quickstart

The **core has zero third-party dependencies**. Extras are opt-in.

```bash
# core only (detection, redaction, policy, adapters, pipeline, benchmark, CLI)
pip install -e .

# with the HTTP server / NLP / YAML / crypto extras as needed
pip install -e '.[server]'      # FastAPI proxy
pip install -e '.[nlp]'         # Presidio contextual PII
pip install -e '.[dev]'         # pytest + pyyaml (tests & benchmark)
```

### CLI

```bash
# scan text (exit code 3 if the request would be blocked)
echo "DNI 12345678Z, IBAN ES9121000418450200051332, key sk-ant-api03-…" | bpg scan

bpg scan --json report.txt          # machine-readable
bpg benchmark --markdown            # reproduce the benchmark
bpg policy config/policy.example.yaml   # show the effective policy
bpg serve --policy config/policy.example.yaml   # run the gateway (needs [server])
```

### Python API

```python
from bivelio_privacy_gateway.gateway.pipeline import Sanitizer
from bivelio_privacy_gateway.policy.engine import Policy

sanitizer = Sanitizer(Policy.default())

outcome = sanitizer.sanitize_request(
    "/v1/chat/completions",
    {"model": "gpt-4o", "messages": [
        {"role": "user", "content": "Mi DNI es 12345678Z y mail ana@bivelio.com"}]},
    tenant="acme", session="conv-42", request_id="req-1",
)

outcome.decision          # "forward" | "route_local" | "deny"
outcome.body              # sanitised payload (safe to forward to LiteLLM)
outcome.audit.by_type     # {"SPANISH_DNI": 1, "EMAIL": 1}  -- counts, no values

# on the way back, restore reversible tokens inside the perimeter
reply = sanitizer.restore_response(provider_text, tenant="acme", session="conv-42")
```

### Docker

```bash
docker compose up --build      # gateway + LiteLLM router (see docker-compose.yml)
```

---

## Network egress control (mandatory)

Application-level inspection is necessary but not sufficient. At the
infrastructure layer:

- BiVelio services have **no direct outbound internet access**.
- **Only** the gateway may reach provider domains / the LiteLLM router.
- LiteLLM accepts traffic **only** from the gateway.
- Provider credentials live **only** in the gateway / router, never in Brain.

This makes it technically impossible for a new SDK, script or dependency to call
a provider directly and bypass BV-PRGA. See [`docs/THREAT_MODEL.md`](./docs/THREAT_MODEL.md).

---

## Policy

Policies are declarative (defaults in code, overridable via YAML). Example:

```yaml
mode: fail_closed
unknown_endpoint: deny
unsupported_content: deny
actions:
  ANTHROPIC_API_KEY: block
  PRIVATE_KEY: block
  CREDIT_CARD: redact
  EMAIL: pseudonymize
  INTERNAL_CONFIDENTIAL: route_local
detectors:
  pii: { engine: presidio, enabled: false, languages: [es, ca, fr, en] }
  tenant_dictionaries:
    terms: { INTERNAL_CONFIDENTIAL: ["Proyecto Aurora"] }
storage:
  token_vault: { ttl_seconds: 900 }
```

Full action table and rationale in [`docs/POLICY.md`](./docs/POLICY.md).

---

## Project layout

```
src/bivelio_privacy_gateway/
├── detectors/    checksums, structured IDs, secrets, tenant dicts, engine, presidio
├── redaction/    token vault (scoped/TTL) + transformer (redact/pseudonymize/block)
├── policy/       declarative entity→action engine
├── adapters/     recursive walker + OpenAI/Anthropic/embeddings + registry
├── audit/        value-free events + synthetic canaries
├── gateway/      fail-closed pipeline + FastAPI app + streaming restorer
├── benchmark/    seeded corpus + runner + markdown report
└── cli.py        bpg scan | benchmark | policy | serve
licensing/    BV-LIC: model, token codec, keys, verifier, enforcement, issuer
services/         CP-0 control plane (Hetzner side): common · licensing · metering
apps/web/         privacy.bivelio.com marketing site (Next.js → Vercel)
deploy/           control-plane Dockerfiles · compose · Keycloak realm notes
tests/            74 tests across every layer (gateway + CP-0)
docs/             ARCHITECTURE · THREAT_MODEL · POLICY · BENCHMARKS · LICENSING ·
                  ROADMAP · COMMERCIALIZATION-PLAN · CONTROL-PLANE · DEPLOYMENT-TOPOLOGY
```

---

## Development

```bash
make install      # editable install with dev extras
make test         # pytest (53 tests)
make benchmark    # JSON benchmark
make lint         # ruff (if installed)
```

---

## Roadmap

- [x] Presidio contextual-PII layer integrated (multilingual es/ca/fr/en) and
      **evaluated** — oracle recall 0.99 / auto F1 0.94, combined residual leak
      ~3 %. See [`docs/BENCHMARKS.md`](./docs/BENCHMARKS.md). Next: evaluate on a
      real internal corpus and tune thresholds/allowlists.
- [ ] Output-side DLP (scan tool outputs / generated URLs before they reach tools).
- [ ] Clustered, encrypted vault backend.
- [ ] NeMo Guardrails integration inside Brain/RAG (retrieval + execution + output rails).
- [ ] Batch/Files/Vector-store adapters (currently denied).

---

## Commercial model — `BV-LIC`

BV-PRGA is monetised as a **self-hosted** product: the gateway runs on the
customer's infrastructure (their data never leaves their perimeter), and a
signed **BV-LIC** license key unlocks paid capabilities. Key properties:

- **Offline-verifiable** Ed25519 licenses (work air-gapped); the customer embeds
  only BiVelio's public key.
- **Fail-closed-safe enforcement**: an expired/invalid/missing license degrades to
  **Community** (commercial features off) but **never** disables DLP protection.
- **Privacy-safe metering**: the value-free audit events (counts + decisions +
  `license_id`/`tier`, never content) are exactly what BiVelio's control plane
  ingests for usage-based billing — no sensitive data reaches BiVelio.

```bash
bpg license verify "$BPG_LICENSE_TOKEN" --tenant acme   # customer side
python scripts/bvlic_issue.py issue ...                  # BiVelio control plane
```

Data plane = customer infra. Control plane (licensing + metering + updates) =
BiVelio Hetzner (shared Keycloak / Hyperswitch / Qdrant / Postgres). Full design
in [`docs/COMMERCIALIZATION-PLAN.md`](./docs/COMMERCIALIZATION-PLAN.md) and
[`docs/LICENSING.md`](./docs/LICENSING.md).

## License

Proprietary. © 2026 BiVelio Inc. All rights reserved. The BV-PRGA algorithm and
all related inventions are the property of BiVelio Inc., which reserves all
patent rights. See [`LICENSE`](./LICENSE) and [`NOTICE`](./NOTICE).
