Investigation

The Claude Code Hidden-Signal Story, Explained

In late June 2026, developers found that Claude Code was quietly adding a small, hard-to-see signal to its own system prompt under one narrow condition. This is a calm, source-checked explainer: what was actually verified, how the mechanism worked, why it likely existed, and where the loud framing goes past the evidence.

Editorial hero illustration for The Claude Code Hidden-Signal Story, Explained

Get the latest on AI, LLMs & developer tools

New MCP servers, model updates, and guides like this one — delivered weekly.

What Was Found

Between roughly the last days of June and July 1, 2026, two independent developers found that Claude Code, Anthropic's command-line coding agent, was adding a small hidden signal to its own system prompt under one specific condition: when the tool was pointed at a non-Anthropic endpoint through the ANTHROPIC_BASE_URL environment variable. The reason this finding is worth an explainer rather than a shrug is that it is unusually well-supported. It was reverse-engineered by one researcher, independently reproduced by a second named researcher, filed as a public GitHub issue, and then acknowledged by Anthropic, which shipped a new version to remove the code.

The original write-up came from a developer who goes by thereallo, who inspected the Claude Code 2.1.196 binary and published a full analysis on June 30. Independently, security researcher Adnane Khan confirmed the same behavior in versions 2.1.193, 2.1.195, and 2.1.196, describing it as "a covert information channel embedded in system prompts" that he found "verified true in every material particular." The behavior was then filed as GitHub issue #72518 against the anthropics/claude-code repository, which framed it as "covert environment fingerprinting" that was "unacceptable without explicit disclosure and user consent."

Anthropic acknowledged the code and said it would remove it. Version 2.1.197 shipped in the early hours of July 1. One loose end, noted by press coverage: the public changelog for that release does not explicitly mention the removal, so the timing is confirmed but the changelog paper trail is not.

How the Mechanism Works

The most important fact about the mechanism is what switches it off. If ANTHROPIC_BASE_URL is unset, or if it points at api.anthropic.com, the code returns early and adds nothing. It only runs when Claude Code is talking to a custom gateway, proxy, or reseller endpoint. For the large majority of users who talk to Anthropic directly, it never fired.

When it did activate, both researchers describe the same sequence. The code reads two pieces of environment, the system timezone and the proxy hostname, then checks the hostname against two obfuscated lists baked into the binary: roughly 147 domains (a mix of Chinese corporate, cloud, and AI-lab domains plus reseller gateways) and 11 AI-lab keywords. It then encodes the outcome into the one line every Claude Code user has seen, the Today's date is ... line, using two nearly invisible properties of that string.

When ANTHROPIC_BASE_URL is unset OR its host is api.anthropic.com:
    return early   ->   nothing is added to the prompt

Otherwise, on a custom gateway / proxy / reseller endpoint:

    host = hostname(ANTHROPIC_BASE_URL)
    tz   = system timezone

    domainHit  = host matches one of ~147 obfuscated domains
                 (Chinese corporate / cloud / AI-lab + reseller gateways)
    keywordHit = host contains one of 11 AI-lab keywords
                 (deepseek, moonshot, minimax, zhipu, baichuan, stepfun, dashscope, ...)

    // the two lists are NOT plain text in the binary:
    // base64-encoded, then XOR-ed with key 91  (light obfuscation, not encryption)

    // SIGNAL 1 - the date separator
    separator = "/"  when tz is Asia/Shanghai or Asia/Urumqi
                "-"  otherwise

    // SIGNAL 2 - the apostrophe glyph inside the word Today's
    glyph = U+0027  ASCII apostrophe             ->  neither list matched
            U+2019  right single quotation mark  ->  a domain matched
            U+02BC  modifier letter apostrophe   ->  a keyword matched
            U+02B9  modifier letter prime        ->  both matched

    // net result: the visible "Today's date is ..." line now carries ~3 bits

Put together, the two lists plus the timezone flag combine into what both researchers describe as a covert fingerprint of roughly three bits. That is enough to distinguish "matched a known domain," "matched a lab keyword," "matched both," or "matched neither," plus a separate timezone tell. It rode along inside a string that looks like an ordinary timestamp, which is why it sat unnoticed for months.

Verified vs Interpretation

It is worth being precise about which parts of this story are established and which are inference, because the two blur together quickly once a headline is attached.

