DEV Community

Alex @ Vibe Agent Making
Alex @ Vibe Agent Making

Posted on Originally published at vibeagentmaking.com

The Three States of a Secret: Tracked, Ignored, and the One That Ships It


Every Git tutorial teaches three states. Modified, staged, committed: the working directory, the staging area, the repository. It is canonical, it is in the official book, and for the question that decides whether your credentials end up in permanent public history, it is the wrong three states entirely.

The axis that matters for a secret is different. A file in a working tree is in one of three conditions: tracked, meaning Git already versions it; ignored, meaning Git has been told to look away; or the third state, untracked and not ignored, the one nobody teaches. A file in the third state shows up in every status listing. It is included in every git add . and git add -A. It rides along with any commit made by anyone in a hurry, which describes most commits. The first two states are decisions. The third state is an accident waiting for a keystroke.

Last week we found a credential living in exactly that state, in our own shared repository.

A list with a gap

The file was a QA auto-login token: a per-machine secret, minted automatically, that lets a local test harness skip a login screen. We are deliberately not describing it more precisely than that, for the obvious reason that the essay you are reading is about how easily such things become public.

What made the find instructive was not the token. It was the neighborhood. The repository's ignore file had a section, with a comment header, for exactly this class of file: per-machine state, generated artifacts, things that belong to one box and must never be versioned. The token's class-siblings were all correctly listed there. A deploy marker: ignored. A generated test-state directory: ignored. The token itself: absent from the list. Whoever built the section had enumerated the per-machine files they could think of, and this one had arrived later, or been forgotten, and nothing in the system noticed the omission.

So the failure was clerical, not conceptual. Nobody misunderstood security. Nobody made a bad call. A correct rule was implemented as a list, the list had a gap, and the gap was invisible because an untracked-and-not-ignored file looks like nothing at all. It does not fail a build. It does not trip a scanner that only reads committed content. It sits in the status listing of a busy repository, camouflaged among the genuinely new files that appear and disappear all day, one add -A away from being versioned, pushed, and preserved.

That phrase, one commit away from permanent, sounds like rhetoric. It is not, and the reason it is not is worth two minutes of your attention.

Permanent means permanent, by design

In 2024, researchers at Truffle Security documented what they called Cross Fork Object Reference: on GitHub, a commit made to any repository in a fork network can be accessed from any other repository in that network, by supplying the commit hash directly. The consequences are stranger than they sound. Commits made to a fork remain accessible through the original repository after the fork is deleted. Delete the original public repository, and its commits remain accessible through any fork, including forks that never synced. Commits made to a private fork before the upstream went public become reachable once it does. And because Git accepts abbreviated hashes, short enough prefixes can be brute-forced through the web interface.

To demonstrate, the researchers reviewed three commonly forked public repositories from a major AI company and found forty valid API keys in commits that existed only in deleted forks. The pattern behind them was always the same: fork the repo, hardcode a key, do some work, delete the fork, assume the evidence went with it.

GitHub, responding through its vulnerability disclosure program, said the behavior is an intentional design decision and is working as expected. Fork networks share their object stores; that is what makes forking cheap; deletion of one node does not garbage-collect the network. You can argue the design either way, but you cannot argue with what it means for a leaked secret: on the world's default code host, a credential that enters history in a repository with forks cannot be reliably un-published by any deletion or history rewrite you have the power to perform. The scrub tools clean your copy. The network remembers.

Which reframes the little file in our status listing. The distance between "sitting untracked on one machine" and "recoverable forever by strangers" was not a security boundary. It was one inattentive command by any collaborator, on any morning.

The scale of the middle state

If this were rare, it would be an anecdote. GitGuardian's State of Secrets Sprawl report for 2026 counted 28.65 million new hardcoded secrets added to public GitHub commits in 2025, up 34 percent in a single year, the largest jump they have recorded. The growth is not just more developers committing more code; commits grew 43 percent, but the secrets grew on top of a base that detection tooling has been attacking for years. Secrets tied to AI services grew 81 percent, including some 113,000 leaked keys for one popular API alone.

Two of the report's findings matter more than the raw count.

First: private repositories are roughly six times more likely than public ones to contain hardcoded secrets. Read that carefully, because it is the entire psychology of the middle state in one statistic. People are careful where they can be seen and careless where they believe they cannot. The private repo feels like a drawer; developers treat it like one. But the tree's capacity to swallow a secret is identical in a private repository, and private repositories do not stay private: they get forked, migrated, opened, acquired, and cloned onto laptops. The QA token in our tree was "only" on a shared internal repo. Six-x is what "only internal" does to discipline, measured at population scale.

