Beta: This project is experimental and still very much a WIP. APIs and behavior may change, and I'll definitely make enhancements to the detection mechanism to cover more edge cases.
Pre-commit hook that detects BIP39 mnemonic seed phrases in staged files and blocks the commit. Prevents accidental secret leaks that have historically led to exploits and big "uh-ohs", as they are technically referred to in the crypto space.
Run this from your project root:
curl -fsSL https://raw.githubusercontent.com/larkiny/nomonic/main/setup.sh | bash
That's it. The script will:
package.json) or non-Nodegit commitYou handle seed phrases during development. Testing wallets, integrating key management, debugging transactions - all the things. But one overconfident git commit later and that BIG SECRET THING is in your repo history forever (it's still kinda in the room with us even after you delete it).
It's happened before. nomonic is a pre-commit hook that catches seed phrases before they make it into version control. Or that's what I hope, anyway. Like GTA6, it's still coming along.
GitHub Push Protection detects structured secrets — API keys, tokens, and private keys that have recognizable patterns like prefixes, checksums, or high-entropy signatures. It does not detect BIP39 seed phrases because a mnemonic is just a sequence of common English words with no structural fingerprint.
nomonic fills that gap. The two tools are complementary: Push Protection catches your API keys, nomonic catches your seed phrases.
| GitHub Push Protection | nomonic | |
|---|---|---|
| API keys & tokens | ✅ | ❌ |
| BIP39 seed phrases | ❌ | ✅ |
| Runs | Server-side (on push) | Client-side (pre-commit) |
| Requires | GitHub Advanced Security | Nothing (zero dependencies) |
The detector scans git diff --cached (staged changes) for sequences of consecutive words from the BIP39 English wordlist (2048 common English words). The core methodology:
Tokenization with punctuation stripping — Each token is stripped of surrounding non-alphabetic characters (quotes, commas, brackets, numbering like 1., etc.) before matching. This catches seed phrases in any common formatting. Tokens with interior punctuation (hyphens, apostrophes) are disqualified entirely — this is the primary false-positive defense, since normal English prose contains contractions (they're) and compound words (open-source) that would otherwise match.
Single-line detection — Finds runs of consecutive BIP39 words within each line. Non-BIP39 words reset the counter. This handles space-separated, comma-separated, JSON array, and inline numbered formats.
Cross-line detection — Accumulates BIP39 words across consecutive lines where every meaningful token is a BIP39 word ("BIP39-pure" lines). Blank lines and lines containing only annotation labels (like Word 1: or Recovery phrase:) are transparent — they don't break or contribute to a sequence. This catches one-per-line, numbered list, and grid formats.
Threshold gating — A violation is only reported when 8 or more consecutive BIP39 words are found (configurable via BIP39_THRESHOLD). Since the BIP39 wordlist contains common English words, shorter sequences occur naturally in prose.
This catches standard 12/24-word BIP39 mnemonics as well as legacy 25-word Algorand account mnemonics (which use the same wordlist).
nomonic is a best-effort safety net, not a guarantee. Known limitations:
git commit --no-verify skips all hooks. The full-repo scanner (scan-repo) can be used in CI to catch what hooks miss.scan-repo for full-repo auditing.abandon, ability, access, art, carbon, code, etc.). Technical documentation or prose that happens to use 8+ consecutive BIP39 words will trigger a block. Raise the threshold or use --no-verify for legitimate cases.| Format | Example |
|---|---|
| Space-separated | abandon ability able about above |
| Comma-separated | abandon, ability, able, about, above |
| Quoted CSV | "abandon", "ability", "able", "about", "above" |
| JSON arrays | ["abandon", "ability", "able", "about", "above"] |
| Numbered (single line) | 1. abandon 2. ability 3. able 4. about 5. above |
| Numbered (multi-line) | 1. force2. clay3. airport... |
| Grid layout (Algorand wallet) | 1. force 2. clay 3. airport4. shoot 5. fence 6. fine... |
| Plain one-per-line | forceclayairport... |
| Blank-separated blocks | abandon abilityable about above |
| Annotated one-per-line | Word 1: abandonWord 2: ability... |
| Labeled phrases | mnemonic: abandon ability able about above |
package-lock.json, yarn.lock, etc.)8 or more consecutive words that are all:
Common seed-labeling words (word, words, mnemonic, seed, phrase, key, backup, recovery, secret, passphrase) are treated as transparent — they don't break or contribute to a BIP39 sequence. This catches formats like Word 1: abandon without false-flagging documentation that mentions "seed phrase" in prose.
For CI pipelines or manual auditing, scan all tracked files (not just staged changes):
pnpm exec tsx scripts/nomonic/scan-repo.ts
bash scripts/nomonic/scan-repo.sh
| Flag | Description |
|---|---|
--git |
(Default) Scan git-tracked files |
--dir <path> |
Scan all files in directory recursively |
--include-lockfiles |
Include lockfiles in scan (excluded by default) |
--threshold <n> |
Override detection threshold (default: 8) |
--json |
Output violations as JSON (TypeScript only) |
Adjust the detection threshold via environment variable:
# Require 12+ consecutive words instead of the default 8
export BIP39_THRESHOLD=12
For legitimate cases (e.g., committing the detector files themselves on first install):
git commit --no-verify
Re-run the setup command to pull the latest detector files:
curl -fsSL https://raw.githubusercontent.com/larkiny/nomonic/main/setup.sh | bash
The script is idempotent — it won't duplicate hook entries.
.husky/pre-commit, delete the npx tsx scripts/nomonic/check-staged.ts line.git/hooks/pre-commit, delete the ./scripts/nomonic/check-bip39-seeds.sh linerm -rf scripts/nomonic/rm -rf scripts/nomonic/bash/
check-bip39-seeds.sh # Standalone bash detector (zero dependencies)
scan-repo.sh # Full-repo scanner (zero dependencies)
ts/
wordlist.ts # BIP39 English wordlist as a Set
detect.ts # Core detection algorithm
detect.test.ts # Unit tests (vitest)
check-staged.ts # CLI entry point (pre-commit hook)
check-staged.test.ts # Integration tests (vitest)
scan-repo.ts # Full-repo scanner CLI
scan-repo.test.ts # Scanner tests (vitest)
setup.sh # One-command installer
The Unlicense — public domain. Do whatever you want with it.