Skip to main content

Git pre-commit hook for preventing accidental secret commits

Project description

keygate

PyPI version License: MIT Python 3.11+ PyPI Downloads

Stops API keys and passwords from reaching your git history.

pipx install keygate
keygate activate

That's it. keygate now runs automatically before every git commit.

日本語 | 中文


Why this matters

During development, it's easy to write API keys or passwords directly in code. Once committed with git commit, they become permanently embedded in the repository history.

Even if you delete them later, they remain accessible from past commits — and once exposed on GitHub or similar platforms, they can be exploited almost immediately. There are countless cases of AWS key leaks resulting in massive unexpected bills.

keygate blocks the commit before it happens. Zero configuration required.


What it detects

  • AWS Access Keys
  • OpenAI API Keys
  • GitHub Tokens
  • Slack Tokens
  • Private Keys (PEM format)
  • JWT Tokens
  • Long random-looking strings (high-entropy detection)
  • Variable names like api_key, password, secret paired with values

Getting started

Step 1: Install

keygate is a Python CLI tool. The easiest way to install it is via pipx.

pipx install keygate

If you don't have pipx, install it with pip install pipx. Using pipx makes the keygate command available from any project directory.

Step 2: Activate

cd path/to/your-project
keygate activate

This installs keygate as a Git pre-commit hook. If your repo has core.hooksPath configured, keygate installs there instead of forcing .git/hooks.

The generated hook prefers the current Python environment (python -m keygate.cli scan) and falls back to keygate scan, which makes it more reliable on systems where the hook PATH is limited.

That's all the setup you need.

Step 3: Use it

Just run git add and git commit as usual. If nothing dangerous is found, nothing happens.

If a secret is detected, the commit is blocked like this:

[BLOCK] High confidence secret detected

File: config.py:12
Rule: aws-access-key
Score: 100

Reason:
AWS Access Key detected; sensitive context detected

Remediation:
  - Remove the key from the code
  - Rotate the AWS credentials immediately
  - Use environment variables or AWS IAM roles instead

To ignore:
  Add comment: # keygate: ignore reason="..."

How to read the output:

  • File: config.py:12 — the file and line number where the issue was found
  • Rule: aws-access-key — what was detected
  • Score: 100 — severity (70+ blocks the commit; 40–69 warns only)
  • Reason — why it was flagged
  • Remediation — suggested fixes

Step 4: Update

If you installed keygate with pipx, upgrade it like this:

pipx upgrade keygate

If you installed it with pip, use:

python -m pip install -U keygate

Use with Claude Code (plugin)

keygate is also available as a Claude Code plugin. With it installed, Claude can scan staged changes for secrets automatically before you commit, and you can run keygate operations from Claude Code as slash commands.

Step 1: Install the keygate CLI

The plugin wraps the CLI, so the CLI must be installed first. Pick one:

pipx install keygate          # if you use pipx
uv tool install keygate       # if you use uv
pip install --user keygate    # fallback

Step 2: Add the marketplace and install the plugin

In Claude Code:

/plugin marketplace add kanekoyuichi/keygate
/plugin install keygate

What you get

  • Skill keygate-secret-scan — Claude triggers this automatically before commits or when staged changes contain credential-like values. It runs keygate scan --profile agent, parses the JSON output, and reports findings with masked snippets.
  • Slash commands:
    • /keygate:scan — scan staged changes on demand
    • /keygate:install-hook — install the Git pre-commit hook
    • /keygate:baseline-create — record current findings as accepted
    • /keygate:baseline-update — append newly-detected findings

The plugin uses keygate's agent JSON profile (schema_version: "1") internally, so detection logic and policies are identical to the CLI.


Manual scan

You can also scan without using the hook.

git add .
keygate scan

This scans git diff --cached (staged changes only).

JSON output for AI agents and automation

By default, keygate scan prints human-readable text. For AI agents or scripts that need to parse the result, use JSON output:

keygate scan --format json    # JSON only on stdout
keygate scan --json           # alias for --format json
keygate scan --profile agent  # forces JSON, no human prose

Default text output starts with a machine-readable summary line so simple tools can also pick up the status:

[KEYGATE] status=block findings=1

When a commit is blocked, the text output also points to the JSON command so an agent can re-run and parse the result. The JSON payload follows a fixed schema (schema_version: "1") with status, summary, and findings[] (including rule_id, policy, score, verdict, file, line, message, and a masked snippet when available).

Exit codes are unchanged: 0 for pass/warn, 1 for block, 2 for usage errors (such as combining --format text with --json).


Handling false positives

keygate errs on the side of caution, so it may occasionally flag things that aren't real secrets. There are three ways to deal with this.

Option 1: Inline ignore comment

Suppresses detection for that specific line. A reason is required.

api_key = "dummy-key-for-testing"  # keygate: ignore reason="test data"

Option 2: Allowlist paths or patterns

Create a keygate.toml file in your project root and specify paths or patterns to exclude.

[allowlist]
paths = ["vendor/*", "third_party/*"]  # ignore code you don't own
patterns = ["dummy", "example"]         # regex patterns for lines to ignore
keywords = ["fixture"]                  # case-insensitive keywords for lines to ignore

