Edge

Introduction

Dedomena Edge is the on-premise edition of the Dedomena.AI platform: a Python library you install on your own servers to check the quality of your datasets, detect sensitive data, anonymize it and train synthesizers for synthetic data, without your data ever leaving your infrastructure.

This guide explains how to download and install Dedomena Edge, how it is organized, what you need to run it and how to use each of its features. At the end you will find a reference manual with every command, variable and error message.

> Your data stays in your environment. Edge only connects to Dedomena.AI to download the package and the models. Detection, anonymization, training and validation always run on your machine, and the licence is verified locally, offline.

What Dedomena Edge includes

Edge brings three libraries together in a single Python package, dedomena:

LibraryWhat it is forMain functions
Axon (dedomena.axon)Checking and preparing a dataset before using itInspector, clean, ApplyRules, report, risk, insight
Detection (dedomena.detection)Finding what is sensitive in an asset, and wheredetect, detect_in_text, detect_columns
Nucleus (dedomena.nucleus)Changing the data: anonymizing it, or training synthesizers for synthetic dataAnonymization, Generic, Transactional, TimeSeries, Relational, load_synthesizer

Together they cover every kind of asset:

AssetDetectAnonymizeTrain synthesizersValidate with Axon
Tables (CSV, Parquet, Excel, databases)✔✔✔✔
Images (PNG, JPG, TIFF, BMP)✔✔——
Documents (PDF, DOCX, TXT)✔✔——
Audio (WAV, MP3, M4A, WMA)✔✔——
Video (MP4, MOV, AVI)✔———

Anonymization method names, sensitive element types and synthesis algorithms are the same as in the Dedomena.AI cloud platform. A configuration written for the cloud works the same way in Edge.

Architecture and requirements

How an installation is organized

An Edge installation has three parts, and all three live in your infrastructure:

  • The library is the code you run from Python. It is installed in a virtual environment and processes your data locally.

  • The models are the AI models used by detection. They are downloaded separately from the code and stored in a local folder. You can choose their size, or use none at all (see Model size: small, full or none).

  • The licence is a signed token you get from the Edge page of the platform. The library verifies it locally, without connecting to anything.

The Dedomena.AI platform is only involved in downloading the package, the models and the token from the Edge page, and in giving you the results once you upload to it the encrypted files your workflows produce: for example, the synthetic data generated from a synthesizer you trained with Edge (see Generating data).

What leaves your machine and what does not:

WhenConnection to Dedomena.AI
Installing with pipYes: the package and its dependencies are downloaded.
Installing from the full downloadNo: the downloaded folder carries everything needed.
Downloading the models (dedomena-edge setup)Yes: the models are downloaded.
Detecting, anonymizing, training, validatingNo. Everything runs locally.
Generating synthetic dataYes: you upload the synthesizer to the Dedomena.AI platform and generate the data there, or through the API (see Generating data).
Checking the licenceNo: it is verified locally.
Errors and diagnosticsNo: they are written to local files. You only send them to support if you need to (see Messages, logs and support).

Modules

The package is delivered in modules: you choose the ones you need and only those are installed. You can choose several at once (for example, Text + Vision).

ModuleWhat it includesAlso includes
Tables (tables)Detection of sensitive columns, table anonymization, and synthesizer training and evaluationAxon
Text (text)Detection and anonymization of free text with the language model, including the text columns of tables and the methods that need that modelTables and Axon
Vision (vision)Image detection and anonymization: faces, signatures, logos, fingerprints and the text inside the imageAxon
Audio (audio)Transcription, detection of data spoken out loud and audio anonymizationAxon
Documents (documents)Detection and anonymization of PDF, DOCX and TXTAxon
Axon (axon)Dataset inspection, cleaning, rules and quality report— (always included)
All (all)All the modules above—

A few things to keep in mind:

  • Axon is always included, whatever you choose.

  • Text includes Tables: the language model is also used on the free-text columns of tables.

  • If you try to use something your package does not include, the library does not fail with a Python error: it tells you that module is not part of your installation and which modules it does include.

Model size: small, full or none

The code package is the same for every size. The size only decides which models are downloaded:

SizeWhat it isWhere it runs
smallLightweight models. They cover all five modalities with just over 2 GB in total. The recommended option to start with.CPU (a GPU is optional)
fullModels with the highest detection quality.Designed for an NVIDIA GPU (the text and vision ones require it)
noneNo models. Detection uses patterns and validations only (emails, Spanish IDs, IBAN, cards, phone numbers…). It does not find names or companies in free text.CPU

Models downloaded for each module and size:

Modulesmallfullnone
TablesLightweight text detection (0.18 GB)Lightweight text detection (0.18 GB)✔ available
DocumentsLightweight text detection (0.18 GB)Lightweight text detection (0.18 GB)✔ available
TextLightweight text detection (0.18 GB)Full text model (28.7 GB) · GPU✔ available
VisionLightweight image detection, signature detection and lightweight text detection (≈1.5 GB)Full image model, resolution enhancement, signature detection and lightweight text detection (≈36.5 GB) · GPU✘ not available
AudioLightweight transcription and lightweight text detection (≈0.85 GB; ≈2.7 GB in its GPU variant)Full transcription and lightweight text detection (≈0.7 GB) · runs on CPU✘ not available
All≈2.2 GB≈65.5 GB · GPU✘ not available
Axon onlyUses no modelsUses no models—
  • With several modules, the union of their models is downloaded, without repeats. When the full model of a type is included, the lightweight model of that same type is not downloaded as well.

  • Vision and audio always need a model. Without one there is no image detection and no transcription, so the none size is not offered with those modules.

  • Generative image editing (optional). An additional model of about 57.7 GB that replaces the data in an image with other data that looks natural, instead of drawing it on top. Only available with the Vision module and GPU hardware.

System requirements

Operating system and Python

RequirementValue
Operating systemLinux x86_64 with glibc 2.28 or later (RHEL/Rocky/Alma 8 or later, Ubuntu 20.04 or later, Debian 10 or later). Windows and macOS are not supported yet.
Python3.10, 3.11 or 3.12 (the same package works for all three). If your distribution ships another version, install one of these first.
EnvironmentA dedicated virtual environment for Edge is recommended (the installer creates one if needed).

CPU and memory

UseRecommendation
Tables, Axon, documents and small modelsMulti-core CPU (8 or more cores recommended) and 32 GB of RAM (16 GB minimum)
Training synthesizers on large tablesMore cores and more RAM speed up training. A GPU is optional and speeds it up too.

GPU (only for gpu hardware)

RequirementValue
CardNVIDIA GPU
DriverNVIDIA 580 or later (CUDA 13). The installer does not check it: verify it beforehand with nvidia-smi.
full text and vision modelsAda Lovelace, Hopper or Blackwell architecture. Video memory: 48 GB minimum (with a reduced context for the image model), 80 GB recommended and 96 GB to keep the text and image models loaded at the same time. With less memory, the library loads them in turns.
Generative image editingGPU with 80 GB of memory or more.
small modelsNo GPU needed. If there is one, transcription can use it (GPU variant) and the others can be moved to it (see Registering a model explicitly).

Before loading any model you can check whether they fit on your card with models.report() (see Which engine will be used).

Disk

  • Library and dependencies: a few GB (more in the GPU variant, which includes the NVIDIA libraries).

  • Models: as shown in the table in Model size: small, full or none. Leave extra room for your data and results.

Network

  • Installing with pip: outbound HTTPS to api.dedomena.ai and to the public Python package repositories during installation.

  • Downloading models with dedomena-edge setup: outbound HTTPS to api.dedomena.ai and to the download servers it redirects to. Ask Dedomena.AI for the list of domains if your firewall works with an allowlist.

  • Installing from the full download: the target machine needs no network.

  • Usage: no network needed.

Which combination to choose

If you want to…ModulesHardwareSize
Anonymize tables and train synthesizersTablesCPUsmall (or none if your columns have no free text with names)
The above, plus finding names and companies in free text with the highest qualityTextGPUfull
Anonymize ID cards, passports, scanned contracts or photosVision (+ Documents)CPUsmall
The same, with the highest detection qualityVision (+ Documents)GPUfull
Anonymize recorded callsAudioCPUsmall or full
Only validate and clean datasetsAxonCPU—
EverythingAllGPUfull

Download and installation

There are two ways to install Edge. Both start from the Edge page of the Dedomena.AI platform:

Full downloadInstall with pip
HowYou download a folder with the package, all its dependencies, the models and an installerYou install the package with pip from the Dedomena.AI index and download the models with a command
Network on the target machineNot neededYes, during installation
Recommended forServers without internet access, regulated environments, reproducible installationsMachines with internet access, quick updates

> You need a platform account with a plan that includes Dedomena Edge. If your plan does not include it, the download replies that you have no access to Edge: contact Dedomena.AI.

The Edge page

edge_full_en.png

The page has three blocks, from top to bottom:

  1. The package options: operating system, modules, hardware and model size.

  2. Your licence token.

  3. The two ways to install: Download and Install with pip.

Choosing the options

  1. Operating system: Linux.

  2. Modules: select one or more. Modules included with another one are selected automatically and locked (for example, selecting Text selects Tables). Axon is always selected. All installs everything.

  3. Hardware:

    • CPU: for machines without a GPU. Installs the lightweight CPU build of the computing libraries.

    • GPU: for machines with an NVIDIA GPU (CUDA 13, driver 580 or later).

  4. Model size: small, full or none (see Model size: small, full or none). The page disables the combinations that are not possible:

    • full with CPU hardware, if you choose Text, Vision or All (those models need a GPU);

    • none, if you choose Vision, Audio or All (they need a model).

  5. Generative image editing (optional): only shown with Vision and GPU hardware.

