↑ ↓ to move · Enter to open · Esc to close Something not working? Troubleshooting
    On this page

    Mask Secrets Already Stored

    Suvadu masks secrets before it writes them, so a rule added in an upgrade never reaches what was recorded before it. suv redact (since 0.5.3) applies today's rules to the history you already have.

    Quick Start

    Look first. A dry run lists each changed row's first change, masked, and writes nothing:

    suv redact --dry-run
    # Dry run — nothing will change.
    #
    # history.db: 3 rows
    #   Commands                    3
    #
    # Would become (each row's first change):
    #   Commands: curl -H "x-api-key: ***REDACTED***" https://api.example.com/v1/items
    #   Commands: export GITHUB_TOKEN=***REDACTED***
    #   Commands: psql "postgres://app:***REDACTED***@db.example.com/app"

    Then mask. A real run asks before it changes anything (-y skips the question), copies the database first, and says what it did:

    suv redact
    # Backed up to ~/Library/Application Support/tech.appachi.suvadu/backups/preredact-20261111-110100.db before masking.
    # ✓ history.db: masked 3 rows.

    Run it after an upgrade that adds redaction rules, after adding your own extra_patterns, or once you notice a secret in your history. A second run finds nothing left to mask.

    What It Masks

    Every place Suvadu keeps text that could hold a secret:

    • Commands, and the agent prompt and git branch in a command's context
    • Failure output kept by Keep Failure Output
    • Agent sessions: prompts, replies, saved summaries and git snapshots
    • Cached prompt files that link an agent's commands to the prompt behind them
    • The backups in the backups directory, which keep the schema they were taken under

    It uses the built-in rules, your extra_patterns, and each directory's .suvadu.toml, so a row is masked with the rules of the directory it was recorded in. It runs even with redaction.enabled = false, because asking for it is asking for redaction.

    A secret masked once is masked everywhere

    When a rule masks a value, that value is then masked wherever else it appears, so a key pasted into an agent prompt as plain prose goes as well as the command that used it. Only a literal value of at least 16 characters spreads: password=hunter2 says nothing about grep hunter2, and ${DB_PASSWORD} is a reference rather than a secret. A secret that no rule recognises anywhere stays where it is.

    What it counts but leaves alone

    Bookmarks, notes and aliases are counted and left as they are: you wrote them, and masking one would break it. Edit or remove those yourself.

    The Copy It Keeps

    Masking works by pattern and cannot be undone, so a real run first copies history.db to backups/preredact-<timestamp>.db. If that copy fails, nothing is masked. --no-backup skips it. Older backups are masked in place, with no copy of their own.

    That copy still holds every secret. No later run masks it, and every run names it until you delete it. Check the result, then delete the copy.

    How the Old Text Leaves the File

    Rewriting a row is not enough to remove the old text from disk. The search index marks a replaced term deleted without removing it, a shorter row leaves the start of the old one in freed space, and stale copies sit in the unused space of pages that split while the row was recorded. So each changed file also has its search indexes rebuilt, is compacted with VACUUM, and has its write-ahead log emptied.

    The masked rows are committed with a note in the database header that the file still owes this compaction, so a run that stops in between is finished by the next one rather than reporting nothing to do. If another process is reading the database, the log empties when the last reader exits, and the run says so.

    What It Does Not Reach

    • the pre-redact copy it just made, until you delete it
    • suv export files and suv backup --out copies written elsewhere
    • filesystem snapshots and Time Machine
    • your shell's own history file, and agents' own transcript files

    On a copy-on-write filesystem or an SSD, rewriting a file also does not promise that the old bytes are unrecoverable.

    Rotate the secret. Masking a stored secret does not make it safe: anything that could read the file before could have copied it. Rotate any secret suv redact finds that is still in use.

    Afterwards

    • Agent session summaries of sessions whose commands were masked read as stale, because the text they were written from has changed. Summarize those sessions again if you need them current.
    • Delete the preredact-*.db copy once you are satisfied.

    Options

    FlagDescription
    --dry-runList what would change, masked, and write nothing
    -y, --yesSkip the confirmation question
    --no-backupSkip the preredact-*.db copy taken before masking

    Redact or Delete?

    suv redact keeps each command and masks only the secret in it, everywhere it is stored. suv delete removes whole entries, which suits a command you never want back but leaves the backups as they were. For a secret, start with suv redact.

    SECURITY.md covers what redaction does and does not catch.