The policy engine
A ScaiLog policy is declarative YAML that decides, for every PI-marked field, exactly one of four things to do with it: encrypt, redact, drop, or keep. This page explains how a policy is written, how the engine resolves which rule wins, and how policies are versioned so old entries stay explainable forever.
What a policy decides#
Each PI field carries a pi_type from the taxonomy. When an entry is processed,
the engine resolves an action for every PI field from the tuple
(service, environment, pi_type), yielding exactly one of:
| Action | Effect on the field |
|---|---|
encrypt |
Encrypt the value under the subject's DEK; store as ciphertext in pi_fields. |
redact |
Replace the value with [REDACTED:<pi_type>]; store no PI. |
drop |
Discard the value entirely; record only that a field of this type was dropped. |
keep |
Store the value in plaintext (only appropriate for pseudonymous or DPO-cleared types). |
The action is recorded per field in the entry's pi_meta, so you can always
explain why a given value was encrypted, redacted, dropped, or kept.
A policy is YAML#
A policy is a named, versioned document with a list of rules. Each rule matches
some subset of (service, environment, pi_type) and names an action; a rule can
also set retention_days. The built-in default policy simply falls every type
through to its taxonomy default:
1 2 3 4 5 6 7 | |
action: default means "use the taxonomy default for this pi_type" — so with
the policy above, direct_identifier encrypts, credentials drops,
pseudonymous keeps, and so on, exactly as the taxonomy prescribes. A more
specific policy narrows behaviour, for example:
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
Most-specific rule wins#
Resolution is deterministic. The engine collects every rule that matches the
field's (service, environment, pi_type), then picks the most specific one.
Specificity is scored by which dimensions a rule pins down:
- matching an exact
pi_type(not*) is worth the most, - then a specific
service, - then a specific
environment.
If two rules tie on specificity, the later rule (higher index) wins. If the
chosen rule says action: default, or if no rule matches at all, the engine
falls through to the taxonomy default for that pi_type. A field with a custom
pi_type that has neither a matching rule nor a taxonomy default is dropped —
the safe default is never to store an unrecognized PI value.
Versioning and validation#
Policies are never edited in place. Storing a new policy creates a new version
(the next version number, or a higher one the document names explicitly), and
old versions are kept forever. The resolved policy_version is frozen into every
entry, so you can always reconstruct exactly which policy governed a historical
entry and why each field was handled as it was.
Before a policy is accepted it is validated. Hard errors — such as an unknown
action — raise immediately. Softer issues produce warnings: notably, a keep
action on a PI type other than pseudonymous warns that it stores plaintext PI
and is only lawful for pseudonymous or DPO-cleared custom types.
The two retention fields (entry_retention_days and pi_entry_retention_days)
let PI-bearing entries expire sooner than ordinary entries.