Verified. The code existed. The trigger condition, a custom base URL, is real. The mechanism (timezone plus hostname, checked against two obfuscated lists, encoded into a glyph and a separator) is described the same way by two independent researchers. A public GitHub issue documents it. And Anthropic acknowledged it and shipped a build to remove it. Multiple independent sources plus a vendor acknowledgment is a high bar, and this clears it.

Interpretation. The words "spyware," "backdoor," and "surveillance" are characterizations of intent, not confirmed facts. The original researcher, thereallo, was careful here: they described it as "not a malicious feature," but "a weird choice for a tool that asks for trust," and pointed out that it is trivially bypassed. An environment variable, a VPN, or a renamed proxy defeats it, so in practice it "mostly punishes normal developers doing weird but legitimate things."

One specific claim also deserves a correction. An early version of this story described the code as a "March anti-abuse experiment." That wording is unverified. What is documented is that the code first appeared in version 2.1.91, around April 3, 2026, and that Anthropic later acknowledged it and promised removal. No source has quoted Anthropic describing it as a "March experiment," so that framing should be treated as unconfirmed.

The Likely Rationale

The most plausible, on-the-record reason has little to do with individual users and a lot to do with model distillation. In February 2026, Anthropic publicly disclosed a wave of distillation attacks: it said DeepSeek, Moonshot, and MiniMax had collectively run more than 16 million exchanges through roughly 24,000 fraudulent accounts, harvesting Claude outputs to train competing models. In June 2026, Anthropic told U.S. lawmakers that Alibaba's Qwen had run the largest such effort.

Read against that backdrop, a signal that only fires when Claude Code is pointed at a non-Anthropic proxy looks less like generic surveillance and more like an attempt to detect resale or laundering of Claude access through third-party gateways, which are the same channels distillation operations tend to use. That is the reading most of the technical community converged on, and it is both the charitable and probably the correct one. It still does not resolve the transparency question, which is the part that actually upset people: a trust-critical developer tool was modifying its own prompt in a way users could not see and did not consent to.

Why the Community Is Split

The clearest evidence that this is a genuinely divided reaction, rather than a one-sided pile-on, is a single number. The most-upvoted Reddit thread on the topic, posted with the loaded framing this article is deliberately avoiding, reached about 1,497 upvotes but with an unusually low 0.73 upvote ratio across 312 comments. On Reddit, a 0.73 ratio means a large share of voters actively disagreed with the framing; most viral threads sit well above 0.9. The ratio, not the headline, is the story.

The top comment, sitting above 1,300 upvotes, was dismissive rather than alarmed. It pointed out that ordinary software collects far more ("wait til you hear about DNS," and a nod to what large social platforms already store). On the other side, the GitHub issue and the researchers argued the transparency principle: it does not matter how small the signal is if a tool you rely on embeds it without telling you. Both camps can be right at once. The data collected is trivial and trivially bypassed, and shipping it silently in a developer tool was a self-inflicted trust problem.

Inspect Your Own System Prompt

If you want to check your own install, the reassuring part is that for most people there is nothing to find. The code only ever activated behind a custom ANTHROPIC_BASE_URL, and Anthropic says it was removed in 2.1.197. If you do run Claude Code through a proxy or gateway and want to look anyway, the tell lives entirely inside the Today's date is ... line of the system prompt.

Two things carry the signal: the date separator (a slash instead of a hyphen) and the exact apostrophe character in the word Today's. Because several Unicode characters look almost identical on screen, you have to inspect the codepoints rather than trust your eyes.

# The signal, if present, lives entirely in the "Today's date is ..." line.
# Because the glyphs look identical, inspect the raw codepoints, not the pixels.

# 1) The apostrophe position in the word "Today's" - look for these bytes:
#      27          =  U+0027  ASCII apostrophe            (nothing encoded)
#      e2 80 99    =  U+2019  right single quotation mark (a domain matched)
#      ca bc       =  U+02BC  modifier letter apostrophe  (a keyword matched)
#      ca b9       =  U+02B9  modifier letter prime       (both matched)

# 2) The date separator:
#      2d  =  "-"   (normal)
#      2f  =  "/"   (timezone flag: Asia/Shanghai or Asia/Urumqi)

# Example: pipe the captured system-prompt line through a hex viewer
cat captured-system-prompt.txt | hexdump -C | grep -i "today"

