symfonic.capabilities.memory.scrubbing¶
scrubbing ¶
Credential scrubbing, applied before a memory exists rather than after.
Two scans, because a secret arrives two ways and only one of them is covered by the shipped scrubber:
- Key names —
symfonic.agent.hygienedropspropertieskeys whose names look like credentials (api_key,authorization). That list is restated here (the capability layer imports nothing outside itself) and pinned to the shipped one by a test, because a drift silently un-redacts every future write. - Values — the shipped hygiene module explicitly leaves free-form value
scanning out of scope, so a bearer token pasted into a sentence is stored
verbatim and then recalled into a prompt on some later turn. The value
patterns here are the corpus-scrub set (
symfonic.tools.corpus_scrub), which already ships in this repository for exactly these shapes.
Placement is the point. A memory is scrubbed before the record is minted, so the write port never receives a credential — there is no window in which a secret is persisted and a later pass has to find it again. The same scrubber runs over the turn before it reaches the extraction model, because a secret handed to a provider is disclosed whether or not it is ever stored.
Redaction replaces rather than deletes: [REDACTED:TOKEN] keeps the sentence
readable, which is what makes the surrounding memory still worth recalling, and
makes scrubbing idempotent — a redacted text scrubs to itself.
CredentialScrubber ¶
CredentialScrubber(*, value_patterns: Iterable[tuple[str, Pattern[str]]] | None = None, key_parts: Iterable[str] | None = None)
Removes credentials from memory text and from memory properties.
Build a scrubber.
None selects the built-in set; an empty iterable disables that
scan. The distinction is deliberate and matches the shipped hygiene
contract: switching a scrubber off is something a deployment must say,
not something it can fall into by passing an empty config.
Source code in src/symfonic/capabilities/memory/scrubbing.py
scrub_properties ¶
Drop credential-named keys, and scrub the string values that remain.
Shallow, like the shipped scrubber: graph properties are persisted flat, so a nested dict is not a shape any backend writes.
Source code in src/symfonic/capabilities/memory/scrubbing.py
scrub_text ¶
Replace every credential-shaped value in text.