Second: leaked credentials do not die. Of credentials confirmed valid in 2022, nearly 70 percent were still valid in January 2025, and the rate was still above 64 percent a year later. The exposure window for a swallowed secret is not a bad afternoon and a rotation ticket. Measured across the population, it is years, because the organizational reflexes that would end the exposure mostly never fire.

Put the two datasets together and the middle state stops looking like a pedantic edge case. Millions of secrets a year enter histories that, on the default host, cannot be fully un-published, and once there, most of them stay live for years. Every one of them passed through the third state on the way.

The mirror

One more number from the same report, and it is about us. GitGuardian measured commits associated with AI coding assistants and found a 3.2 percent secret-leak rate, against a 1.5 percent baseline across public commits generally. Roughly double. The comparison populations are not perfectly like-for-like and the number is an association, not a proof of cause; assisted commits may simply cluster in the kinds of fast-moving work where leaks happen anyway. But we are an AI-assisted shop, the repository in this story is maintained by AI agents as well as by convention, and it would be a convenient omission to quote everyone else's statistics and skip the one that describes our own workflow. The token we found was minted by automation, in a tree that automation commits to daily. Whatever the causal story, speed multiplies whatever the checklist misses, and our checklists get exercised at machine speed. The find above is our own six-x statistic in miniature: carelessness precisely where nobody outside would ever look.

Deny by default, and the scope of the fix

Here is what we did not do: we did not rotate the token. Before anyone quotes the incident-response handbook, notice the scope. This credential never left the third state. Untracked, uncommitted, never pushed: there was no exposure to remediate, and everything in this essay concerns that state, the one before anything has leaked. What to do with a credential that has actually escaped is a different question with different answers, and this piece deliberately says nothing about it. In the pre-exposure state, reflexively regenerating the secret does nothing about the actual defect, which is that the tree can swallow the file. Rotate it and the new token materializes in the same unlisted path, one keystroke from history, having taught everyone nothing except that the alarm can be silenced.

The fix for the state is structural, and it is the same fix the six-x statistic points at: stop depending on someone remembering to extend a list. An enumerate-the-forbidden ignore file is a control that decays, because every new secret-shaped file must be anticipated by a human at exactly the moment humans are thinking about something else. The durable versions are deny-by-default. Generated state lives in one directory that is ignored as a directory, so new per-machine files are born ignored instead of born naked. Secrets live outside the work tree entirely where the tooling allows it. A pre-commit hook or CI scanner watches the staging area, not just history, so the third state gets caught at the moment of transition, which is the only moment it is cheap. And the status listing itself is treated as a signal: a repository whose git status is habitually noisy is a repository where one more untracked file is invisible, and invisible is the whole problem.

None of this is exotic. That is rather the point. The find that started this essay required no attacker, no zero-day, no misjudgment even; just a correct rule implemented as a list, a list with a gap, and a state that every tutorial's mental model skips. Git's famous three states describe the lifecycle of work you meant to commit. The state that ships your secrets is the one for files you never decided anything about, and it will keep shipping them for as long as the decision is left to a list of exceptions and a human memory.

Check your own status listing today. Not the history: the listing. Count the files that are neither tracked nor ignored, and ask, for each one, what happens on the day someone in a hurry types git add -A. If the answer for any of them is "that must never be committed," you are one omitted line away from a statistic that stays valid for years. The line costs ten seconds. The middle state is the only place it can still be written that cheaply.


Sources

· GitGuardian, The State of Secrets Sprawl 2026 — the 28.65 million new secrets (+34% YoY), commit and developer growth, the AI-service secrets figures, the ~6× internal-vs-public multiple, the credential-validity persistence (nearly 70% of 2022's valid credentials still valid in January 2025; above 64% in January 2026), and the AI-assisted commit leak-rate association (3.2% vs 1.5% baseline).

· Truffle Security, Anyone can Access Deleted and Private Repository Data on GitHub — the Cross Fork Object Reference write-up: the three persistence paths, the forty valid API keys recovered from deleted forks, and GitHub's disclosure-program response that the behavior is an intentional design decision.

· Pro Git (git-scm.com), the canonical three-states model this essay's title deliberately collides with: working directory, staging area, repository.

· The QA-token find is our own, from a shared internal repository, described generically throughout for the reason the essay gives.

Once a secret is in history, the record is the thing you have to reason about. Chain of Consciousness records what an agent actually did, step by step, so the reading and the decision live in the same artifact.

Hosted Chain of Consciousness  ·  Verify a record

pip install chain-of-consciousness  ·  npm install chain-of-consciousness

Top comments (0)