Below the download button, the page shows what the download contains and how much the models weigh.

Your licence token

edge_token_en.png

The token is your Edge licence. You need it to install, and the library uses it every time you work with it. It is personal: do not share it or commit it to a code repository.

  • Show displays the token and Copy copies it.

  • Generate new token generates a new one. Use it when the current one expires.

  • Valid until shows the expiry date.

The installation commands on the page already include your token. More details in The licence.

Option A: full download (no internet on the target machine)

edge_download_en.png

  1. Click Download and choose a folder on your computer.

    • In Chrome and Edge, the browser writes the files directly into that folder, checks each one and, if the download is interrupted, you can resume it where it stopped.

    • In Firefox and Safari, the download is saved as a single .zip. If it is interrupted, it has to start over. With full models (tens of GB) we recommend Chrome or Edge.

  2. When it finishes you have a dedomena-edge folder with these contents:

    ItemContents
    install.shThe installer.
    wheels/The Edge package and all its dependencies, for Python 3.10, 3.11 and 3.12.
    models/The chosen models. Not present with the none size.
  3. Copy the folder to the machine where you will use Edge, by whatever means you normally use (SFTP, disk, internal repository…). If you downloaded a .zip, extract it there.

  4. Install:

    bash
    
    cd dedomena-edge
    
    bash install.sh \<TOKEN\>
    
    . ./env.sh          # in every new session, before using the library
    
    

What install.sh does:

  1. Chooses where to install (see the table below) and checks that the Python version is 3.10, 3.11 or 3.12.

  2. Installs Edge from ./wheels, without an internet connection.

  3. Writes env.sh, which activates the virtual environment (if it created one) and points the library to the ./models folder (if there are models).

  4. Saves your licence on the machine, so the library finds it without you having to pass it again.

  5. Runs dedomena-edge status, which shows the installed modules and models.

Where it installs:

CommandWhere it installs
bash install.sh \<TOKEN\>In the Python environment you have active (venv or conda). If none is active, it creates a new one in ./venv.
bash install.sh \<TOKEN\> --venvAlways in a new environment: ./venv, or another path with VENV=/path bash install.sh \<TOKEN\> --venv.
bash install.sh \<TOKEN\> --no-venvAlways in the active environment, without creating another one.

Additional options:

  • PYTHON=/path/to/python3.11 bash install.sh \<TOKEN\> uses a specific interpreter.

  • The token can also be passed in the DEDOMENA_TOKEN variable instead of as an argument.

  • bash install.sh --help shows the help.

> Watch out with conda. If your terminal activates conda's base environment automatically, the installer treats it as the "active environment" and installs there. Use --venv if you prefer a separate environment.

> Do not move the folder after installing. The virtual environment and env.sh use absolute paths, and the models stay in ./models. If you need another location, copy the folder to its final place before running install.sh.

If something fails during installation:

MessageWhat to do
ERROR: these packages are for Python 3.10 3.11 3.12 on Linux x86_64, and none was foundInstall Python 3.10, 3.11 or 3.12, or point to it: PYTHON=/path/to/python bash install.sh \<TOKEN\>.
ERROR: … cannot create virtual environments: the venv module is missing.On Debian/Ubuntu install the package the message names (for example apt install python3.11-venv), or activate an environment you already have and use --no-venv.
ERROR: the active environment (…) is Python X.Y; these packages are for Python 3.10 3.11 3.12.Activate an environment with a supported version, or use --venv.
ERROR: unknown option: …Check the command. bash install.sh --help shows the valid options.
Error while saving the licence (expired or invalid token)The library is already installed. Generate a new token on the page and save it as explained in Saving the licence on the machine.

Option B: install with pip

edge_pip_install_en.png

  1. Create and activate a virtual environment for Edge:

    bash
    
    python3.11 -m venv /opt/dedomena/venv
    
    . /opt/dedomena/venv/bin/activate
    
    
  2. Install the package with the command shown on the page. It already carries your modules, your hardware and your token:

    bash
    
    pip install --index-url "https://licence:\<TOKEN\>@api.dedomena.ai/edge/\<modules\>/\<hardware\>/simple/" dedomena-edge
    
    
    • \<modules\>: all, one module (tables, text, vision, audio, documents, axon) or several joined with + (for example text+vision). The order does not matter.

    • \<hardware\>: cpu or gpu.

    • The token is the index password; the user name (licence) is ignored.

    With gpu hardware and full models, also install the GPU engine used by the full models:

    bash
    
    pip install --index-url "https://licence:\<TOKEN\>@api.dedomena.ai/edge/\<modules\>/gpu/simple/" "dedomena-edge[gpu]"
    
    
  3. Download the models and save the licence on the machine:

    bash
    
    dedomena-edge setup --size small --token "\<TOKEN\>"     # or --size full / --size none
    
    

    setup reads which modules your installation has and downloads the models they need for that size. If a download is interrupted, run the same command again: it resumes where it stopped. Each file is verified when it finishes, and a half-downloaded model is never used.

  4. Check the installation:

    bash
    
    dedomena-edge status
    
    

dedomena-edge setup options:

OptionWhat it does
--size small|full|noneRequired. The model size. With none nothing is downloaded, but the licence is saved.
--token "\<TOKEN\>"Saves the licence on this machine.
--gpu auto|yes|noDefault auto: detects whether there is an NVIDIA GPU. With a GPU, the GPU variants are downloaded where they exist. Use yes if you are downloading the models for another machine that does have a GPU.
--image-editAlso downloads the generative image editing model.
--dir /pathDownloads the models to another folder (default ~/.local/share/dedomena/models). If you use it, export DEDOMENA_MODELS_DIR=/path afterwards.
--type text|vision|audioDownloads only the models of that type. Can be repeated.
--forceDownloads again even if they are already there.
--quietNo progress bar.
--workers NParallel downloads (8 by default).

setup messages you may see:

MessageMeaning
These models need an NVIDIA GPU and this machine has none: …You asked for the full text or vision models on a machine without a GPU. Use --size small, or --gpu yes if you are downloading them for another machine.
These modules need a model: vision, audio. Use --size small--size none is not available with Vision or Audio.
No models: detection uses patterns only …With --size none: the licence is saved and nothing is downloaded.
The full models run on the GPU engine, which is an optional extra …Install dedomena-edge[gpu] with the command from step 2.
The model server refused the request (403): …Your plan does not include Edge, or your user is disabled.
Could not reach the model server …Check the connection and the firewall (see System requirements).
Download kept failing … run it again to resumeRun the same command again: the download resumes.

Common pip errors:

SymptomCause
pip asks for a user name and password, or replies 401The token is invalid or has expired. Generate a new one on the page.
403Your plan does not include Edge.
404The module or hardware combination in the URL does not exist. Copy the command exactly as shown on the page.
No matching distributionYour Python is not 3.10, 3.11 or 3.12, or your system is not Linux x86_64.

Checking the installation

bash

dedomena-edge status

code

dedomena-edge 5.6.2

Modules    : axon, small, tables, text

Models dir : /home/user/.local/share/dedomena/models

GPU        : no

  small-text       1.0.0

  • Modules shows the modules of the installed package. small is the code for the lightweight models, which comes with every package that includes detection.

  • Models dir is the folder where the library looks for the models.

  • Below, one line per installed model, with its version.

To see the licence:

bash

python -c "import dedomena; print(dedomena.auth_status())"

And to see which detection engine each modality will use and whether the models fit on your GPU:

bash

python -c "from dedomena.detection import models; print(models.report())"

Updating, extending or changing the installation

I want to…What to do
Install a new version of EdgeFull download: download again and run install.sh in the new folder. pip: repeat the installation command with --upgrade.
Add modulesThe package is different for each combination of modules: download or install the package with the new combination in a new environment (or with pip install --upgrade --force-reinstall in the same one). Then run dedomena-edge setup to download any missing models.
Change the model sizededomena-edge setup --size full (or small). It only downloads what is missing.
Renew the licenceGenerate a new token on the page and save it (see Saving the licence on the machine). No need to reinstall.
Use the models from another folderMove the models folder and export DEDOMENA_MODELS_DIR=/new/path.
Prepare the models for a machine without internetOn a machine with internet: dedomena-edge setup --size full --gpu yes --dir /path/models. Copy that folder to the target machine and export DEDOMENA_MODELS_DIR there.

The licence

The licence is the token from the Edge page. It is a signed token that carries your identifier, your permissions and its expiry date. The library verifies it locally, without connecting to anything, so it works on machines without internet access.

Saving the licence on the machine

install.sh and dedomena-edge setup --token already save it for you. If you need to do it by hand (for example, when renewing it):

python

import dedomena

dedomena.login("\<TOKEN\>", persist=True)   # verifies and saves it

dedomena.auth_status()

# Session(subject='...', scopes=('axon', 'detection', 'nucleus.anonymize', ...), expires=..., source=...)

From then on you do not need to pass the token to any function: they all find it on their own.

  • persist=True saves the token in ~/.config/dedomena/token, readable only by your user. Without persist=True, login() applies to the current Python session and writes nothing to disk.

  • dedomena.logout() closes the session.

