Attested erasure — how it works
Erase a person from your records without deleting a row, and prove it to an authority.
The problem it solves
When someone asks to be erased, you often have to choose between two bad answers: delete the row and break your accounting, or refuse and be in breach. Either way, your only evidence is your own word.
The three steps
1. You declare your perimeter
You name your tables, and in each the fields that identify a person. Your declaration is signed and timestamped. That is exactly what the attestation will cover — no more, no less.
2. Your exports go through the library
The declared fields are encrypted with a key that exists nowhere in full: half on your side, a living half at Seal. The rest of the row stays readable — the amounts still balance.
3. You erase, and you receive an attestation
Seal destroys its half. The person stops being readable everywhere at once, including in files already sent and in backups you can no longer write to. You receive a twelve-character reference.
Anyone can verify it
The person concerned reads their reference out to an authority, who opens it with no account, no contract and without asking our permission. An attestation only our customers could verify would be worth nothing.
/effacement/verifier →Installing, step by step
Seven steps. The first five are done once; the last two are what you will do each time someone asks to be erased.
Before you start: an active organisation account, access to your database, and a safe place for a production secret (environment variable or vault). Allow an hour the first time.
Step 1 — Declare your perimeter
Open « Erasure » in the left menu, then « Sign my declaration ». Write one line per table: the table name, a colon, then the fields that identify a person, separated by commas. Sign it. Nothing works before this step, and what you declare here is exactly what the attestation will cover.
Step 2 — Generate your half of the key
It never leaves your systems and Seal will never see it. Generate 32 random bytes, keep them in your vault or an environment variable, and never in your code repository. Losing it makes your exports unreadable forever, including for people who asked for nothing.
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
# -> a place dans SEAL_PART_LOCALE, dans ton coffre. Jamais dans le depot.
Step 3 — Copy the library into your project
The « client-effacement » folder has no dependencies and uses only WebCrypto. Node 18 or newer, or a browser. It runs at your end, never at ours.
cp -r client-effacement/ mon-projet/
# puis, dans ton code :
import { Enveloppe } from "./client-effacement/index.mjs";
Step 4 — Set the four values
Your organisation identifier and your token are in the portal; the perimeter reference is shown on the step 1 screen. The protected fields must match exactly what you declared: a field encrypted but not declared would be covered by no attestation.
const env = new Enveloppe({
orgId: process.env.SEAL_ORG, // etape 4 — le portail
jeton: process.env.SEAL_JETON, // etape 4 — le portail
perimetre: process.env.SEAL_PERIMETRE, // etape 1 — l'ecran du perimetre
partLocale: process.env.SEAL_PART_LOCALE, // etape 2 — ne sort jamais
epoque: 1,
champsProteges: ["nom", "courriel", "telephone"],
seal: "https://seal.lifaio.com",
});
Step 5 — Wrap an export
Pass your rows to the library. The declared fields come out encrypted, the rest stays readable. The function you supply must return your internal reference for the person — never a name or an email address: Seal must see no recognisable identifier.
const lignes = await db.query("SELECT * FROM factures");
const fichier = await env.envelopper(lignes, (l) => l.client_id);
fs.writeFileSync("export-2026-09.seal", fichier);
Step 6 — Open an export, asking only for what you are looking for
Ask only for the people you need. This is not an optimisation, it is the detection mechanism: legitimate use looks for someone specific, a thief decrypts everything and hits the decoys. An already erased person returns a marker, not an error, so the rest of the file stays usable.
const { lignes } = await env.ouvrir(fichier, ["CLI-4471"]);
// une personne effacee rend { efface: true } — pas une erreur.
Step 7 — Erase, and hand over the reference
On the « Erasure » screen, enter the person's internal reference and confirm. It is irreversible. You receive a twelve-character attestation reference: hand it to the person concerned. They can have it verified by an authority on a public page, with no account and without asking our permission.
What the person concerned can do with their reference
They read it out to whoever they choose — an authority, a lawyer, themselves. The page opens with no account and no contract, and shows the attestation with its scope and limits written on it.
/effacement/verifier →When the person comes back
An erased person can become a customer again. Here is the exact mechanism, and it is by design.
Destruction is final for the reference and epoch concerned: the seed becomes NULL, and no route and no administration button can bring it back.
Envelopes are keyed by organisation, reference and EPOCH. Moving to the next epoch creates a fresh seed: the new record works, and the old data stays unreadable forever.
// LE RETOUR. On passe a l'epoque suivante ; rien d'autre ne change.
const env2 = new Enveloppe({ ...memesReglages, epoque: 2 });
// CLI-1 en epoque 1 -> detruite pour toujours
// CLI-1 en epoque 2 -> graine neuve, le nouveau dossier fonctionne
//
// RECOMMANDE : donner une reference neuve plutot que reutiliser l'ancienne.
const env2b = new Enveloppe({ ...memesReglages, epoque: 2 });
await env2b.envelopper(lignes, (l) => l.id); // l.id = 'CLI-8891'
We recommend assigning a new internal reference rather than reusing the old one: your attestation then keeps designating unambiguously what was destroyed.
What you lose, and it is deliberate: you can no longer recognise that an erased record was the same person. An identifier surviving erasure would not be an erasure.
Unique constraints in your database
A unique index on an identifying field causes no collision: after enveloping, that field holds NULL or ciphertext, never the plaintext value. SQLite, PostgreSQL and MySQL all accept several NULLs in a unique index.
Installing beside the database, without changing your columns
You do not have to alter your existing columns. Set the declared fields to NULL in your original table and keep the encrypted block in a separate table, indexed by reference and epoch. No type migration, and your existing indexes hold.
-- LA TABLE A COTE. Aucune colonne existante n'est modifiee.
CREATE TABLE clients_enveloppes (
sujet_ref TEXT NOT NULL,
epoque INTEGER NOT NULL,
p TEXT NOT NULL, -- le bloc chiffre
PRIMARY KEY (sujet_ref, epoque)
);
-- L'enveloppage met a NULL les champs declares, et rien d'autre.
UPDATE clients SET nom = NULL, courriel = NULL WHERE id = 'CLI-1';
-- vos index UNIQUE tiennent : plusieurs NULL sont acceptes.
Why the key is computed on your side
To read data it must exist in memory at some instant — on your side, where you hold it legitimately. If Seal computed on your behalf it would receive the ciphertext and the full key: it would hold the data and could no longer say it has no access. The key is non-extractable and is never written anywhere.
Your half of the key is a production secret: an environment variable or a vault, never your repository. Losing it makes your exports unreadable, and nobody can give it back to you.
What it does not do — and you have to read this
- It does not prove that no copy was taken before the erasure. No method can, and that is written on the attestation itself.
- It only protects the fields you declare. A name left in a free-text note is outside the perimeter and stays readable.
- It does not replace encryption of your database. Seal guarantees the erasure and its proof, not the secrecy of your data at rest.
- It is irreversible. Neither you, nor Seal, nor both together can go back — which is precisely what makes it evidence.
Seal holds no data
Seal never sees a name, an email address or a postal address. It receives only your internal reference for the person and returns a cryptographic contribution. We cannot be compelled to hand over what we do not hold.