Obscura's base deterministic path does not require model weights, accelerator libraries, external services, or network access during inference. Model-backed, parser-backed, and ONNX capabilities are explicit additions.
The machine-readable contracts are:
priv/obscura/capabilities.jsonpriv/obscura/model_assets.json
Obscura.Capabilities validates and exposes those manifests. Tests verify that
the listed Hex dependencies exist in mix.exs, documented environment
variables exist in the codebase, stable profiles have capability coverage, and
the manifests contain no local absolute paths or obvious credential material.
Product Profile Matrix
| Profile | Stability | Base behavior | Required optional dependencies | Assets | Preferred development backend |
|---|---|---|---|---|---|
:fast | stable | Deterministic structured PII | None | None | BEAM |
:balanced | stable, external assets require deployer review | Deterministic plus TNER NER | nx, bumblebee | TNER model and tokenizer | Emily on Apple Silicon; EXLA where supported |
:accurate | stable, external assets require deployer review | Deterministic plus output-aware TNER/Jean location cascade | nx, bumblebee | Two models and two tokenizers | Emily on Apple Silicon; EXLA where supported |
:hybrid_gliner_urchade | experimental | Deterministic plus Urchade GLiNER | ortex, tokenizers | Pinned local ONNX, tokenizer, and config export | Ortex CPU |
:openmed_pii | experimental | Native model-only OpenMed/Nemotron PII | nx, safetensors | Validated Privacy Filter checkpoint | Explicitly selected Binary, EXLA, or Emily |
Optional dependencies are declared with optional: true so downstream
applications only install the capabilities they select. Obscura never downloads
assets from Obscura.analyze/2 or Obscura.redact/2.
Base Installation
The base library requires only its normal Hex dependencies:
def deps do
[
{:obscura, "~> 0.1"}
]
endUse the deterministic product profile explicitly:
Obscura.analyze("Contact jane@example.com", profile: :fast)No model cache, environment variable, or setup task is required.
Parser-Backed Phone Validation
The optional ex_phone_number integration improves international telephone
validation and remains caller-controlled because region policy affects which
digit strings are valid.
def deps do
[
{:obscura, "~> 0.1"},
{:ex_phone_number, "~> 0.4.11"}
]
endSelect the parser and regions explicitly. Do not assume a global default region is appropriate for every tenant or dataset.
Obscura.analyze(text,
profile: :fast,
phone_parser: Obscura.Recognizer.Phone.ExPhoneNumberValidator,
phone_regions: ["US", "GB"]
)Obscura rejects obvious date-shaped and repeated-digit junk before accepting parser-backed phone candidates, but applications must still benchmark their region mix.
Bumblebee/Nx Profiles
The :balanced and :accurate profiles use token classification through
Bumblebee/Nx.
Host applications should include:
def deps do
[
{:obscura, "~> 0.1"},
{:nx, "~> 0.12"},
{:bumblebee, "~> 0.7"}
]
endThen choose a backend. BinaryBackend is useful for correctness smoke tests but is not the recommended performance path for large NER models.
Emily on macOS Apple Silicon
Emily is the current development backend used for Metal/GPU model benchmarks:
{:emily, "~> 0.6"}Project setup:
OBSCURA_REAL_MODEL_BACKEND=emily mix deps.get
For validation, forbid silent fallback:
OBSCURA_REAL_MODEL=1 \
OBSCURA_REAL_MODEL_BACKEND=emily \
OBSCURA_EMILY_DEVICE=gpu \
OBSCURA_EMILY_FALLBACK=raise \
mix test --include real_model
The presence of Emily does not by itself prove GPU use. Runtime and benchmark metadata must identify the requested backend, actual backend, selected device, and fallback policy. If device proof is unavailable, report it as unknown.
Emily is currently validated primarily for macOS Apple Silicon development and benchmarking. It is not yet Obscura's universal production backend promise.
EXLA
EXLA is conditional in the Obscura development project:
OBSCURA_REAL_MODEL=1 mix deps.get
Downstream applications can add:
{:exla, "~> 0.12"}EXLA does not imply GPU. Record whether the actual client is CPU, CUDA, or another supported target.
Explicit Runtime Preparation
Preparing a profile is an explicit operation which may populate the configured Hugging Face cache:
{:ok, runtime} =
Obscura.Profile.prepare(:balanced,
allow_download: true,
real_model_backend: :emily,
compile: [batch_size: 1, sequence_length: 128]
)
Obscura.analyze("Rachel works at Google in Paris.", profile: runtime)The runtime owns reusable serving resources. Build it during controlled application initialization, supervision setup, or a dedicated warmup phase. Do not build it inside every request.
allow_download defaults to false. Without it, remote repositories are
opened with Bumblebee offline mode and only complete cached assets may be used.
offline: true always forbids network access, even if download authorization
was also passed. This separation makes accepting third-party terms and
performing network I/O an explicit deployment action.
Use the preparation task to populate a cache with structured progress:
mix obscura.profile.prepare \
--profile balanced \
--backend emily \
--allow-download \
--timeout 1800000 \
--inactivity-timeout 300000
The effective cache directory follows explicit repository cache_dir, then
BUMBLEBEE_CACHE_DIR, then the operating-system Bumblebee cache location.
The local Mix task prints that path; public diagnostics and telemetry expose
only the source and status so home directories do not leak. --json is
newline-delimited JSON and suppresses Bumblebee's terminal progress bar.
The controlled preparation worker has an overall timeout and an inactivity
timeout. Cache-byte growth and stage transitions reset inactivity. On timeout,
abnormal exit, or interrupted transfer, unreferenced files lacking Bumblebee
metadata are moved under .obscura-quarantine in the selected cache. A later
authorized preparation retries cleanly. Complete metadata-backed entries are
never quarantined.
mix obscura.operational.benchmark enforces offline model/tokenizer loading,
counts lifecycle construction stages, and fails promotion if the canonical
request path reconstructs the runtime. Promoted operational conclusions are
summarized in docs/benchmark-status.md.
The accurate profile prepares both models:
{:ok, runtime} =
Obscura.Profile.prepare(:accurate,
allow_download: true,
real_model_backend: :emily
)It routes requests by requested entity. For location requests TNER always runs;
Jean-Baptiste runs only when TNER has no accepted location. A request that
excludes :location does not invoke the specialist, but the runtime prepares
both resources up front to keep request handling network-free and reusable.
Model Assets
TNER OntoNotes5
- Alias:
tner_roberta_large_ontonotes5 - Model:
tner/roberta-large-ontonotes5 - Tokenizer:
tner/roberta-large-ontonotes5 - Pinned revision:
0bce50f7884d5bb040469c907c897d4b061ccbb4 - Published weight size: approximately 1.42 GB, excluding cache metadata and backend/runtime memory
- Used by: stable
:balancedand:accurate - Adapter: Bumblebee token classification
- License status: not established by Obscura; verify upstream terms
The profile integration is stable, but Obscura does not distribute or license
the checkpoint. Deployers must establish whether their selected assets and use
are permitted. See docs/model-asset-licensing.md.
Urchade GLiNER Multi PII
- Alias:
urchade_gliner_multi_pii_v1 - Model:
urchade/gliner_multi_pii-v1 - Pinned revision:
1fcf13e85f4eef5394e1fcd406cf2ca9ea82351d - Used by: public experimental profile
:hybrid_gliner_urchade - Adapter:
Obscura.Recognizer.GLiNER.Ortex - Dependency: conditional optional Obscura fork of
ortex 0.1.10 - Assets: locally exported
model.onnx, tokenizer/config files frommicrosoft/mdeberta-v3-base - Reported license: Apache-2.0; full provenance still requires project-owner review
Host applications selecting this profile must add the CPU runtime and tokenizer dependencies explicitly:
def deps do
[
{:obscura, "~> 0.1"},
{:ortex, "~> 0.1.10"},
{:tokenizers, "~> 0.5.1"}
]
endThe upstream checkpoint does not publish a directly usable ONNX bundle.
Generate it with the pinned script and lock in eval/gliner/; do not place
weights in the application package or repository. The native adapter passes
its parity checks but failed the three :balanced promotion gates. It is
publicly available only as an experimental CPU-only general NER option, not as
an accuracy replacement for :balanced. Current comparative metrics are in
benchmark-status.md.
Prepare and reuse the runtime:
{:ok, runtime} =
Obscura.Profile.prepare(:hybrid_gliner_urchade,
model_dir: ".cache/gliner/urchade-gliner-multi-pii-v1"
)
Obscura.analyze("Rachel works at Google in Paris.", profile: runtime)The project-local Ortex fork adds structured CoreML options and ONNX Runtime profiling. On Apple Silicon, callers can explicitly request:
Obscura.Recognizer.GLiNER.Ortex.build(
model: :urchade_gliner_multi_pii_v1,
model_dir: model_dir,
execution_providers: [:coreml],
coreml_options: [
model_format: :ml_program,
compute_units: :cpu_and_gpu,
require_static_input_shapes: false,
enable_on_subgraphs: false
],
profile_prefix: ".cache/ortex-profiles/urchade"
)This is a diagnostic path, not a recommended accelerator configuration. The
current dynamic export preserves output parity but is substantially slower than
CPU and uses extensive CPU fallback. finish_profiling/1 proves provider
assignment, not GPU-only execution. Model assets and generated native binaries
remain outside the package and repository.
Urchade also has an experimental native Nx/Emily adapter:
Obscura.Recognizer.GLiNER.NativeEnable its development dependency path with OBSCURA_GLINER_NATIVE=1. It
requires Emily, Tokenizers, Nx, Safetensors, a pinned local
model.safetensors export, tokenizer.json, gliner_config.json, and
obscura_native_manifest.json. It does not require Ortex or ONNX Runtime.
Both Emily fallback paths are forced to :raise. Layer and decoded-span parity
pass, but the adapter is not promoted because it does not beat Ortex CPU
latency or the :balanced accuracy gates.
The registry and asset manifest pin the reviewed revision. Authoritative benchmarks must continue recording it so an upstream branch change cannot silently alter profile behavior.
Jean-Baptiste Location Specialist
- Alias:
jean_baptiste_roberta_large_ner_english - Model:
Jean-Baptiste/roberta-large-ner-english - Tokenizer fallback:
FacebookAI/roberta-large - Model revision:
8f3abc1ef81ffbbb0e80568d4fed1dd10d459548 - Tokenizer revision:
722cf37b1afa9454edce342e7895e588b6ff1d59 - Published fine-tuned weight size: approximately 1.42 GB;
:accuratealso needs the TNER weights and both tokenizer repositories - Used by: stable
:accurate - Adapter: Bumblebee token classification
- Fine-tuned model license: MIT; verify base/tokenizer terms
The tokenizer fallback is intentional because the fine-tuned repository does not provide the Rust-compatible tokenizer artifact expected by the current path. Authoritative runs must record both pinned repositories and revisions.
Native Privacy Filter
The :openmed_pii profile uses Obscura's native Privacy Filter architecture.
It does not use Bumblebee's normal token-classification architecture loader.
Host dependencies:
def deps do
[
{:obscura, "~> 0.1"},
{:nx, "~> 0.12"},
{:safetensors, "~> 0.1.3"}
]
endPrepare the checkpoint explicitly:
mix obscura.privacy_filter.setup \
--repo OpenMed/privacy-filter-nemotron-v2 \
--checkpoint .cache/privacy-filter/openmed-nemotron-v2
Validate before starting inference:
mix obscura.privacy_filter.checkpoint \
--checkpoint .cache/privacy-filter/openmed-nemotron-v2
Prepare a reusable runtime:
{:ok, runtime} =
Obscura.Profile.prepare(:openmed_pii,
checkpoint: ".cache/privacy-filter/openmed-nemotron-v2",
backend: :emily
)At minimum, the native checkpoint requires config.json and one or more
Safetensors files. The inspected OpenMed v2 repository also publishes
tokenizer.json, tokenizer_config.json, and label_space_fine_v1.json.
Python-original layout additionally requires dtypes.json and
viterbi_calibration.json and must be selected explicitly.
Checkpoint paths and hashes belong in local configuration and authoritative benchmark metadata, not in the Hex package. The model's inspected license metadata is not a clear production grant; verify it before deployment.
GLiNER, Native Emily, and Generic Ortex
GLiNER and generic token-classification Ortex adapters remain advanced, experimental capabilities rather than stable product profiles.
Project development setup:
OBSCURA_GLINER_ORTEX=1 mix deps.get
OBSCURA_GLINER_NATIVE=1 mix deps.get
Dependencies:
{:ortex, "~> 0.1.10"}
{:tokenizers, "~> 0.5.1"}GLiNER requires a compatible ONNX graph, tokenizer.json, and GLiNER config.
Configure OBSCURA_GLINER_MODEL_DIR or explicit paths. Generic token
classification uses OBSCURA_NER_ORTEX_MODEL_DIR or explicit options.
The experimental :nvidia_gliner_pii_v1 model alias uses a pinned local export
of nvidia/gliner-PII. It has no public product profile and is not included in
the authoritative matrix. The export is approximately 1.78 GB and requires
the exact ONNX, tokenizer, and config hashes recorded in
eval/gliner/nvidia-export-reference.json. Configure the asset directory with
OBSCURA_GLINER_NVIDIA_MODEL_DIR for tagged parity tests or pass model_dir
directly.
The native Emily path uses a Safetensors checkpoint instead of ONNX. Configure
OBSCURA_GLINER_NATIVE_MODEL_DIR; generated assets remain local and ignored.
No ONNX model is bundled. ONNX Runtime execution provider availability is platform-specific and must be recorded in benchmark metadata.
Python Reference Environments
Python environments under .presidio-venv/, .opf-venv/, and
.privacy-filter-reference-venv/ are ignored development tools. They are not
runtime dependencies of the library.
Each authoritative Python comparison must record:
- Python version;
- fully pinned package versions;
- model repository and immutable revision;
- dataset fingerprint and sample IDs;
- entity mapping and offset policy;
- checkpoint hashes;
- exact command;
- whether CPU or GPU was actually used.
Network Policy
| Operation | Network allowed by default? |
|---|---|
Obscura.analyze/2 with local profiles | No |
Obscura.redact/2 with local profiles | No |
Obscura.Profile.validate_runtime/2 | No |
Obscura.Profile.prepare/2 with Hugging Face sources | Yes, explicit operation |
| Privacy Filter setup Mix task | Yes, explicit operation |
External-service calls are application-owned custom-recognizer behavior and are not part of Obscura's built-in profiles or capability manifest.
Readiness checks must be offline. They inspect modules, paths, layouts, hashes, and reusable serving presence without contacting model hubs or providers.
Cache and Cleanup
Obscura does not own one global cache. The host controls:
- Hugging Face/Bumblebee cache location;
.cache/privacy-filtercheckpoint directories;- GLiNER/Ortex model directories;
- Mix dependency/build caches;
- ignored Python virtual environments.
Never delete shared caches automatically from library code. Cleanup commands are operator actions and should target only an explicitly configured directory or dependency.
Common Diagnostics
| Code | Meaning |
|---|---|
missing_optional_dependency | Required optional Hex dependency is unavailable |
missing_model_asset | Reusable serving or model files were not prepared |
missing_tokenizer_asset | Required tokenizer is unavailable or mismatched |
missing_checkpoint | Privacy Filter checkpoint path was not supplied/found |
checkpoint_incomplete | Required checkpoint files are missing/truncated |
checkpoint_layout_mismatch | Native versus Python-original contract is wrong |
checkpoint_hash_mismatch | Local file differs from the pinned manifest |
unsupported_backend | Backend name is not supported by the adapter |
backend_device_unavailable | Requested CPU/GPU device could not be selected |
backend_fallback_forbidden | Runtime attempted a forbidden fallback |
model_load_failed | Model architecture/files could not be loaded |
tokenizer_load_failed | Tokenizer files or architecture are incompatible |
serving_build_failed | Runtime serving could not be constructed |
Use Obscura.Diagnostic.format/1 for human output and inspect its stable code
for programmatic handling. Diagnostic inspection omits nested causes and local
paths by default.
Production Checklist
Before deploying an optional capability:
- Pin Obscura and optional dependency versions.
- Pin immutable model and tokenizer revisions.
- Record and verify local asset hashes.
- Review every model and base-model license.
- Run profile readiness offline.
- Warm reusable serving resources before accepting traffic.
- Prove actual backend/device and forbid silent fallback.
- Benchmark the deployment hardware and representative data.
- Define memory, concurrency, timeout, and failure behavior.
- Ensure reports, telemetry, and logs omit raw PII and credentials.