LakeLogic
Loading…
Back to Blog
Governance

The Erasure Request Nobody Can Close: Masking, Erasure and Retention From Contracts

Jul 12, 2026
LakeLogic Team
The Erasure Request Nobody Can Close: Masking, Erasure and Retention From Contracts

Picture a passenger on a ride-hailing app asking to be forgotten. The ticket lands with the data team, who start a spreadsheet: which tables hold the passenger's rider_id, which of those hold the rider's name or email, which were copied into a mart last quarter. Weeks later the ticket is still open, the spreadsheet has a tab called missed_tables_v2, and nobody can say with confidence that the job is done.

Privacy work in a lakehouse fails for an ordinary reason: knowledge of which columns are personal lives in people's heads and in scattered scripts. Masking is a step someone remembers to add; erasure is a one-off query someone writes under pressure; retention is a policy document that no job checks against the tables.

This article compares the usual ways teams handle masking, erasure and retention, then shows how RideFlow, a fictional ride-hailing business, drives all three from its data contracts. It is about mechanics and evidence. It does not claim a contract makes anyone compliant with GDPR or any other regulation; that takes legal judgement, access control and processes beyond the pipeline.

Why privacy work stays manual

  • Personal fields are not declared. If nothing marks date_of_birth as personal, no tool can find it, mask it or erase it.
  • Each control is built separately. Masking sits in transformation code, erasure in an ad-hoc notebook, retention in a policy PDF. None reads the same list of fields.
  • There is no record. When an auditor asks how a request was handled, the answer is a Slack thread and a query someone ran.

Recent adverts for governance leads and data engineers ask for this directly: governance controls over ownership, access, sharing, retention and classification, PII classification, and GDPR handling. The responsibility is common; the tooling is usually homemade.

The ways teams solve it

ApproachHow it worksGood fitWatch out for
Masking in transformation codeEngineers hash or drop columns in each pipelineOne team, few personal fieldsDepends on every new pipeline remembering
Unity Catalog column masks and row filtersA SQL function masks values at query time, per readerRestricting raw values on DatabricksControls reads, not storage; erasure is separate
Erasure scripts per requestUPDATE or DELETE for the tables someone knowsRare requests, small estatesTables missed; little evidence afterwards
Delta DELETE plus VACUUMRemove rows, then vacuum old filesAny Delta estate, whatever else you useOld versions readable until vacuum passes retention
Privacy management platformsTrack requests, data maps and consentA privacy office, many systemsTrack and route; lakehouse deletion may stay yours
Contract-driven controlsContracts declare personal fields; one runtime acts on themMany tables and teamsContracted tables only; history is platform work

These combine well. Unity Catalog masks govern who reads what; Delta vacuum governs what history remains. The gap is the list itself: which tables and columns hold this person's data, and how do we know we got them all? That is what a contract-driven approach answers.

How RideFlow drives it from contracts

RideFlow uses LakeLogic, an open-source contract runtime. Everything below is in its reference repository.

1. Declare personal fields once

Each contract marks personal fields where they are defined:

# contracts/operations/onboarding/contracts/silver/silver_onboarding_driving_licences.yaml (excerpt)
- name: licence_no
  type: string
  pii: true
  classification: licence_id
- name: date_of_birth
  type: string
  pii: true
  classification: date_of_birth

That list is what the masking, erasure and contract checks all read, so they cannot disagree about which columns are personal.

2. Mask on write, or say why not

Adding masking: hash (or nullify, redact, partial, encrypt) to a field makes LakeLogic mask it in accepted rows before they are written. If masking fails, the run stops instead of writing values in the clear. A field tagged pii: true with no strategy is written unmasked with a warning, and LakeLogic's contract checks report it as personal data with no masking, so the gap is visible in review rather than discovered later. RideFlow sets no masking strategy on its personal fields, so its contract checks report them; who can read those tables is left to platform permissions.

3. Erase from the declared list, dry run first

Each domain sets its default erasure strategy:

# contracts/operations/_domain.yaml (excerpt)
compliance:
  frameworks:
    gdpr:
      applicable: true
      legal_basis: legal_obligation
  erasure:
    strategy: nullify

RideFlow's erasure job (pl_privacy_erasure) runs one task per system. For each contract that has personal fields and the subject column, it counts matching rows and applies the strategy to the personal columns. It plans by default: it changes data only with dry_run=false, confirm=ERASE and a case reference. After applying, it re-queries each table for rows that still hold values, marks the table incomplete if any remain, and fails the job. Each table gets an evidence row in lakelogic_erasure_evidence with counts, the strategy and a digest of the subject list, never the subject IDs themselves.

The opening ticket becomes: run the job in plan mode, read which tables match, run it again with confirmation, and attach the evidence rows to the case.

4. Check retention without deleting

Each domain declares a hold period per layer:

retention:
  bronze: P7D
  silver: P90D
  gold: P7Y

The retention check job compares each table's oldest record with its window and records the result in lakelogic_retention_evidence. It is read-only by design: it reports what is held too long and can fail the job, but it never deletes. Deciding what to remove stays with the data owner.

When you might not need this

  • Very little personal data. If two tables hold it, a reviewed script and Unity Catalog masks may be enough.
  • Personal data never enters the lakehouse. Tokenised upstream, with the mapping held elsewhere, there is little to erase here.
  • A privacy platform already maps and executes deletions in your lakehouse. Contracts can still declare fields for review, but the workflow can stay where it is.

Limits worth knowing

  • Only tables with contracts are covered. An export, a notebook scratch table or a BI extract without a contract is invisible to the job.
  • History and copies need platform work. Erasure updates current table data. Delta keeps earlier versions until VACUUM runs past the retention window, and quarantine tables, raw landing files and backups each need their own handling.
  • Declared is not enforced. legal_basis and retention record intent. Retention is checked, not enforced; lawful basis is a legal decision the contract only writes down.
  • Masking on write is not access control. Who can read raw values is still governed by Unity Catalog grants and masks.

Key takeaways

  1. Declare personal fields in one place, and make masking, erasure and checks all read that list.
  2. A personal-data tag without a masking strategy changes nothing at write time; flag it in review.
  3. Run erasure in plan mode first, apply only with explicit confirmation, and verify nothing remains.
  4. Keep evidence that proves what was done without storing who it was done for.
  5. Pair contract-driven erasure with Delta vacuum and access controls; each covers a part the others do not.

See it in the reference project

The RideFlow repository contains the domain compliance blocks, the erasure and retention jobs and the notebooks behind them. For personal data that starts life in PDFs and photos, read 90 PDFs in, 0 fields out.

Ready to build trusted data products?

See LakeLogic in action.