Note · · 6 min read

redact, Rewritten in Go: One Binary, a TUI, and Reproducible Scans

The new version of redact ships as a single static Go binary with an interactive TUI and a headless CLI. Same two scanners, plus persisted, reproducible results -- and no Python to set up.

Earlier this year we open-sourced redact — a tool that scans cloned repositories for secrets, credentials, and personally identifiable information, so you can build guardrail lists and redaction rules before any content reaches an LLM. Today we are releasing a new version, rewritten from scratch in Go. It now lives at github.com/lab34-es/redact.

The original was a Python CLI: clone the repo, run setup.sh, let it create a virtualenv, then drive everything through a wrapper script. That worked, but distributing a Python environment across security teams, CI runners, and air-gapped machines was friction we did not want. The new redact is a single static binary with two faces:

  • An interactive TUI — run ./redact in a terminal and walk from path selection to a browsable results view, with live progress in between.
  • A headless CLI./redact scan and ./redact ollama, for scripts, cron jobs, and CI pipelines.

Both run the same engine, and every run is persisted to a timestamped results directory that records exactly what was scanned, at which git commit, with which configuration.

What Changed

Python versionGo version
Installsetup.sh + virtualenvone static binary
Interfacerun-scan.sh wrapperinteractive TUI + headless CLI
Resultsprinted or exported on demandevery run persisted to results/<date_time>/
Allowlistingregex.txt suppression pass before scanning
CI integrationmeaningful exit codes (1 = findings present)
Card number patternsregex onlyregex + Luhn validation

What did not change: the two complementary scanning approaches, the 45 regex patterns across 8 categories, git history scanning, multi-repo discovery, and the output formats (table, json, txt, csv).

The TUI

Run ./redact with no arguments and the TUI takes you through five screens:

  1. Path selection — type the path to scan and the discovery depth.
  2. Repository selection — every discovered repo is listed with its HEAD SHA. Toggle with space, a selects all, n none.
  3. Configuration — pick the scanner with ←/→. For Ollama, the model picker is filled from the server’s model list; if the server is unreachable you can type a model name. Set include/exclude globs, a suppression regex file, and git-history options.
  4. Live progress — an overall repo progress bar, per-file progress, a live findings ticker, and (for Ollama) the model’s streaming output for the file being analyzed. q cancels; partial results are still saved.
  5. Results browser — a findings table with severity filters (h/m/l, a for all). enter opens a detail view, n starts a new scan, and the footer shows where results were saved.

./redact tui --path /repos --depth 3 prefills the first screen, so you can script your way to the interactive part.

The CLI

The headless interface keeps the shape of the previous version — one subcommand per scanner:

# Scan a directory of cloned repos with the regex patterns
./redact scan --path /path/to/repos --depth 2

# Context-aware scan with a local Ollama model
./redact ollama --path /path/to/repos --model llama3

# Persist per-repo JSON, or a single CSV
./redact scan --path /path/to/repos --output json
./redact scan --path /path/to/repos --output csv --output-dir ./reports

# Only scan Python and YAML files, skip git history
./redact scan --path /path/to/repos --include '*.py' --include '*.yml' --no-git-history

# 8 parallel workers per repo, 4 repos in parallel
./redact scan --path /path/to/repos --workers 8 --processes 4

The flags you know from the Python version are still here (--path, --depth, --output, --include/--exclude, --no-git-history, --history-depth, --workers, --processes), joined by a few new ones:

FlagDescriptionDefault
--regex-fileSuppression patterns applied before scanning-
--output-dirBase directory for the timestamped results dir./results
--quietNo per-finding progress lines on stderrfalse
--no-colorDisable colored output (NO_COLOR is honored too)false
--max-file-sizeOllama only: skip files larger than N bytes102400
--ollama-hostOllama only: API URLhttp://localhost:11434
--ollama-timeoutOllama only: seconds without model output before a file is aborted120

Every Run Is Persisted

Whether you scan from the TUI or the CLI, redact writes a results directory for every run:

results/2026-08-22_14-05-33/
├── README.md        # scanner, config, and a table of scanned paths with their git SHAs
├── findings.json    # full machine-readable results (config + all findings)
├── config.json      # the exact resolved configuration, for reproducibility
└── ...              # extras per --output: <repo>.json / <repo>.txt, or findings.csv

The README’s path table records what was scanned and at which commit:

PathGit SHAFindingsFiles scannedCommits scanned
/repos/org-a/api3f2a1c9...1224087

This turns every scan into an auditable artifact. When a compliance review asks “what did you scan, and when?”, the answer is a directory you can archive — including the exact configuration needed to reproduce the run. Interrupted runs (ctrl+c, or q in the TUI) still write the directory with the partial results and a note.

Built for CI

Exit codes now mean something, so redact can gate a pipeline:

CodeMeaning
0Scan completed, no findings
1Scan completed, findings present
2Usage error (bad flags)
3Runtime failure (unreachable Ollama, unreadable path, …)
# Fail the job when sensitive data is found
./redact scan --path . --quiet --output csv --output-dir ./scan-results

Allowlisting with a Suppression File

Once you have triaged findings, you do not want the same known-safe matches showing up in every scan. --regex-file points at a file with one Go (RE2) regular expression per line (# comments allowed). Every match is replaced with [REDACTED] before scanning — in files and in git history — so allowlisted values can never produce findings again.

Two things to know: RE2 has no lookahead, lookbehind, or backreferences, so patterns written for Python’s re may need adjusting; and invalid patterns fail fast with their line number.

How It Works

  Interactive TUI              Headless CLI
    ./redact              ./redact scan | ollama
        |                          |
        +------------+-------------+
                     v
         [ Repository discovery ]
     walks --path for .git dirs up to --depth
                     |
                     v
             [ Scan engine ]
     repos in parallel (--processes)
     files in parallel (--workers)
     working-tree files + git history
     suppression pass (regex.txt -> [REDACTED])
     regex scanner  |  Ollama scanner
                     |
                     v
              [ Findings ]
     severity - category - file:line - commit
          |                   |
          v                   v
   terminal output     results/<date_time>/
   (table, json, ...)  (README, findings.json, config.json)

The engine is UI-agnostic: the TUI and the CLI consume the same progress-event stream, and both persist every run — including partial results when a scan is cancelled.

Migrating from the Python Version

  • ./run-scan.sh scan ... becomes ./redact scan ..., and ./run-scan.sh ollama ... becomes ./redact ollama .... There is no setup.sh and no virtualenv anymore.
  • --include '*.py' '*.yml' (space-separated values) is now --include '*.py' --include '*.yml' or --include '*.py,*.yml'. A stray positional argument is a hard usage error, so the change cannot pass silently.
  • Results are always persisted now; --output-dir is the base directory for the timestamped results directory rather than a plain export target.

Getting Started

# Clone and build
git clone https://github.com/lab34-es/redact.git
cd redact
go build -o redact ./cmd/llm-redact

# Interactive TUI
./redact

# Headless scans
./redact scan --path /path/to/repos --depth 2
./redact ollama --path /path/to/repos --model llama3

Open Source

redact is MIT-licensed and available on GitHub. We welcome contributions, bug reports, and feature requests.