Pseudonymisation/anonymisation engine with encrypted mapping storage
Project description
cloakdf
Install
pip install cloakdf
How to use
We’ll demonstrate cloakdf using the Northwind
dataset. A classic
sample database with customers, orders, and employees tables that share
keys across them.
#| eval: false
import httpx
from pathlib import Path
base = "https://raw.githubusercontent.com/neo4j-contrib/northwind-neo4j/refs/heads/master/data"
files = ["customers.csv", "orders.csv", "employees.csv"]
data = Path("../data")
data.mkdir(exist_ok=True)
for f in files:
(data/f).write_bytes(httpx.get(f"{base}/{f}").content)
1. Define your table configuration
Each table’s config specifies two kinds of columns:
idgroups (e.g.id1,id2) — key columns that are shared across tables. Columns in the same group get consistent pseudonyms:customerIDin bothcustomersandordersmaps to the same UUID, preserving referential integrity.
Note: All id columns must be string dtype before encoding — cast
with .astype(str) if needed. - mask — sensitive columns to
replace with opaque hex tokens. These are stored in a shared vault, so
identical values (e.g. the same address appearing twice) get the same
token.
tables = {
'customers': {
'id1': 'customerID',
'mask': ['contactName', 'address', 'phone', 'companyName', 'fax', 'city']
},
'orders': {
'id1': 'customerID',
'id2': 'employeeID',
'mask': ['shipName', 'shipAddress']
},
'employees': {
'id2': 'employeeID',
'mask': ['firstName', 'lastName', 'address', 'homePhone', 'birthDate', 'notes']
},
}
2. Encode your DataFrames
import pandas as pd
from pathlib import Path
data = Path("../data")
unk = CloakDF(tables)
originals, encoded = {}, {}
for name in tables:
df = pd.read_csv(data/f"{name}.csv", on_bad_lines='skip')
for k, v in tables[name].items():
if k.startswith('id'): df[v] = df[v].astype(str)
originals[name] = df
encoded[name] = unk.encode(name, df)
encoded['customers'].head(3)
| customerID | companyName | contactName | contactTitle | address | city | region | postalCode | country | phone | fax | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 9eb2c3d4-32d3-434c-863c-44d8ac4067d9 | 9288db060f26 | 5c1dfdfc8f62 | Sales Representative | 80e2676376a1 | c1f7bd6a22f0 | NaN | 12209 | Germany | 715e53fda07f | cd0efa7df34e |
| 1 | 82d494d5-a9e7-4690-b5ec-3104dad5b87d | 18db1aa91f8c | 3c4420341c1a | Owner | 135d66291b39 | 42d81129a68e | NaN | 05021 | Mexico | a5611db2c349 | 25fabc23caa4 |
| 2 | fa465d8d-ead0-4d03-ae91-8a65715c7fd1 | 9458f0d21f98 | 08b3087d3dda | Owner | a93f1f586e03 | 42d81129a68e | NaN | 05023 | Mexico | 2a648576b9be | NaN |
3. Save encrypted mappings
The mapping dictionaries (key_maps and vault) are the sensitive
artefacts — they allow de-anonymisation. Generate a Fernet key and
encrypt them at rest. You can store the key to a file or an environment
variable:
⚠️ Never commit your Fernet key or encrypted mappings to version control. Ensure your
.gitignoreincludes key files and*.encfiles (e.g.*.key,*.enc,data/).
import os
key = CloakDF.generate_key()
unk.save(data/"mappings.enc", key)
# Optionally store the key in an environment variable
os.environ['CLOAKDF_KEY'] = key.decode()
print("✓ Mappings saved and key stored in env var")
✓ Mappings saved and key stored in env var
4. Load and decode
Load the encrypted mappings (using the key directly or from an environment variable) and reverse the encoding:
# Load key from env var (or pass `key` directly)
loaded_key = CloakDF.load_key(env_var='CLOAKDF_KEY')
# loaded_key = CloakDF.load_key(path="path/to/keyfile") # alternative: from file
unk2 = CloakDF.load(data/"mappings.enc", loaded_key, tables)
for name in tables:
decoded = unk2.decode(name, encoded[name])
pd.testing.assert_frame_equal(decoded, originals[name])
print(f"✓ {name} round-trip OK")
decoded.head(3)
✓ customers round-trip OK
✓ orders round-trip OK
✓ employees round-trip OK
| employeeID | lastName | firstName | title | titleOfCourtesy | birthDate | hireDate | address | city | region | postalCode | country | homePhone | extension | photo | notes | reportsTo | photoPath | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | Davolio | Nancy | Sales Representative | Ms. | 1948-12-08 00:00:00.000 | 1992-05-01 00:00:00.000 | 507 - 20th Ave. E. Apt. 2A | Seattle | WA | 98122 | USA | (206) 555-9857 | 5467 | 0x151C2F00020000000D000E0014002100FFFFFFFF4269... | Education includes a BA in psychology from Col... | 2.0 | http://accweb/emmployees/davolio.bmp |
| 1 | 2 | Fuller | Andrew | Vice President, Sales | Dr. | 1952-02-19 00:00:00.000 | 1992-08-14 00:00:00.000 | 908 W. Capital Way | Tacoma | WA | 98401 | USA | (206) 555-9482 | 3457 | 0x151C2F00020000000D000E0014002100FFFFFFFF4269... | Andrew received his BTS commercial in 1974 and... | NaN | http://accweb/emmployees/fuller.bmp |
| 2 | 3 | Leverling | Janet | Sales Representative | Ms. | 1963-08-30 00:00:00.000 | 1992-04-01 00:00:00.000 | 722 Moss Bay Blvd. | Kirkland | WA | 98033 | USA | (206) 555-3412 | 3355 | 0x151C2F00020000000D000E0014002100FFFFFFFF4269... | Janet has a BS degree in chemistry from Boston... | 2.0 | http://accweb/emmployees/leverling.bmp |
5. Compare encoded vs decoded
Here’s what the employees table looks like — encoded (pseudonymised) vs decoded (original):
cols = ['employeeID', 'firstName', 'lastName', 'title', 'address', 'homePhone']
display(encoded['employees'][cols].head(3))
display(decoded[cols].head(3))
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
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 cloakdf-0.0.3.tar.gz.
File metadata
- Download URL: cloakdf-0.0.3.tar.gz
- Upload date:
- Size: 10.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3eb8e3b06484c02818f1dad525d296c373c56a6e524ff750e4e7138493188f99
|
|
| MD5 |
88f86f3686243c2dd36a9a1c0680d427
|
|
| BLAKE2b-256 |
a451f6962b9b26e643512b78fb35c8ee7586880eefda09c92217397212228969
|
File details
Details for the file cloakdf-0.0.3-py3-none-any.whl.
File metadata
- Download URL: cloakdf-0.0.3-py3-none-any.whl
- Upload date:
- Size: 10.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5ee4c61f349cfec08026e6484cd638acbd7dce3f5d88cde93b6f60dc6ae40cc6
|
|
| MD5 |
e39bcc04f43680b33a0191bb1fd20a08
|
|
| BLAKE2b-256 |
99d4b07c7782f80704764b473076253a23d44921cb0c77059ba0dab9d27d36b8
|