Where the library looks for the token

In this order:

  1. The token= argument of the function you call.

  2. The session opened with dedomena.login(...).

  3. The DEDOMENA_TOKEN environment variable.

  4. The saved licence file (~/.config/dedomena/token).

Containers, CI and secret managers. If your policy does not allow credentials to be stored on disk, pass the token through the DEDOMENA_TOKEN variable and set DEDOMENA_NO_PERSIST=1: the library will not write anything to the user's home directory. DEDOMENA_CONFIG_DIR changes the folder of the licence file.

Permissions

The token states what you can do:

PermissionAllows
axonValidating and preparing datasets
detectionUsing detection directly and downloading models
nucleus.anonymizeAnonymizing and reversing anonymizations
nucleus.synthesisTraining synthesizers

If you try something your licence does not cover, the library says so, naming the activity: Your Dedomena licence does not cover training a synthesizer. It covers: …. In that case, ask Dedomena.AI for the corresponding permission.

Expiry and renewal

  • The expiry date is shown on the page (Valid until) and in dedomena.auth_status().

  • 15 days before it expires, the library warns: Warning: Your Dedomena token expires on … (N day(s) left).

  • Once it expires, functions stop running with the message Your Dedomena token expired on …. Generate a new token on the page and save it with dedomena.login("\<NEW_TOKEN\>", persist=True). No need to reinstall anything.

  • Turning the system clock back does not extend the licence.


Getting started with the library

Before you start

If you installed with the full download, load the environment in every new session:

bash

. /path/to/dedomena-edge/env.sh

Then:

python

import dedomena

print(dedomena.__version__)

from dedomena.nucleus import anonymize            # anonymization

from dedomena.nucleus import Auto, Generic        # synthesizers

from dedomena.detection import detect             # detection

from dedomena.axon import Inspector, clean        # data quality

import dedomena loads nothing heavy: each library is loaded the first time you use it.

Data sources

Every function that takes data accepts the same sources:

SourceHow to specify it
FileThe path: "data/customers.csv". The type is inferred from the extension.
CSV.csv. The separator and the encoding are detected automatically.
Parquet.parquet
Excel.xlsx
Sparse matrix.mtx, or source_format="MTX"
pandas DataFrameThe DataFrame itself.
DatabaseThe connection URL, with source_format="DATABASE" and query= (a table name or an SQL query).
Image, document, audio, videoThe file path (see the formats in What Dedomena Edge includes).

Databases. PostgreSQL, MySQL/MariaDB, SQL Server, Oracle, SQLite and IBM Db2 are supported:

python

anonymize(

    "postgresql://user:password@server:5432/crm",

    source_format="DATABASE",

    query="SELECT * FROM customers WHERE signup \>= '2026-01-01'",

    config="auto",

    output="output/customers_anonymized.parquet",

    output_format="PARQUET",

)

If query is a single word, it is read as a table name (SELECT * FROM "customers").

IBM Db2. Download IBM's JDBC driver (db2jcc4.jar) and copy it into the DB2Driver folder inside the installed package. You also need a Java runtime on the machine:

bash

python -c "import dedomena, os; print(os.path.join(os.path.dirname(dedomena.__file__), 'DB2Driver'))"

The URL looks like this:

text

jdbc:db2://\<host\>:\<port\>/\<database\>:sslConnection=true;user=\<user\>;password=\<password\>;

Db2 JDBC drivers: <https://www.ibm.com/support/pages/db2-jdbc-driver-versions-and-downloads\>

The asset (Asset)

Almost every function returns an asset (dedomena.core.Asset): an object that carries the data and what is known about it.

AttributeWhat it holds
contentThe data: a DataFrame, or the path of the file written.
kindtable, image, document, audio or video.
sensitive_elementsThe sensitive elements found: [\{'value', 'type', …location\}].
schema, quality, infoWhat Axon has learned about the dataset.
lineageThe history of operations applied (inspection, cleaning, anonymization, synthesis…).
ai_generatedWhether any operation generated new content (see Generated-content marking).

You can pass an asset from one function to the next: Axon prepares, detection finds, Nucleus anonymizes or synthesizes, and the history is kept. To save it:

python

asset.save("output/", data_format="CSV")   # output/data.csv + output/meta.json

Messages, logs and support

The library does not show Python tracebacks. When something fails, you see a single line:

code

Dedomena error [3f9a1c02]: Failed to train the synthesizer.

Details were written to /path/logHistory.log

FileWhat it containsFor whom
logHistory.logThe run log: what was done, and one line per error with its identifier. Written to the results folder or to the current folder.For you
dedomena-support.encThe technical diagnostics of the errors, encrypted so that only Dedomena.AI support can read them. It does not contain your data: values are removed before encryption.For sending to support if you need to
  • DEDOMENA_LOG_FILE=/path/my_log.log changes the location of the log. Useful with several jobs in parallel.

If you need help, send support the error identifier ([3f9a1c02]), logHistory.log and, if present, dedomena-support.enc.


Sensitive element detection

Detection finds the sensitive data in an asset and says where it is. Anonymization runs it on its own when needed, but you can also use it directly: to audit what data you hold, to review the findings before anonymizing, or to feed your own processes.

detect()

python

from dedomena.detection import detect

detect("documents/id_card.png")       # image

detect("documents/contract.pdf")      # PDF, DOCX or TXT

detect("calls/support.mp3")           # audio

detect(dataframe)                     # table: which column is of which type

detect("My DNI is 12345678Z")         # free text

It returns a list of elements with the same shape for every modality: the value, its type and where it is.

AssetLocationExample
Textstart, end (character positions)\{'value': '12345678Z', 'type': 'dni', 'start': 10, 'end': 19\}
Tablecolumn\{'value': None, 'type': 'email', 'column': 'mail'\}
Imagebox (pixels: x0, y0, x1, y1)\{'value': '', 'type': 'face', 'box': (120, 80, 260, 240)\}
PDFpage and box\{'value': 'Ana García', 'type': 'name', 'page': 0, 'box': (…)\}
DOCXparagraph
Audiostart, end (seconds)\{'value': '612 345 678', 'type': 'phone_number', 'start': 12.4, 'end': 15.1\}
Videostart, end and box

It requires the detection permission. If your package does not include the asset's modality, the function tells you so with the message Detection for '…' is not part of this build.

How detection works

Edge combines several layers, and they always work together:

  • Patterns and validations (always on): emails, phone numbers, Spanish DNI/NIE/CIF with their check letter, IBAN with its check digits, cards with their verification algorithm, licence plates, IP addresses… They win any overlap: a number that passes its validation is a fact.

  • AI models (depending on the installed size): they find what no pattern can reach, such as names, companies, free-form addresses, special categories of data (health, religion, trade union membership…), faces, signatures, logos and fingerprints.

  • Reading text in images (in Vision and in scanned PDFs): the text in the image is read and analysed like any other text, in Spanish, English, Basque, Catalan and Galician.

  • Transcription (in Audio): the audio is transcribed with per-word timestamps, and detection runs on the transcript. Numbers dictated in words ("six, one, two…") are also recovered when they pass their validation.

The library automatically picks the best engine installed for each modality (see Models: automatic, registered and your own). The shape of the result does not change with the engine; only the coverage does.

> No image model, no image detection. If your package includes Vision and you have not downloaded any model, the first image fails with a message that gives the command: dedomena-edge setup --size small. That way an image is never delivered after being only half-checked.

Sensitive element types

Each element has a type: the code you use in the anonymization configuration. The types are the same as in the cloud platform.

CategoryCodes
Identityname, first_name, last_name, birthdate, age, gender, nationality, dni, nie, nif, vat, ssn, passport_number, document_support_number, health_card
Identity (visual)face, signature, fingerprint
Address and locationaddress, street_address, street_name, city, postcode, country, country_code, coordinate, latitude, longitude
Networkemail, company_email, url, domain_name, hostname, ipv4, ipv4_private, ipv4_public, ipv6, mac_address
Telecommunicationsphone_number, msisdn, country_calling_code
Banking and financialiban, bban, aba, swift, swift8, swift11, bank_country, credit_card_number, credit_card_full, credit_card_expire, credit_card_provider, credit_card_security_code
Company and brandcompany, logo (visual)
Vehiclelicense_plate, vin
Barcodesean, ean8, ean13, localized_ean, localized_ean8, localized_ean13
Credentialspassword, pin, api_key, access_token, refresh_token, private_key, secret
Special categories and healthreligion, ethnicity, sexual_orientation, political_opinion, trade_union, medical_condition, mental_health, biometric_data, genetic_data

To limit text detection to specific types:

python

from dedomena.detection import detect_in_text

detect_in_text("Call 612 345 678 or write to ana@example.com", types=["phone_number"])

detect_in_text(text, certain_only=True) returns only the values that passed a validation (check letter, check digits…).

Tables: which column is of which type

python

from dedomena.detection import detect_columns

detect_columns(dataframe)

# [\{'value': None, 'type': 'email', 'column': 'mail'\}, \{'value': None, 'type': 'dni', 'column': 'document'\}, ...]

It needs the Tables module. Columns are classified by their name and by a sample of their values.

Reviewing and reusing a detection

You can detect, review the result (or have a person review it) and then anonymize exactly what was reviewed, without detecting again:

python

from dedomena.core import Asset

from dedomena.detection import detect

from dedomena.nucleus import anonymize

asset = Asset("documents/id_card.png")

