stella-anonymize-core
Python bindings for the stella anonymization Rust core.
Install
Prebuilt wheels on PyPI ship the bundled native pipeline packages, so no monorepo checkout is needed:
uv add stella-anonymize-core
# or: pip install stella-anonymize-core
Wheels target Python 3.11+ (abi3) on manylinux x64/aarch64, macOS x64/arm64,
and Windows x64. Only wheels are published; there is no source distribution.
The build.rs step needs the monorepo's generated .stlanonpkg native
pipeline packages, so a source build cannot be self-contained. To build from a
checkout instead, run bun run build first so those packages exist, then:
uv add ./crates/anonymize-py
Usage
Prepare or load the anonymizer once, then reuse it for documents.
import stella_anonymize as anonymize
languages = anonymize.available_default_native_pipeline_languages()
prepared = anonymize.preload_default_native_pipeline(
language="en" if "en" in languages else None
)
result = prepared.redact_text(text, redact_string="***")
print(result.redaction.redacted_text)
Reverse replacement placeholders with the returned redaction map (a mapping of
placeholder -> original, a sequence of RedactionEntry, or
(placeholder, original) pairs; entries apply in order):
restored = anonymize.deanonymise(
result.redaction.redacted_text,
result.redaction.redaction_map,
)
For related documents, create an explicit in-memory session from the prepared anonymizer. Repeated normalized entities reuse their placeholders within that session:
session = prepared.create_redaction_session("opaque_case_1")
first = session.redact_text(first_document)
second = session.redact_text(second_document)
restored_text = session.restore_text(first.redaction.redacted_text)
restore_text() restores complete known placeholders in one non-cascading
pass. Other session namespaces remain unchanged; unknown placeholders owned by
the session fail closed. Lifecycle sessions also require the caller-supplied
observed_at_epoch_seconds argument.
session.to_plaintext_json() supports deterministic in-memory transfer between
runtime instances. Its output contains original personal data in plaintext: do
not log it or persist it without an application-owned protection layer. Restore
validated transfer state with prepared.restore_redaction_session(json_state).
For persistence, use the authenticated binary archive API with a caller-owned 32-byte key. Restoring requires the expected session identity so an archive cannot be substituted across records:
archive = session.to_encrypted_archive(application_key)
restored = prepared.restore_encrypted_redaction_session(
archive,
application_key,
session.session_id(),
)
Generate, store, rotate, and authorize access to the key outside the SDK. The
archive contains personal data as ciphertext; do not log the archive or key.
Lifecycle sessions use to_encrypted_archive_at() and require
observed_at_epoch_seconds when restored.
Sessions can carry explicit lifecycle bounds. The engine never reads the system clock; supply the UTC epoch-second observation time for each lifecycle-aware operation:
session = prepared.create_redaction_session_with_lifecycle(
"opaque_case_2",
created_at_epoch_seconds=1_800_000_000,
expires_at_epoch_seconds=1_800_086_400,
)
result = session.redact_text_at(
document,
observed_at_epoch_seconds=1_800_000_100,
)
metadata = session.inspect(1_800_000_100) # contains no entity values
deletion = session.delete()
Expiry is fail-closed at its exact boundary. delete() performs logical
deletion: it clears the session mappings and prevents future use, but does not
revoke earlier exported copies or claim physical erasure of process memory.
DOCX uses the same session mappings and fail-closed coverage policy as the TypeScript document binding. Extraction and rewrite offsets are UTF-16 code units because locations and plans are portable across runtimes:
extraction = anonymize.extract_docx_text(document_bytes)
result = anonymize.anonymize_docx(
document_bytes,
session,
session.session_id(),
{"coverage": {"mode": "require-full"}},
)
restored = anonymize.restore_docx_text(
result["document"],
session,
session.session_id(),
)
require-full rejects packages containing unhandled metadata, custom XML,
external relationship targets, or unsupported WordprocessingML constructs.
Use {"mode": "allow-partial"} only when the caller has explicitly accepted
the returned coverage inventory. Rewriting refuses signed packages rather than
silently invalidating their signature.
Caller-produced detections use Python character indexes and enter the same resolution and redaction pipeline as built-in detections:
result = prepared.redact_text_with_caller_detections(
"😀Alice signed.",
[{"start": 1, "end": 6, "label": "person", "score": 0.95,
"provider_id": "example-ner", "detection_id": "person-1"}],
)
Pass {"organization": "keep"} as the operators argument to preserve
detected organizations while processing other labels normally. Kept entities
remain in the result and operator map, but create no reversible mapping entry.
Use a tagged mask configuration to replace a number of visible Unicode grapheme clusters from the start or end:
operators = {
"email address": {
"type": "mask",
"masking_character": "*",
"characters_to_mask": 6,
"direction": "start",
}
}
provider_id and detection_id are required 1–128 byte ASCII identifiers:
they start with an alphanumeric character and otherwise contain only
alphanumerics, ., _, :, or -. Do not encode personal data in them.
Retained entities preserve both IDs;
redact_text_with_caller_detections_diagnostics_json() reports audit-safe
external input and retained counts without matched text.
Portable model or service output can be validated with
convert_external_detection_batch(document_bytes, batch). The v1 batch uses
the same provider-neutral, SHA-256-bound contract as Node, with an explicit
utf8-byte, utf16-code-unit, or unicode-code-point offset unit and explicit
provider-label mappings. It has no model dependency and does not require
GLiNER. provider.id is the immutable, versionable audit identity retained on
detections. provider.name and provider.version are validated descriptive
batch metadata but are not copied into caller detections; retain the original
batch if an audit record needs them. The returned value feeds the existing
caller-detection API after the shared Rust contract validates and converts its
spans.
PDF inspection
inspect_pdf() inventories PDF structures that can retain sensitive content
and returns fail-closed page coverage. It does not redact PDFs. Without explicit
renderer/OCR page observations, every page is reported as
page-content-not-observed; opaque rectangle overlays are never treated as
anonymization.
from pathlib import Path
import stella_anonymize as anonymize
inspection = anonymize.inspect_pdf(Path("contract.pdf").read_bytes())
print(inspection["risks"])
print(inspection["coverage"])
Regional codes use the exact package when present and otherwise fall back to
the base language package, so en-US can use the shipped en artifact.
anonymize_pdf_raster() is the destructive output API. The caller supplies a
complete observation and RGB8 pixel buffer for every page; the function runs
the prepared anonymizer, maps selected spans to glyph geometry, fills those
pixels, and returns a new image-only PDF plus its verification certificate.
Python does not bundle a renderer or OCR engine.
rewrite_pdf_raster_from_detections() is the lower-level seam for callers that
already own validated UTF-16 detection ranges. Both APIs reject incomplete page
coverage, unmapped detections, mismatched pixels, source-object reuse, and
limit violations. A successful certificate proves the destructive rewrite and
fresh output structure, not perfect OCR or PII recall; piiCleanGuaranteed is
always false.
For caller-owned configs, prepare package bytes before serving documents and load them at runtime:
import stella_anonymize as anonymize
package_bytes = anonymize.prepare_search_package(config_json)
prepared = anonymize.load_prepared_package(package_bytes)
prepared.warm_lazy_regex()
result = prepared.redact_text(text, redact_string="***")
get_default_native_pipeline() defers lazy regex warmup by default so the first
call only pays for regexes the document actually touches. Use
preload_default_native_pipeline() or pass warmup="lazy-regex" when startup can
absorb that cost before serving documents. Top-level redact_text() and
redact_text_json() are available for one-off calls, but they prepare from config
on each invocation. Use load_prepared_package() or load_prepared_package_file()
for repeated document processing.
API
prepare_search_package(config_json | config_bytes | config_mapping, compressed=True) -> bytesload_prepared_package(package_bytes) -> PreparedAnonymizerload_prepared_package_file(package_path) -> PreparedAnonymizeravailable_default_native_pipeline_languages() -> tuple[str, ...]read_default_native_pipeline_package_file(language=None) -> bytesget_default_native_pipeline(language=None, package_path=None, warmup="none") -> PreparedAnonymizerpreload_default_native_pipeline(language=None, package_path=None) -> PreparedAnonymizerPreparedAnonymizer.warm_lazy_regex()PreparedAnonymizer.warm_lazy_regex_diagnostics_json()PreparedAnonymizer.create_redaction_session(session_id) -> PreparedRedactionSessionPreparedAnonymizer.create_redaction_session_with_lifecycle(...) -> PreparedRedactionSessionPreparedAnonymizer.restore_redaction_session(plaintext_json) -> PreparedRedactionSessionPreparedRedactionSession.restore_text(full_text, observed_at_epoch_seconds=None) -> strdeanonymise(redacted_text, redaction_map) -> strinspect_pdf(document, page_observations=None) -> dictanonymize_pdf_raster(document, anonymizer, provider, pages, fill_rgb=(0, 0, 0)) -> (bytes, dict)rewrite_pdf_raster_from_detections(document, request, page_pixels) -> (bytes, dict)PreparedAnonymizer.redact_text(text, operators=None, redact_string=None)PreparedAnonymizer.redact_text_json(text, operators=None, redact_string=None)PreparedAnonymizer.diagnostics_json(text, operators=None, redact_string=None)
PreparedSearch is an alias for PreparedAnonymizer.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file stella_anonymize_core-2.7.8-cp311-abi3-win_amd64.whl.
File metadata
- Download URL: stella_anonymize_core-2.7.8-cp311-abi3-win_amd64.whl
- Upload date:
- Size: 27.8 MB
- Tags: CPython 3.11+, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ce96cba37bf6f30b99776be92903675657acb2b1108b2983a0cff4d0b3af620b
|
|
| MD5 |
141db2b0792c5942b299826261c39542
|
|
| BLAKE2b-256 |
71387829e1cda23f85daa9ba58dc966acb7fc32cb39f52633fa8a55395ddb3d5
|
Provenance
The following attestation bundles were made for stella_anonymize_core-2.7.8-cp311-abi3-win_amd64.whl:
Publisher:
release.yml on stella/anonymize
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
stella_anonymize_core-2.7.8-cp311-abi3-win_amd64.whl -
Subject digest:
ce96cba37bf6f30b99776be92903675657acb2b1108b2983a0cff4d0b3af620b - Sigstore transparency entry: 2503998630
- Sigstore integration time:
-
Permalink:
stella/anonymize@22e52184b3ad6ea06a224b4278e574a9abc930cf -
Branch / Tag:
refs/heads/main - Owner: https://github.com/stella
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@22e52184b3ad6ea06a224b4278e574a9abc930cf -
Trigger Event:
push
-
Statement type:
File details
Details for the file stella_anonymize_core-2.7.8-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: stella_anonymize_core-2.7.8-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 27.9 MB
- Tags: CPython 3.11+, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
83ec24a88b717c6b365f2acfcf1f2916f46441ec2e21d906f9efdbf8c1d58e59
|
|
| MD5 |
90b867c9d894430cbeb3fd1b1e4f13fd
|
|
| BLAKE2b-256 |
7ff8564f764d89e423d1dacd7db2ba573808c2b3bba1e8c27b66d198561bbfc1
|
Provenance
The following attestation bundles were made for stella_anonymize_core-2.7.8-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:
Publisher:
release.yml on stella/anonymize
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
stella_anonymize_core-2.7.8-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl -
Subject digest:
83ec24a88b717c6b365f2acfcf1f2916f46441ec2e21d906f9efdbf8c1d58e59 - Sigstore transparency entry: 2503998383
- Sigstore integration time:
-
Permalink:
stella/anonymize@22e52184b3ad6ea06a224b4278e574a9abc930cf -
Branch / Tag:
refs/heads/main - Owner: https://github.com/stella
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@22e52184b3ad6ea06a224b4278e574a9abc930cf -
Trigger Event:
push
-
Statement type:
File details
Details for the file stella_anonymize_core-2.7.8-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.
File metadata
- Download URL: stella_anonymize_core-2.7.8-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
- Upload date:
- Size: 27.6 MB
- Tags: CPython 3.11+, manylinux: glibc 2.17+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d4453fb659a34a3f6cbcb81a7bec9517fc69d385fbd83ca199b500d264f657b8
|
|
| MD5 |
e47ef588b9441c9420ce8e38371df90e
|
|
| BLAKE2b-256 |
1146aafa43705b27898bd552322c442231d1c3f20c5a752f4affc8cd0079ec36
|
Provenance
The following attestation bundles were made for stella_anonymize_core-2.7.8-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:
Publisher:
release.yml on stella/anonymize
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
stella_anonymize_core-2.7.8-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl -
Subject digest:
d4453fb659a34a3f6cbcb81a7bec9517fc69d385fbd83ca199b500d264f657b8 - Sigstore transparency entry: 2503998542
- Sigstore integration time:
-
Permalink:
stella/anonymize@22e52184b3ad6ea06a224b4278e574a9abc930cf -
Branch / Tag:
refs/heads/main - Owner: https://github.com/stella
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@22e52184b3ad6ea06a224b4278e574a9abc930cf -
Trigger Event:
push
-
Statement type:
File details
Details for the file stella_anonymize_core-2.7.8-cp311-abi3-macosx_11_0_arm64.whl.
File metadata
- Download URL: stella_anonymize_core-2.7.8-cp311-abi3-macosx_11_0_arm64.whl
- Upload date:
- Size: 27.4 MB
- Tags: CPython 3.11+, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4fabc64fcb266099b08f3e4d3b9b71bcee30ffbd5734de462073beeb5b2e25b9
|
|
| MD5 |
ca3995acae1495e010c666804eb75b37
|
|
| BLAKE2b-256 |
f42d78a8a6a122322ae3277ecd785d85f741aaa2a4d28a211498f5cc83261966
|
Provenance
The following attestation bundles were made for stella_anonymize_core-2.7.8-cp311-abi3-macosx_11_0_arm64.whl:
Publisher:
release.yml on stella/anonymize
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
stella_anonymize_core-2.7.8-cp311-abi3-macosx_11_0_arm64.whl -
Subject digest:
4fabc64fcb266099b08f3e4d3b9b71bcee30ffbd5734de462073beeb5b2e25b9 - Sigstore transparency entry: 2503998209
- Sigstore integration time:
-
Permalink:
stella/anonymize@22e52184b3ad6ea06a224b4278e574a9abc930cf -
Branch / Tag:
refs/heads/main - Owner: https://github.com/stella
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@22e52184b3ad6ea06a224b4278e574a9abc930cf -
Trigger Event:
push
-
Statement type:
File details
Details for the file stella_anonymize_core-2.7.8-cp311-abi3-macosx_10_12_x86_64.whl.
File metadata
- Download URL: stella_anonymize_core-2.7.8-cp311-abi3-macosx_10_12_x86_64.whl
- Upload date:
- Size: 27.7 MB
- Tags: CPython 3.11+, macOS 10.12+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
eb46deae7a639d17fb759c05b61a61b31739ccf4b19eea9dc80b44c13f326968
|
|
| MD5 |
837c29fb0753cea9384c8439e25f8c44
|
|
| BLAKE2b-256 |
2fa07bbf6716ed870a73b53a7421c3c719b3045f8bd7c13406852d9a4ad5f86e
|
Provenance
The following attestation bundles were made for stella_anonymize_core-2.7.8-cp311-abi3-macosx_10_12_x86_64.whl:
Publisher:
release.yml on stella/anonymize
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
stella_anonymize_core-2.7.8-cp311-abi3-macosx_10_12_x86_64.whl -
Subject digest:
eb46deae7a639d17fb759c05b61a61b31739ccf4b19eea9dc80b44c13f326968 - Sigstore transparency entry: 2503998295
- Sigstore integration time:
-
Permalink:
stella/anonymize@22e52184b3ad6ea06a224b4278e574a9abc930cf -
Branch / Tag:
refs/heads/main - Owner: https://github.com/stella
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@22e52184b3ad6ea06a224b4278e574a9abc930cf -
Trigger Event:
push
-
Statement type: