Data masking¶
kafkito can redact parts of Kafka records before they reach a user. Rules are
configured per cluster under clusters[].data_masking in the YAML config and
apply to every path that returns or derives from record data: consume, the
path-picker sample, search, raw download, replay, topic copy and message compare.
Rule reference¶
clusters:
- name: prod
brokers: ["broker-1:9092"]
data_masking:
# Value only (no targets): exactly the behaviour before targets existed.
- topics: ["^orders$", "^user-.*"]
fields: ["$.email", "$.customer.phone"]
replacement: "***"
# Customer ids in the key, JSON or plain text.
- topics: ["^orders$"]
targets: [key]
fields: ["$.customer_id"]
regex:
- match: "cust-[0-9]+"
replacement: "cust-***"
# Tokens in the authorization and x-api-* headers.
- topics: ["^orders$"]
targets: [headers]
headers: ["(?i)^authorization$", "(?i)^x-api-"]
regex:
- match: ".+"
replacement: "[redacted]"
| Key | Type | Meaning |
|---|---|---|
topics |
list of Go regex | Topics the rule applies to; a rule without topics applies to every topic. Patterns are not anchored — use ^…$ for an exact name. |
targets |
list of value, key, headers |
Record parts the rule masks. Omitted or empty = [value], so rules written before targets existed behave exactly as before. |
headers |
list of Go regex | With the headers target: only header keys matching one of the patterns have their values masked. Omitted = every header. Setting it without the headers target is a config error. Header keys themselves are never masked. |
fields |
list of JSONPath | Replaced with replacement when the part is valid JSON (the decoded JSON for Schema Registry payloads). |
regex |
list of {match, replacement} |
Go regex substitutions on the string form of the part, applied after fields. An empty replacement means ***. |
replacement |
string | Replacement for fields; empty = ***. |
Binary parts are masked on their 0x… hex rendering, the same text the UI
shows. All rules matching a topic and a part apply: first the fields of
every such rule, then their regex substitutions, each in config order.
Validation¶
The rules are validated at startup. An unknown target, a headers selector
without the headers target, or a malformed topic/header pattern, JSONPath or
regex fails startup with an error naming the rule, for example:
Behaviour per path¶
| Path | Masked value | Masked key | Masked header value |
|---|---|---|---|
Consume (GET .../messages) and sample (GET .../sample) |
Masked on the full decoded value before the 64 KB preview cut, masked: true, value_b64 omitted |
Masked, key_masked: true, key_b64 omitted |
Masked, header key listed in masked_headers, its headers_b64 entry omitted |
Search (POST .../messages/search) |
Matched in masked form (all modes) | Matched in masked form (contains key zone, js key) |
Matched in masked form (contains headers zone, js headers) |
| Search parse errors | Details withheld on topics with any rule | same | same |
Raw download (GET .../raw) |
403 value_masked |
Allowed — the body is the value only | Allowed — the body is the value only |
Topic copy (POST .../copy) |
Record skipped (skipped) |
Record skipped | Record skipped |
| Replay (UI) | Blocked with the reason shown | Blocked with the reason shown | Blocked with the reason shown |
| Compare messages (UI) | Compared in masked form; Not comparable when only one side is masked; fingerprint over the masked rendering | same | same |
| PathSense suggestions (UI) | Built from the masked rendering; truncated masked values are not hydrated | Not used | Not used |
| Count and timeline | Offsets only, no record data | same | same |
The UI marks masked parts with a crossed-out eye and the word masked
(masked, key masked, headers masked, and per header line in the
detail view).
Private clusters¶
Private clusters (the X-Kafkito-Cluster header) never carry masking: the
user brings their own credentials and sees the raw data. A data_masking
list inside a private cluster config is ignored.