asset.sensitive_elements = detect(asset)

asset.save_elements("review/id_card.elements.json")      # JSON to review or edit

# ... review ...

reviewed = Asset.load_elements("review/id_card.elements.json")

anonymize(reviewed, config="auto", output="output/id_card_anonymized.png")

An asset with its elements assigned is considered already reviewed: anonymization does not detect again and does not overwrite the review.

Audio in batches

To process many audio files, use the batch functions. Each file writes its own result, so a long run can be resumed:

python

from dedomena.detection import audio

paths = audio.discover("/data/calls", recursive=True)

results = audio.detect_many(

    paths,

    output_dir="/data/output/detections",     # one \<audio\>.detection.json per file

    types=\{"iban", "credit_card_number", "credit_card_security_code", "credit_card_expire"\},

    resume=True,                              # skips files already processed

    return_transcript=True,

    workers=1,

)

audio.write_summary(results, "/data/output/detections")

For a single file:

python

aligned, unaligned, transcript = audio.transcribe_and_detect(

    "call.mp3", language="es", return_transcript=True)

  • aligned are the elements with their time range.

  • unaligned are the detected values that could not be placed in time. They are returned separately so they are never silently lost.

  • If you keep the transcript and its timestamps, you can repeat detection with other types without transcribing again: audio.detect_in_transcript(transcript, words, types=…).

  • device="auto" (default) uses the GPU for lightweight transcription if available. device="cpu" forces the CPU.


Anonymization

Anonymization modifies the sensitive parts of your real data and leaves everything else exactly as it was. The result is a new anonymized file. It requires the nucleus.anonymize permission; the detection it runs internally does not need the detection permission.

First example

python

import dedomena

from dedomena.nucleus import anonymize

result = anonymize(

    "data/customers.csv",

    config="auto",                                   # detects and applies each element's default method

    output="output/customers_anonymized.csv",

    output_dir="results",                            # mapping, run log and conformance report

)

print(result.content)    # 'output/customers_anonymized.csv'

anonymize() works the same way with a table, an image, a document or an audio file: the type is inferred from the source.

anonymize() parameters:

ParameterDefaultDescription
source—Path, DataFrame, database URL or Asset.
config"auto"Which method to apply to each column or element (see The configuration).
output\<name\>_anonymized.\<ext\> in the current folderWhere to write the result. We recommend always setting it.
output_formatfrom the extensionFor tables: CSV, PARQUET, EXCEL or DATAFRAME (returns the result in memory, in result.content, without writing a file).
output_dir"results"Folder for the reversal mapping, the run log and the conformance report.
source_format, query—For databases ("DATABASE" and the query) or to force a file's format.
country"Spain"Country of the data (in English): decides the format of the generated values (names, IDs, IBANs, phone numbers…).
sensitive_elements—Elements already detected (by you or in a review). When given, detection does not run again. For tables it also accepts \{column: type\}.
image_options—Image options (see Images).
audio_options—Audio options (see Audio).
noise_level0.05Default noise for perturb.
seeds—Seeds to make the generated values reproducible.
verboseTrueShow progress.

It returns an Asset whose content is the path written (or the DataFrame, with output_format="DATAFRAME") and whose history records the anonymization.

The configuration

The configuration says what to do with each column (in tables) or with each element type (in images, documents and audio).

Automatic mode. config="auto" detects the sensitive elements and applies each one the default method of its type (see Which methods each type accepts).

Short form. The method name:

python

config = \{

    "email":  "mask",

    "salary": "perturb",

    "notes":  "drop",

\}

Extended form. A dictionary with the method and, if it takes one, its parameter (param):

python

config = \{

    "dni":         \{"method": "pseudonym", "param": r"CUS-\d\{6\}"\},

    "name":        \{"method": "simulation", "param": "name"\},

    "email":       \{"method": "simulation", "param": "email", "derive_from": "name"\},

    "postcode":    \{"method": "mask", "param": 3\},

    "signup_date": \{"method": "date_shift", "param": "customer_id"\},

    "code":        \{"method": "mask", "type": "string"\},

\}

KeyWhat for
methodThe method (see Anonymization methods).
paramThe method's single parameter, if it has one.
typeForces the column type: string, categorical, numeric, integer, float, boolean, datetime or text. Use it, for example, so that a postcode like 08001 does not lose its leading zero.
derive_fromGenerates the value from another, already anonymized column. For example, the simulated email is built from the simulated name.
references"table.column": the column is a foreign key and reuses the value that entity received in the other table (see Deliveries of several assets).
group_variantsOn by default: variants of the same entity ("Ana García" and "Ana") receive a consistent replacement.
  • In tables, the keys are the column names. Columns not listed in the configuration are left untouched.

  • In images, documents, audio and video, the keys are the element type codes (see Sensitive element types). Upper case, spaces and underscores are all accepted.

  • Method names accept synonyms: simulate is simulation, tokenize is coding, blackout is redact, mute is silence, and so on. The names used by the cloud platform are also accepted.

Anonymization methods

