cadrumo.core.classification.policies module¶
Sensitivity classification primitives for persisted state.
Every persisted record (SQL row, file-backed envelope, blob, secret-store
entry, audit-log entry) declares a SensitivityClass. Each class maps to
a default ClassificationPolicy resolved by default_policy_for();
the policy pins the at-rest treatment (plaintext or ciphertext-required),
retention behaviour, and the redaction rule references that the audit sink and
run-trace path honour.
The default policy table is the single point of truth. Per-domain
repositories MAY override the default for an individual record (e.g.
when an operator tags a corpus blob as identity-bearing), but the
default is always available via default_policy_for(). Redaction
rule references stored as names are resolved to live
RedactionRule instances by core.redaction.
- class SensitivityClass(*values)[source]¶
Bases:
StrEnumClosed catalogue of sensitivity classes for persisted state.
- Variables:
SECRET – Long-lived authentication material — OAuth client secrets, service-account private keys, certificate passphrases, refresh tokens. Treatment: ciphertext at rest; never logged; deletion verifiable; finite TTL required.
SESSION – Short-lived bearer state — Playwright
storage_state, OAuth access-token caches, AEAT session sidecars. Treatment: ciphertext at rest; integrity-bound to the providing factor.IDENTITY – Operator and taxpayer identity records — NIF, full name, contact email, business profile. Treatment: ciphertext at rest where the field is not needed for query; never logged at INFO or above.
FINANCIAL – Bank transaction rows, invoice records, attachment blobs, usage ratios, draft and submission payloads, amendment records. Treatment: ciphertext at rest by default; redaction rules govern log echo; retention is consumer-owned and separately governed.
AUDIT – Submission audit log, run-trace records, divergence records, workflow-run records. Treatment: redaction at write time (NIF hashed, URL host-only, token fingerprinted) on top of ciphertext at rest for the underlying record.
CACHE – LLM response cache, schema cache, status cache, corpora-derived caches. Treatment: defaults to plaintext for public reference data; identity-bearing caches escalate to IDENTITY.
CORPUS – Public reference material — manuals, normatives, BOE PDFs, registry definitions. Treatment: plaintext at rest is acceptable; integrity (SHA-256) MUST still be tracked.
OPERATIONAL – Settings, build manifests, low-sensitivity configuration that legitimately remains in plaintext.
DIAGNOSTIC –
scratch/outputs, browser traces, screenshots, network captures. Treatment: governed retention default (e.g. seven days); explicit redaction; opt-in capture.
- SECRET¶
- SESSION¶
- IDENTITY¶
- FINANCIAL¶
- AUDIT¶
- CACHE¶
- CORPUS¶
- OPERATIONAL¶
- DIAGNOSTIC¶
- class AtRestTreatment(*values)[source]¶
Bases:
StrEnumClosed catalogue of at-rest data-protection treatments.
- Variables:
PLAINTEXT – The record is stored as-is, with integrity tracking but without confidentiality protection. Acceptable for CORPUS and OPERATIONAL classes.
CIPHERTEXT_REQUIRED – The record MUST be stored as ciphertext. Repositories enforce this at write time and refuse mismatches with
ClassificationError.
- PLAINTEXT¶
- CIPHERTEXT_REQUIRED¶
- class RetentionPolicy(**data)[source]¶
Bases:
BaseModelRetention envelope for a
SensitivityClass.Only
require_explicit_expirybinds. It is read at the secret store’s write door, which refuses a record of a class demanding an expiry that does not carry one.max_ageandarchive_afterare declared here and on the shipped policy table and are read by nothing – not by a repository, not by a gate, not by a test. Treat a value in either as a statement of intent, never as a guarantee some caller already honours.Wiring
max_ageup is not a matter of finding its missing reader. The field is currently an unused declaration and must not be treated as a statutory retention rule. Any operational retention boundary is owned by the consumer that enforces it.The retention that does ship – LLM usage and run telemetry pruning, external session-file pruning – runs on its own consumer-owned bounds and never consults this policy. Their working retention is not evidence that these two fields do anything.
- Variables:
max_age – Intended maximum live-record age.
Nonemeans unbounded (e.g. for CORPUS material whose lifetime is the project lifetime). Unread; see above.archive_after – Intended age at which a record should be archived from the live store.
Nonemeans archival is not policy- mandated. Unread; no archival path consults it.require_explicit_expiry – When
True, a record of this class MUST carry an explicitexpires_atfield at write time. Set for SECRET and SESSION, and enforced for them.
- Parameters:
- max_age: timedelta | None¶
- archive_after: timedelta | None¶
- require_explicit_expiry: bool¶
- class RedactionStrategy(*values)[source]¶
Bases:
StrEnumClosed catalogue of redaction strategies applied at write time.
- Variables:
SHA256_PREFIX – Replace the matched value with the first eight hex characters of its SHA-256 digest.
SHA256_PREFIX_IF_IDENTITY – As
SHA256_PREFIX, but only when the matched span has the lexical shape of a Spanish tax identity; filing-grade leader and checksum validation remains an explicit authority operation at the owning boundary. This keeps the core redaction primitive independent of the runtime registry.SHA256_PREFIX_IF_IBAN – As
SHA256_PREFIX, but only when the matched span passes the ISO 13616 mod-97 check. An IBAN shape is a long alphanumeric run that collides freely with hashes, opaque ids, and tokens; the checksum is what makes matching one safe.SHA256_PREFIX_IF_NIF_IVA – As
SHA256_PREFIX, but only when the matched span is an IVA identification number – anES-prefixed Spanish identity, or another Member State’s number matching the structure its own prefix claims. The bare Spanish shapes are covered by the two strategies above and this one covers the PREFIXED form, which they miss: the prefix defeats the word boundary their patterns anchor on, soESB12345674survived a funnel that redactsB12345674. The prefix is a claim and not evidence, so the per-State structure decides, exactly as the checksum decides for an IBAN.HOST_ONLY – For URL-shaped values, retain only the host component; drop path, query, and fragment.
FINGERPRINT – Replace bearer / OAuth token-shaped values with their SHA-256 fingerprint formatted as
token:sha256:<8hex>.ELLIPSIS – Replace the matched value with three ASCII-safe full stops (
...).
- SHA256_PREFIX¶
- SHA256_PREFIX_IF_IDENTITY¶
- SHA256_PREFIX_IF_IBAN¶
- SHA256_PREFIX_IF_NIF_IVA¶
- HOST_ONLY¶
- FINGERPRINT¶
- ELLIPSIS¶
- class RedactionRule(**data)[source]¶
Bases:
BaseModelOne rule applied at write time by the audit sink and run-trace path.
The rule shape is stable;
core.redaction.rules.redact()andcore.redaction.rules.redact_structured()consume tuples of rules and apply them in order.- Variables:
name – Stable identifier for log diagnostics. Lowercase kebab-case.
pattern – Regex (Python
reflavour) matched against string values to detect the field shape that should be redacted. The regex is compiled at use time, not at declaration; rules can be loaded from configuration without requiring a working regex compiler at import.strategy – How the matched value is rewritten.
- Parameters:
name (str)
pattern (str)
strategy (RedactionStrategy)
Where a rule applies is NOT declared here. It is decided by the policies that name the rule in their
redaction_rules, andcore.redaction.rules.default_rules_for()reads nothing else. This record once also carried anapplies_totuple of sensitivity classes, which was a second declaration of that same fact: never consulted, free to disagree with the policy table, and typed againstSensitivityClassso it could not describe the OUTPUT policy table at all – the table that decides operator-facing redaction. A duplicate the code ignores can only drift into a lie, so the fact is declared once, where it is read.- name: str¶
- pattern: str¶
- strategy: RedactionStrategy¶
- class ClassificationPolicy(**data)[source]¶
Bases:
BaseModelDefault policy attached to one
SensitivityClass.Per-domain repositories consult this record at write time to decide on encryption, retention, and redaction. Per-record overrides are supported by the consumer; the default is always available via
default_policy_for().- Variables:
sensitivity – The class this policy governs.
at_rest – At-rest treatment.
retention – Retention behaviour.
redaction_rules – Tuple of redaction-rule names (matched by
RedactionRule.name) that apply when this class participates in audit-sink writes. Resolution to liveRedactionRuleinstances is performed bycore.redaction.rules.default_rules_for(); the policy carries names only so the table can be loaded eagerly without depending on the rule registry.
- Parameters:
sensitivity (SensitivityClass)
at_rest (AtRestTreatment)
retention (RetentionPolicy)
- sensitivity: SensitivityClass¶
- at_rest: AtRestTreatment¶
- retention: RetentionPolicy¶
- redaction_rules: tuple[str, ...]¶
- default_policy_for(sensitivity)[source]¶
Return the default
ClassificationPolicyforsensitivity.- Parameters:
sensitivity (
SensitivityClass) – TheSensitivityClassto look up.- Return type:
- Returns:
The default policy. The returned record is frozen and shared; callers must not mutate it. Per-record overrides are made by constructing a fresh
ClassificationPolicy.