Skip to main content

smart-commit-guard

CI PyPI License: MIT Python 3.12+

Stop real secrets (API keys, passwords, private keys, tokens, connection strings) from being committed.

Deterministic rules catch the obvious cases. A small decision model judges the ambiguous ones, such as DB_PASS = "Winter2026!Admin" (real) versus API_KEY = "your-api-key-here" (placeholder). It runs as a git pre-commit hook and as a CI step. Zero runtime dependencies, Python 3.12+.

Install

uv tool install smart-commit-guard      # or: pipx install smart-commit-guard

Quickstart

smart-commit-guard install-hook          # this clone only: writes .git/hooks/pre-commit
smart-commit-guard doctor                # checks the hook, the rules and the model

To share the hook with everyone who clones the repo, see Sharing the hook.

Add a CI step too: it catches git commit --no-verify and cannot be bypassed with SKIP_SECRET_GUARD.

smart-commit-guard scan --diff origin/main...HEAD

How it works

  1. Only added lines are scanned. Lockfiles, binaries, minified files and .env.example are skipped.
  2. High-confidence rules (private keys, AWS keys, sk-/ghp_/xox/AIza/glpat- tokens, chat webhook URLs, connection strings with a password) block immediately, with no model call, outside test/doc/example paths.
  3. A staged environment file (.env, .env.local, prod.env; not .env.example/.sample/.template) blocks as a whole, with no model call, as long as it has any non-comment line. Add it to .gitignore, or allowlist it if intentional.
  4. A staged file whose name marks it as sensitive blocks as a whole, with no model call, binaries included: SSH private keys (id_rsa, id_ed25519...), key stores (.p12, .pfx, .jks, .keystore, .ppk), KeePass databases, .netrc, .pgpass, .pypirc, .htpasswd, .git-credentials, .aws/credentials, .docker/config.json, .kube/config, kubeconfig, Terraform state and .tfvars, credentials.json, Google client_secret*.json and service-account keys, and iOS provisioning profiles. Names containing example, sample, template or defaults are exempt. Public files (id_rsa.pub, .pem/.crt certificates) are not matched by name; a private key inside them is still caught by content.
  5. Candidates (password = "...", Bearer tokens, -pSECRET CLI flags, unquoted YAML values, high-entropy literals) go to the model, one per request. Obvious placeholders (changeme, your-..., xxxx) are dropped first.
  6. The policy lives in code, not in the model: the model's probability p blocks at p >= 0.5 and warns at p >= 0.4 (configurable).
  7. If the model is unreachable: rule hits still block; model-only candidates warn and allow.

Findings never print the secret, only a shape-preserving mask such as sk_live_A9a9A9a9....

Exit codes: 0 allowed (warnings may print), 1 blocked, 2 tool error.

Commands

Command Purpose
smart-commit-guard scan --staged the staged diff (what the hook runs)
smart-commit-guard scan --diff RANGE a revision range, for CI (origin/main...HEAD)
smart-commit-guard scan --files PATH... whole files
--json, --no-model machine-readable output; rules only (no model call)
smart-commit-guard install-hook [--force] per-clone hook in the effective hooks directory
smart-commit-guard install-hook --shared committable .githooks/pre-commit plus core.hooksPath
smart-commit-guard doctor verify the hook, the rules and the model

Environment variables

Variable Default Meaning
SECRET_GUARD_BASE_URL http://localhost:11435 model server
SECRET_GUARD_MODEL jevk5:4b model name sent to the server
SECRET_GUARD_API_KEY unset Bearer token, only if the server wants one
SECRET_GUARD_TIMEOUT 10 seconds per model call
SECRET_GUARD_BLOCK_AT / SECRET_GUARD_WARN_AT 0.5 / 0.4 thresholds, see "Other models"
SECRET_GUARD_ALLOW_HOSTED unset must be 1 to use a non-local URL, see "Privacy"
SKIP_SECRET_GUARD unset 1 bypasses the commit hook once, see below

Bypassing a false positive

Two ways, from narrowest to widest.

1. Allowlist the line (permanent, reviewed in PRs)

Every finding prints an allowlist: <fingerprint>. Put it in .secret-guard.toml at the repo root:

# Files to skip entirely (fnmatch globs against repo-relative paths)
skip = ["tests/fixtures/*", "docs/*"]

# Findings to accept. A fingerprint is sha256(path + masked line), so the file holds no secret
# and only matches that line shape in that file.
allowlist = ["b37018356662157b"]

Allowlisting works for the hook and for CI.

2. SKIP_SECRET_GUARD=1 (one commit, local only)

SKIP_SECRET_GUARD=1 git commit -m "..."

An explicit acknowledgement that a block is a false positive. Details:

  • Only the exact value 1 counts (0, empty, true do not).
  • It applies only to the commit hook (scan --staged). --diff and --files ignore it, so a bypassed commit still has to pass CI. Use an allowlist entry for a false positive that must pass CI.
  • Nothing is scanned and the model is not called. A loud SKIPPED notice is printed.
  • Every bypass is logged (see below).
  • Do not export it in your shell profile; smart-commit-guard doctor warns if it is set.

