Self-hosted service
GitGuardian/ggshield avatar
GitGuardian/ggshield

ggshield: GitGuardian's CLI for Secret Detection Before a Commit Reaches the Repository

Detect and validate 500+ types of hardcoded secrets with advanced checks. Use it as a pre-commit hook, GitHub Action, or CLI for proactive secret detection and security.

2,004 stars219 forksPythonMIT

At a glance

What is it?
ggshield is a Python CLI that scans commits, files, and CI pipelines for more than 500 types of hardcoded secrets by sending content to the GitGuardian API for analysis. Files are processed during each scan but not stored on GitGuardian's side, and the tool runs as a pre-commit hook, a GitHub Action, or a standalone command.
Who is it for?
Security teams already using GitGuardian's dashboard will find ggshield integrates cleanly: the pre-commit hook, the CI action, and the CLI share the same authentication and detector set. Teams in air-gapped networks will find the tool unusable without a self-hosted instance pointed to by GITGUARDIAN_INSTANCE.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 3 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 11, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The commit-time gap that post-push scanning cannot close

When a developer commits an AWS key or a database password directly into tracked files, the credential lands in version control history even if it is deleted in a follow-up commit. Code review operates after a push; a CI scanner that runs on pull requests fires even later. ggshield targets the moment before a commit object is written, running as a pre-commit hook, a GitHub Action, or a standalone CLI command. The intended users are developers who want a safety net at the workstation level and DevSecOps engineers who need to enforce that net across a team without deploying a separate internal service.

The project description frames detection and validation as distinct steps, calling the combined process "advanced checks." Detection without validation flags any string that matches a known pattern, including test fixtures and expired values. Whether validation in ggshield confirms a credential is syntactically complete or queries the originating service is not described in the repository material available here, but the distinction matters for triage: a scanner that cannot separate active credentials from revoked ones creates findings that require manual sorting.

File contents sent to the API, processed but not stored

ggshield sends scan content to GitGuardian's public API at api.gitguardian.com through the py-gitguardian library, which returns findings. Files are processed during the scan but not retained: call time, request size, and scan mode are the only fields stored. Findings do not appear on the GitGuardian dashboard, and the secrets themselves are not recorded.

That architecture places the full detector set on the API side. Additions to the detector library, when GitGuardian extends coverage to a new credential type, take effect without a ggshield upgrade on the client. The practical consequence is that the package version in your environment does not determine which secrets get caught; the API's detector version does. A second consequence is that any disruption to the API endpoint makes the tool non-functional: there is no cached detection mode.

The py-gitguardian library is listed as a separate dependency in pyproject.toml, meaning the API bridge is a maintained package with its own update cycle rather than code embedded in ggshield.

Installing via the one-command script and authenticating

The recommended installation path on Linux and macOS is the install script:

bash
curl -sSfL \
  https://raw.githubusercontent.com/GitGuardian/ggshield/main/scripts/install/install.sh | bash

On Windows via PowerShell:

powershell
irm https://raw.githubusercontent.com/GitGuardian/ggshield/main/scripts/install/install.ps1 | iex

After installation, authenticate with your GitGuardian account:

bash
ggshield auth login

For non-interactive environments such as CI pipelines or Docker containers, skip the interactive flow and set the `GITGUARDIAN_API_KEY` environment variable directly. The .env.example file at the repository root shows both variables in use: `GITGUARDIAN_API_KEY` for the token and `GITGUARDIAN_INSTANCE` for the target endpoint. Setting `GITGUARDIAN_INSTANCE` points the CLI at an EU workspace or a self-hosted GitGuardian deployment rather than the default endpoint shown in .env.example, https://dashboard.gitguardian.com/.

Homebrew users on macOS can install and upgrade with `brew install ggshield`. Linux packages for Deb and RPM formats are available on Cloudsmith, with format-specific setup pages linked from the README. Chocolatey covers Windows: `choco install ggshield`.

Teams using the official Docker image get a pre-built environment. The Dockerfile uses a three-stage build: a uv stage, a builder stage that installs dependencies, and the final image. The build sets UV_COMPILE_BYTECODE=1, shipping pre-compiled .pyc files rather than recompiling on each invocation. A comment in the Dockerfile notes this was measured: omitting bytecode compilation added approximately 0.2 seconds to startup even for a bare version-check call in a fresh container.

Pre-commit and CI integration through the shipped hook definition files

The repository root contains .pre-commit-hooks.yaml, which allows any project using the pre-commit framework to reference ggshield as a hook source by repository URL. A local development reference, .pre-commit-config.yaml, and a customization template, pre-commit-config.local-example.yml, are also in the root. With the hook active, every git commit call scans staged content before the commit object is written; a match blocks the commit and prints the finding to the terminal.

This setup shifts enforcement to the developer's workstation before code travels to any remote. Teams that add the GitHub Action on top get a second checkpoint: content committed from machines where the local hook was bypassed or missing will still be caught during the CI run. The two checkpoints cover different failure modes. The hook catches secrets before they enter history at all; the action catches anything the hook missed after the push.

