The personal hook was the version that did not survive, and the not-surviving is what the story is about. The hook lived on the laptop. The laptop is where the habit lived. The habit in the laptop is the habit that dies with the laptop, and the laptop changes, the laptop is shared, and the laptop is what the team does not control.
The move was to put the scanner in the repository instead of the machine, and the in-the-repository is what changed the ownership. The repository is what every clone gets, every clone is what every developer has, and every developer is what the habit now runs on, without the developer doing anything. The new developer clones the repository. The repository has the hook file. The hook file has the scan. The scan runs on the first push. The first push is what every developer does, so the habit runs on every developer, and the running does not depend on the onboarding doc being read, the meeting being attended, or the person remembering. The section below is the move in detail: the hook file in the repository, the CI line, the two layers, and the turnover test, because the turnover test is what the personal hook fails and the repository hook passes, and the test is the person leaving and the habit staying.
Every tool has a before and an after, and the story is the after, told from the inside, which means the story includes the part that did not work and the part that was not the tool's fault, because the not-fault is the part the ad leaves out and the story keeps. The tool is dotguard: Scan .env files, config files, and source code for exposed secrets. Zero dependencies, JSON reports, 1000+ files in seconds.. The install line is npx @wuchunjie/dotguard, and the line is in the story at the moment the story's character ran it, because the moment is where the line belongs, and the belongs is what the feature list does not give. The sections below are the story in the order it went, and the order is the part that makes the ending the ending instead of the claim.
What Zero Dependencies Buys You in a Security Tool
A security tool has a special trust problem: you are asking it to read your most sensitive files. The natural question is what the tool itself trusts. For this one, the answer is nothing. No dependencies, no network calls, no telemetry, no update daemon. It reads files, matches patterns, and writes a report. That matters in the places where security tooling gets blocked: restricted CI runners, air-gapped builds, compliance environments that require an audit of every third-party package in the pipeline. A single-file scanner with zero dependencies is auditable in an afternoon by a security reviewer who would never approve a forty-package tree. In security, small is not a feature. Small is the product. The auditability is the trust model: you can read the whole thing, you can verify what it matches, and you can be confident that the thing reading your secrets is not also phoning home with them. That confidence is not a nice property of the design; it is the design.
Monorepos, Home Directories, and the Recursive Flag
The scanner assumes your secrets live in one project, and then breaks that assumption on purpose. The path flag lets you point it at a subdirectory — scan just the deploy configs, or just one service in the tree. The recursive flag lets you do the opposite: point it at your projects directory and audit everything you have ever written, including the old side projects you forgot about. That last use is the one that pays for itself. Leaks do not respect project boundaries; the forgotten demo repository from a few years ago is a live credential the moment someone finds it. A scanner that can sweep an entire directory tree in seconds turns auditing all your code from a month-long project into a coffee break. The monorepo use is the inverse: one command covers every service, so the scan cost does not grow with the organization. Either direction, the same principle: the scan should be cheap enough to run often, and running it often is what catches the leak while it is still cheap to fix.
The Rotation Checklist the Report Triggers
The finding without the rotation is a list, and the list is the part that ages badly. The rotation checklist is the four steps that turn the finding into the closed loop, and the steps are short enough to be a habit. Step one is the verify, the five-second check that the value is live and not a fixture. Step two is the rotate, the call to the provider that replaces the key and invalidates the old one. Step three is the update, the new value in the place the build reads, and the old value deleted from everywhere the working tree still holds it. Step four is the re-scan, the same command run again, and the re-scan is the proof, because the proof is the old value not in the report. The four steps take the length of a coffee, and the coffee is the part that makes the loop close on the day of the finding instead of the day of the reminder. The report is the trigger, and the trigger without the loop is the notification, and the notification is what gets ignored.
The JSON Report That CI Can Actually Use
A security tool is only as good as its integration surface, and the scanner's is a JSON report. Run it with the report flag and you get a machine-readable list of every finding: file, line, rule, severity. Exit codes are meaningful, so a pipeline can fail the build on any finding without parsing colored terminal output. That design decision pays off in the boring way: the tool fits into CI or a local pre-push script with zero glue code. When the report is data instead of text, other tools can consume it — a dashboard, a chat alert, a compliance export. Security tooling that cannot be integrated is a demo, not infrastructure. The JSON report is the part of the design that turns a one-person tool into a team habit, and it costs nothing to use: the same command, one extra flag, and the output becomes something the rest of the system can act on.
The Exit Code Is the Integration
The scanner's interface to the machine is the exit code, and the exit code is the part that makes it CI-ready without any glue. A clean scan exits zero, a scan with findings exits nonzero, and the nonzero is what the pipeline turns into a failure, and the failure is what blocks the push. The pre-push hook uses the same code: the hook runs the scan, and the push goes through only if the exit code is zero. The JSON report is the second interface, for the things that consume data instead of pass/fail: the dashboard that charts findings over time, the chat alert that posts the file and the line, the compliance export that needs the record. The two interfaces cover the two consumers, the pipeline and the person, and the coverage is the design. A tool that needs a wrapper script to be useful in CI is a tool that will not be in CI, because the wrapper is the step that does not get written, and the not-written is the check that does not run.
One Line in GitHub Actions
The entire CI integration is one step: a run line that invokes the scanner via npx. No service container, no token to configure, no daemon. Every push gets a full scan, and the build fails if a secret is found, which means the secret never reaches the default branch. The beauty of the one-line integration is the maintenance cost: there is nothing to update, no version to pin, and no vendor to renew. When a security control costs one line of workflow file, the only question is why it is not already there. That is the bar every pre-merge security control should meet, and the scanner was designed to meet it on purpose. The one line is also the onboarding story: new contributors see the check in the workflow file, understand what it is doing, and never have to be told to run it. The pipeline is the policy, and the policy is one line long.
Rotation Beats Detection, Every Time
There is a comfortable misconception that finding a secret is the win. It is not. The win is the key that no longer works. A detected secret that stays valid is a secret that is still leaking; the only difference is that now you know about it, which is a strange kind of liability. The workflow that matters is: scan, confirm, rotate, and then scan again to confirm the rotation happened and the old value is gone from the working tree or accepted as a known dead value. Rotation is boring, it touches other teams, and it is the step everyone skips. A detection tool that makes rotation feel like a natural next step — by giving you the exact file and line, so the fix is a two-minute job — is doing more than pattern matching. The finding is a ticket, and the ticket's definition of done is the rotated key, not the closed alert. Designing the tool around that definition of done is what separates a scanner from a security workflow.
The Rules: What the Scanner Actually Matches
The scanner works on two rule families, and knowing both is what makes the report readable. The first family is the known-shape rule: the cloud provider key prefixes, the git token formats, the webhook secret shapes, the database URL patterns. These are the rules with names, and they fire on the credentials the ecosystem has already standardized. The second family is the entropy heuristic: a string with the statistical fingerprint of a generated secret, assigned to a variable whose name says what it is. The heuristic is the net for the custom key, the one that follows no documented format, and it is the family that needs the five-second human check, because the heuristic is a candidate, not a verdict. The report marks which family fired, and the mark is what separates the rotate-today finding from the probably-a-fixture one. The two families together cover the cases that matter, and the report tells you which one caught the finding, which is the part that makes the triage fast instead of anxious.
Scanning the Worktree, and the History Behind It
The scanner covers what is on disk: the environment files, the config files, the source in your working tree. That catches the obvious case — the file you just created and are about to commit. For the subtler case, the secret that is already in history, you combine it with the version control system: find the files that ever contained the pattern, then scan them. A committed secret does not stop being a secret when it is deleted from the current branch; it lives in every clone, every fork, and every mirror. The honest workflow is two steps: scan the present automatically, and audit the past with the same rules applied to the files that history touched. Detection is a habit, and habits are easier to keep when the tooling is small enough to run by reflex. The present scan is the reflex; the past audit is the quarterly deep clean, and both use the same rules, which is what makes the pair coherent instead of two unrelated chores.
The Team-Wide Hook: Distributing the Habit
Personal hooks die with the person who set them up. The pattern that actually works is distribution: put the scan in a hook that lives in the repository itself, committed to version control, so every clone gets the behavior automatically. A new developer clones the repository, runs their first command, and the scanner is already there. Nobody has to be told, nobody has to remember, and the behavior cannot be forgotten because it is part of the repository. This is the difference between a tool and a practice: a tool is what one person runs, a practice is what the repository does to everyone. The scanner is small enough that the distributed version costs nothing to maintain — one file, one command, zero configuration — which is exactly the size at which a team habit becomes cheaper than the individual habit it replaces. The repository becomes the enforcer, and enforcement by repository is the only enforcement that survives turnover.
Five Seconds to Verify a Finding
The verification is the step that turns the finding into a decision, and the five seconds is the design target, because the five seconds is what keeps the check happening. The report gives the file, the line, and the rule, and the verification is open the file, look at the line, and answer one question: is this value live, or is it a fixture? The live value is the one that the build reads, that the deploy uses, or that a request would present to a real service. The fixture is the one that the test asserts against, that the README shows as an example, that the mock returns. The answer changes the next step: live means rotate and re-scan, fixture means note it and move on. The five seconds works because the report is specific, and the specificity is the feature. A scanner that says suspicious content found is a scanner that takes an hour to triage, and the hour is what stops the habit. The five seconds is the habit.
The takeaway
The ending of the story is the state, and the state is the part the tool holds. dotguard is Scan .env files, config files, and source code for exposed secrets. Zero dependencies, JSON reports, 1000+ files in seconds., and the install is npx @wuchunjie/dotguard, and the repository is https://github.com/wuchunjie00/dotguard. The reader at the end of the story is the reader who has the before, and the before is the part the story gave, because the gave is the specific date and the specific number and the specific moment, and the three are what the general article does not. The state above is the one the story reached, and the reached is the part the next story starts from, and the starts-from is what the habit is.
Top comments (0)