# 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 display name
identifies them, and their email address is contact data without saying so
([Contact details and names need no mark](/docs/marking-personal-fields.md#contact-details-and-names-need-no-mark)).

`domains/accounts/entities/user.jadpo`:

```jadpo
type DisplayName = Text {
    max_length: 80
}

entity User {
    id: Uuid
    email: Email {
        unique: true
    }
    display_name: DisplayName {
        personal: PersonalData.identifying
    }

    person: true
}
```

`Email` and `PersonalData` are built in. `DisplayName` and `User` are defined
here. `person: true` says each `User` record is a person, so erasure can find
the marked field ([The person](/docs/person-entities.md)).

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.

`personal:` goes on a field of an entity, an event or an email, or of an
object, input or output a package declares, wherever Jadpo keeps or sends the
value. A provider package marks
what its API takes and gives, so an app sees what it is sending and
receiving without reading the provider's documentation. Here the package
says a customer's phone number is contact data, whatever the app fills it
with:

```jadpo
// packages/stripe/v2024_06_20/types/customer_request.jadpo
/// A customer to create.
public type CustomerRequest = Input {
    phone: Text {
        personal: PersonalData.contact
    }
}
```

The category is checked the same way everywhere (`type.setting_value`). An
app's own object, input or output carries no mark
(`type.unknown_setting`): it holds what a marked field gave it, and
Jadpo follows the value back to that field.

## Contact details and names need no mark

These standard types are personal data without a `personal:`, on a field of the
type or of one built on it, and so is a value of one wherever it goes:

| Type | Counts as |
|---|---|
| `Email`, `EmailWithName` | `PersonalData.contact` |
| `PhoneNumber` | `PersonalData.contact` |
| `PostalAddress` | `PersonalData.contact` |
| `PersonName`, `GivenName`, `FamilyName` | `PersonalData.identifying` |

- It is hashed in logs, and [the report](/docs/the-personal-data-report.md#where-personal-data-goes) lists each
  connection it reaches, the address an email is sent to among them. A
  connection that can't erase it needs
  [an acceptance](/docs/erasure-at-providers.md).
- A model is sent one only where `ai: { personal_data: { allowed: } }` lists its
  category
  ([Personal data sent to a model](/docs/safety-and-privacy.md#personal-data-sent-to-a-model)),
  [a public cache](/docs/output-and-status.md#answers-that-never-change) can't keep one, and an
  [audited entity](/docs/audit-trail.md) can't hold one yet.
- Erasure clears it as it clears a marked field, so on a record erasure keeps
  it is optional, or says `erase: Erasure.retained` on a person's own record
  ([Erasure](/docs/erasure.md)).
- [The first record](/docs/comparing-two-builds.md#what-a-first-record-holds) that
  lets anyone read one, with no sign-in, says so to a person.

Here an order is kept when its buyer is erased, and what it was sent to goes:

```jadpo
entity User {
    id: Uuid
    email: Email

    person: true
}

entity Order {
    id: Uuid
    buyer_id: User.id? {
        on_delete: OnDelete.cleared
    }
    recipient: PersonName?
    ship_to: PostalAddress?
    delivery_phone: PhoneNumber?
}
```

Written `delivery_phone: PhoneNumber`, it couldn't be cleared
([`data.erase_field_required`](/docs/errors/data.erase_field_required.md)):

```jadpo
entity User {
    id: Uuid
    email: Email

    person: true
}

entity Order {
    id: Uuid
    buyer_id: User.id? {
        on_delete: OnDelete.cleared
    }
    delivery_phone: PhoneNumber
}
```

```text
error: Erasing a `User` can't clear `Order.delivery_phone`
 --> main.jadpo:13:5
   |
10 |     buyer_id: User.id? {
   |     -------- keeps the record without the person
13 |     delivery_phone: PhoneNumber
   |     ^^^^^^^^^^^^^^ personal data that can't be `none`
  reason: `buyer_id` is `OnDelete.cleared`, which means `Erasure.anonymised`: erasing a `User` keeps each order that refers to them, clearing the reference and every marked field. `delivery_phone` is a phone number, which counts as `PersonalData.contact`, and only an optional field (`PhoneNumber?`) can be cleared.
  next: Make `delivery_phone` `PhoneNumber?`, or say `erase: Erasure.deleted` on `buyer_id` to delete the order instead (human)
  rule: data.erase_field_required
  help: https://jadpo.dev/docs/errors/data.erase_field_required

check failed: 1 error, 1 file
```

The other standard types that are personal data, such as a `PostalCode` or a
`Username`, are hashed, listed and checked the same way, and erasure clears
them only when marked.

A business's own address, phone number or manager's name is one of these too,
and there is no mark that says one isn't personal data. Nothing here stops one
being kept: an entity erasing a person never reaches may hold it, as a shop may
hold its own phone number
([What the compiler checks](/docs/personal-data-checks.md)). It is listed as any
other is, a person reads the notice and goes on, and a connection it reaches
takes a one-line acceptance. On a record erasure keeps it is cleared like a
person's, so a shop's number belongs on the shop, not copied onto each order. A
category written on one, `personal: PersonalData.contact` or any other, is read
as on any field.

## Marked fields in logs

A marked field is written to logs as a keyed hash, whatever its type, and so
is an `Email` or another standard type that is personal data with no mark. So a
marked `date_of_birth: CalendarDate` is hashed, though
[logs](/docs/logging.md) 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](/docs/every-standard-type-meets-one-contract.md).

## Sensitive fields

A sensitive field is never put in a cache, and every read of one is
recorded in the [access records](/docs/access-records.md).

## 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](/docs/person-entities.md)), so it is exported and erased with them. Mark
only the fields whose value points at, or describes, a person.
