privacy block (PrivacyBlock)¶
How one step treats contract-flagged fields (field_bindings entries
carrying pii: true or restricted: true) at its sink boundary.
The same block appears in two places:
- a pipeline step's contract block —
steps[].contract.privacy(StepContractBlock.privacy); - a stream's contract block —
streams[].contract.privacy(StreamContract.privacy, see Streaming Worker Config).
Model: pycharter.pipeline_artifact.models.PrivacyBlock — frozen,
extra: forbid (an unknown key is a config error, not a silent no-op).
For the workflow — flagging fields, reading the report, preflight rule codes — see the guide Privacy flags.
steps:
- id: load_orders
type: load
input: extract_orders
contract:
ref: {type: file, path: orders.yaml}
privacy:
mode: enforce # off | detect | enforce
clearance: [pii] # flags this sink may receive in clear
target: {type: postgres, db_schema: public, table: orders}
Fields¶
| Field | Type | Default | Notes |
|---|---|---|---|
mode |
enum off | detect | enforce |
detect |
off restores pre-feature behaviour: records untouched, step absent from the report. detect never mutates and counts flagged values that reached the sink in clear. enforce tokenizes pii and redacts restricted immediately before the sink write — after the KB-emitter and extraction-sink tees — unless the flag is listed in clearance. |
clearance |
list of pii | restricted |
[] |
Flags this step's sink is allowed to receive in clear. Declared by the pipeline author, never by the contract, and echoed in the run report. Only honoured on a load step; preflight rejects it elsewhere ([clearance_on_non_load]). An unknown flag is rejected at parse time. |
Absent block means {mode: detect, clearance: []}, so attaching a
flagged contract to an existing step changes no output and starts
reporting.
Handling derived from the flags¶
One derivation, shared by the load boundary, the streaming sink, both DLQ wrappers, preflight, and validation diagnostics:
| Contract flag | Handling at an uncleared sink | Result in the sink |
|---|---|---|
restricted: true |
redact | null (not a sentinel — typed columns stay loadable) |
pii: true |
tokenize | 64-char hex HMAC-SHA256 token, equal to the KB match_token for the same value |
| both | redact | null; the effective flag is restricted, so only a restricted clearance passes it in clear |
| a non-scalar value under tokenize | redact | null, counted as non_scalar_redacted |
Tokenization requires an org secret: PYCHARTER_KB_ORG_SECRET, or
Pipeline.run(privacy_secret=...) / arun(privacy_secret=...). When
none resolves, preflight fails the run with [missing_secret] — a
flagged value is never written in clear as a fallback.
What the step reports¶
Each step running under detect or enforce with a flagged contract
contributes one entry to PipelineResult.privacy_report (also on
StepResult.privacy, the pycharterPrivacy lineage run facet, and the
pycharter pipeline run summary line):
| Key | Meaning |
|---|---|
step_id, mode, sink |
Which boundary, under which mode. sink is credential-free (postgres:public.orders). |
contract_id, contract_version |
The contract whose flags were applied. |
clearance |
Flags this sink was cleared for. |
records |
Records that crossed the boundary. |
fields[] |
Per flagged field: field, flag, handling, cleared, values, handled, non_scalar_redacted. |
dlq_records_handled, dlq_records_withheld |
Quarantined payloads handled, and (streaming) withheld because they could not be classified. |
Counts only — no report, log, or facet ever carries a flagged value.