0>1.software

// blog/how-claude-code-s-hidden-prompt-markers-work

How Claude Code's Hidden Prompt Markers Work

John Sasser
John Sasser
July 6, 2026
6 min read
claude-codesecuritysteganographyai-toolingprivacy
A minimal branded text card reading 'One hidden apostrophe secretly classifies your API traffic,' with supporting lines about custom base URLs, flagged domains, and easy bypass, under the kicker CLAUDE CODE SECURITY.

Claude Code's client binary contains logic that silently varies the punctuation of a single system prompt sentence based on your API base URL and your system timezone. The sentence is the date line: Today's date is 2026-06-30. Depending on what the client detects about your environment, the apostrophe in "Today's" becomes one of four different Unicode codepoints, and the date separators can flip from hyphens to slashes. To a human reading the prompt, and to the model consuming it, nothing looks unusual. In the raw bytes of the request, it is a classification tag.

The mechanism was found by a researcher who was inspecting Claude Code for privacy reasons and published a teardown of version 2.1.196, with the relevant functions reconstructed from the minified bundle. This affects more than one tool because the client that ships your coding agent has filesystem, shell, and git access, and its own behavior deserves the same scrutiny as anything it runs.

What the marker encodes

The marker lives entirely inside one rendered sentence. Cleaned up from the bundle, the encoding function looks like this:

function edp(known, labKw) {
  if (!known && !labKw) return "'";       // U+0027, plain ASCII
  if (known && !labKw)  return "\u2019";  // right single quotation mark
  if (!known && labKw)  return "\u02BC";  // modifier letter apostrophe
  return "\u02B9";                        // modifier letter prime
}

Two booleans go in: whether the API hostname matches a known domain list, and whether it contains an AI lab keyword. The four combinations map to four visually near-identical apostrophes:

ConditionApostrophe
Normal' (U+0027)
Known domain (U+2019)
Lab keywordʼ (U+02BC)
Known domain and lab keywordʹ (U+02B9)

A third signal rides on the date itself. If the system timezone is Asia/Shanghai or Asia/Urumqi, the separator changes: 2026-06-30 becomes 2026/06/30. In most monospace fonts you would never notice any of it. The researcher's term for this is prompt steganography: data hidden in plain sight inside a sentence that reads as boilerplate.

What triggers the check

The path is gated on ANTHROPIC_BASE_URL, the environment variable Claude Code uses to override its API endpoint:

function Crt() {
  let baseUrl = process.env.ANTHROPIC_BASE_URL;
  if (!baseUrl) return true;               // no override: bail out
  let host = new URL(baseUrl).host;
  return ["api.anthropic.com"].includes(host);  // official endpoint: bail out
}

If Crt() returns true, the marker function returns null and the date sentence renders normally. So for anyone running a stock install against the official Anthropic API, this code path is inert. The classification only fires when a custom base URL is set, at which point the hostname is lowercased and tested two ways: exact or suffix match against a domain list, and substring match against a keyword list. The timezone check runs independently.

The obfuscated lists

Neither list appears in the bundle as plain text. Both are stored as base64 strings and XOR-decoded with the key 91 at runtime. Decoded, the keyword list is:

deepseek,moonshot,minimax,xaminim,zhipu,bigmodel,baichuan,stepfun,01ai,dashscope,volces

Those are identifiers associated with Chinese AI labs and their API infrastructure. The domain list is much larger and mixes three categories: Chinese corporate domains (baidu.com, alibaba-inc.com, bytedance.net, jd.com, xiaohongshu.com), AI company domains (moonshot.ai, stepfun-inc.com, iflytek.com), and a long tail of proxy, reseller, and gateway domains including anyrouter.top, claude-code-hub.app, claude-opus.top, openclaude.me, yunwu.ai, zenmux.ai. The researcher published the full decoded list.

XOR with a single-byte key plus base64 stops nobody who is looking; it took one short decode function to dump the whole thing. It keeps the list out of a casual grep of the bundle. The complete picture of which domains Anthropic apparently treats as gateways and resellers now ships, lightly scrambled, inside every Claude Code install.

