bivelio-privacy-gateway (BV-PRGA)
Commercial license — © 2026 BiVelio, Inc. All Rights Reserved. Distributed under the BiVelio Shield — Gateway Commercial License. You may download, install, and run it; Evaluation Use is free, and Production Use requires a valid BV-LIC Seat License (USD 4.00 / Seat / month). Modifying, redistributing, reverse-engineering, or circumventing the BV-LIC licensing is not permitted; BiVelio reserves all copyright and patent rights. See LICENSE.
Part of BiVelio Shield. BiVelio Shield is the product brand for BiVelio's local-first privacy layer for AI. It ships in two form factors that share the same deterministic engine (
@bivelio/redact-core/ BV-PRGA): the browser extension (consumer, redacts your prompts to ChatGPT & Claude on your own machine) and this gateway / SDK (developer & enterprise, the server-side egress frontier). This package is BiVelio Shield — Gateway.
BV-PRGA (BiVelio Privacy Redaction & Gating Algorithm) is the fail-closed data-loss-prevention (DLP) frontier at the core of BiVelio Shield. It 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.
What it catches — and its documented limits
Honest scope (a DLP tool that overclaims is a liability):
- Strong, deterministic layer (always on): checksum-gated structured identifiers (credit cards via Luhn, IBAN mod-97, Spanish DNI/NIE/CIF, US SSN and more national IDs), emails, phones, and API keys / secrets (OpenAI, AWS, GitHub, Stripe, Slack, …). What is actually benchmarked is narrower than what ships: the corpus scores 15 entity types, while the structured detector alone carries 99 rules over 96 distinct types. On those 15 the layer measures at precision/recall 1.000 with zero residual leak — that result covers about a sixth of the shipped rule surface, and says nothing about the rest.
- Names & free-form PII: covered only by the optional NLP layer (Presidio / GLiNER), not the deterministic core.
- Documented residuals (surfaced by adversarial review, tracked for a dedicated
detector follow-up, NOT hidden): a credit card written with digits glued
contiguously with no separator may be missed; a small number of streaming
egress frame shapes were closed iteratively. See
CHANGELOG/ the hardening history. Treat the deterministic core as strong on structured data with known edges, not as total coverage of every possible input.
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, on the 15 entity
types the corpus covers (seed 1234, core track). Full methodology and the
honesty caveats (what is not measured) are in
docs/BENCHMARKS.md, which is regenerated from code.
| Metric | Value |
|---|---|
| Micro precision / recall / F1 | 1.000 / 1.000 / 1.000 (4522 entities, 1000 docs) |
| Residual leak rate (whole values still present after sanitisation) | 0.0000 % |
| Partial-leak surface (gold values not fully covered by a detection) | 0.0000 % (0/4522) |
| Pseudonymisation round-trip fidelity | 100 % (2689/2689) |
| Irreversible-redaction leaks | 0 |
| Throughput (single-thread, pure-Python core) | ~0.35–0.40 M chars/s (~790–900 docs/s) — see below |
Reproduce: make benchmark (JSON) or make benchmark-doc (regenerates the doc).
On the throughput figure. It is the only number here that is not deterministic, and it is hardware- and load-specific: the range above is twelve runs on an Apple M4 Pro (14 cores, 48 GB), macOS 15.7.4, CPython 3.13.7, and a run taken while that machine was at load average 15 fell to 192 k chars/s. This README previously advertised ~3.0 M chars/s (~6.9 k docs/s) — roughly 8× what the code measures anywhere we can reproduce it, and ~5.7× the figure
docs/BENCHMARKS.mdwas itself generating, which is how the gap was found: the repo's own generated doc disagreed with its own front page. Do not quote a throughput number you have not run on your own hardware; runmake benchmark-docand read it out of the generated block.
The detection numbers are deterministic-layer numbers by design — checksum gating makes near perfect precision/recall achievable for IDs and secrets, on the types the corpus exercises. 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.
The four detection layers
- 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.
- Contextual PII (optional
[nlp]) — Presidio + local spaCy/GLiNER for names, addresses, locations in es/ca/fr/en/pt/de (thePolicy.languagesdefault; the deterministic benchmark corpus, separately, is es/ca/fr/en). - Secrets & credentials — provider keys (OpenAI/Anthropic), AWS, GCP, GitHub, Slack, Stripe, JWT (header-validated), PEM private keys, bearer tokens, connection strings, basic-auth URLs.
- 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/messages/count_tokens (Anthropic) |
the same walker as /v1/messages; the answer is never restored |
GET /v1/models, GET /v1/models/{id} |
request line only: limit, before_id, after_id and the id, each through the deterministic detector (a value → 400) |
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. With the [documents] extra and a TEAM+ licence, the
unsupported_content: redact policy mode instead extracts PDF / DOCX / XLSX /
PPTX / OpenDocument (ODT, ODS, ODP) / legacy .doc, .xls, .ppt / text
attachments in process and forwards their redacted text (same detectors,
same vault, same [BV:…] tags as the prompt) — anything unreadable, over
budget or unlicensed still denies, never forwards (see docs/POLICY.md).
The separate [legacy] extra adds the pre-2007 binary Office formats —
.doc, .xls, .ppt (xlrd + olefile: pure Python, BSD, no subprocess and
no network). These are gateway-only: the browser extension cannot parse an
OLE2 compound file and asks the user what to do instead, so a 1998 Word
attachment is inspected here or nowhere.
pip install "bivelio-privacy-gateway[documents,legacy]" # take either, or both
Quickstart
Installing from PyPI (how a customer gets it — this page is also the PyPI front page, and until 1.0.1 every command on it assumed a source checkout):
pip install "bivelio-privacy-gateway[server]" # or [server,nlp] for the paid layer
# or: docker pull ghcr.io/bivelio/bivelio-privacy-gateway:latest
bpg policy example > policy.yaml # the example policy, from the package itself
BPG_GATEWAY_KEYS='sk-alice:acme:reidentify' \
BPG_UPSTREAM_BASE_URL='http://localhost:4000' \
bpg serve --policy policy.yaml
Working from a source checkout (contributors):
# 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
# scan text (exit code 3 if the request would be blocked — a real credential;
# the elided `sk-ant-api03-…` below is not one, so this line exits 0)
echo "DNI 12345678Z, IBAN ES9121000418450200051332, key sk-ant-api03-…" | bpg scan
bpg scan --json report.txt # machine-readable (includes the sanitised text)
# CI: many files, type and position only — never the value (docs/SCAN-CI.md)
bpg scan --paths . --exclude tests/fixtures # path:line:col: TYPE SEVERITY
bpg scan --paths . --format sarif > bpg.sarif # SARIF 2.1.0 for code scanning
# coding-agent hook: event JSON on stdin, exit 2 blocks (docs/GANCHOS-AGENTES.md)
bpg hook claude-code # or: bpg hook cursor
bpg benchmark --markdown # reproduce the benchmark
bpg policy example # print the packaged example policy
bpg policy config/policy.example.yaml # show the effective policy (source checkout)
bpg license fetch --url https://privacy.bivelio.com # rotate BPG_LICENSE_TOKEN after a renewal
bpg audit-verify audit.jsonl # verify the hash-chained audit log (BPG_AUDIT_CHAIN_FILE)
bpg audit-export audit.jsonl --format cef # value-free export; Team plan (docs/EXPORTACION-SIEM.md)
# run the gateway (needs [server]). It binds 127.0.0.1 by default and REFUSES to
# start without an API-key map — see "Authenticating the gateway" below.
BPG_GATEWAY_KEYS='sk-alice:acme:reidentify' \
bpg serve --policy config/policy.example.yaml
Authenticating the gateway (required)
The gateway will not start unless it can tell its callers apart:
# one key per seat: "key:tenant[:perms]", comma-separated
export BPG_GATEWAY_KEYS='sk-alice:acme:reidentify,sk-bob:acme'
# ...or a JSON file: {"sk-alice": {"tenant": "acme", "permissions": ["reidentify"]}}
export BPG_GATEWAY_KEYS_FILE=/etc/bivelio/keys.json
Three things follow from it, and they are why it is not optional:
- the tenant comes from the key, not from the spoofable
x-bivelio-tenantheader, so two callers can never share a vault scope; - re-identification is a permission. Only a key holding
reidentifygets[BV:…]tokens restored on the way back. Without a key map, tokens are returned opaque — measured, an unauthenticated deployment let a second caller sending no headers at all recover the real IBAN behind the first caller's token; - the seat count is the key fingerprint.
active_seatsisCOUNT(DISTINCT subject)and the subject is that fingerprint, so per-seat usage stays at zero without this even when the meter is fully configured.
For a single-tenant box whose port is reachable only by callers you already
trust, BPG_ALLOW_UNAUTHENTICATED=1 is the acknowledgement; add
BPG_LEGACY_REIDENTIFY=1 if that deployment also needs tokens restored for
anonymous callers. Both are documented in .env.example.
Python API
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
cp .env.example .env # then set BPG_VAULT_SECRET and BPG_GATEWAY_KEYS
docker compose up --build # gateway + LiteLLM router (see docker-compose.yml)
.env.example documents every variable the gateway reads, grouped with the
must-set ones first. The compose file publishes the gateway on 127.0.0.1:8080
only: services on the internal network reach it by name and never needed a
published port, and the short 8080:8080 form binds every interface on the host
— Docker's iptables rules bypass ufw, so that port was reachable from the
network whatever the host firewall said.
The first line was missing, and without it a clean clone stops at
required variable BPG_VAULT_SECRET is missing a value. The refusal is correct —
the vault secret is what makes tokens unforgeable, so compose declines to invent
one — but cp .env.example .env appeared in no document in the tree, so the
quickstart was one line short of working.
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.
Policy
Policies are declarative (defaults in code, overridable via YAML). Example:
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.
Coding agents (Claude Code)
BPG_AGENT_MODE=1 plus egress.restore_tools: [Write, Edit, MultiEdit, NotebookEdit, Bash] lets a coding agent write real values into local files
while every network tool (WebFetch, WebSearch, mcp__*) keeps the opaque
token; the vault session is stable per conversation, and
/v1/messages/count_tokens and GET /v1/models are routed. See
docs/PERFIL-AGENTE-CODIGO.md.
SIEM export and audit export (Team plan)
BPG_SIEM_OTLP_URL and/or BPG_SIEM_SYSLOG_URL (CEF or OCSF) mirror each
audit event to your SIEM with counts, types and decisions only — never a
prompt, a value or a vault token — and bpg audit-export exports a verified
audit chain as JSONL, CSV, CEF or OCSF. Both need the licence's
audit_export bit (the Team plan). See
docs/EXPORTACION-SIEM.md.
Local MCP connector (Claude Desktop)
python -m bivelio_privacy_gateway.mcp_local -- <folders> is a stdio MCP
server with no network that hands Claude a file's text with its values
replaced by vault tokens, and writes the restored result back to disk. See
docs/MCP-LOCAL.md.
MCP proxy (remote MCP servers)
Off by default: with BPG_MCP_CONFIG naming a JSON (or YAML) file of
mcp.servers, the gateway mounts POST /mcp/{server} and proxies MCP over
Streamable HTTP (2026-07-28 and 2025-11-25) to the remote servers configured
per tenant. Only the tools in each server's explicit allowed_tools are listed
or callable — anything else is refused without contacting the remote — and
arguments and results go through the same pipeline as a prompt. It needs a
key map, and an invalid file refuses to start. See
docs/MCP-PROXY.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
├── mcp_local/ stdio MCP connector for Claude Desktop (docs/MCP-LOCAL.md)
├── mcp_proxy/ /mcp/{server} MCP proxy with allowed_tools (docs/MCP-PROXY.md)
├── 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/ ~1,200 tests across every layer (gateway + CP-0) — `make test`
docs/ ARCHITECTURE · THREAT_MODEL · POLICY · BENCHMARKS · LICENSING ·
ROADMAP · COMMERCIALIZATION-PLAN · CONTROL-PLANE · DEPLOYMENT-TOPOLOGY
Development
make install # editable install with dev extras
make test # pytest — 1,196 collected on 2026-08-09, and growing; the
# README used to claim 53, and elsewhere 74. Run it rather
# than quoting it: an exact count in prose is stale on arrival.
make benchmark # JSON benchmark
make lint # ruff (if installed)
Roadmap
- Presidio contextual-PII layer integrated (multilingual es/ca/fr/en/pt/de —
six languages, the
Policy.languagesdefault) and evaluated — oracle recall 0.989 / auto F1 0.938, language detection 0.86, combined residual leak 4.9 % (39 of 800, seed 2026, re-measured 2026-08-09). Seedocs/BENCHMARKS.md. Next: evaluate on a real internal corpus and tune thresholds/allowlists — and fix the language guesser, which is what separates auto recall from oracle recall. - 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.
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 and
docs/LICENSING.md.
License
Commercial. © 2026 BiVelio, Inc. All rights reserved. Distributed under the
BiVelio Shield — Gateway Commercial License: download / install / run
granted, Evaluation Use free, Production Use requires a valid BV-LIC Seat
License (USD 4.00 / Seat / month); all other rights (modify, redistribute,
reverse-engineer, circumvent BV-LIC) reserved. The BV-PRGA algorithm and all
related inventions are the property of BiVelio, Inc., which reserves all copyright
and patent rights. See LICENSE, NOTICE, and
THIRD-PARTY-NOTICES.md.
Metadata
Release files for bivelio-privacy-gateway 1.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| bivelio_privacy_gateway-1.2.0.tar.gz | 1.1 MB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| bivelio_privacy_gateway-1.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.6 MB
Release files / bivelio_privacy_gateway-1.2.0.tar.gz
| Download URL | bivelio_privacy_gateway-1.2.0.tar.gz |
|---|---|
| Size | 1.1 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
035a125896247e3a8ee79b17c84cd6279f8fc856b2d8dbd8d0afb95f56edf8e2
|
|
BLAKE2b-256 checksum How to use checksums |
582657ccc258bc0ebb93455301d8242fdd88414ff091e6459aaad36e59c438e8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 29, 2026.
Transparency logRelease files / bivelio_privacy_gateway-1.2.0-py3-none-any.whl
| Download URL | bivelio_privacy_gateway-1.2.0-py3-none-any.whl |
|---|---|
| Size | 573.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ab49b870c01713209f41bb651a9c0249ae81ab236dad466fbe1722c10c0ed0e9
|
|
BLAKE2b-256 checksum How to use checksums |
b7aa1eb1a78def09fd2ea83a19e80bc6c205e6f3cc4d2d76771d06c2c3a8423d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 29, 2026.
Transparency logSecurity for bivelio-privacy-gateway 1.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for bivelio_privacy_gateway-1.2.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 29, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.