{
  "markdown": "# Ocultar\n\n[![Apache 2.0](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE)\n[![Go 1.24+](https://img.shields.io/badge/go-1.24%2B-00ADD8.svg)](https://go.dev)\n[![Docker](https://img.shields.io/badge/docker-ghcr.io%2Focultar--dev%2Focultar-blue?logo=docker)](https://github.com/ocultar-dev/ocultar/pkgs/container/ocultar)\n[![Release](https://img.shields.io/github/v/release/ocultar-dev/ocultar)](https://github.com/ocultar-dev/ocultar/releases/latest)\n\nOcultar is an open-source local PII/PHI masking engine for AI workflows.\n\nIt runs as a local HTTP sidecar. Send it text before it reaches a cloud LLM; it returns the\nsame text with every piece of personal data replaced by a deterministic, reversible token\n(`[EMAIL_9c8f7a1b]`, `[PERSON_3a12b4cd]`, …). Originals are encrypted and stored in a\nlocal vault. Callers with the auditor token can restore them.\n\nNo PII ever reaches the upstream model.\n\n---\n\n## Quick start — Docker\n\n```bash\nexport OCU_MASTER_KEY=$(openssl rand -hex 32)\nexport OCU_SALT=$(openssl rand -hex 16)\nexport OCU_AUDITOR_TOKEN=$(openssl rand -hex 24)\n\ndocker run --rm -p 4141:4141 \\\n  -e OCU_MASTER_KEY \\\n  -e OCU_SALT \\\n  -e OCU_AUDITOR_TOKEN \\\n  ghcr.io/ocultar-dev/ocultar:latest -serve 4141\n```\n\n## Quick start — build from source\n\n```bash\nCGO_ENABLED=1 go build -o ocultar ./services/refinery/cmd/\n\nOCU_MASTER_KEY=$(openssl rand -hex 32) \\\nOCU_SALT=$(openssl rand -hex 16) \\\nOCU_AUDITOR_TOKEN=$(openssl rand -hex 24) \\\n./ocultar -serve 4141\n```\n\n---\n\n## API reference\n\n### `GET /api/health`\n\nReturns engine status. No authentication required.\n\n```json\n{\n  \"status\": \"healthy\",\n  \"version\": \"1.14\",\n  \"vault\": { \"status\": \"online\" },\n  \"slm\":   { \"status\": \"online\", \"circuit\": \"closed\" }\n}\n```\n\n---\n\n### `POST /api/refine`\n\nMask PII in text or JSON. No authentication required.\n\n**Request body**: raw text string or any JSON value.\n\n**Response**:\n\n```json\n{\n  \"refined\": \"{\\\"message\\\":\\\"Hello [PERSON_3a12b4cd], your order [EMAIL_9c8f7a1b] is ready.\\\"}\",\n  \"report\": {\n    \"hits\": 2,\n    \"types\": [\"PERSON\", \"EMAIL\"]\n  }\n}\n```\n\n> `refined` is a JSON-encoded string — parse it once to get the masked payload.\n\n---\n\n### `POST /api/reveal`\n\nRestore vault tokens back to originals.\n\n**Authentication**: `Authorization: Bearer <OCU_AUDITOR_TOKEN>` header required.\nReturns `403` if `OCU_AUDITOR_TOKEN` is not set on the server.\n\n**Request body**:\n\n```json\n{ \"tokens\": [\"[PERSON_3a12b4cd]\", \"[EMAIL_9c8f7a1b]\"] }\n```\n\n**Response**:\n\n```json\n{\n  \"results\": {\n    \"[PERSON_3a12b4cd]\": \"Alice Martin\",\n    \"[EMAIL_9c8f7a1b]\": \"alice@example.com\"\n  }\n}\n```\n\n---\n\n### `GET /api/entities` · `POST /api/entities` · `POST /api/entities/seed`\n\nManage the persistent entity registry (pre-seed canonical names so all variants map to the\nsame token). Requires `Authorization: Bearer <OCU_AUDITOR_TOKEN>`.\n\n---\n\n## Architecture\n\nOcultar runs two detection tiers before any text leaves the machine:\n\n### Tier 1 — Deterministic regex / heuristics (fast, zero-egress)\n\n| Sub-tier | Shield | What it catches |\n|----------|--------|-----------------|\n| 0 | Dictionary | VIP names, org names from `configs/protected_entities.json` |\n| 0.5 | Pattern + Entropy | High-entropy strings (API keys, secrets) via Shannon scoring |\n| 1 | Rule Engine | EMAIL, SSN, IBAN, credit cards, 50+ national ID formats |\n| 1.1 | Phone Shield | libphonenumber validation |\n| 1.2 | Address Shield | Heuristic street address parser (EN/FR/ES/DE) |\n| 1.5 | Contextual | Names in greetings, signatures, interrogative sentences |\n\n### Tier 2 — SLM-based NER (higher recall, configurable endpoint)\n\nSends text to a local AI sidecar for named-entity recognition. The scanner is always\ninitialized but produces no results unless a compatible sidecar is running at `SLM_SIDECAR_URL`.\nPoint it at a [privacy-filter](https://huggingface.co/openai/privacy-filter) or llama.cpp instance to activate NER.\n\n```bash\nSLM_SIDECAR_URL=http://localhost:8085 ./ocultar -serve 4141\n```\n\nUse `SLM_ADAPTER=openai-chat` for a llama.cpp / Qwen endpoint, or leave unset for the\nprivacy-filter protocol (default).\n\n---\n\n## Privacy model\n\n- **Zero-egress design.** Masked tokens (`[EMAIL_9c8f7a1b]`, …) are the only data forwarded to the upstream model. Raw text is not transmitted.\n- **Local vault only.** The mapping of each token back to its original value is stored in an encrypted DuckDB vault (`vault.db`) on the local filesystem using AES-256-GCM with HKDF-SHA256. The vault file is never transmitted.\n- **Raw prompt retention.** The refinery logs each raw (unmasked) prompt locally to the vault to support the audit diff view. This data is encrypted at rest alongside the token mappings and is not sent anywhere. If prompt retention is not desired, do not configure `OCU_AUDITOR_TOKEN` — without an auditor token the reveal endpoint returns `403` and the diff view is inaccessible.\n- **Fail-closed design.** If the refinery encounters an error or is unavailable, the gateway returns a `5xx` error and stops — it does not forward raw text as a fallback.\n\n---\n\n## Configuration\n\n| Variable | Required | Default | Purpose |\n|---|---|---|---|\n| `OCU_MASTER_KEY` | Yes (production) | insecure dev key | 32+ byte AES key material for HKDF |\n| `OCU_SALT` | Yes (production) | built-in default | Per-deployment HKDF salt |\n| `OCU_AUDITOR_TOKEN` | Yes | — | Bearer token for `/api/reveal` and `/api/entities` |\n| `OCU_VAULT_PATH` | No | `vault.db` | DuckDB vault file path |\n| `SLM_SIDECAR_URL` | No | `http://localhost:8085` | Tier 2 NER sidecar endpoint |\n| `SLM_ADAPTER` | No | `privacy-filter` | Sidecar protocol: `privacy-filter` or `openai-chat` |\n\n---\n\n## Building from source\n\nRequires Go 1.24+ with CGO enabled (DuckDB and libphonenumber need a C compiler).\n\n```bash\ngit clone https://github.com/ocultar-dev/ocultar.git\ncd ocultar\nmake build\n```\n\nRun tests:\n\n```bash\nCGO_ENABLED=1 go test ./...\n```\n\n---\n\n## License\n\nApache 2.0 — see [LICENSE](LICENSE).\n\n",
  "bytes": 5933,
  "sha": "0c01c2f8d12e3c052cea1a794e936dad31e0e5c038dd4ba9db3dcf4824cb10cd",
  "repo_slug": "edu963/ocultar",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_edu963_ocultar_pii_2368384c/readme"
}