Skip to main content

Pre-commit hooks

Status: Scaffolded 2026-09-24

Every repo has a husky pre-commit hook that runs lint-staged. Two things happen to every commit before it exists: Biome rewrites the staged code files, and secretlint reads every staged file of any type. The hook is installed by npm install through the prepare script, so a fresh clone gets it without a setup step.

The hook​

.husky/pre-commit is one line:

npx lint-staged

lint-staged is configured in each package.json. The code glob differs per repo because the file types differ; the secretlint line is identical everywhere.

RepoBiome runs onsecretlint runs on
vegan-grove-api*.{ts,js,json}*
vegan-grove-web*.{ts,tsx,js,json,css}*
vegan-grove-mobile*.{ts,tsx,js,json}*
vegan-grove-docs*.{ts,tsx,js,json,css}*
"lint-staged": {
"*.{ts,tsx,js,json,css}": "biome check --write",
"*": "secretlint"
}

biome check --write formats, sorts imports, and applies safe lint fixes, then re-stages the result. An unfixable lint error (an unused import in a file you did not touch does not count; only staged files are checked) fails the commit with the same output npm run lint would give.

secretlint​

.secretlintrc.json loads @secretlint/secretlint-rule-preset-recommend: AWS keys, private keys, GitHub and Slack tokens, database connection strings with credentials, npm tokens, and generic high-entropy patterns. Because the glob is *, it reads Markdown, YAML, .env.example, shell scripts, and images too (binaries are skipped by content type).

This is the working-tree half of the secrets defense. gitleaks in CI is the history half. Both exist because a hook can be skipped by a tool that commits without invoking it; CI cannot be skipped.

A finding is a stop​

There is no approved way to bypass the hook. git commit --no-verify is not used in these repos, and a PR whose history shows a commit that could only have been made that way is closed.

When secretlint fires:

  1. Read the finding. If it is a real credential, it is already compromised in your working tree and possibly your shell history: rotate it now, before anything else.
  2. Move the value to .env (gitignored) and reference it by name. .env.example carries names and one comment each, never values.
  3. If it is a false positive (an example key in docs, a test fixture), add a narrow allows entry to .secretlintrc.json in the same commit, with a comment saying why, and call it out in the PR summary. Broad allowlists are a review failure.

The reason for the hard line is in the threat model: the repos are public, and The Trick Book shipped Atlas credentials, an admin JWT, and a CDN key to public git through exactly the "just this once" path.