Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Your first scan

You have the binary on your PATH. Now:

keyhog scan .

That walks the current directory, hands every file through the scanner, and prints findings. The exit code carries the verdict:

Exit codeMeaning
0Scan finished, no findings
1Findings present, none confirmed live
2User error - bad config, bad path, unsupported flag
3System error - local I/O or detector-corpus audit failure
10Live credential confirmed under --verify
11Scanner thread panicked; re-run before trusting results
12Selected or required GPU became unavailable
13Requested source failed or coverage incomplete

So a CI step that should fail the build when a credential leaks is just:

keyhog scan .

No grep, no jq, no exit-code arithmetic. Findings exit non-zero, so the build goes red; with --verify, live credentials use exit 10.

What you get out of it

By default, output is human-readable:

$ keyhog scan .
K E Y H O G
───────────
v0.5.41 · secret scanner · 922 detectors
by santh

⚡ 16 cores | GPU: NVIDIA GeForce RTX 5090 | SIMD: AVX-512 | Hyperscan | 922 detectors (6061 patterns) io_uring | backend=simd-regex | gpu=none

  ┌    CRITICAL ─── Stripe Secret Key
  │ Secret:     sk_l...p7dc
  │ Location:   src/config/staging.env:14
  │ Confidence: ■■■■■■ 100%
  │ Action:     Roll the exposed Stripe secret key in the Dashboard, update production consumers, then delete the old key.
  │ Docs:       https://docs.stripe.com/keys#roll-api-key
  └─────────────────────────────────────────────

  ━━━ Results ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  1 secret found · 1 unverified

  1. Revoke active secrets in the provider's dashboard.

The banner (on stderr, only when it is a terminal) tells you the binary version and detector count. With --progress, the capability line also shows the current host’s CPU/GPU labels, scanner engine, compiled pattern count, selected backend, and GPU engagement result. Each finding renders as a severity-colored box: header severity + detector, then Secret: (redacted to its first and last few characters), Location:, a Confidence: bar, and an Action:/Docs: remediation hint. The Results footer joins the counts with · and lists the numbered next steps.

Default suppressions

KeyHog ships with a Tier-B suppression list of publicly documented test fixtures - credentials that appear in vendor docs as examples. Findings on these are suppressed by default. Examples:

  • Stripe’s sk_live_4eC39HqLyjWDarjtT1zdp7dc (docs sample)
  • AWS’s AKIAIOSFODNN7EXAMPLE (docs sample)
  • The RFC 7519 specimen JWT
  • GitHub’s ghp_aBcDeFgHiJ… placeholder

To see what was suppressed, pass --no-suppress-test-fixtures. The list lives at crates/cli/data/suppressions/test-fixtures.toml inside the source tree and is baked into the binary at build time. It is one visible suppression layer; detector-owned examples, structural/context gates, default path policy, .keyhogignore, and .keyhogignore.toml have distinct documented ownership. See Suppressions for the full order.

JSON output

keyhog scan . --format json

Each finding is a JSON object with these fields, every one always present (consumers like SARIF converters and CI gates rely on the schema being stable):

{
  "detector_id":        "stripe-secret-key",
  "detector_name":      "Stripe Secret Key",
  "service":            "stripe",
  "severity":           "critical",
  "credential_redacted": "sk_l...p7dc",
  "credential_hash":     "sha256-hex",
  "location": {
    "source":    "filesystem",
    "file_path": "src/config/staging.env",
    "line":      14,
    "offset":    12,
    "commit":    null,
    "author":    null,
    "date":      null
  },
  "verification": "skipped",
  "metadata": {},
  "additional_locations": [],
  "confidence": 1.0,
  "remediation": {
    "action":     "Roll the exposed Stripe secret key in the Dashboard, update production consumers, then delete the old key.",
    "revoke_url":  "https://docs.stripe.com/keys#roll-api-key",
    "docs_url":    "https://docs.stripe.com/keys"
  }
}

Pipe it into jq, into a SARIF converter for the GitHub Security tab, or into your own dedup / triage tooling.

Limiting scope

keyhog scan src/                        # one subdirectory
keyhog scan src/config/staging.env      # one file
keyhog scan --stdin < staging.env       # from stdin (CI: cat | keyhog)
keyhog scan . --exclude-paths 'docs/*'  # exclude a glob

Common patterns the default walk already skips include .git/, node_modules/, __pycache__/, vendored/build output, minified assets, and editor backup files. The canonical behavior and opt-out are documented under path suppressions.

Going further

Once the basic scan works:

  • Output formats - JSON, SARIF, plain text.
  • Verification - --verify makes API calls to confirm credentials are live; a dead credential is downgraded one severity tier (criticalhigh, …), never collapsed to a fixed level.
  • Pre-commit hook - block leaked creds before they hit the repo.
  • CI integration - GitHub Actions, GitLab CI, CircleCI patterns.