foley.obs.redact
Redaction of sensitive narration / prompt / query text in telemetry (#11).
A published product’s narration is frequently confidential or pre-release, yet foley exfiltrates prompt/query text to LLM, generation, and search backends (report 12 §privacy). So every value foley writes into a span attribute or a run-manifest is passed through this module’s SSOT redactor, which — by default — replaces a sensitive string with a salted content hash + length (never the raw text), so a manifest still joins a prompt to its provenance without exposing it.
Stdlib-only. Applied at TWO boundaries (belt-and-suspenders, mirroring
qc._json_safe’s construction-time clamp + serialization sweep):
record-time (primary): the recorder routes every sensitive value through
Redactor.redact_value()before it enters the in-memorySpanRecord/RunManifest— so raw text never reaches the OTel mirror either;emit-time (net):
Redactor.redact_manifest()deep-walks the serialized manifest before the store write, catching anything stuffed past record-time.
- foley.obs.redact.REDACT_FIELDS: frozenset[str] = frozenset({'context_text', 'gen_ai.completion', 'gen_ai.prompt', 'generation_prompt', 'narration', 'negative_prompt', 'onset', 'prompt', 'query'})
The attribute / field keys whose string values are sensitive and redacted. The narration/prompt/query surfaces report 12 §11 names;
context_text/narration/gen_ai.*producers arrive with the #7 agent, but the seam + keys exist now.
- class foley.obs.redact.RedactionMode(value)[source]
How a sensitive string is rendered in telemetry (
str-Enum → serializes cleanly).
- class foley.obs.redact.Redactor(mode: RedactionMode = RedactionMode.hash, salt: str = 'foley-obs-v1', preview_chars: int = 0, fields: frozenset[str] = <factory>)[source]
The SSOT applier: redacts sensitive keys in values, attribute dicts, and manifests.
- redact_attrs(attrs: dict | None) dict[source]
Redact every sensitive key in a (shallow) attribute/inputs dict.
- redact_error(exc: BaseException) str[source]
Redact an exception for storage/export.
An exception message can echo the raw prompt/query/narration (hosted backends commonly do), so by default only the exception type name is recorded (safe + still useful); the full
repris kept only infullmode (opt-in local debug).
- foley.obs.redact.redact_text(text: str | None, *, mode: RedactionMode = RedactionMode.hash, salt: str = 'foley-obs-v1', preview_chars: int = 0)[source]
Redact one string per
mode.- Parameters:
text – The (possibly sensitive) string, or
None.mode –
off→None;full→textverbatim;hash(default) →{"sha256": <salted hex>, "len": <n>}(+"preview"only ifpreview_chars > 0).salt – Salt mixed into the hash (injectable; default fixed for diffability).
preview_chars – If > 0 (hash mode), include a leading
text[:preview_chars]preview. Default 0 → zero content leak.
- Returns:
None, the raw string, or a hash dict — depending onmode.