For a per-version reference of exactly what Claude Code's system prompt contains across releases, the community-maintained Piebald-AI/claude-code-system-prompts repository is the easiest place to compare. If your work regularly points Claude Code at external tools and endpoints, our guide to the best MCP servers for Claude Code covers how those integrations are configured and where the trust boundaries sit.

FAQ

What is the Claude Code hidden signal?

It was a small block of code in the Claude Code command-line tool that, under one specific condition, added a hard-to-see marker to the tool's own system prompt. Two independent researchers reverse-engineered it, it was filed as a public GitHub issue, and Anthropic acknowledged it and shipped a version to remove it.

Did it affect every Claude Code user?

No. The code returned early and did nothing when the base URL was unset or pointed at api.anthropic.com. It only activated when Claude Code was configured to talk to a custom gateway, proxy, or reseller endpoint through the ANTHROPIC_BASE_URL environment variable.

What information did it read?

According to both researchers, it read the system timezone and the hostname of the proxy, then checked the hostname against two obfuscated lists baked into the binary: roughly 147 domains and 11 AI-lab keywords. The result was encoded as a few bits into the visible date line of the prompt.

Did Anthropic confirm it and remove it?

Yes. Reporting indicates Anthropic acknowledged the code and said it would remove it, and version 2.1.197 shipped in the early hours of July 1, 2026. One caveat noted by press coverage is that the public changelog for that version does not explicitly mention the removal.

Was this spyware or a backdoor?

Those words describe intent, and intent here is interpretation, not established fact. The original researcher explicitly called it not a malicious feature, noted it is trivially bypassed, and framed it as a trust and transparency problem rather than surveillance. Reasonable people disagree on the labels.

Is the March anti-abuse experiment explanation accurate?

That specific framing is unverified. What is documented is that the code first appeared in version 2.1.91 around April 3, 2026, and that Anthropic later acknowledged it and promised removal. No source has quoted Anthropic describing it as a March experiment, so treat that wording as unconfirmed.

How can I check my own Claude Code install?

For most people there is nothing to find, because the code only activated behind a custom base URL and was reportedly removed in 2.1.197. If you do run a proxy, inspect the Today's date line of the system prompt at the codepoint level: look for a slash date separator instead of a hyphen, or a non-ASCII apostrophe in the word Today's.

Glossary

  • System prompt: The hidden instruction block a tool like Claude Code sends with every request. Users normally never see it, which is why a change to it can go unnoticed.
  • ANTHROPIC_BASE_URL: An environment variable that points the Claude Code CLI at a different endpoint, such as a corporate gateway, a proxy, or a reseller. Leaving it unset points at Anthropic directly.
  • Steganography: Hiding information inside something that looks ordinary. Here, a few bits were tucked into a line that reads like a normal timestamp.
  • Fingerprint: A small identifying signal derived from the environment. In this case it distinguished which kind of endpoint the tool was talking to.
  • Distillation attack: Harvesting one model's outputs at scale to train a competing model. Anthropic has publicly disclosed several such campaigns.
  • Base64 + XOR: Two light obfuscation steps used to keep the domain and keyword lists from appearing as readable text in the binary. This is disguise, not encryption.
  • Codepoint: The numeric identity of a Unicode character. Different codepoints can render as near-identical glyphs, which is what made the apostrophe trick hard to spot.

Verdict

The honest frame here is neither a scandal nor a nothingburger. A widely trusted developer tool modified its own system prompt to carry a small hidden signal, under a narrow condition, without telling anyone. When that came to light, it was independently verified, and the vendor removed it. The likely purpose, detecting distillation and access resale, is defensible. The way it was done, silently, in a tool that asks for your trust, is what drew fire, and fairly so.

thereallo's own summary is the cleanest takeaway, and a better hook than any geopolitical framing: trust is earned in the boring parts. A model's headline benchmarks and its refusal policies get scrutinized closely; a date string in a system prompt does not. That is exactly why the boring parts matter, and why the fix mattered more than the outrage.

If you are weighing which Claude model to run inside Claude Code in the first place, our comparison of Claude Sonnet 5 vs Opus 4.8 breaks down the tradeoffs, and our timeline of Claude Fable 5's return covers the export-control saga that reshaped model access the same week this story broke.

Sources

This explainer separates the original discovery, the independent verification, the official backdrop, and press coverage so you can weigh each on its own terms.

Original

Verification

Official

Press

Get the latest on AI, LLMs & developer tools

New MCP servers, model updates, and guides like this one — delivered weekly.