The skips log

Each bypass appends an entry to secret-guard-skips.log inside the git directory (.git/ in a normal clone, so it is never committed): time, branch, staged files, and what the rules would have flagged, with masked previews and allowlist fingerprints. It never contains a secret.

2026-10-05T15:15:23+00:00 SKIP branch=main files=1
  file: cfg.py
  BLOCK cfg.py:1 API token pattern (rule, no model needed) STRIPE = "sk_live_A9a9A9a9..." allowlist=b3701835...

The log is local and per clone, so it is for your own audit ("what did I skip, and was it really a false positive?"). The authoritative backstop is CI.

Sharing the hook with your team

Git does not version .git/hooks/, so a plain install-hook only protects your own clone. To share it:

smart-commit-guard install-hook --shared
git add .githooks .gitattributes && git commit -m "Add the smart-commit-guard pre-commit hook"

This writes .githooks/pre-commit (no machine-specific paths: it finds smart-commit-guard on PATH, then in the repo's .venv, then through uvx), sets core.hooksPath to .githooks, and adds /.githooks/* text eol=lf to .gitattributes so the script keeps LF endings on Windows. core.hooksPath is local git config, so each clone runs once:

git config core.hooksPath .githooks     # or: smart-commit-guard install-hook --shared

Put that line in your setup docs or bootstrap task. A teammate without smart-commit-guard installed is blocked with a message telling them how to install it (the gate fails closed rather than silently not running).

core.hooksPath replaces .git/hooks/ for the clone. If you already use other hooks, call them from .githooks/pre-commit or use a hook manager.

Testing that the hook works

Run the built-in check first:

smart-commit-guard doctor

It verifies that the hook exists at the effective path, is executable and runs scan --staged, that a synthetic secret is blocked by the rules, and that the model answers (a missing model is a warning, not a failure).

Then prove it through real git, in a throwaway repo:

cd "$(mktemp -d)" && git init -q && smart-commit-guard install-hook
K=A1b2C3d4E5f6G7h8I9j0K1l2; echo "KEY = "sk_live_$K"" > cfg.py && git add cfg.py
git commit -m test                         # blocked, exit 1
SKIP_SECRET_GUARD=1 git commit -m test     # allowed, prints SKIPPED
cat .git/secret-guard-skips.log            # the logged bypass

Model setup

The default is a local model server that speaks the POST /v1/systemone API, with the jevk5:4b model (for example ollaya serving jevk5:4b on port 11435). Typical latency is about 70 ms per candidate. Commits with no candidates never call the model.

Other models

Thresholds are calibrated per model. The defaults come from evals/ (tuning and held-out sets, synthetic only): recall and precision 1.00 at block 0.5 / warn 0.4 for jevk5:4b. Those sets are small and the rules were adjusted after seeing the first held-out misses, so treat the numbers as optimistic. For another model, rerun the eval and choose your own thresholds:

PYTHONPATH=src uv run python evals/run_eval.py

Privacy

The staged diff may contain real secrets, so the default backend is local and nothing is sent anywhere else. A non-local SECRET_GUARD_BASE_URL is refused (exit 2) unless SECRET_GUARD_ALLOW_HOSTED=1. With a hosted backend only masked lines are sent, never the secret value, a full file or the unmasked diff. No telemetry.

Development

uv sync
uv run task setup-hooks   # dogfood the shared hook
uv run task test
uv run ruff check . && uv run ty check

Behavior is specified in specs/secret-scan/SPEC.md.

Limitations

  • Scans added lines only, not git history. If a secret was committed, rotate it.
  • Not a replacement for server-side scanning such as GitHub push protection; use both.
  • PII detection is out of scope for now.

License

MIT

Metadata

Release files for smart-commit-guard 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for smart-commit-guard 0.1.0
File Size Uploaded
smart_commit_guard-0.1.0.tar.gz 18.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for smart-commit-guard 0.1.0
File Interpreter ABI Platform
smart_commit_guard-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 42.2 kB

Release files / smart_commit_guard-0.1.0.tar.gz

Download URL smart_commit_guard-0.1.0.tar.gz
Size 18.9 kB
Tags Source
SHA-256 checksum
How to use checksums
a8052e316494ed48901d93272c95f6638e6b674fde263df62a0b0647b921975d
BLAKE2b-256 checksum
How to use checksums
a49d8fd1a53a59ad1d046affc974375f3f2b7f4b2852d3e1e2a7aa12bd838703
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 5, 2026.

Transparency log

Release files / smart_commit_guard-0.1.0-py3-none-any.whl

Download URL smart_commit_guard-0.1.0-py3-none-any.whl
Size 23.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
340538152e30844a491f9ceadaba57712cc8699c92c28f44988c77099493ce79
BLAKE2b-256 checksum
How to use checksums
223ba2c0e025f742beb538ee72aec802f52c03389bf80b21c0b5509d47c6e8dc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 5, 2026.

Transparency log

Release history Release notifications | RSS feed

0.3.0

2 release files

0.2.0

2 release files

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page