MethodWhat it doesParameter (param)Reversible
maskHides characters of the value.Mode: asterisks (default, keeps the first and last characters), opaque (hides everything and keeps separators) or a number n (keeps the first n characters: 28001 with 3 → 280**)Yes
hashReplaces the value with its SHA-256 fingerprint. The same value always gives the same fingerprint.—No
pseudonymReplaces each value with a unique, stable pseudonym.Optional pattern, for example EMP-\d\{6\}Yes
simulationReplaces the value with another realistic one of the same type (a name with another name, an IBAN with another valid IBAN), in the format of the data's country.Type of value to generate: any code from Sensitive element types (default name)Yes
codingIn free text, replaces each sensitive item with a label (\{\{ Name_1 \}\}) or with an invented value.Mode: tokens (default, labels) or invented (invented values)Yes
text_regenerateRewrites a whole free text with the language model, without personal data, keeping the column's language, tone and format.—Yes
shuffleShuffles the column's values across rows.—No
generalizeGroups values into ranges (1980-1990). For categories, groups the infrequent ones into Others.Range size (default 10)Yes
perturbAdds random noise to numeric values or dates.Noise level between 0 and 1 (default 0.05)No
date_shiftShifts dates by a random interval, the same for every date of an entity.Entity column (for example customer_id) to keep the same shift across all its rows and tablesYes
dropRemoves the column from the result.—No
blurBlurs the region (face, signature, logo, fingerprint). The strength adapts to the region's size.—No
pixelatePixelates the region.Block size, from 4 to 64 (default, based on the region's size)No
redactCovers the region with an opaque rectangle.—No
beepReplaces the audio segment with a beep.—No
silenceSilences the audio segment, without changing its length.—No
removeCuts the audio segment out.—No

Methods that need the language model. text_regenerate needs the Text module with a registered model (see Registering a model explicitly). Without it, anonymization stops with an error instead of delivering the column empty or untouched. It cannot be used on columns that are not free text either: for a column of URLs or codes, use simulation or coding.

Which methods each type accepts

The method must be valid for the type of the column or element. The first one in each list is the one config="auto" applies.

TypeMethods (the first is the default)
Text element (in images, documents and free text)mask, coding, simulation, pseudonym, hash
Visual element (face, signature, logo, fingerprint)pixelate, blur, redact
Audio elementbeep, silence, remove
Text columnmask, simulation, pseudonym, coding, hash, shuffle, drop, generalize, text_regenerate, date_shift
Categorical columnmask, simulation, pseudonym, shuffle, hash, coding, drop, generalize, text_regenerate
Numeric columnperturb, shuffle, mask, hash, drop
Boolean columnpseudonym, shuffle, mask, simulation, hash, drop
Date columndate_shift, mask, shuffle, simulation, drop, perturb

date_shift is also accepted on text columns, because dates read from a CSV usually arrive as text.

Tables

python

from dedomena.nucleus import anonymize

anonymize(

    "data/employees.parquet",

    config=\{

        "dni":        \{"method": "pseudonym", "param": r"EMP-\d\{6\}"\},

        "name":       \{"method": "simulation", "param": "name"\},

        "email":      \{"method": "simulation", "param": "email", "derive_from": "name"\},

        "phone":      "mask",

        "salary":     \{"method": "perturb", "param": 0.1\},

        "birth_year": \{"method": "generalize", "param": 10\},

        "comments":   \{"method": "coding", "param": "tokens"\},

    \},

    output="output/employees_anonymized.parquet",

    output_format="PARQUET",

    output_dir="results",

    country="Spain",

)

  • Tables are anonymized on CPU. They only need a model if you use text_regenerate, or if you want coding to find names and companies inside free text (with the lightweight or the full model).

  • To get the result in memory: output_format="DATAFRAME", and it is in result.content.

  • If you do not set output, the file is written to the current folder as \<name\>_anonymized.\<ext\>.

Images

python

anonymize(

    "documents/id_card.png",

    config=\{

        "face":      "blur",

        "signature": "redact",

        "name":      \{"method": "simulation", "param": "name"\},

        "dni":       "mask",

        "birthdate": "mask",

    \},

    output="output/id_card_anonymized.png",

)

  • Visual elements (face, signature, logo, fingerprint): the whole region is hidden, including any text on top of it.

  • Text inside the image: the original value is erased and the new value is drawn to fit its space, with colours taken from the image itself.

  • MRZ zone of passports and ID documents: replaced by a valid synthetic MRZ (with its check digits), consistent with the document's already anonymized data. If it cannot be rebuilt safely, it is pixelated.

  • Generative editing (optional, requires its model and a GPU): if installed, text is replaced with a natural look instead of being drawn on top. If not installed, the drawing method is used, which is always predictable.

  • If a configured element has no location, anonymization fails instead of delivering an image that looks anonymized but is not.

Image options (image_options):

OptionDefaultDescription
blur_radiusbased on sizeFixed blur strength.
pixelate_blockbased on sizeFixed pixelation block size.
fill_color(0, 0, 0)Colour used by redact.
text_color, background_colortaken from the imageColours of the redrawn text.
font_pathautomaticFont for the redrawn text.
padding2Margin in pixels around each region.
mrz_synthTrueReplace the MRZ zone with a valid synthetic one.
generative_replaceTrueTry generative editing if its model is installed. With False, drawing is always used.

Documents

python

anonymize(

    "documents/contract.pdf",

    config=\{"name": "pseudonym", "address": "mask", "iban": "hash", "signature": "redact"\},

    output="output/contract_anonymized.pdf",

)

  • PDF: sensitive text is removed from the document, not just covered (it cannot be recovered by selecting or copying it), and the replacement is written with the original font, size and colour. Form fields, embedded images and visual regions (signatures, faces, logos) are anonymized too.

  • DOCX: the body, tables, headers and footers are processed. A modified paragraph may lose any character formatting it had inside.

  • TXT: direct text replacement.

Audio

python

anonymize(

    "calls/customer_support.mp3",

    config=\{"name": "beep", "phone_number": "beep", "iban": "silence", "address": "beep"\},

    output="output/customer_support_anonymized.mp3",

    audio_options=\{"language": "es"\},

)

Audio options (audio_options):

OptionDefaultDescription
languageautomatic detectionLanguage of the recording ("es", "en", "fr"…). Setting it improves transcription.
beep_frequency1000Beep frequency, in Hz.
beep_gain_db-3Beep volume, relative to the recording's average volume.
padding_ms50Margin added on each side of the anonymized segment, in milliseconds.
fail_on_unalignedFalseIf True, anonymization fails when a detected value cannot be placed in time. If False, it is recorded in the conformance report.
  • If two segments overlap, the most destructive method is applied (remove > silence > beep).

  • The result keeps the input format.

Deliveries of several assets

When you anonymize several related assets (several tables from the same system, or a table and the documents of those same customers), use the Anonymization class. It guarantees that:

  • the same person receives the same replacement in every asset;

  • the dates of the same entity are shifted equally in every table;

  • foreign keys (references) still match after anonymization.

python

from dedomena.nucleus import Anonymization

policy = \{

    "customer_id": \{"method": "pseudonym", "param": r"C-\d\{6\}"\},

    "name":        \{"method": "simulation", "param": "name"\},

    "email":       \{"method": "simulation", "param": "email", "derive_from": "name"\},

    "date":        \{"method": "date_shift", "param": "customer_id"\},

\}

tables = [

    \{"name": "orders",    "data": "data/orders.csv",

     "config": \{**policy, "customer_id": \{"method": "pseudonym", "references": "customers.customer_id"\}\}\},

    \{"name": "customers", "data": "data/customers.csv", "config": policy\},

]

anon = Anonymization(config=policy, output_dir="delivery_october", country="Spain")

for table in anon.ordered(tables):          # referenced tables first

    anon.run(table["data"], name=table["name"], config=table["config"],

             output=f"delivery_october/\{table['name']\}.csv")

anon.conformance()                          # conformance report as PDF and JSON

print(anon.findings)                        # anything left untreated

  • ordered() sorts the tables so that a referenced table is anonymized before the tables that reference it.

  • run() accepts the same sources as anonymize(), plus name (the table's name in the delivery, which references point to) and config (to override the policy for that asset).

Files generated

FileWhereWhat it contains
The anonymized assetoutputYour file, anonymized.
metadata_anonymization_mapping.jsonoutput_dirThe \{original: anonymized\} mapping of the reversible methods. It is the only file that contains the original values: keep it protected.
metadata_anonymization_run.jsonoutput_dirWhich assets were processed, under which policy and what was treated in each.
conformance.jsonoutput_dirThe conformance record, updated after each asset.
conformance.pdfoutput_dirThe conformance report, when calling Anonymization.conformance().
\<file\>-metadata.jsonnext to the assetThe generated-content marking (see Generated-content marking), only if a method that generates values was applied.
logHistory.logoutput_dirThe run log.

The conformance report describes what was detected, what was treated and with which method, which values changed, which entities are shared between assets, and what each treatment protects against (and what it does not). If anything was left untreated (a configured column that does not exist, an audio value that could not be placed in time…), it appears in the report and is also printed on screen when that asset finishes, even with verbose=False.

Generated-content marking

When a method writes new values (simulation, text_regenerate or coding in invented mode) and has treated at least one element, Edge marks the result as AI-generated content, as required by Article 50 of the EU AI Act:

  • the asset's history says so (result.ai_generated is True);

  • a \<file\>-metadata.json file with the provenance information is written next to the file.

Methods that hide, remove or transform what was there (mask, hash, pseudonym, blur, pixelate, redact, beep, drop, perturb, shuffle, generalize, date_shift) do not mark the result. Synthetic data is always marked.

Reversal and incremental deliveries

Reversible methods (mask, pseudonym, simulation, generalize, coding, date_shift, text_regenerate) save their mapping in output_dir. With it, an authorized person can reverse an anonymized table:

python

from dedomena.nucleus import deanonymize

original = deanonymize("output/customers_anonymized.csv",

                       mapping_dir="results",

                       output="reversed/customers.csv")

hash, shuffle, perturb, drop and the visual and audio methods cannot be reversed. Reversal applies to tables.

Incremental deliveries. To anonymize the next month's data with exactly the same replacements as the previous delivery (the same person gets the same pseudonym, their dates are shifted the same way), load the previous delivery:

python

from dedomena.nucleus import Anonymization

anon = Anonymization.load("delivery_october")

anon.run("data/customers_november.csv", name="customers",

         output="delivery_november/customers.csv")


Synthetic data

A synthesizer learns the statistical properties of your real data, so that new synthetic data can be generated from it without copying the original records. The workflow is always the same:

  1. Train the synthesizer on your data, on your machine (train).

  2. Evaluate its quality, privacy and utility (evaluate).

  3. Save it to a file (save).

  4. Upload the synthesizer to the Dedomena.AI platform.

  5. Generate the synthetic data from the platform, or through the API.

Training requires the Tables module and the nucleus.synthesis permission. Synthetic data is generated on the Dedomena.AI platform, from the synthesizer you upload (see Generating data).

Choosing the algorithm

AlgorithmClassFor what data
GenericGenericA table in which each row is independent (customers, policies, patients…).
TransactionalTransactionalMovements of an entity over time, with date, amount and description (bank transactions, purchases…).
Time seriesTimeSeriesPeriodic measurements of several entities (meter readings, sensors…).
RelationalRelationalSeveral tables related by keys (customers → orders → order lines).
AutomaticAutoReads the data and chooses between Generic, Transactional and Relational.
python

from dedomena.nucleus import Auto, choose_algorithm

choose_algorithm("data/sales.csv")            # ('transactional', 'reason…'), without training anything

model = Auto(epochs=150)

model.train("data/sales.csv")

print(model.algorithm, model.reason)

Auto never chooses TimeSeries: if your data is a time series, use that class directly.

Full example (Generic)

python

import dedomena

from dedomena.nucleus import Generic

# 1. Configure

model = Generic(

    synthesizer_name="customers_v1",

    synthesizer_description="Retail customers 2026",

    epochs=200,

    batch_size=256,

    configuration=\{

        "categorical_columns": ["province", "segment"],

        "integer_columns": ["age"],

        "float_columns": ["income"],

        "date_columns": \{"signup_date": "%Y-%m-%d"\},

        "id_columns": \{"customer_id": r"CUS-\d\{6\}"\},

        "target": "segment",

    \},

    sensitive=\{"name": "name", "email": "email", "dni": "dni"\},

    constraints=["age\>=18", "income\>=0"],

)

# 2. Train

model.train("data/customers.csv")

# 3. Evaluate (writes report/report.pdf and report/evaluation.json)

privacy, quality, utility = model.evaluate(output_dir="report")

# 4. Save (writes synthesizers/synthesizer.zip, with the report inside)

model.save("synthesizers")

# 5. Upload synthesizers/synthesizer.zip to the Dedomena.AI platform and generate the data there

Common options

These options are passed when creating Generic, Transactional, TimeSeries or Auto:

OptionDefaultDescription
epochs150Training passes. Between 150 and 300 is recommended. It can also be set in train(epochs=…).
batch_size128Batch size (128, 256, 512…).
configuration\{\}The column types (see the table below).
sensitive\{\}\{column: type\}. Sensitive columns that, in the generated data, are filled with realistic fictitious values of the given type (name, first_name, last_name, email, phone_number, address, city, iban, credit_card_number, company, dni, nie, nif, cif, ssn, birthdate, gender, nationality, health_card, passport_number…).
datasets_country"Spain"Country of the data (in English): decides the format of the fictitious values.
constraints[]Rules the generated data must always meet (see Constraints).
amplify"default""default" generates new records. "quality" maximizes fidelity by starting from the real records, slightly modified: it offers much less privacy and requires at least 150 passes. Use it only when privacy is not the goal.
imputeTrueFills missing values during training, and the generated data has no gaps. With False, gaps are kept.
max_categories—Maximum number of distinct values a categorical column keeps; the less frequent ones are grouped into Others - DM.
min_freq_categories—Minimum frequency to keep a value; less frequent ones are grouped into Others - DM.
num_cat—Number of most frequent numeric values treated as categories (improves common prices, round amounts, zeros…).
coordinate_columns[]Latitude and longitude pairs: [\{"latitude": "lat", "longitude": "lon"\}].
drop_primaryFalseExcludes from the model categorical columns that are practically a key (99 % distinct values).
cudaTrueTrains on GPU if one is available; otherwise on CPU. cuda=False forces the CPU.
synthesizer_name, synthesizer_description—Name and description, shown in the metadata and in the report.

Column types (inside configuration):

KeyFormatDescription
categorical_columnslistColumns with discrete values.
integer_columnslistWhole numbers.
float_columnslistDecimal numbers.
boolean_columnslistTrue/false.
date_columnslist or \{column: format\}Dates. With a dictionary, the generated data is written in that format ("%Y-%m-%d").
id_columnslist or \{column: pattern\}Identifiers. With a pattern (r"ID_\d\{4\}"), they are generated from it; in a list, they are numbered 0, 1, 2…
text_columns\{column: instruction\}Free-text columns (see Free-text columns and privacy).
targettextThe column you are most interested in predicting. Used to measure utility in the evaluation.

Columns you do not declare are classified automatically, and the library tells you how it classified them. No column is discarded: if you do not want a column in the model, remove it from the data before training.

> Column types go inside configuration. Passing them as separate arguments (categorical_columns=[…]), as in previous versions, raises an error when creating the model.

Transactional

For an entity's movements over time. Use columns_mapping to say which column of your data plays each role:

RoleMeaningRequired
txn_dateDate of the movementYes
amountAmount (negative = expense, positive = income)Yes
conceptDescription of the movementYes
user_idCustomer or accountNo (without it, a single history is assumed)
balanceBalanceNo
cat_idCategory of the movementNo
python

from dedomena.nucleus import Transactional

model = Transactional(

    columns_mapping=\{"user_id": "account", "txn_date": "date", "amount": "amount_eur",

                     "concept": "description", "balance": "balance_eur", "cat_id": "category"\},

    balance_updated=True,

    transform_descriptions="level2",

    configuration=\{"categorical_columns": ["category"], "date_columns": \{"date": "%Y-%m-%d"\}\},

    datasets_country="Spain",

    epochs=250,

    synthesizer_name="transactions_v1",

)

model.train("data/transactions.parquet")

model.save("synthesizers/transactions")

  • balance_updated: True if each row's balance already includes that row's amount; False if it includes the previous row's. The balance of the generated data is recomputed from the amounts.

  • With a cat_id column, consistency between description and category is preserved.

Transaction descriptions (transform_descriptions, default "level2"): the generated descriptions are rewritten so they do not repeat real texts and are consistent with their row's amount and date.

LevelWhat is replaced in the description
level1Dates, amounts, card numbers and account numbers (IBAN).
level2Everything above, plus people's names, addresses and cities.
level3Everything above, plus merchant names, replaced with others from the same sector (for example, one fast-food chain with another).

Levels 2 and 3 make use of the detection and text models if they are installed. Without them, they rely on patterns and fictitious values.

Time series

For periodic measurements of several entities. Use columns_mapping to give the entity (user_id), the date (txn_date) and the measurement (amount). At least 5 distinct entities are needed.

python

from dedomena.nucleus import TimeSeries

model = TimeSeries(

    columns_mapping=\{"user_id": "meter", "txn_date": "date", "amount": "kwh"\},

    static_columns=["tariff", "province"],      # constant for each entity

    epochs=300,

    batch_size=64,

)

model.train("data/readings.csv")

model.save("synthesizers/readings")

  • Series are modelled with one observation per day. Date columns other than txn_date are not used.

  • When you generate data from a time-series synthesizer, the amount you request is the number of series (entities), not of rows.

Relational

For several related tables. Describe each table in datasets_config and pass the data of all of them to train():

python

from dedomena.nucleus import Relational

config = \{

    "customers": \{

        "primary_key": "customer_id",

        "categorical_columns": ["segment", "province"],

        "integer_columns": ["age"],

        "sensitive": \{"name": "name"\},

        "amplify": "default",

    \},

    "orders": \{

        "primary_key": "order_id",

        "foreign_key": \{"customer_id": "customers"\},

        "date_columns": ["order_date"],

        "float_columns": ["total"],

        "amplify": "default",

    \},

\}

model = Relational(datasets_config=config, epochs=200, datasets_country="Spain",

                   synthesizer_name="shop_v1")

model.train(\{"customers": "data/customers.csv", "orders": "data/orders.parquet"\})

model.save("synthesizers/shop")

Per-table keys in datasets_config:

KeyDescription
primary_keyThe table's primary key (unique values).
foreign_key\{column: "table"\}: the column points to that table's primary key. If the column has a different name: \{column: \{"dataset": "table", "field": "key"\}\}.
categorical_columns, integer_columns, float_columns, boolean_columns, date_columnsThe table's column types.
text_columns, sensitive, target, constraints, columns_mapping, max_categories, min_freq_categories, num_cat, drop_primaryAs in the other classes, for that table.
algorithm"generic" (default) or "transactional". With "transactional", also transform_descriptions and balance_updated.
imputeFalse by default in relational.
amplify"quality" by default in relational. Set "default" on each table if you want new records and maximum privacy (see Common options).
  • Relationships must form a graph without cycles, and every foreign key must point to a primary key. Rows whose keys point nowhere are discarded during training, with a warning.

  • Relational training requires 150 or more passes.

  • When you generate data from a relational synthesizer, the amount you request is the number of complete copies of the schema. Keys still match across the generated tables.

  • A relational synthesizer cannot be retrained: train a new one with the updated data.

Constraints

Rules the generated data always meets. They are written without spaces:

FormMeaning
col1\<-\>col2\<-\>col3Only combinations that exist in the real data are generated (for example, description ↔ subcategory ↔ category).
col1\<=col2, col1\<col2, col1\ />=col2, col1\>col2Comparison between two columns (for example, start_date\<=end_date).
col1\<=col2\<=col3Order across three columns.
col1\>0, col1\>=0, col1\<0, col1\<=0Positive or negative values.
col1\>=18Minimum value (whole number).
0\<=col1\<=100Value between a minimum and a maximum (whole numbers).
col1//1000Multiple of 1000.

A malformed constraint raises an error during training.

Generating data

Synthetic data is generated on the Dedomena.AI platform, from the synthesizer you train with Edge:

  1. Save the synthesizer with model.save(folder). It writes synthesizer.zip in that folder.

  2. Upload synthesizer.zip to the Dedomena.AI platform, from the web application or through the API.

  3. Generate the synthetic data from the platform, or through the API.

The generation options (number of records, conditional generation…) are described in the platform and API documentation. Synthetic data is always marked as AI-generated content.

Saving, loading and retraining

python

model.save("synthesizers/customers")              # → synthesizers/customers/synthesizer.zip

model = load_synthesizer("synthesizers/customers/synthesizer.zip")

model.metadata                                    # name, algorithm, columns, scores…

  • save() takes a folder and always writes synthesizer.zip inside it. If you ran evaluate() before, the report is included.

  • load_synthesizer(path) loads a saved synthesizer, for example to retrain it.

  • Protect synthesizer.zip as sensitive data: it contains the trained model and may contain samples of the training data.

Retraining with new data (Generic, Transactional and TimeSeries only):

python

model = load_synthesizer("synthesizers/customers/synthesizer.zip")

model.retrain(data="data/customers_november.csv", epochs=100)

model.save("synthesizers/customers_v2")

  • Always set epochs.

  • With amplify="quality", forgetfulness_factor (between 0 and 1) decides how much of the previous data is forgotten: 0 keeps everything, 1 starts from scratch.

Evaluation and report

python

privacy, quality, utility = model.evaluate(output_dir="report")

It measures the synthesizer against the real data it was trained on and returns three scores from 0 to 100:

ScoreWhat it measures
PrivacyHow hard it is to link the synthetic data to real people: exact copies, closeness to real records, singling-out and linkability risk.
QualityHow well the distributions, correlations and coverage of the real data are preserved (and the time patterns, if there are dates).
UtilityWhether a machine learning model trained on synthetic data performs like one trained on real data, on the target column.

The following are written to output_dir:

  • report.pdf: the report, with the scores, each metric explained and its charts;

  • evaluation.json: the same results as data, to integrate into your processes;

  • logHistory.log.

Each score comes with a rating: critical (up to 40), poor (up to 60), fair (up to 80) or excellent.

  • Control data. To measure singling-out and linkability risk, set aside, before training, a part of the real data the model does not see, and pass it as control_data: model.evaluate(output_dir="report", control_data="data/control.csv").

  • Evaluation needs at least 3 typed columns that are neither sensitive nor identifiers.

  • Run evaluate() before save() so the report is included in the synthesizer.

  • In relational, evaluate(output_dir=…) returns the average scores and includes referential integrity.

Evaluating any two datasets. To compare a real dataset with synthetic data you generated on the Dedomena.AI platform:

python

from dedomena.nucleus.evaluations import synthesizer_evaluations

privacy, quality, utility = synthesizer_evaluations(

    real_data="data/real.parquet",

    synthetic_data="data/synthetic.parquet",

    categorical_columns=["province", "segment"],

    integer_columns=["age"],

    float_columns=["income"],

    date_columns=["signup_date"],

    target="segment",

    output_dir="report",

    synthesizer_name="customers_v1",

)

For several tables, synthesizer_evaluations_multitable(configuration=\{table: \{...\}\}, output_dir="report") produces a single combined report.

Free-text columns and privacy

Free-text columns (text_columns) are modelled as categories: the generated texts are taken from the real training texts. If those columns contain personal data, anonymize them before training, for example with coding or text_regenerate (see Anonymization), or remove them from the data.

The exception is transaction descriptions in Transactional with transform_descriptions, which are rewritten when generating (see Transactional).

Starting from data prepared with Axon

If you pass train() an asset prepared with Axon, the synthesizer uses the column types Axon detected. What you declare in configuration takes precedence. See From Axon to Nucleus.


Axon: dataset quality and preparation

Axon checks and prepares a dataset before you anonymize or synthesize it: it infers column types, detects outliers and missing-value patterns, cleans the text, applies transformation rules and produces a quality report in PDF. It is included in every package and requires the axon permission.

Each step returns a new asset and leaves the original unchanged; the history accumulates.

Inspecting

python

from dedomena.axon import Inspector

asset = Inspector().run("data/customers.csv", name="customers")

asset.schema["types"]        # categorical_columns, integer_columns, float_columns, boolean_columns, text_columns, date_columns

asset.schema["semantic"]     # identifier, phone, email/URL, coordinate columns…

asset.schema["outliers"]     # \{column: [rows with outliers]\}

asset.info["rows"], asset.info["columns"]

asset.info["axon"]["unavailable"]    # \{\} if everything could run

Inspector optionDefaultDescription
profileTrueStatistical profile of each column.
outliersTrueOutliers in numeric columns.
correlationsTrueCorrelations between columns. Turn it off for very wide tables.
semanticTrueSemantic types (identifiers, phone numbers, emails, coordinates…).
missingTrueMissing-value patterns.
sample—Analyse only the first N rows, for very large tables.

The short form is inspect_dataset(source, **options).

Cleaning

python

from dedomena.axon import clean

cleaned = clean("data/customers.csv", name="customers")

cleaned.info["axon"]["cleaning"]     # duplicate rows, repeated headers, missing values, spaces fixed…

cleaned.quality["cleaning_after"]    # indicative score from 0 to 1

Cleaning:

  • replaces tabs and line breaks with spaces, collapses repeated spaces and trims each cell;

  • removes typographic or unbalanced quotes;

  • converts to numbers the columns whose decimals are written with a comma ("1,5").

It also counts duplicate rows and headers repeated inside the data, but does not remove them. Remove them yourself if needed, for example with df.drop_duplicates().

After cleaning, the columns are left as text. To give them back their type, apply cast_type rules (next section) and inspect again.

Applying rules

python

from dedomena.axon import ApplyRules, Inspector

rules = \{

    "email":  \{"type": "normalize_nulls"\},

    "name":   \{"type": "normalize_text_casing", "params": \{"proposed_case": "title"\}\},

    "age":    [\{"type": "cast_type", "params": \{"target_type": "integer", "mode": "force"\}\},

               \{"type": "nullify_out_of_range", "params": \{"min": 0, "max": 120\}\}],

    "amount": \{"type": "cast_type", "params": \{"target_type": "float", "mode": "force"\}\},

    "city":   \{"type": "enforce_category", "params": \{"allowed": ["Madrid", "Barcelona", "Valencia"]\}\},

\}

prepared = ApplyRules(rules).run(cleaned, name="customers")

prepared.lineage[-1].details["applied"]    # rules applied per column

prepared.lineage[-1].details["failed"]     # rules that could not be applied (e.g. missing column)

prepared = Inspector().run(prepared, name="customers")   # final types

Each column takes one rule or a list of rules, applied in order:

RuleParametersWhat it does
normalize_nulls—Turns null, none, nan, na, n/a, -, -- and empty strings into missing values.
cast_typetarget_type: float, integer, string, category, boolean, datetime; mode: forceChanges the column type. Understands European decimals (1.234,56) and booleans in Spanish and English (sí/no, yes/no, 1/0). With mode: "force", values that cannot be converted become missing; without it, they are kept.
format_datetimestrftimeWrites dates in the given format.
normalize_text_casingproposed_case: upper, lower, titleUpper case, lower case or title case.
impute_nullsmethod: median, median_int, mode, ffill, constant (with value)Fills missing values.
nullify_out_of_rangemin, maxSets values outside the range to missing.
clamp_rangemin, maxClamps values outside the range to the limit.
enforce_categoryallowed (list)Fixes case and accents towards the allowed value and sets values that are not allowed to missing.
enforce_formatpattern (# = one letter or digit, * = anything)Sets values that do not follow the pattern to missing.
replace_valuemapping, case_sensitive, regexReplaces values.
round_decimalsdecimals (default 2)Rounds.

For anonymization and synthesis to see a column's new type, use cast_type with "mode": "force".

Quality report in PDF

python

from dedomena.axon import report

path = report(prepared, "reports/customer_quality", lang="en", name="Customers 2026")

# → 'reports/customer_quality.pdf'

  • lang: "en" or "es".

  • The report is built from what the asset already knows: inspect it before generating the report.

  • The report includes example values from each column. Treat it with the same confidentiality as the dataset. The same applies to meta.json when you save an inspected asset.

Risk and executive summary

These two functions need the language model: the Text module with a model installed.

python

from dedomena.detection import detect, llm

from dedomena.axon import risk, insight, report

llm.register()                                       # needed for insight()

prepared.sensitive_elements = detect(prepared)       # detect before computing the risk

with_risk = risk(prepared, name="customers")

summary = with_risk.info["risk"]["summary"]

summary["label"], summary["level"], summary["sensitivity_index"], summary["avg_risk_level_sensitive"]

with_insight = insight(with_risk, language="english", context="Online channel sign-ups", rows=200)

print(with_insight.info["insight"]["text"])

report(with_insight, "reports/customer_risk.pdf", lang="en")   # adds the risk and insights sections

  • risk() computes the re-identification risk level from the detected sensitive columns: run detect() first; without detection, the result is "no risk".

  • insight() writes an executive summary of what the data says, from the first rows rows. language accepts "spanish" or "english".

From Axon to Nucleus

The recommended workflow:

  1. Inspect the original data with Inspector, to get a diagnosis.

  2. Clean with clean.

  3. Apply rules with ApplyRules, using cast_type with "mode": "force" to fix the types.

  4. Inspect again with Inspector, so the schema describes the prepared data.

  5. Anonymize or synthesize with Nucleus.

python

from dedomena.nucleus import anonymize, Generic

anonymized = anonymize(prepared, config="auto", output="output/customers_anon.csv")

model = Generic()

model.train(prepared)            # uses the types Axon detected

  • Synthesis: uses Axon's column types for everything you have not declared in configuration.

  • Anonymization: uses the cleaned content and, if the asset already has detected elements, uses them without detecting again.

  • Always set output= when anonymizing an Axon asset (or output_format="DATAFRAME").

Checking the Axon engine

python

from dedomena.axon import engine_report

engine_report()      # \{'basic_cleaner': None, 'profiling': None, ...\}   None = works

If part of the engine cannot run on your machine (for example, because of a dependency), Inspector skips it, warns you and records it in asset.info["axon"]["unavailable"] with the reason. The rest of the analysis continues.


Models: automatic, registered and your own

How the library chooses

You do not need to configure anything: the library uses the best engine it finds installed for each modality.

ModalityOrder of preference
TextFull text model → lightweight text model → patterns only
Image (also scanned pages and video frames)Full image model → lightweight image model → error: a model is required
AudioLightweight transcription → full transcription
  • Patterns and validations are always applied, with any engine.

  • When loaded, each engine writes a line to the log with what was chosen and the device (text detection engine selected: …, small-text device selected: cpu…).

  • If an engine cannot be loaded, the library moves on to the next one and warns you. If what is missing are the models, the warning includes the command to download them:

    code
    
    text detection engine selected: patterns (...).
    
    Download the models with your licence: `dedomena-edge setup --size small`.
    
    
  • Lightweight models run on CPU even when there is a GPU, so they do not take up card memory. You can move them to the GPU (see Registering a model explicitly).

Which engine will be used

python

from dedomena.detection import models

print(models.report())          # what will be used for each modality, and whether it fits on the GPU

code

GPU: 96 GB

residency: shared

runs on:

  audio: small-audio on cpu

  images: …

  text: …

  • models.runtime_report() returns the same as a dictionary.

  • models.plan() tells you, for the full models, whether they fit on your GPU: fits, tight (they fit with a reduced context) or no.

  • None of this loads the models.

Per modality:

python

from dedomena.detection import llm, small, audio

llm.model_report()                     # (path, engine, problem): problem None = it can be loaded

small.report()                         # available lightweight models, and what is missing

audio.transcriber_report(device="auto")

Registering a model explicitly

Registering a model loads it into memory and connects it to detection and anonymization. The library does this on its own when it needs to, but registering it yourself lets you choose which capabilities to enable, on which device, and when to pay the loading time.

Language model (Text module):

python

from dedomena.detection import llm

llm.register()        # with the installed models

llm.register optionDefaultWhat it enables
model—Path to a specific model (see Using a model you already have).
detectTrueThe model detects sensitive data in free text (in addition to the patterns).
generateTrueEnables text_regenerate and text rewriting. Required for text_regenerate.
confirmFalseThe model reviews the elements another detector proposes: it removes false positives and corrects types, never adds. Useful with detect=False (patterns propose and the model judges) or with elements that arrive already detected.
insightTrueEnables axon.insight().
others—Engine options, such as n_ctx (context, 8192 by default) or temperature (0 by default, for reproducible results).

llm.unregister() hands text detection back to the patterns. llm.available() tells you whether it can be loaded.

> text_regenerate needs the model to be registered. Automatic detection loads the language model for detection only. To use text_regenerate, call llm.register() first.

Lightweight models:

python

from dedomena.detection import small

small.register(device="cuda")    # moves the lightweight text and image models to the GPU

small.upgrade()                  # swaps the lightweight models for the full ones, if installed

Full image model:

python

from dedomena.detection.images import vlm

vlm.register(confirm=True)       # also reviews each proposed region by cropping it

Using a model you already have

If your organization already has its own language model, Edge can use it for free-text detection, text_regenerate and Axon's executive summaries. You need the Text module.

Model requirements:

  • A conversational language model (instruction-tuned), with a context of at least 8,192 tokens.

  • Multilingual, if your data is not in English.

  • Edge sends it its own instructions. Detection quality depends on the model: test it with your data before using it in production.

Option 1: your weights, on your disk

Give the path of the model:

bash

export DEDOMENA_LLM_MODEL=/models/my-model

or when registering it:

python

from dedomena.detection import llm

llm.register(model="/models/my-model")

FormatWhere it runsWhat you need
Standard model folder, with config.json, the tokenizer and the weightsGPU with enough memory (GPU engine, dedomena-edge[gpu]); without a GPU, on CPU, much more slowlygpu hardware to use the GPU
A single quantized .gguf fileCPUThe text extra and a C++ compiler to install it: pip install --index-url "https://licence:\<TOKEN\>@api.dedomena.ai/edge/\<modules\>/cpu/simple/" "dedomena-edge[text]"

If the folder contains both a model folder and a .gguf file, the folder wins.

Option 2: your own model server

If you already serve a model on your internal network with an API compatible with OpenAI's chat API (/v1/chat/completions):

bash

export DEDOMENA_LLM_ENDPOINT=http://models.internal:8000      # without /v1

export DEDOMENA_LLM_ENDPOINT_MODEL=model-name-on-your-server

python

from dedomena.detection import llm

llm.register()

  • Edge calls \<DEDOMENA_LLM_ENDPOINT\>/v1/chat/completions.

  • The server must be on your network: that way the data still does not leave your infrastructure.

  • Edge does not send an authentication header. If your server requires one, expose it behind an internal proxy that adds it.

  • With this variable set, Edge uses the server instead of a local model.

Your own image model

It works the same way with DEDOMENA_VISION_MODEL (a model folder) or with DEDOMENA_VISION_ENDPOINT and DEDOMENA_VISION_ENDPOINT_MODEL (a compatible server). Edge asks it to locate regions (faces, signatures, logos, fingerprints) and values within the image and to return their coordinates, so it must be a vision-language model capable of doing so. We recommend using Dedomena.AI's image model, unless you validate yours with your documents.

Forcing an engine

The library chooses on its own how to run the model, based on its format and your hardware. If you need to force a different way of running it (for example, because the GPU is busy with another process), ask Dedomena.AI support about the DEDOMENA_LLM_BACKEND and DEDOMENA_VISION_BACKEND variables.

Registering your own detector

If you have your own detector (for example, an internal model for a document type of your own), you can plug it in and every function in the library will use it instead of the built-in engine:

python

from dedomena.detection import register_detector, detect

def my_text_detector(text, types=None):

    """Receives a text and returns the elements it finds."""

    results = []

    for number in find_policy_numbers(text):               # your logic

        results.append(\{"value": number, "type": "health_card"\})

    return results

register_detector("text", my_text_detector)

detect("Policy AX-99812 is in the name of…")

ChannelReceivesReturns
"text"(text, types=None)[\{'value', 'type'\}], with optional start/end (if not given, every occurrence of the value in the text is located)
"image"An image (PIL)[\{'value', 'type', 'box': (x0, y0, x1, y1)\}]
"audio"(path, language=None)Segments with value, type, start, end (seconds)
  • Patterns still run before your detector and win overlaps.

  • A text value that does not appear verbatim in the text is discarded.

  • If your detector fails, the library falls back to the patterns for that batch and warns you in the log.

  • To remove it: from dedomena.detection.registry import unregister_detector; unregister_detector("text").

In the same way, register_generator("text_regenerate", function) lets you plug in your own text generator. The function receives (prompts, max_tokens=...) and returns one string per prompt.

Where the models are

The library looks for each model in this order:

  1. The path you give when registering it (model=).

  2. Its environment variable: DEDOMENA_LLM_MODEL, DEDOMENA_VISION_MODEL, DEDOMENA_AUDIO_MODEL, DEDOMENA_SMALL_TEXT_MODEL, DEDOMENA_SMALL_VISION_MODEL, DEDOMENA_SMALL_AUDIO_MODEL…

  3. The models folder: DEDOMENA_MODELS_DIR, or ~/.local/share/dedomena/models if it is not set.

dedomena-edge setup downloads into the same folder where the library looks, so you do not need to configure anything. With the full download, env.sh sets DEDOMENA_MODELS_DIR to ./models.

Integrity. Each model carries a manifest with the size and fingerprint of each file. The fingerprint is checked on download, and the size every time the model is loaded: a half-copied model is not used.

GPU: memory and sharing

If your package has the full text and image models, both share the GPU:

  • if they fit at the same time, both are loaded (shared);

  • if not, they are loaded in turns (exclusive): each one gets the whole card and the other is unloaded when switching. Each switch costs a load, so order the work by modality (tables first, then images).

The library decides by measuring the card. You can force it with DEDOMENA_GPU_POLICY=shared|exclusive, and set each model's share of memory with DEDOMENA_LLM_VRAM and DEDOMENA_VISION_VRAM (for example, 0.45).


Reference manual

Commands

CommandWhat it does
bash install.sh \<TOKEN\> [--venv | --no-venv]Installs from the full download (see Option A: full download (no internet on the target machine)).
. ./env.shLoads the environment of an installation made with install.sh.
dedomena-edge setup --size small|full|none [--token …] [options]Downloads your installation's models and saves the licence (see Option B: install with pip).
dedomena-edge statusShows the version, the modules, the models folder, whether there is a GPU and the installed models.

Environment variables

All of them are optional: without any, the library decides on its own.

VariableWhat for
DEDOMENA_TOKENThe licence, without saving it to disk.
DEDOMENA_NO_PERSISTWith 1, the library writes nothing to the user's home directory.
DEDOMENA_CONFIG_DIRFolder of the licence file (default ~/.config/dedomena).
DEDOMENA_MODELS_DIRModels folder (default ~/.local/share/dedomena/models).
DEDOMENA_LLM_MODEL, DEDOMENA_VISION_MODEL, DEDOMENA_AUDIO_MODELPath of a specific full model.
DEDOMENA_SMALL_TEXT_MODEL, DEDOMENA_SMALL_VISION_MODEL, DEDOMENA_SMALL_AUDIO_MODELPath of a specific lightweight model.
DEDOMENA_LLM_ENDPOINT, DEDOMENA_LLM_ENDPOINT_MODELYour language model server and the model's name on it.
DEDOMENA_VISION_ENDPOINT, DEDOMENA_VISION_ENDPOINT_MODELYour image model server and the model's name on it.
DEDOMENA_LLM_BACKEND, DEDOMENA_VISION_BACKENDForce the way the model runs. Only when support tells you to (see Using a model you already have).
DEDOMENA_GPU_POLICYshared or exclusive (see GPU: memory and sharing).
DEDOMENA_LLM_VRAM, DEDOMENA_VISION_VRAMShare of GPU memory for each model.
DEDOMENA_LOG_FILEPath of the run log.

Common errors

MessageCause and solution
No Dedomena token foundNo licence. Save it with dedomena.login("\<TOKEN\>", persist=True) or set DEDOMENA_TOKEN.
Your Dedomena token expired on …The licence has expired. Generate a new token on the Edge page and save it.
Your Dedomena licence does not cover …Your licence does not include that permission (see Permissions).
… is not part of this build. It includes: …Your package does not include that module. Install a package that does (see Updating, extending or changing the installation).
'Auto' needs the tables module, which is not part of this buildSynthesis is in the Tables module (or Text).
Image detection needs a vision model and none is installedRun dedomena-edge setup --size small (or full).
These models need an NVIDIA GPU …The full text and image models need a GPU. Use --size small.
Unknown anonymization method '…'The method does not exist. Check Anonymization methods.
None of the columns could be reversedThe mapping in mapping_dir does not belong to that file.
IBM Db2 driver not foundCopy db2jcc4.jar into the package's DB2Driver folder (see Data sources).
Dedomena error [id]: … Details were written to …/logHistory.logCheck the log. If you need help, send support the identifier, the log and dedomena-support.enc.
Dedomena Edge: Data Intelligence at the Edge | Dedomena.AI | Dedomena AI