Attribute Sensitivity
Operator runbook for IdentityMesh’s per-attribute sensitivity
classification — schema-level metadata that lets you mark each
attribute as Public, Internal, Confidential, or Restricted.
Phase 1 today: the classification is metadata only and surfaces in
the schema view; later releases consume it for data masking and
DSAR scoping. Maps to ISO 27001:2022 Annex A 5.12 (Classification
of information) and 5.13 (Labelling of information).
Why it exists
Auditors and compliance reviewers ask a sharp version of the same question every time: “How do you tell the difference between someone’s display name and their salary?” Without a classification taxonomy at the schema level, every downstream tool — masking, exports, DSARs, audit-log redaction — has to invent its own ad-hoc rule for “is this field sensitive?” and they all drift.
The Sensitivity column on IM_MeshAttributeSchema gives you a
single, authoritative answer that everything else can build on.
The four levels
The taxonomy is deliberately small. Four levels is the standard pattern in 27001 / NIST guidance because it fits neatly onto operator intuition and avoids decision paralysis when you classify a new attribute.
| Level | Numeric | Examples | Default visibility |
|---|---|---|---|
Public | 0 | display name, login alias, public email | Anywhere — including unauthenticated |
Internal | 1 | department, manager, location, job title | Authenticated operators (default) |
Confidential | 2 | cost-center, internal employee IDs, badge numbers | Operators with explicit need-to-know |
Restricted | 3 | SSN / TFN, salary, health markers, passport number | Tightly held; auditing every access |
The numeric values are stable across releases — they form the storage and wire contract. They will never be renumbered. New levels, if ever added, will use new numbers above 3.
Internal is the default. When you create a new attribute schema
entry without specifying a level, it lands at Internal. When the
ER-049 migration runs against an existing database, every existing
row gets Internal too. This is deliberate: it’s the
safe-but-visible level, and it forces a conscious decision to
upgrade something to Confidential or Restricted rather than
silently downgrading anything to Public.
How to set it
On creation
POST /api/attribute-schemas accepts an optional sensitivity
property in the request body. Either form works:
{
"attributeName": "salary",
"objectType": "User",
"dataType": "int",
"sensitivity": "Restricted"
}
{
"attributeName": "costCenter",
"objectType": "User",
"dataType": "string",
"sensitivity": 2
}
Omit it and you get Internal.
On an existing entry
Two routes update the classification:
-
PUT /api/attribute-schemas/{id}— the existing full-update route. Includesensitivityto change it; omit it and the stored value is left intact (a legacy client that doesn’t know about the field can’t accidentally down-classify anything). -
PATCH /api/attribute-schemas/{id}/sensitivity— the focused single-field route. This is the one to use when all you’re doing is reclassifying:PATCH /api/attribute-schemas/42/sensitivity Content-Type: application/json { "sensitivity": "Confidential" }
Both routes are gated on the schema.write permission and are
recorded automatically in IM_AdminAudit by the admin-audit
middleware. The auditor’s question — “who reclassified this
field, when?” — has a single-query answer.
Reading the classification
GET /api/attribute-schemas and GET /api/attribute-schemas/{id}
both return sensitivity on every entry.
What it does today
- Stored as an
INT NOT NULLcolumn onIM_MeshAttributeSchemawith a column DEFAULT of1(Internal), so legacy rows are unambiguous. - Returned on every schema read, so the Admin UI and any third-party tooling that reads the schema can render a classification badge alongside each attribute.
- Filterable in EF queries:
Where(s => s.Sensitivity == Sensitivity.Confidential)works directly. - Audit-trailed: every change goes through
schema.write, so the admin-audit middleware records the before/after and the change is part of the tamper-evident hash chain (audit-chain.md).
What it does NOT do today (yet)
Phase 1 is metadata only. These are explicitly out of scope for this release and will land in follow-up work:
- No automatic redaction in audit logs. A
Restrictedattribute’s value still appears inIM_ObjectAudit.ChangeJsonthe same as any other field. Audit-log redaction by sensitivity is part of the data-masking work (engineering-internal ER-048). - No role-based field-level access enforcement. A user with
mesh.readcan read every attribute regardless of its classification. Sensitivity-aware authorization is also part of ER-048. - No automatic propagation to exports. Connector exports carry every attribute the projection rule says to carry — classification doesn’t yet filter the outgoing payload.
- No DSAR scoping. When a Data Subject Access Request runbook lands, it will use sensitivity to scope what’s auto-included vs. what needs explicit operator approval — but that’s not in this release.
These gaps are intentional. Phase 1 establishes the taxonomy and the system of record; phases 2 and 3 build the enforcement and redaction layers on top.
Choosing the right level
A few rules of thumb:
- If the field would appear on a public org chart or a “from”
line in an email, it’s
Public. - If you’d be comfortable putting it on an internal directory
page that anyone in the company can read, it’s
Internal. - If access requires a business reason — finance roles, security
roles, manager-of-record — it’s
Confidential. - If a leak triggers a regulatory notification (GDPR Art. 33,
HIPAA breach notification, state PII statutes), it’s
Restricted.
When in doubt, classify higher. You can always reclassify down once a field is in production and you’ve watched the access patterns; reclassifying a leaked field up after the fact is a much harder conversation.
Related documents
audit-retention.md— how the admin-audit and object-audit rows that record classification changes age out.audit-chain.md— how those audit rows are tamper-evident, so the “who reclassified what, when” answer is provably intact.- The internal compliance dossier (
compliance-iso27001.mdin the engineering repo, not published here) carries the cross-reference back to Annex A 5.12 / 5.13.