Marking personal fields
A field that identifies or describes a person says so in its block, with
personal: and one category from a fixed list. The category decides how
carefully the compiler treats the value. Here a user's email is contact
data and their display name identifies them:
// domains/accounts/entities/user.jadpoentity User { id: Uuid email: Email { unique: true personal: PersonalData.contact } display_name: DisplayName { personal: PersonalData.identifying } ...}
Every marked field needs a category: there is no bare personal: true, and
you can't add categories of your own. These are the fourteen:
PersonalData. |
For | Sensitive |
|---|---|---|
identifying |
Names, usernames, account numbers | |
contact |
Email, phone, postal address | |
financial |
Bank and card details, income, purchases | |
online |
IP address, device and cookie identifiers, browsing | |
demographic |
Date of birth, gender, nationality | |
government_id |
Passport, national insurance, driving licence | yes |
location |
Precise location | yes |
health |
Physical or mental health, including disability | yes |
biometric |
Fingerprints, face data used to identify | yes |
genetic |
Genetic data | yes |
beliefs |
Religion, political opinions, trade union membership | yes |
ethnicity |
Racial or ethnic origin | yes |
sex_life |
Sex life or sexual orientation | yes |
criminal |
Criminal convictions and offences | yes |
"Sensitive" covers GDPR's special categories (Articles 9 and 10) and the extra ones CCPA/CPRA lists: government IDs and precise location.
Marked fields in logs
A marked field is written to logs as a keyed hash, whatever its type. So a
marked date_of_birth: CalendarDate is hashed, though
logs otherwise show dates and numbers as
values, and so is a marked field in a failure's context. The marking, not
the type, decides.
The hash covers the UTF-8 bytes of the value's canonical text, as
text.from gives it: 2026-10-25 for a date, GBP 10.50 for an amount.
A type with no single text form, such as a PostalAddress, is hashed from
its JSON as json.write writes it. So one value hashes the same in every
log and on both executors, because every boundary goes through
the standard types' contract.
Sensitive fields
A sensitive field is never put in a cache, and every read of one is recorded in the access records.
Records a person owns
You don't mark every field of a record a person owns. A todo's title is the user's data because the todo belongs to them (see The person), so it is exported and erased with them. Mark only the fields whose value points at, or describes, a person.
Markdown for agents: /docs/marking-personal-fields.md.Built from Jadpo main at a707efb.