Compliance & PII Masking
Protect sensitive data throughout the reconciliation pipeline with PII masking, audit trails, and automated retention enforcement.
Overview
Healthcare, financial, and global organizations need identity resolution that respects data privacy regulations. Kanoniv's compliance system lets you declare which fields contain PII, whether audit trails are mandatory, and how long data should be retained. The engine enforces these settings at runtime.
Cloud Feature
Compliance configuration requires the Cloud tier.
Spec Configuration
Add a compliance block to your entity definition:
entity:
name: patient
description: Healthcare patient identity
compliance:
frameworks:
- hipaa
- gdpr
pii_fields:
- email
- phone
- ssn
- date_of_birth
audit_required: true
retention_days: 2555 # 7 yearsFields
| Field | Type | Required | Runtime Behavior |
|---|---|---|---|
pii_fields | string[] | No | Fields listed here are masked with *** in audit logs, match results, and API responses for non-admin callers |
audit_required | bool | No | When true, forces audit trail logging for every match decision (overrides audit.log_all_comparisons) |
retention_days | int | No | Cleanup pipeline deletes external entities older than this many days |
frameworks | string[] | No | Declarative tags stored in the compiled plan. Not enforced at runtime; see below |
The frameworks field
The frameworks field accepts an array of strings (e.g., hipaa, gdpr, soc2, ccpa) that are compiled into the identity plan and stored alongside your configuration. However, these values are never read or acted upon by any runtime code path. No framework-specific enforcement logic exists. There is no automatic mapping from hipaa to stricter retention, from gdpr to right-to-erasure behavior, or from soc2 to additional access controls.
The frameworks field is useful for:
- Audit log filtering: query which identity plans operate under a given regulatory framework
- Organizational documentation: declare intent so that teams reviewing specs understand which regulations apply
- CI/CD policy enforcement: external tooling (linters, policy-as-code) can read the compiled plan and validate that
pii_fields,audit_required, andretention_daysare configured appropriately for the declared frameworks
All actual runtime enforcement comes from the other three fields: pii_fields, audit_required, and retention_days. If you need HIPAA-grade protection, you must explicitly configure those fields yourself.
What Each Field Does at Runtime
pii_fields: PII Masking
Fields listed in pii_fields are masked in two places:
1. Audit logs and match results (pipeline)
During reconciliation, the worker collects PII field names from both the entity-level compliance.pii_fields list and any source-level pii_fields declarations. Before persisting match results and audit records, field values that match a PII field name are replaced with ***. Raw values are used in-memory for matching but never written to storage unmasked.
2. API responses (canonical entity reads)
When a non-admin caller reads a canonical entity through the API, any keys in canonical_data that match a declared PII field are replaced with ***. Admin callers see the full unmasked data.
import httpx
# Admin key -- sees full data
resp = httpx.get(
"https://api.kanoniv.com/v1/entities/ent_123",
headers={"X-API-Key": "kn_admin_..."},
)
entity = resp.json()
print(entity["canonical_data"]["email"])
# -> "[email protected]"
# Regular key -- PII is masked
resp = httpx.get(
"https://api.kanoniv.com/v1/entities/ent_123",
headers={"X-API-Key": "kn_readonly_..."},
)
entity = resp.json()
print(entity["canonical_data"]["email"])
# -> "***"# Admin key -- full data
curl "https://api.kanoniv.com/v1/entities/ent_123" \
-H "X-API-Key: kn_admin_..."
# -> { "canonical_data": { "email": "[email protected]", ... } }
# Non-admin key -- masked PII
curl "https://api.kanoniv.com/v1/entities/ent_123" \
-H "X-API-Key: kn_readonly_..."
# -> { "canonical_data": { "email": "***", ... } }audit_required: Forced Audit Trail
When audit_required is set to true, the pipeline logs an audit record for every match decision, including NoMerge outcomes that would otherwise be skipped. This overrides the per-decision filtering controlled by audit.log_decisions and audit.log_all_comparisons.
Without audit_required, audit logging follows the settings in the decision.audit block:
decision:
audit:
log_all_comparisons: false # Only logs non-NoMerge decisions by default
log_decisions: trueWith compliance.audit_required: true, every comparison is logged regardless of those settings.
retention_days: Automated Data Cleanup
When retention_days is set, the cleanup pipeline (which runs periodically) deletes external entities older than the specified number of days:
DELETE FROM external_entities
WHERE tenant_id = $1
AND entity_type = $2
AND created_at < NOW() - '<retention_days> days'::intervalDeletions are logged with the tenant ID, entity type, retention period, and count of removed records.
Schema-Level PII Declaration
In addition to the entity-level pii_fields list, you can declare PII at the source schema level:
sources:
- name: crm
adapter: postgres
location: public.contacts
primary_key: id
schema:
email:
type: string
pii: true
ssn:
type: string
pii: true
name:
type: string
nullable: trueFields marked pii: true in the schema are automatically included in compliance masking, even if not listed in pii_fields. At runtime, the pipeline merges both sets before applying masking.
WARNING
If a source declares PII fields in its schema but no compliance block exists on the entity, the validator will emit a warning. Always pair PII declarations with a compliance config.
Full Example
api_version: kanoniv/v2
identity_version: patient_v2
metadata:
name: Patient Identity Resolution
owner: data-engineering
tags:
- healthcare
- production
entity:
name: patient
description: Cross-system patient matching
compliance:
frameworks:
- hipaa
pii_fields:
- email
- phone
- ssn
- date_of_birth
audit_required: true
retention_days: 2555
sources:
- name: ehr
adapter: postgres
location: clinical.patients
primary_key: patient_id
schema:
email:
type: string
pii: true
phone:
type: string
pii: true
ssn:
type: string
pii: true
date_of_birth:
type: date
pii: true
name:
type: string
attributes:
email: email_address
phone: phone_number
name: full_name
- name: claims
adapter: snowflake
location: warehouse.claims.members
primary_key: member_id
attributes:
email: member_email
phone: contact_phone
name: member_name
rules:
- type: exact
name: email_match
field: email
weight: 1.0
- type: similarity
name: name_match
field: name
algorithm: jaro_winkler
threshold: 0.88
weight: 0.7
decision:
thresholds:
match: 0.9
review: 0.7
audit:
log_all_comparisons: true
log_decisions: true
log_conflicts: trueIn this example:
pii_fieldsensures thatemail,phone,ssn, anddate_of_birthare masked in all persisted match results, audit logs, and non-admin API responsesaudit_required: trueguarantees every comparison is logged, evenNoMergedecisionsretention_days: 2555causes the cleanup pipeline to delete entities older than ~7 yearsframeworks: [hipaa]is stored as a declarative tag but does not trigger any automatic behavior
Next Steps
- Audit Logs: Configure detailed match trail logging
- SIEM Integration: Stream compliance events to security tools
