Profiles and coverage
Profiles
A profile is the policy for one scrub. The control plane names it in the claim (absent: strict); the runner
resolves it with profiles.get, fails the job on an unknown or unavailable name, activates it, and reports
profiles.summary(p) (id, version, enforced fields, pending fields). HIPAA profiles are always strict
underneath (extends: strict) and add their own date, linkage and stand-in rules.
profiles.json 1.3.0; default strict.
| Field | Strict (strict) |
Standard (standard) |
B2B (b2b) |
Strict, HIPAA Safe Harbor (hipaa_safe_harbor) |
Strict, HIPAA Expert Determination (hipaa_expert) |
|---|---|---|---|---|---|
| dates | deal_offset | deal_offset | deal_offset | year | deal_offset |
| zip3 | yes | no | no | yes | yes |
| age_cap | 90 | – | – | 90 | 90 |
| faces | blur | blur | blur | blur | blur |
| voices | hold | hold | hold | hold | hold |
| special_categories | hold | flag | flag | hold | hold |
| minors | hold | flag | flag | hold | hold |
| third_party_companies | tokenize | tokenize | keep | tokenize | tokenize |
| fallback_allowed | no | yes | yes | no | no |
| linkage_table | risk_checked | risk_checked | risk_checked | off | risk_checked |
| stand_ins | keyed | keyed | keyed | random | random |
| skus | keep | keep | keep | keep | keep |
| surrogates | tags | tags | tags | tags | tags |
- Enforced fields:
dates,zip3,age_cap,stand_ins,fallback_allowed,third_party_companies,linkage_table,special_categories,minors,faces,voices,skus,surrogates - Pending capabilities (
profiles.MISSING): none - Allowed values:
datesyear, deal_offset, keep;zip3yes, no;age_capinteger from 18 to 120, or null for no cap;facesblur, keep;voiceshold, keep;special_categorieshold, flag;minorshold, flag;third_party_companiestokenize, keep;fallback_allowedyes, no;linkage_tablerisk_checked, off;stand_inskeyed, random;skuskeep, tokenize;surrogatestags, realistic - Expert Determination certificate fields:
id,expert,scope,expiresAt
What each field does:
| Field | Effect |
|---|---|
dates |
deal_offset: every date shifted by the deal's keyed whole-week offset; year: every date element but the year removed (fixed-width healthcare date fields become the year's first day); keep: unchanged. Birth dates go to the year under every mode but keep |
zip3 |
ZIP columns cut to 3 digits; the restricted ZIP3 areas become 000 |
age_cap |
age columns at or over the cap become <cap>+ |
faces |
blur: faces boxed in images and video frames |
voices |
hold: audio with speech is held (its scrubbed transcript still ships) |
special_categories, minors |
hold: the record (or file, where there are no records) is held; flag: counted and reported, shipped scrubbed |
third_party_companies |
tokenize: other companies' names become [ORG_…]; keep: they ship |
fallback_allowed |
false: a missing or mismatched model, or a detector error, fails the job or holds the file instead of falling back |
linkage_table |
risk_checked: the person-linkage table ships only for people in a linked-view class of at least k (held whole when more than half would be cut); off: no table |
stand_ins |
keyed: HMAC stand-ins; random: CSPRNG tokens held in the sealed identity map (needs the map, fails closed without it) |
skus |
tokenize: product codes become stand-ins; keep: they ship |
surrogates |
tags: bracketed stand-ins; realistic: plausible fake names, emails and phones (never with random stand-ins or a HIPAA profile) |
Expert Determination (hipaa_expert) also needs a certificate reference in the claim (id, expert, scope,
expiry). profiles.check_certificate checks its shape and that it hasn't expired; it can't check that the
determination is real or covers this data. That is the expert's and the control plane's job.
Sensitive attributes
GDPR Art. 9 special categories, Art. 10 criminal data and minors, by sensitive.json. Text is classified per
row, record or message where the format has records, and per file otherwise; code, config and git history are
not read for them. A held record is dropped and counted (sensitive_held:<reason>); a held file is listed
with the reason. The desk can release the releasable reasons for a source; the never-released ones can't be.
sensitive.json 1.0.0. Person cue window: 80 characters; 149 person cues; 62 global cancel contexts.
| Category | Strong | Medium | Weak | Not | Column rule |
|---|---|---|---|---|---|
| health | 113 | 59 | 28 | 12 | yes |
| racial_ethnic | 17 | 7 | 17 | 4 | yes |
| political | 21 | 3 | 21 | 6 | yes |
| religion | 19 | 33 | 44 | 9 | yes |
| trade_union | 21 | 8 | 19 | 4 | yes |
| genetic | 22 | 5 | 8 | 3 | yes |
| biometric | 23 | 3 | 7 | 4 | yes |
| sex_life | 23 | 8 | 10 | 5 | yes |
| criminal | 50 | 14 | 18 | 13 | yes |
| minor | 37 | 11 | 32 | 5 | yes |
| sexual content | 15 | 0 | 5 | 2 | – |
- Tiers: strong: flags on its own. medium: a term specific to the category; flags beside a person (within window chars). weak: a common word; needs a person and a second distinct hit. not: contexts that cancel any hit they overlap.
- Releasable by the desk:
health,racial_ethnic,political,religion,trade_union,genetic,biometric,sex_life,criminal,minor,image_unclassified,pending_detector - Never released:
sexual_minor,image_sexual,escalated,voice
Media: the image classifier and voice detector hooks exist (policy.image, policy.audio) but no classifier
is registered in the reference runner yet. Under a profile with no fallback, an image that would need one is
held (image_unclassified, pending_detector). Not live: image content classification.
Coverage matrix
coverage.json maps every identifier class the package claims to handle to the rules that detect it and the
corpus cases that prove it. tests/test_coverage.py fails when a row cites a rule or case that doesn't exist,
cites a known-gap case as proof, or cites no case at all. Status words:
- covered: rules and passing cases for the class as defined;
- partial: rules and cases exist, but part of the class is missed (the note says which);
- planned: not handled yet; the phase names the plan step that builds it.
coverage.json 1.0.0.
HIPAA Safe Harbor, 45 CFR 164.514(b)(2)(i)
| Row | Ref | Identifier | Status | Phase |
|---|---|---|---|---|
| hipaa.A | 164.514(b)(2)(i)(A) | Names | partial | ensemble |
| hipaa.B | 164.514(b)(2)(i)(B) | Geographic subdivisions smaller than a state, ZIP beyond the first 3 digits | partial | ensemble |
| hipaa.C | 164.514(b)(2)(i)(C) | Dates (except year) related to an individual; ages over 89 | partial | hipaa |
| hipaa.D | 164.514(b)(2)(i)(D) | Telephone numbers | covered | – |
| hipaa.E | 164.514(b)(2)(i)(E) | Fax numbers | covered | – |
| hipaa.F | 164.514(b)(2)(i)(F) | Email addresses | covered | – |
| hipaa.G | 164.514(b)(2)(i)(G) | Social Security numbers | covered | – |
| hipaa.H | 164.514(b)(2)(i)(H) | Medical record numbers | covered | – |
| hipaa.I | 164.514(b)(2)(i)(I) | Health plan beneficiary numbers | partial | ensemble |
| hipaa.J | 164.514(b)(2)(i)(J) | Account numbers | covered | – |
| hipaa.K | 164.514(b)(2)(i)(K) | Certificate and license numbers | partial | ensemble |
| hipaa.L | 164.514(b)(2)(i)(L) | Vehicle identifiers and serial numbers, including license plates | partial | images |
| hipaa.M | 164.514(b)(2)(i)(M) | Device identifiers and serial numbers | partial | ensemble |
| hipaa.N | 164.514(b)(2)(i)(N) | Web URLs | partial | ensemble |
| hipaa.O | 164.514(b)(2)(i)(O) | IP addresses | covered | – |
| hipaa.P | 164.514(b)(2)(i)(P) | Biometric identifiers, including finger and voice prints | planned | av |
| hipaa.Q | 164.514(b)(2)(i)(Q) | Full-face photographs and comparable images | planned | images |
| hipaa.R | 164.514(b)(2)(i)(R) | Any other unique identifying number, characteristic or code | partial | workflow |
GDPR
| Row | Ref | Identifier | Status | Phase |
|---|---|---|---|---|
| gdpr.name | Art. 4(1) name | Names | partial | ensemble |
| gdpr.identification_number | Art. 4(1) identification number | National and tax identification numbers | partial | ensemble |
| gdpr.location | Art. 4(1) location data | Location data | partial | ensemble |
| gdpr.online_identifier | Art. 4(1) online identifier; Recital 30 | Online identifiers: IP, cookie, device and ad ids | partial | ensemble |
| gdpr.contact | Art. 4(1) | Contact details: email and phone | covered | – |
| gdpr.financial | Art. 4(1) | Bank and payment identifiers | covered | – |
| gdpr.special_categories | Art. 9(1) | Special categories: health, racial or ethnic origin, political opinions, religious beliefs, union membership, genetic, biometric, sex life or orientation | partial | sensitive |
| gdpr.criminal | Art. 10 | Criminal convictions and offences | partial | sensitive |
| gdpr.children | Art. 8 | Children's data | partial | sensitive |
| gdpr.identity_factors | Art. 4(1) physical, physiological, genetic, mental, economic, cultural or social identity | Quasi-identifiers in combination | planned | surrogates |
PCI DSS cardholder data
| Row | Ref | Identifier | Status | Phase |
|---|---|---|---|---|
| pci.pan | PCI DSS 3.3, 3.4 | Primary account number | partial | ensemble |
| pci.cardholder_name | PCI DSS cardholder data | Cardholder name | partial | ensemble |
| pci.expiry | PCI DSS cardholder data | Expiration date | partial | ensemble |
| pci.cvv | PCI DSS 3.3.1.2 sensitive authentication data | Card verification code | partial | ensemble |
| pci.track | PCI DSS 3.3.1.1 sensitive authentication data | Full track data | covered | – |
Totals: hipaa 7 covered, 9 partial, 2 planned; gdpr 2 covered, 7 partial, 1 planned; pci 1 covered, 4 partial Rules, corpus cases and each row's note on what it misses are in the auditor edition.
Two notes in the matrix are older than the code. hipaa.P (voice prints) says the voices field is pending and
hipaa.Q (full-face photographs) says the faces field is pending and images are redacted for text only; at
this version profiles.json enforces both fields and the reference runner boxes faces with its pinned face
detector. The status words (planned) and notes will change in the next coverage.json version, which also
changes every report's engine.rules hash; until then the matrix is the conservative reading.
The report's coverageOpen metric counts partial and planned rows, and the Strong privacy band needs it at 0. With
the rows above open, no report can reach Strong privacy at this version; Good is the ceiling.