AI coding assistant integrations appear as a subsection in the repository's integrations section, alongside the CI and pre-commit options, though that section's content was not shown in the repository material.

charset-normalizer pinned at 3.1 in pyproject.toml to prevent silent misreads

ggshield's pyproject.toml pins charset-normalizer at `charset-normalizer~=3.1.0`, which is tighter than a typical floor constraint. The comment in that file explains the reason directly: charset-normalizer 3.2+ changed its encoding detection in a way that mis-decodes some valid UTF-8 content, and the comment identifies this as something that "would degrade secret scanning." The affected function is named: `_decode_bytes` in `core/scan/scannable.py`.

This is an accuracy constraint, not a compatibility one. Upgrading charset-normalizer past 3.1 would not crash ggshield or produce an API error; it would mis-decode certain files before they reach the pattern matcher, which the pyproject.toml comment identifies as degrading secret scanning. The scanner would process the file, return no findings, and give no indication that the content was read incorrectly.

Automated dependency tools like Dependabot routinely propose bumping version floors. This constraint is the kind that disappears silently if an automated update proposes a newer charset-normalizer version and the test suite does not cover the specific UTF-8 encoding edge cases the comment describes. The comment points to `_decode_bytes` by name, giving a specific file and function for anyone who needs to evaluate a proposed bump.

The ggshield-py binary on macOS and the Keychain prompt placed before the install steps

The README opens with a callout block warning macOS users: upgrading to 1.54.0 triggers a macOS Keychain access prompt for a process named `ggshield-py`. This is described as a second binary introduced as a performance improvement that reads the stored authentication token. The callout instructs users to click "Always Allow"; it states the prompt will not appear again after that.

The callout sits above the table of contents and above the installation instructions, so a developer who reads the README top to bottom will encounter it before starting setup. A developer who navigates directly to the installation steps will miss it and encounter an unexpected system dialog during first use with no prior context.

What the README does not cover: the location of `ggshield-py` on disk, how to confirm the binary's origin if the prompt appears unexpectedly, or whether the behavior differs between standalone .pkg installs and Homebrew installs. The note asks users to trust a process they have not previously seen based on a single-sentence explanation in a callout block.

Where ggshield cannot operate: offline networks, EOL Python, and standalone upgrade gaps

ggshield has no offline mode. A machine that cannot reach the GitGuardian API at scan time will not complete a scan. Organizations in environments without outbound internet access must configure a self-hosted GitGuardian deployment and set the `GITGUARDIAN_INSTANCE` variable; the CLI itself has no standalone detection capability.

The PyPI installation path requires Python >=3.9 in a non-EOL version. The pyproject.toml classifiers show support for 3.9 through 3.13. A machine below the 3.9 minimum requires a Python upgrade before pip or pipx installation works. Standalone packages, the .pkg for macOS and the .zip for Windows, bypass the Python requirement but trade away automatic upgrades: each update requires a manual download. Teams distributing those packages across multiple machines need their own update mechanism. Homebrew, Chocolatey, and the Deb/RPM packages each handle upgrades through the package manager.

The MIT licence is in the LICENSE file at the repository root, and the last push was on 2026-10-06.

Editorial conclusion

Security teams already using GitGuardian's dashboard will find ggshield integrates cleanly: the pre-commit hook, the CI action, and the CLI share the same authentication and detector set. Teams in air-gapped networks will find the tool unusable without a self-hosted instance pointed to by GITGUARDIAN_INSTANCE. Before rolling it out, confirm that Python >=3.9 and non-EOL is available on target machines, and check whether the ggshield-py Keychain prompt on macOS requires user communication before deployment. No offline fallback exists.

Frequently asked questions

Does ggshield store my files or the secrets it detects on GitGuardian's servers?

No. Only metadata is stored from each scan: call time, request size, and scan mode. File contents and detected secrets are not retained and do not appear on the GitGuardian dashboard.

Can ggshield run without a GitGuardian account or API key?

No. ggshield requires a GITGUARDIAN_API_KEY to authenticate with the GitGuardian API. For interactive setup run ggshield auth login; for non-interactive environments such as CI pipelines, set the variable directly.

Which Python versions does ggshield support?

ggshield requires Python 3.9 or later and does not support Python versions that have reached end-of-life. The pyproject.toml classifiers list support for 3.9 through 3.13. Standalone .pkg and .zip packages bypass the Python requirement entirely.

What is the ggshield-py binary that triggers a Keychain prompt on macOS?

ggshield-py is a second binary introduced as a performance improvement that reads the stored GitGuardian authentication token from the macOS Keychain. The README instructs users to click Always Allow when the prompt appears and states it will not prompt again after that.

How do I point ggshield at a self-hosted or EU GitGuardian instance?

Set the GITGUARDIAN_INSTANCE environment variable to the URL of your endpoint before running any ggshield command. The .env.example file in the repository root shows this variable alongside GITGUARDIAN_API_KEY, with the default dashboard URL at https://dashboard.gitguardian.com/.

Official sources

  1. GitGuardian/ggshield on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/gitguardian-ggshield.svg)](https://hysenlabs.com/projects/gitguardian-ggshield)