Note: Adding tests/* to the allowlist globally will cause keygate to miss real secrets embedded in test code. Use option 1 (inline ignore) or option 3 (baseline) for false positives in tests.

Option 3: Baseline — register existing findings to ignore

Useful when you only want to catch newly added secrets, not existing ones.

keygate baseline create

The current findings are saved to .keygate.baseline.json. From that point on, the same findings are ignored. The file looks like this:

{
  "version": 1,
  "entries": [
    {
      "fingerprint": "e5282a7860678bc768d280eb3e77d2ca8a44286357c743dd024d74fe0605fe09",
      "file_path": "src/app/config.py",
      "line_number": 42,
      "rule_id": "url-credentials",
      "created_at": "2026-04-22T09:30:00+00:00"
    }
  ]
}

The fingerprint is a SHA256 hash of file_path + line_number + matched string. The actual secret value is never stored, so committing the baseline file to Git is safe.

If .keygate.baseline.json already exists, keygate baseline create preserves existing entries and adds newly detected findings on top. Re-running it will not silently discard your current baseline.

To add newly discovered findings to the baseline:

keygate baseline update

Sharing with your team

We recommend committing .keygate.baseline.json to Git so the whole team uses the same ignore list.

git add .keygate.baseline.json
git commit -m "Add keygate baseline"

New team members only need to run pipx install keygate and keygate activate — the shared baseline is picked up automatically.


Configuration (optional)

The defaults work well out of the box, but you can customize behavior by creating keygate.toml in your project root.

[scan]
entropy_threshold = 4.2    # threshold for random-looking strings (lower = stricter)
block_score = 70           # commits are blocked at this score or above

[allowlist]
paths = ["vendor/*"]
patterns = ["dummy", "example"]
keywords = ["fixture"]

[baseline]
path = ".keygate.baseline.json"

If no config file is present, defaults are used.


FAQ

Q. I accidentally committed a secret. What should I do?

A. Revoke (rotate) the key immediately. Removing it from Git history is not enough. Assume any leaked key has already reached an attacker.

Q. How do I temporarily disable the hook?

A. Use git commit --no-verify to skip all hooks for a single commit. To remove the hook entirely, run keygate deactivate.

Q. How do we share this across a team?

A. Commit keygate.toml and .keygate.baseline.json to Git. Each team member needs to run keygate activate individually.

Q. How do I update keygate?

A. If you installed it with pipx, run pipx upgrade keygate. If you installed it with pip, run python -m pip install -U keygate.


Detection accuracy

Measured against a labeled corpus of 100 samples (50 known secrets, 50 benign strings).

Metric Value
Recall 100.0%
Precision 80.6%
F1 89.3%
True Positive 50
False Negative 0
False Positive 12
True Negative 38

Recall 100.0% means every known secret in the corpus was detected (BLOCK or WARN). In other words, the benchmark had zero missed secrets.

Precision 80.6% reflects 12 false positives. These include masked URL credentials, placeholders, Stripe publishable keys, and empty values such as API_KEY=. They are not always real secrets, but they look close enough to secret-like values that keygate reports them before commit so you can review them.

The corpus and thresholds are enforced as a regression test. To re-run:

python -m tests.benchmark.benchmark

Disclaimer

keygate is a best-effort detection tool. Please understand the following before use.

  • Detection is not guaranteed: Unknown secret formats, obfuscated values, or custom formats may not be detected (false negatives).
  • False positives can occur: Non-secret strings may be flagged. Use allowlist / baseline / inline ignore to address them.
  • Not a replacement for proper secret management: This tool is an additional safeguard at commit time. Secrets should be managed via environment variables, secret managers, or KMS — never stored in the repository.
  • Hooks can be bypassed: git commit --no-verify skips all hooks. For organizational enforcement, combine with server-side checks (pre-receive hooks, CI scanning, etc.).
  • You are responsible for any leaks caused by missed detections: The authors and contributors accept no liability for damages arising from use of this tool (see LICENSE for details).
  • If a secret is detected, rotate the key promptly: Even if the commit was blocked, the value may remain in local files, editor history, clipboard, or other devices.

This tool is designed as a last-resort safety net to catch human mistakes — not as a substitute for doing secret management correctly.


License

Distributed under the MIT License. Free to use, modify, and redistribute, including for commercial use. See LICENSE for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

keygate-0.2.0.tar.gz (42.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

keygate-0.2.0-py3-none-any.whl (23.3 kB view details)

Uploaded Python 3

File details

Details for the file keygate-0.2.0.tar.gz.

File metadata

  • Download URL: keygate-0.2.0.tar.gz
  • Upload date:
  • Size: 42.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for keygate-0.2.0.tar.gz
Algorithm Hash digest
SHA256 06117f85a267679303d0c587edd849581f6cb8857a5ac099802d4cf296a15a77
MD5 da0bf4eb4196c72f2cfdc14b49643776
BLAKE2b-256 346fa674194569a7f309fec6390f627405dc04aba745172836205e1f6beb915a

See more details on using hashes here.

Provenance

The following attestation bundles were made for keygate-0.2.0.tar.gz:

Publisher: publish.yml on kanekoyuichi/keygate

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file keygate-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: keygate-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 23.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for keygate-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 15f54369d8870499e53dea8c8a88003d1f8d9797f7fbe4228dc0455c8d049469
MD5 6f9f0a056718cdaf5f58374520e8e622
BLAKE2b-256 efb0efcd422c91e292bcb1ebda3558e07bc55fc0663aa9572aa64ec335860cce

See more details on using hashes here.

Provenance

The following attestation bundles were made for keygate-0.2.0-py3-none-any.whl:

Publisher: publish.yml on kanekoyuichi/keygate

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page