glpi-anonymizer
Strip personal data out of GLPI ticket exports — with real Moroccan identifiers (CIN, RIB, ICE, GSM) and check-digit validation.
No dependencies. Nothing leaves your machine.
The problem
You want to analyse your helpdesk tickets — train a classifier, measure resolution times, share a sample with a vendor. The tickets are full of phone numbers, ID card numbers and bank details typed in by users, so you cannot.
Generic PII tools know about US Social Security numbers and miss a Moroccan CIN entirely. Tools that do match local formats tend to match too much: any 24-digit string becomes a "bank account", and your asset tags get shredded.
The approach: validate, don't just match
Every identifier that carries a check digit is verified before it is treated as personal data:
| Identifier | Shape | Validated |
|---|---|---|
| RIB (Moroccan bank account) | 24 digits | ✅ mod-97 check key |
| IBAN | MA + 26 chars |
✅ ISO 7064 MOD-97-10 |
| Card number | 12–19 digits | ✅ Luhn |
| ICE (company id) | 15 digits | pattern |
| CIN (national ID) | 1–2 letters + 5–6 digits | pattern |
| GSM / landline | +212 / 0 + 5,6,7 + 8 digits |
pattern |
| Email · URL · IPv4 | — | pattern (IPv4 range-checked) |
The effect: of 200 arbitrary 24-digit numbers, at least 190 are correctly left alone, because only about 1 in 97 can carry a valid key. Your inventory references survive; the bank details do not.
Installation
pip install glpi-anonymizer
Use it from the command line
# Straight redaction
glpi-anonymize tickets.json -o clean.json
# Keep records linkable without revealing anything
export GLPI_ANONYMIZER_KEY="a long random secret"
glpi-anonymize tickets.json -s pseudonymize -o clean.json --stats
# Leave a recognisable tail for support staff
glpi-anonymize tickets.json -s partial --keep 4 -o clean.json
It reads either a plain list of tickets or GLPI's {"data": [...]} envelope,
and writes the same shape back. - reads stdin.
Use it from Python
from glpi_anonymizer import Anonymizer
anonymizer = Anonymizer()
result = anonymizer.anonymize("Call A123456 on 0612345678 or a.b@example.ma")
print(result.text)
# Call [CIN] on [PHONE] or [EMAIL]
print(result.counts_by_label())
# {<Label.CIN: 'CIN'>: 1, <Label.PHONE: 'PHONE'>: 1, <Label.EMAIL: 'EMAIL'>: 1}
clean_tickets, matches = anonymizer.anonymize_tickets(tickets)
Three replacement strategies
| Strategy | 0612345678 becomes |
Use when |
|---|---|---|
Redact() (default) |
[PHONE] |
you just need the data gone |
Pseudonymize(key=...) |
[PHONE:4f2a9c1b] |
you still need to count per person |
PartialMask(keep=4) |
******5678 |
a human has to recognise the record |
Pseudonymize is an HMAC of the normalized value, so the same number always
yields the same token — across tickets, across files, across runs — while the
original cannot be recovered. Normalisation means 06 12 34 56 78 and
0612345678 collapse to one token.
Keep the key secret. Identifier spaces are small enough to enumerate: anyone holding the key can confirm whether a given CIN is in your dataset.
Works with glpi-rest
Pull tickets and clean them in one pass:
from glpi_rest import GLPIClient
from glpi_anonymizer import Anonymizer
anonymizer = Anonymizer()
with GLPIClient(url, app_token=..., user_token=...) as glpi:
tickets = glpi.get_items("Ticket", range="0-999")
clean, matches = anonymizer.anonymize_tickets(tickets)
print(f"{len(matches)} values removed from {len(clean)} tickets")
How it works
graph LR
IN["Ticket text<br/><i>Call A123456 on 0612345678</i>"]
subgraph DETECT["1 · Detect"]
PAT["Patterns<br/>CIN · GSM · RIB · ICE<br/>IBAN · card · email · IP · URL"]
CHK["Check digits<br/>mod-97 · Luhn<br/><b>rejects look-alikes</b>"]
end
RES["2 · Resolve overlaps<br/>longest match wins<br/>RIB before ICE"]
subgraph REPL["3 · Replace"]
R1["Redact<br/>[PHONE]"]
R2["Pseudonymize<br/>[PHONE:4f2a9c1b]"]
R3["PartialMask<br/>******5678"]
end
OUT["Clean text + audit trail<br/><i>Call [CIN] on [PHONE]</i>"]
IN --> PAT --> CHK --> RES
RES --> R1 & R2 & R3 --> OUT
style DETECT fill:#e8f0fe,stroke:#1a73e8,stroke-width:2px
style REPL fill:#e6f4ea,stroke:#1e8e3e,stroke-width:2px
style CHK fill:#fce8e6,stroke:#d93025,stroke-width:2px
style OUT fill:#fef7e0,stroke:#f9ab00,stroke-width:2px
Overlaps are resolved before anything is replaced: the longest match wins, ties go to the higher-priority detector. That is why a 24-digit RIB is never carved up into a 15-digit "ICE".
Anonymizing an already-anonymized export is a no-op, so re-running the tool is safe.
Limitations — read before relying on this
This is a deterministic pattern matcher, not a language model. It does not detect:
- Names of people, which need named-entity recognition;
- addresses written in prose;
- personal details described rather than written as an identifier ("the manager on the third floor");
- identifiers split across lines or mangled by copy-paste.
Treat it as a strong first pass that removes structured identifiers reliably, not as a guarantee that a document is anonymous. Review a sample of the output before publishing anything. Under GDPR and Moroccan law 09-08, pseudonymized data is still personal data.
Security
No network access, no telemetry, no dependencies — the whole pipeline runs locally and the source is short enough to audit. See SECURITY.md.
Contributing
New detectors for other Maghreb identifiers are very welcome — see CONTRIBUTING.md.
git clone https://github.com/novagiosAI/glpi-anonymizer.git
cd glpi-anonymizer
python -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e ".[dev]"
pytest
License
Apache License 2.0 — free for commercial use.
Built and maintained by Novagios — IT services, AI and ITSM.
Metadata
Release files for glpi-anonymizer 0.1.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 | |
|---|---|---|---|---|
| glpi_anonymizer-0.1.0.tar.gz | 21.9 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| glpi_anonymizer-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 39.8 kB
Release files / glpi_anonymizer-0.1.0.tar.gz
| Download URL | glpi_anonymizer-0.1.0.tar.gz |
|---|---|
| Size | 21.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
15b2cb6f6e71e5a2b9e0b1b961d37e82f9faff23174cc2dae6ec173531917285
|
|
BLAKE2b-256 checksum How to use checksums |
ddefac2cb1a4b4f6d6431d7795a1e1a9e7e84a1ded4155f924f85fa8c4a56067
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.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 Oct 11, 2026.
Transparency logRelease files / glpi_anonymizer-0.1.0-py3-none-any.whl
| Download URL | glpi_anonymizer-0.1.0-py3-none-any.whl |
|---|---|
| Size | 17.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3895b505a9fbfbf255eb33b2948acf8cfbd860a81ed399ffb3cffe7ac2b581c9
|
|
BLAKE2b-256 checksum How to use checksums |
add5b2cbd8976ca056a73a06044b19ca2cdddd9afaa4605cc67d6da8730578cb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.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 Oct 11, 2026.
Transparency logSecurity for glpi-anonymizer 0.1.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 glpi_anonymizer-0.1.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 Oct 11, 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.