How the marker reaches the backend

The marked sentence is assembled into the agent context alongside other environment fields:

{
  ...userEmail && { userEmail: `The user's email address is ${userEmail}.` },
  ...attachedProject && { attachedProject },
  currentDate: Vla(GSe())
}

From there it travels wherever the request goes. A reseller or internal gateway that forwards traffic onward to Anthropic delivers the marker straight to a backend that can parse it. But when ANTHROPIC_BASE_URL points at a hostname containing deepseek or zhipu, the request is not going to Anthropic at all; the marked prompt lands at a third-party endpoint. One plausible reading is that the marker is meant to survive into whatever logs or training corpora get built from those requests, tagging Claude Code traffic that was redirected wholesale to another provider. The researcher frames the likely intent as detecting API resellers, unauthorized Claude Code gateways, and model distillation pipelines, and all three fit the list contents.

Who actually gets marked

For most users, nothing changes. The researcher verified this on their own machine: an Anthropic-signed binary (TeamIdentifier=Q6L2SF6YDW, timestamped June 29, 2026), ANTHROPIC_BASE_URL unset, timezone Asia/Hong_Kong. That environment produces the plain apostrophe and the plain YYYY-MM-DD date. Hong Kong is absent from the timezone check, which targets only Asia/Shanghai and Asia/Urumqi.

The populations that do trip the check are the ones the researcher enumerates: internal corporate gateways, local proxies, model routers, resellers, and research setups. Plenty of enterprises terminate all LLM traffic at an internal gateway for logging and policy enforcement; plenty of engineers run Claude Code through a local proxy to inspect requests, which is how this finding happened in the first place. They all emit classification bits they were never told about.

The bypass is trivial: change the hostname, spoof the timezone, patch the binary, or wrap the process with a rewriting proxy. Any operation sophisticated enough to run a gray-market Claude Code reselling business can strip a Unicode apostrophe. The users who get reliably fingerprinted are the ones not trying to hide anything.

Why the implementation drew criticism

Anthropic has legitimate reasons to detect this traffic. Reseller gateways violate terms, and distillation pipelines that farm a frontier model's outputs to train a competitor are a real and well-known concern. Providers enforce against both. The criticism is aimed at the shape of the enforcement.

The same signal could have been an explicit, documented field in the request: a client_endpoint_class in metadata, a line in the release notes, a paragraph in the privacy documentation. Instead it is invisible punctuation in a sentence engineered to look like boilerplate, backed by an XOR-scrambled blocklist. The researcher concluded: "Hiding the signal in the system prompt makes every other privacy claim harder to believe," and "Trust is earned in the boring parts."

Anyone deploying coding agents should consider this. These tools read repositories that contain secrets, run shell commands, install packages, and push commits. Developers accept that exposure because the productivity is worth it. The acceptability of that trade rests on the client behaving as documented. Once published, the feature had little enforcement value, but it continues to raise a trust question. It appears designed to stay dormant for ordinary users, but it was found by exactly the kind of inspection every agent binary should expect.

Checking your own traffic

If you route Claude Code through any custom base URL, verifying what your installs emit takes minutes. Capture an outbound request at your proxy and hex-inspect the date sentence in the system context: E2 80 99 where you expect 27 means your hostname matched the domain list; CA BC means it matched a lab keyword; slashes in the date mean the machine's timezone matched the check. Gateway operators should also consider whether their own pipelines normalize Unicode, because a middlebox that folds smart quotes to ASCII will silently destroy the marker, and one that injects them will manufacture a false positive.

Security teams learned to treat browser extensions as signed binaries from reputable vendors that still need unbundling and review, because behavior is rarely in the documentation. Claude Code's bundle is minified JavaScript, which is why one person with a deobfuscator could produce this finding. The next undisclosed mechanism in some agent client may not be so easy to read, and it may not be dormant by default.


Sources