<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mehmet Aras</title>
    <description>The latest articles on DEV Community by Mehmet Aras (@arasovic).</description>
    <link>https://dev.to/arasovic</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1121708%2Fc6b55773-e173-4ad2-b221-d8076dd10851.JPG</url>
      <title>DEV Community: Mehmet Aras</title>
      <link>https://dev.to/arasovic</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/arasovic"/>
    <language>en</language>
    <item>
      <title>Inside React2Shell</title>
      <dc:creator>Mehmet Aras</dc:creator>
      <pubDate>Fri, 24 Apr 2026 01:42:40 +0000</pubDate>
      <link>https://dev.to/arasovic/notes-on-react2shell-52cg</link>
      <guid>https://dev.to/arasovic/notes-on-react2shell-52cg</guid>
      <description>&lt;p&gt;&lt;em&gt;A Turkish version of this post was originally published on &lt;a href="https://blog.arasmehmet.com/react2shell-tek-http-request-ile-serverda-kod-%C3%A7al%C4%B1%C5%9Ft%C4%B1rmak-ed368bcaacad" rel="noopener noreferrer"&gt;blog.arasmehmet.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer:&lt;/strong&gt; This is a retrospective analysis of a publicly disclosed CVE that has been patched since disclosure. All exploit mechanics discussed are conceptual; nothing here is a working exploit.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;December 3rd, 2025. The React Security Advisory published CVE-2025-55182, nicknamed React2Shell. CVSS 10.0, the highest possible severity. A specially-crafted HTTP request, no authentication, arbitrary code execution on any app running React Server Components.&lt;/p&gt;

&lt;p&gt;Lachlan Davidson reported it to Meta's Bug Bounty four days earlier. Meta's security team verified it the next day. The patch and the public disclosure went out together on December 3rd.&lt;/p&gt;

&lt;p&gt;I went through the advisory, the patch diff and the postmortems. Not the security-industry hot takes, the actual mechanics. Below is what stood out.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What's in here:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The vulnerability&lt;/li&gt;
&lt;li&gt;The Flight protocol and thenables&lt;/li&gt;
&lt;li&gt;RCE through the prototype chain&lt;/li&gt;
&lt;li&gt;Why default Next.js was vulnerable&lt;/li&gt;
&lt;li&gt;Affected packages&lt;/li&gt;
&lt;li&gt;Discovery and disclosure&lt;/li&gt;
&lt;li&gt;Exploitation in the wild&lt;/li&gt;
&lt;li&gt;Impact&lt;/li&gt;
&lt;li&gt;The 700-line decoy patch&lt;/li&gt;
&lt;li&gt;AI-generated fake PoCs&lt;/li&gt;
&lt;li&gt;The Cloudflare outage&lt;/li&gt;
&lt;li&gt;The fix&lt;/li&gt;
&lt;li&gt;What to do&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  1. The vulnerability
&lt;/h2&gt;

&lt;p&gt;React 19 introduced Server Components, which talk to the client over a protocol called Flight. Flight serializes data on one side of the wire and deserializes it on the other.&lt;/p&gt;

&lt;p&gt;Flight wasn't validating incoming payloads properly. An attacker could send a specially-crafted HTTP request containing a fake "Chunk" object. When React tried to resolve that object as a Promise, a &lt;code&gt;then&lt;/code&gt; method defined on the object would execute.&lt;/p&gt;

&lt;p&gt;The exploit chain:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Attacker sends a fake Chunk object.&lt;/li&gt;
&lt;li&gt;React tries to process it as a Promise.&lt;/li&gt;
&lt;li&gt;The fake &lt;code&gt;then&lt;/code&gt; method runs.&lt;/li&gt;
&lt;li&gt;Through JavaScript's prototype chain, &lt;code&gt;Array.constructor&lt;/code&gt; leads to the &lt;code&gt;Function()&lt;/code&gt; constructor.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Function()&lt;/code&gt; compiles strings into executable code at runtime, so arbitrary code runs on the server.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  2. The Flight protocol and thenables
&lt;/h2&gt;

&lt;p&gt;The Flight protocol moves RSC payloads in this format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"MyComponent"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Server"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"$"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"div"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,{&lt;/span&gt;&lt;span class="nl"&gt;"children"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Hello"&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The issue is that React was parsing incoming objects and trying to treat them as Promises without first checking the &lt;code&gt;then&lt;/code&gt; method. In JavaScript, any object with a &lt;code&gt;then&lt;/code&gt; method is considered "thenable":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Regular Promise&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;promise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// 42&lt;/span&gt;

&lt;span class="c1"&gt;// Thenable object, acts like a Promise&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;thenable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;then&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;thenable&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// 42&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Attackers exploited exactly this. A fake Chunk object with a custom &lt;code&gt;then&lt;/code&gt; method would run when React tried to resolve it, and from there the attacker got access to internal state.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. RCE through the prototype chain
&lt;/h2&gt;

&lt;p&gt;In JavaScript, every array's constructor points to the &lt;code&gt;Function&lt;/code&gt; constructor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;[].&lt;/span&gt;&lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;constructor&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nb"&gt;Function&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;

&lt;span class="c1"&gt;// Which means:&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[].&lt;/span&gt;&lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;return process.env&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// environment variables from the server&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Function()&lt;/code&gt; compiles strings into executable code at runtime, the same dynamic-code-execution behavior people warn about in JavaScript. Attackers used this chain to run arbitrary code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Conceptual exploit (simplified)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;maliciousChunk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;then&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Reach Function via Array constructor&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;FnCtor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[].&lt;/span&gt;&lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// From here, any Node API is reachable: file system,&lt;/span&gt;
    &lt;span class="c1"&gt;// environment, subprocesses, network. PoCs in the wild&lt;/span&gt;
    &lt;span class="c1"&gt;// used this to spawn shell commands like `whoami`.&lt;/span&gt;
    &lt;span class="nc"&gt;FnCtor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/* arbitrary server-side code */&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once this payload was encoded into an HTTP request body and sent to an RSC endpoint, the code ran on the server.&lt;/p&gt;

&lt;p&gt;Important detail: even if your app didn't explicitly use Server Actions, if RSC support was enabled, you were vulnerable. Every Next.js project created with &lt;code&gt;create-next-app&lt;/code&gt; defaults ships with App Router enabled, which means direct exploitation worked out of the box.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Why default Next.js was vulnerable
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx create-next-app@latest my-app
&lt;span class="c"&gt;# Accept all defaults&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command creates a project with the &lt;code&gt;app/&lt;/code&gt; directory, which enables the App Router. The App Router automatically exposes RSC endpoints:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="nf"&gt;POST&lt;/span&gt; &lt;span class="nn"&gt;/_next/rsc&lt;/span&gt; &lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;
&lt;span class="na"&gt;Content-Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;text/x-component&lt;/span&gt;

[malicious Flight payload]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even without a single Server Action defined, RSC payloads get processed through this endpoint. "I don't use Server Actions" wasn't protection.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Affected packages
&lt;/h2&gt;

&lt;p&gt;React packages (versions 19.0.0, 19.1.0, 19.1.1, 19.2.0):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;react-server-dom-webpack&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;react-server-dom-parcel&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;react-server-dom-turbopack&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Affected frameworks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Next.js 15.x and 16.x&lt;/li&gt;
&lt;li&gt;React Router (with RSC support)&lt;/li&gt;
&lt;li&gt;Waku, RedwoodSDK&lt;/li&gt;
&lt;li&gt;Parcel and Vite RSC plugins&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not affected:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Core &lt;code&gt;react&lt;/code&gt; and &lt;code&gt;react-dom&lt;/code&gt; packages&lt;/li&gt;
&lt;li&gt;Client-side-only React apps&lt;/li&gt;
&lt;li&gt;React 18 and earlier&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  6. Discovery and disclosure
&lt;/h2&gt;

&lt;p&gt;Security researcher Lachlan Davidson reported the vulnerability to Meta's Bug Bounty program on November 29th, 2025. Meta's security team verified it the next day, and a patch went out with the React team on December 3rd, 2025. The coordinated disclosure and the patch landed the same day.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Exploitation in the wild
&lt;/h2&gt;

&lt;p&gt;Within hours of the disclosure, China-linked state-sponsored groups started exploiting it. Per Amazon and Palo Alto Networks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Earth Lamia and Jackpot Panda carried out the first attacks.&lt;/li&gt;
&lt;li&gt;UNC5174 hit more than 30 organizations.&lt;/li&gt;
&lt;li&gt;Attackers harvested AWS credentials, SSH keys and cloud metadata.&lt;/li&gt;
&lt;li&gt;Cryptominers, backdoors and RATs were dropped.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;CISA added it to the Known Exploited Vulnerabilities (KEV) catalog on December 5th.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Impact
&lt;/h2&gt;

&lt;p&gt;Per Wiz Research:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;39% of cloud environments had at least one vulnerable React instance.&lt;/li&gt;
&lt;li&gt;44% of them were hosting a public-facing Next.js app.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Censys estimated around 2.15 million internet-facing services were affected.&lt;/p&gt;




&lt;h2&gt;
  
  
  9. The 700-line decoy patch
&lt;/h2&gt;

&lt;p&gt;The React maintainers (sebmarkbage in particular) didn't just fix the bug. They shipped a ~700-line patch that included the actual fix alongside unrelated code changes, general deserialization hardening and structural tweaks.&lt;/p&gt;

&lt;p&gt;The intent was obvious: obfuscation. Make attackers spend more time figuring out where the real vulnerability sat.&lt;/p&gt;

&lt;p&gt;The side effect was that security researchers got misled too. The &lt;code&gt;$F&lt;/code&gt; primitive and the &lt;code&gt;loadServerReference&lt;/code&gt; code path looked suspicious but were decoys. The real exploit path was somewhere else entirely. The community argued about this: it slowed attackers, but it also slowed defenders.&lt;/p&gt;




&lt;h2&gt;
  
  
  10. AI-generated fake PoCs
&lt;/h2&gt;

&lt;p&gt;After the disclosure, dozens of "Proof of Concept" exploits started circulating. Per Trend Micro, around 145 fakes ended up in circulation, and most of them didn't actually trigger the real vulnerability.&lt;/p&gt;

&lt;p&gt;The common shape of these fakes: they required the developer to explicitly expose functions like &lt;code&gt;vm#runInThisContext&lt;/code&gt;, subprocess spawners or &lt;code&gt;fs#writeFile&lt;/code&gt;. The real vulnerability didn't need any of that. It worked on default configurations.&lt;/p&gt;

&lt;p&gt;Two risks came out of this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;False negatives&lt;/strong&gt;: testing with a broken PoC and concluding "we're safe".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Misplaced confidence&lt;/strong&gt;: underestimating the actual scope of the threat.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  11. The Cloudflare outage
&lt;/h2&gt;

&lt;p&gt;December 5th, 2025, 08:47 UTC. 28% of Cloudflare's HTTP traffic went down. A lot of sites went down, including LinkedIn, X, Zoom and Canva.&lt;/p&gt;

&lt;p&gt;The cause: a config error during the emergency WAF rollout for React2Shell. While modifying body parsing logic, Cloudflare triggered a Lua error in the FL1 proxies, and every affected request returned HTTP 500.&lt;/p&gt;

&lt;p&gt;The outage lasted 25 minutes. Cloudflare CTO Dane Knecht's statement: "This wasn't an attack. The changes to our body parsing logic, made while deploying detection and mitigation for the React Server Components vulnerability, triggered this."&lt;/p&gt;

&lt;p&gt;Irony: Cloudflare's China network wasn't affected.&lt;/p&gt;

&lt;p&gt;The incident is a decent reminder that emergency security patches carry their own risk.&lt;/p&gt;




&lt;h2&gt;
  
  
  12. The fix
&lt;/h2&gt;

&lt;p&gt;The patch added validation that checks whether incoming objects are actually real Chunks. Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Vulnerable code (simplified)&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;resolveChunk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Accepts any thenable&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Patched code (simplified)&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;resolveChunk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Check for the internal Chunk symbol&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;REACT_CHUNK_SYMBOL&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Invalid chunk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Patched versions (December 3rd, 2025):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React packages: 19.0.1, 19.1.2, 19.2.1&lt;/li&gt;
&lt;li&gt;Next.js: 15.0.5, 15.1.9, 15.2.6, 15.3.6, 15.4.8, 15.5.7, 16.0.7&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Vercel deployed platform-wide WAF rules and shipped the &lt;code&gt;npx fix-react2shell-next&lt;/code&gt; utility.&lt;/p&gt;




&lt;h2&gt;
  
  
  13. What to do
&lt;/h2&gt;

&lt;p&gt;First, check whether you're vulnerable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check package-lock.json or yarn.lock&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s2"&gt;"react-server-dom-(webpack|parcel|turbopack)"&lt;/span&gt; package-lock.json

&lt;span class="c"&gt;# Or via npm list&lt;/span&gt;
npm list react-server-dom-webpack react-server-dom-parcel react-server-dom-turbopack
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Update immediately&lt;/strong&gt;: bump affected packages to patched versions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rotate secrets&lt;/strong&gt;: if you were exposed between December 3rd and 5th, change every credential.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review logs&lt;/strong&gt;: check for suspicious RSC endpoint requests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add WAF rules&lt;/strong&gt;: temporary coverage, not a substitute for patching.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;React2Shell is the first maximum-severity vulnerability in the React Server Components architecture. Deserialization bugs are one of the most dangerous classes in software security, and this one was exploitable in default configurations. People compared it to Log4Shell, and that comparison wasn't a stretch. Both the blast radius and the ease of exploitation lined up.&lt;/p&gt;

&lt;p&gt;If you're running RSC or Next.js 15+, go patch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sources&lt;/strong&gt;: React Security Advisory, Next.js CVE-2025-66478, Palo Alto Unit 42, Amazon Security Blog, Wiz Research, CISA KEV&lt;/p&gt;

</description>
      <category>security</category>
      <category>react</category>
      <category>nextjs</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Notes from Reading Claude Code's Leaked Source</title>
      <dc:creator>Mehmet Aras</dc:creator>
      <pubDate>Thu, 23 Apr 2026 21:40:19 +0000</pubDate>
      <link>https://dev.to/arasovic/notes-from-reading-claude-codes-leaked-source-4eb5</link>
      <guid>https://dev.to/arasovic/notes-from-reading-claude-codes-leaked-source-4eb5</guid>
      <description>&lt;p&gt;&lt;em&gt;A Turkish version of this post was originally published on &lt;a href="https://blog.arasmehmet.com/claude-codeun-s%C4%B1zan-kaynak-kodunda-neler-var-e9fe90f39a1a" rel="noopener noreferrer"&gt;blog.arasmehmet.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer:&lt;/strong&gt; All source code referenced is property of Anthropic. This analysis is based on the publicly shipped npm package artifact. No proprietary code has been redistributed.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;March 31, 2026. Anthropic's npm package &lt;code&gt;@anthropic-ai/claude-code&lt;/code&gt; went out with a 57MB source map file inside it. It was built with &lt;code&gt;sourcesContent: true&lt;/code&gt;, which meant the whole TypeScript source ended up embedded in the &lt;code&gt;.map&lt;/code&gt;. 1,902 files. Over 512,000 lines of code.&lt;/p&gt;

&lt;p&gt;Someone caught it, mirrored it to GitHub, and within a few hours it had 60K stars.&lt;/p&gt;

&lt;p&gt;I read the code. Not the Twitter takes, the code itself. Notes below.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What's in here:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Identity and internals&lt;/li&gt;
&lt;li&gt;Analytics and privacy&lt;/li&gt;
&lt;li&gt;The Anthropic-employee-only system&lt;/li&gt;
&lt;li&gt;Hidden and undocumented commands&lt;/li&gt;
&lt;li&gt;Context window management&lt;/li&gt;
&lt;li&gt;Cost and retry&lt;/li&gt;
&lt;li&gt;Prompt cache&lt;/li&gt;
&lt;li&gt;Permission system&lt;/li&gt;
&lt;li&gt;Query engine and orchestration&lt;/li&gt;
&lt;li&gt;Skill system&lt;/li&gt;
&lt;li&gt;Plugin system&lt;/li&gt;
&lt;li&gt;MCP server management&lt;/li&gt;
&lt;li&gt;IDE Bridge (Remote Control)&lt;/li&gt;
&lt;li&gt;Session Memory&lt;/li&gt;
&lt;li&gt;Memory directory (memdir)&lt;/li&gt;
&lt;li&gt;OAuth&lt;/li&gt;
&lt;li&gt;Platform features&lt;/li&gt;
&lt;li&gt;Infrastructure&lt;/li&gt;
&lt;li&gt;Other details&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Mechanics of the leak
&lt;/h2&gt;

&lt;p&gt;The Bun bundler generated &lt;code&gt;cli.js.map&lt;/code&gt; with &lt;code&gt;sourcesContent: true&lt;/code&gt;. The &lt;code&gt;files&lt;/code&gt; field in &lt;code&gt;package.json&lt;/code&gt; didn't exclude it. &lt;code&gt;.npmignore&lt;/code&gt; didn't either. Run &lt;code&gt;npm pack @anthropic-ai/claude-code&lt;/code&gt;, unpack the tarball, open &lt;code&gt;cli.js.map&lt;/code&gt;. It's all there.&lt;/p&gt;

&lt;p&gt;A 57MB file only ends up in an npm package if nothing checks the size at publish. Apparently nothing does.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Identity and internals
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Tengu
&lt;/h3&gt;

&lt;p&gt;Claude Code's internal name is Tengu. Every analytics event starts with &lt;code&gt;tengu_&lt;/code&gt;: &lt;code&gt;tengu_api_success&lt;/code&gt;, &lt;code&gt;tengu_init&lt;/code&gt;, &lt;code&gt;tengu_exit&lt;/code&gt;. Same with feature flags: &lt;code&gt;tengu_auto_mode_config&lt;/code&gt;, &lt;code&gt;tengu_harbor&lt;/code&gt;, &lt;code&gt;tengu_amber_quartz_disabled&lt;/code&gt;. The name appears in the source hundreds of times.&lt;/p&gt;

&lt;h3&gt;
  
  
  KAIROS
&lt;/h3&gt;

&lt;p&gt;Another flag that comes up a lot: &lt;code&gt;KAIROS&lt;/code&gt;. It's an internal mode for the Claude.ai desktop app. In this mode, memory consolidation, the cron scheduler, and message layout all work differently. It's not a separate product, just the same codebase running in a different mode.&lt;/p&gt;

&lt;h3&gt;
  
  
  Entry points
&lt;/h3&gt;

&lt;p&gt;Claude Code isn't a single CLI. Looking at &lt;code&gt;src/entrypoints/&lt;/code&gt;, these modes turn up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CLI (standard use)&lt;/li&gt;
&lt;li&gt;MCP server (under the name claude/tengu, exposing its own tools as MCP tools)&lt;/li&gt;
&lt;li&gt;SDK (public schemas: &lt;code&gt;coreSchemas.ts&lt;/code&gt;, &lt;code&gt;controlSchemas.ts&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Chrome extension MCP (&lt;code&gt;--claude-in-chrome-mcp&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Chrome native host (&lt;code&gt;--chrome-native-host&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Computer Use MCP (behind the &lt;code&gt;CHICAGO_MCP&lt;/code&gt; flag)&lt;/li&gt;
&lt;li&gt;Daemon worker (spawned by a supervisor)&lt;/li&gt;
&lt;li&gt;Remote Control (commands: &lt;code&gt;remote-control&lt;/code&gt;, &lt;code&gt;rc&lt;/code&gt;, &lt;code&gt;remote&lt;/code&gt;, &lt;code&gt;sync&lt;/code&gt;, &lt;code&gt;bridge&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Ablation baseline (the &lt;code&gt;ABLATION_BASELINE&lt;/code&gt; flag disables things like thinking and compact to run A/B comparisons)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;--version&lt;/code&gt; loads no modules, just returns &lt;code&gt;MACRO.VERSION&lt;/code&gt;. &lt;code&gt;--dump-system-prompt&lt;/code&gt; writes the system prompt to stdout, but that one's Anthropic-employees-only.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Analytics and privacy
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Two parallel telemetry pipelines
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Datadog&lt;/strong&gt;: a hardcoded client token batches events every 15 seconds to the US5 region. Platform, model name, subscription type, session ID, tool results, OAuth state.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First-party OTEL&lt;/strong&gt;: an OpenTelemetry pipeline sending the same stuff separately to Anthropic's own infrastructure.&lt;/p&gt;

&lt;p&gt;Both are on by default. Can be turned off with &lt;code&gt;DISABLE_TELEMETRY=1&lt;/code&gt;. Auto-disabled for Bedrock, Vertex, and Foundry users.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prompt redaction
&lt;/h3&gt;

&lt;p&gt;In OTEL events, prompt content gets sent as &lt;code&gt;&amp;lt;REDACTED&amp;gt;&lt;/code&gt; by default. Setting &lt;code&gt;OTEL_LOG_USER_PROMPTS=1&lt;/code&gt; includes the raw prompts. Default is safe.&lt;/p&gt;

&lt;h3&gt;
  
  
  Profanity detection
&lt;/h3&gt;

&lt;p&gt;There's a regex in &lt;code&gt;userPromptKeywords.ts&lt;/code&gt;: &lt;code&gt;wtf&lt;/code&gt;, &lt;code&gt;ffs&lt;/code&gt;, &lt;code&gt;shit&lt;/code&gt;, &lt;code&gt;fuck you&lt;/code&gt;, &lt;code&gt;piece of shit&lt;/code&gt;, &lt;code&gt;this sucks&lt;/code&gt;, &lt;code&gt;damn it&lt;/code&gt;, and about 20 more patterns.&lt;/p&gt;

&lt;p&gt;Every prompt runs through this regex. A match sets &lt;code&gt;is_negative: true&lt;/code&gt; in the analytics log. A second flag catches continuation patterns like &lt;code&gt;continue&lt;/code&gt;, &lt;code&gt;keep going&lt;/code&gt;, &lt;code&gt;go on&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Someone on the Claude Code team confirmed this on X. The internal dashboard name is "fucks chart." More cursing = worse experience. It doesn't change Claude's behavior and doesn't store the prompt content. It's essentially rage click detection, just in text form.&lt;/p&gt;

&lt;h3&gt;
  
  
  GrowthBook A/B testing
&lt;/h3&gt;

&lt;p&gt;GrowthBook runs feature flags and A/B tests in real time. What gets sent: user UUID, session ID, device ID, platform, organization UUID, account UUID, subscription type, rate limit tier, first token date, and email address.&lt;/p&gt;

&lt;p&gt;The email transmission isn't mentioned in any user-facing documentation.&lt;/p&gt;

&lt;p&gt;Anthropic employees can override any flag with &lt;code&gt;CLAUDE_INTERNAL_FC_OVERRIDES&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Event sampling and killswitch
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;tengu_event_sampling_config&lt;/code&gt; can percentage-sample specific event types. &lt;code&gt;tengu_frond_boric&lt;/code&gt; is a killswitch that can instantly shut down all analytics streams remotely, so the team can intervene before a release ships.&lt;/p&gt;

&lt;h3&gt;
  
  
  autoDream
&lt;/h3&gt;

&lt;p&gt;A background memory consolidation service. It triggers when two conditions are both met: at least 24 hours since the last run, and 5+ sessions in that window. When both line up, a forked subagent reads past sessions, finds patterns, and updates memory files.&lt;/p&gt;

&lt;p&gt;Not opt-in. On by default if memory is enabled. The thresholds are controlled server-side through GrowthBook (&lt;code&gt;tengu_onyx_plover&lt;/code&gt;). It shows up in the background task panel, but you have to actively look for it to notice.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. The Anthropic-employee-only system
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;USER_TYPE=ant&lt;/code&gt; shows up all over the source. "ant" is the internal identifier for Anthropic employees. It's pinned at build time via &lt;code&gt;--define&lt;/code&gt;, so in external builds these branches get wiped out completely by dead code elimination. Meaning: the code doesn't run in the public npm package, but the source is still readable in the source map.&lt;/p&gt;

&lt;h3&gt;
  
  
  Undercover mode
&lt;/h3&gt;

&lt;p&gt;When Anthropic engineers push to public repos, Claude Code activates "undercover mode." Instructions injected into the system prompt:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Don't put "Claude Code" or any AI reference in commit messages&lt;/li&gt;
&lt;li&gt;Don't add Co-Authored-By&lt;/li&gt;
&lt;li&gt;Don't use internal model codenames (Capybara, Tengu, etc.)&lt;/li&gt;
&lt;li&gt;Don't reveal which model or version is running&lt;/li&gt;
&lt;li&gt;"Write commit messages like a human developer would"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The code says explicitly: "There is NO force-OFF." If the repo's remote isn't on the internal allowlist, the mode is on. In conflicts, it defaults to the safer side (the code says so explicitly).&lt;/p&gt;

&lt;p&gt;Word for word, in the file: "Do not blow your cover."&lt;/p&gt;

&lt;h3&gt;
  
  
  Extra bash restrictions
&lt;/h3&gt;

&lt;p&gt;For Anthropic employees, commands like &lt;code&gt;curl&lt;/code&gt;, &lt;code&gt;wget&lt;/code&gt;, &lt;code&gt;gh api&lt;/code&gt;, &lt;code&gt;kubectl&lt;/code&gt;, &lt;code&gt;aws&lt;/code&gt;, &lt;code&gt;gcloud&lt;/code&gt;, &lt;code&gt;fa run&lt;/code&gt;, &lt;code&gt;coo&lt;/code&gt; go through an additional security check. External users don't have these restrictions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Internal commands
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;/insights&lt;/code&gt; is only available to Anthropic employees. It pulls session files via SCP from internal "Coder" servers and has Opus analyze them.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;--dump-system-prompt&lt;/code&gt; writes the system prompt to stdout. Also ant-only.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CLAUDE_CODE_DUMP_AUTO_MODE=1&lt;/code&gt; writes every prompt going to the auto mode classifier to disk. For debugging.&lt;/p&gt;

&lt;h3&gt;
  
  
  Persistent retry
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;CLAUDE_CODE_UNATTENDED_RETRY=1&lt;/code&gt; is infinite retry mode. No circuit breaker. Max backoff 5 minutes, 6-hour reset cap. On 429 errors, it reads the &lt;code&gt;anthropic-ratelimit-unified-reset&lt;/code&gt; header and waits until the exact reset time. Every 30 seconds it prints a status message so the host environment doesn't mark the session as idle.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fennec
&lt;/h3&gt;

&lt;p&gt;An internal model alias. Migration code redirects &lt;code&gt;fennec-latest&lt;/code&gt; and &lt;code&gt;fennec-fast-latest&lt;/code&gt; to &lt;code&gt;opus&lt;/code&gt;/&lt;code&gt;opus[1m]&lt;/code&gt;. Only runs for ant users.&lt;/p&gt;

&lt;h3&gt;
  
  
  Feature flag override
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;CLAUDE_INTERNAL_FC_OVERRIDES&lt;/code&gt; lets any GrowthBook flag be manually overridden.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Hidden and undocumented commands
&lt;/h2&gt;

&lt;p&gt;Lots of commands are hidden from the UI with &lt;code&gt;isHidden: true&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/heapdump&lt;/code&gt;: dumps the JS heap to the desktop&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/thinkback&lt;/code&gt;: a 2025 year-in-review, Spotify Wrapped-style animation. Behind a GrowthBook gate.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/thinkback-play&lt;/code&gt;: plays the same animation&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/rate-limit-options&lt;/code&gt;: internal rate limit menu&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/output-style&lt;/code&gt;: output style switcher&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/mock-limits&lt;/code&gt;: rate limit simulation&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/good-claude&lt;/code&gt;: probably an internal feedback mechanism&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/bughunter&lt;/code&gt;: unknown&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/ant-trace&lt;/code&gt;: internal tracing&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/teleport&lt;/code&gt;: probably a remote session jump&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/ctx_viz&lt;/code&gt;: context visualization&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/perf-issue&lt;/code&gt;: perf issue diagnosis&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/autofix-pr&lt;/code&gt;: automated PR fixes&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/reset-limits&lt;/code&gt;: rate limit reset&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/env&lt;/code&gt;: environment variables&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/backfill-sessions&lt;/code&gt;: session backfill&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/debug-tool-call&lt;/code&gt;: tool call debug&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most are ant-only. They don't run in public builds.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Context window management
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Auto-compact
&lt;/h3&gt;

&lt;p&gt;Triggers at around 93% of the effective context window. Effective context = total context window minus min(maxOutputTokens, 20,000). 20K tokens get reserved for the compact summary itself. The p99.99 compact output is 17,387 tokens, so that's where the budget came from.&lt;/p&gt;

&lt;p&gt;What happens during compact:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Images get replaced with &lt;code&gt;[image]&lt;/code&gt; placeholders&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;skill_discovery&lt;/code&gt;/&lt;code&gt;skill_listing&lt;/code&gt; attachments are pulled out (they get re-injected post-compact anyway)&lt;/li&gt;
&lt;li&gt;A fork agent summarizes the conversation&lt;/li&gt;
&lt;li&gt;If the compact request gets &lt;code&gt;prompt_too_long&lt;/code&gt;, the oldest messages get cut and it retries up to 3 times&lt;/li&gt;
&lt;li&gt;Post-compact restore: max 5 files (5K tokens each) and max 5 skills (25K tokens total). &lt;code&gt;readFileState&lt;/code&gt; and &lt;code&gt;loadedNestedMemoryPaths&lt;/code&gt; get cleared. Everything else is deleted.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Circuit breaker
&lt;/h3&gt;

&lt;p&gt;3 failed compacts in a row stops the system. Before this was added, failed compacts were generating around 250,000 wasted API calls a day.&lt;/p&gt;

&lt;h3&gt;
  
  
  Session memory compact
&lt;/h3&gt;

&lt;p&gt;If session memory is enabled (needs both GrowthBook flags: &lt;code&gt;tengu_session_memory&lt;/code&gt; + &lt;code&gt;tengu_sm_compact&lt;/code&gt;), the compact doesn't make an API call. Expanding backward from the last summarized message ID, it keeps at least 10K tokens or 5 text-block messages (up to 40K tokens). The memory file serves as the summary.&lt;/p&gt;

&lt;h3&gt;
  
  
  Context Collapse
&lt;/h3&gt;

&lt;p&gt;A separate experimental feature. Autocompact fires at ~93%, context collapse kicks in at 90%, and blocks at 95%. To stop them from racing, they can't run together.&lt;/p&gt;

&lt;h3&gt;
  
  
  Blocking limit
&lt;/h3&gt;

&lt;p&gt;Once the effective context has fewer than 3,000 tokens left, the user can't send a new message.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Cost and retry
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Cost tracker
&lt;/h3&gt;

&lt;p&gt;On every API response, &lt;code&gt;input_tokens&lt;/code&gt;, &lt;code&gt;output_tokens&lt;/code&gt;, &lt;code&gt;cache_read_input_tokens&lt;/code&gt;, &lt;code&gt;cache_creation_input_tokens&lt;/code&gt;, and &lt;code&gt;web_search_requests&lt;/code&gt; get accumulated. A separate &lt;code&gt;ModelUsage&lt;/code&gt; object is kept for each model.&lt;/p&gt;

&lt;p&gt;Display format: costs under $0.50 show 4 decimal places ($0.0023), above that 2 decimals ($1.54). Hardcoded.&lt;/p&gt;

&lt;p&gt;When a session is resumed with &lt;code&gt;/resume&lt;/code&gt;, the cost data gets restored.&lt;/p&gt;

&lt;h3&gt;
  
  
  Retry strategy
&lt;/h3&gt;

&lt;p&gt;Max 10 retries in normal mode. Backoff: 500ms * 2^(attempt-1) + 25% jitter, capped at 32 seconds. If there's a &lt;code&gt;Retry-After&lt;/code&gt; header, that's used directly.&lt;/p&gt;

&lt;p&gt;Errors that get retried: 408, 409, 401 (API key cache gets cleared), 403 (OAuth revoke), 5xx, APIConnectionError, 429 (only for API key users), 529 (only for foreground operations).&lt;/p&gt;

&lt;p&gt;On a 529, background tasks (title generation, suggestions, other minor stuff) give up immediately. Only the main query retries. This is to prevent cascade amplification.&lt;/p&gt;

&lt;p&gt;3 consecutive 529s trigger a drop from Opus to the fallback model.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;x-should-retry: false&lt;/code&gt; is respected. For ant users, this header is ignored on 5xx errors.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fast mode cooldown
&lt;/h3&gt;

&lt;p&gt;On a 429, if &lt;code&gt;Retry-After&lt;/code&gt; is under 20 seconds, it's a short wait and fast mode stays on. At 20 seconds or more, fast mode gets disabled for 30 minutes (min 10, default 30). If an overage header shows up, fast mode turns off for the rest of the session.&lt;/p&gt;

&lt;p&gt;When fast mode "slows down," it probably caught a 429 and got quietly dropped. You don't see an error message.&lt;/p&gt;

&lt;h3&gt;
  
  
  Max tokens overflow
&lt;/h3&gt;

&lt;p&gt;When a 400 comes back with an &lt;code&gt;input + max_tokens &amp;gt; contextLimit&lt;/code&gt; message, it gets parsed and &lt;code&gt;max_tokens&lt;/code&gt; is shrunk for the next attempt with a 1,000-token safety margin. Self-correcting.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Prompt cache
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How it works
&lt;/h3&gt;

&lt;p&gt;Before every API call, &lt;code&gt;recordPromptState()&lt;/code&gt; hashes: the system prompt (with &lt;code&gt;cache_control&lt;/code&gt; stripped), tool schemas, model name, fast mode state, beta header list, &lt;code&gt;cache_control&lt;/code&gt; structure, effort value, and extra body parameters.&lt;/p&gt;

&lt;p&gt;The number of tracked sources is capped at 10. Each entry can hold ~300KB+ of &lt;code&gt;diffableContent&lt;/code&gt; string.&lt;/p&gt;

&lt;h3&gt;
  
  
  Break detection
&lt;/h3&gt;

&lt;p&gt;After the API response, &lt;code&gt;checkResponseForCacheBreak()&lt;/code&gt; runs. If there's a drop of more than &lt;code&gt;prevCacheRead * 0.95&lt;/code&gt; and at least 2,000 tokens lost, a break is detected.&lt;/p&gt;

&lt;p&gt;Causes, in order: model change, system prompt change (via character delta), tool schema change (per-tool hash comparison), fast mode transition, cache strategy change, beta header change, effort change.&lt;/p&gt;

&lt;p&gt;If none of those apply, it looks at time elapsed: &amp;gt;1 hour = "possible 1h TTL expiry", &amp;gt;5 minutes = "possible 5min TTL expiry", &amp;lt;5 minutes = "likely server-side".&lt;/p&gt;

&lt;p&gt;After compact, &lt;code&gt;notifyCompaction()&lt;/code&gt; resets the baseline. Otherwise the post-compact drop would register as a false positive.&lt;/p&gt;

&lt;h3&gt;
  
  
  Practical takeaway
&lt;/h3&gt;

&lt;p&gt;Editing CLAUDE.md often breaks the cache. Every edit changes the system prompt hash. A broken cache = more tokens billed at full price.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Permission system
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Rule hierarchy
&lt;/h3&gt;

&lt;p&gt;Permission rules come from these sources (in priority order): &lt;code&gt;localSettings&lt;/code&gt;, &lt;code&gt;projectSettings&lt;/code&gt;, &lt;code&gt;globalSettings&lt;/code&gt;, &lt;code&gt;cliArg&lt;/code&gt;, &lt;code&gt;command&lt;/code&gt;, &lt;code&gt;session&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Each rule specifies a behavior: &lt;code&gt;allow&lt;/code&gt;, &lt;code&gt;deny&lt;/code&gt;, or &lt;code&gt;ask&lt;/code&gt;. Tool matching: &lt;code&gt;Bash&lt;/code&gt; matches all Bash, &lt;code&gt;Bash(prefix:*)&lt;/code&gt; matches a specific command prefix, &lt;code&gt;mcp__server1&lt;/code&gt; matches all tools coming from that server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Auto mode classifier (yolo)
&lt;/h3&gt;

&lt;p&gt;When auto mode is on, every tool call gets checked by a separate Claude call. The transcript sent to the classifier:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Only text blocks from user messages&lt;/li&gt;
&lt;li&gt;Only &lt;code&gt;tool_use&lt;/code&gt; blocks from assistant messages (text is deliberately excluded, to prevent self-manipulation)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;queued_command&lt;/code&gt; attachments get included as user turns&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The format shifts to JSONL: &lt;code&gt;{"Bash": "ls -la"}&lt;/code&gt;, &lt;code&gt;{"user": "refactor this file"}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The system prompt is built from either &lt;code&gt;permissions_external.txt&lt;/code&gt; or &lt;code&gt;permissions_anthropic.txt&lt;/code&gt;. Anthropic employees see a different template. CLAUDE.md contents are added to the classifier message with &lt;code&gt;cache_control&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Two output formats: tool-use format (&lt;code&gt;shouldBlock: boolean&lt;/code&gt;) and XML format (two-stage, with an "err on the side of blocking" suffix).&lt;/p&gt;

&lt;p&gt;If the classifier keeps rejecting, &lt;code&gt;shouldFallbackToPrompting()&lt;/code&gt; drops back to asking the user.&lt;/p&gt;

&lt;p&gt;Every tool call = 2 API calls. Auto mode doubles token consumption.&lt;/p&gt;

&lt;h3&gt;
  
  
  Policy limits
&lt;/h3&gt;

&lt;p&gt;Organization admins can remotely restrict features. Endpoint: &lt;code&gt;{BASE_API_URL}/api/claude_code/policy_limits&lt;/code&gt;. Initially 10-second timeout, 5 retries. ETag-based HTTP caching. Gets written to disk as &lt;code&gt;~/.claude/policy-limits.json&lt;/code&gt; (mode &lt;code&gt;0600&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Checked in the background once an hour (&lt;code&gt;setInterval&lt;/code&gt; with &lt;code&gt;unref()&lt;/code&gt;, so it doesn't keep the process alive).&lt;/p&gt;

&lt;p&gt;Fail-open: if the API fails and there's no disk cache, no restrictions. Exception: the &lt;code&gt;allow_product_feedback&lt;/code&gt; policy fails closed in HIPAA mode.&lt;/p&gt;

&lt;p&gt;An unknown policy name query defaults to &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Affects Console users + Team/Enterprise OAuth users. Doesn't affect Bedrock/Vertex/third-party provider users.&lt;/p&gt;




&lt;h2&gt;
  
  
  9. Query engine and orchestration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Main query loop
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;query.ts&lt;/code&gt; is 1,729 lines. The main function &lt;code&gt;query()&lt;/code&gt; returns an &lt;code&gt;AsyncGenerator&lt;/code&gt;; the actual work happens in &lt;code&gt;queryLoop()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;At the start of the loop: &lt;code&gt;getMessagesAfterCompactBoundary()&lt;/code&gt; pulls messages from after the compact boundary. &lt;code&gt;applyToolResultBudget()&lt;/code&gt; caps the size of tool results. Skill discovery and memory prefetch get kicked off in parallel.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;while(true)&lt;/code&gt; loop yields &lt;code&gt;stream_request_start&lt;/code&gt; on every iteration. State mutation happens through a single &lt;code&gt;state = { ... }&lt;/code&gt; assignment (this pattern was chosen to clean up 7 separate &lt;code&gt;continue&lt;/code&gt; points).&lt;/p&gt;

&lt;p&gt;At the end of the loop: the memory prefetch and skill discovery prefetch get consumed (&amp;gt;98% of the time they're already ready). &lt;code&gt;refreshTools()&lt;/code&gt; pulls new tools from MCP servers. &lt;code&gt;maxTurns&lt;/code&gt; is checked. For top-level conversations (not subagents), a periodic summary is produced for &lt;code&gt;claude ps&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;taskBudget&lt;/code&gt; field maps to the API's &lt;code&gt;output_config.task_budget&lt;/code&gt; parameter. It's not the same thing as &lt;code&gt;tokenBudget&lt;/code&gt; (the 500K auto-continue).&lt;/p&gt;

&lt;h3&gt;
  
  
  Coordinator mode
&lt;/h3&gt;

&lt;p&gt;Requires both &lt;code&gt;CLAUDE_CODE_COORDINATOR_MODE=1&lt;/code&gt; and &lt;code&gt;feature('COORDINATOR_MODE')&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Worker results are delivered as user-role messages in &lt;code&gt;&amp;lt;task-notification&amp;gt;&lt;/code&gt; XML format. From Claude's perspective, the worker output looks as if the user wrote it. Format: &lt;code&gt;&amp;lt;task-id&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;status&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;summary&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;result&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;usage&amp;gt;&lt;/code&gt; (with &lt;code&gt;total_tokens&lt;/code&gt;, &lt;code&gt;tool_uses&lt;/code&gt;, &lt;code&gt;duration_ms&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Workers have access to tools from &lt;code&gt;ASYNC_AGENT_ALLOWED_TOOLS&lt;/code&gt;. Internal tools like &lt;code&gt;TeamCreateTool&lt;/code&gt;, &lt;code&gt;TeamDeleteTool&lt;/code&gt;, &lt;code&gt;SendMessageTool&lt;/code&gt;, &lt;code&gt;SyntheticOutputTool&lt;/code&gt; are excluded.&lt;/p&gt;

&lt;p&gt;The scratchpad directory is behind the &lt;code&gt;tengu_scratch&lt;/code&gt; feature gate. When enabled, all workers get unrestricted read/write access to a shared directory.&lt;/p&gt;

&lt;p&gt;When a session is resumed with &lt;code&gt;/resume&lt;/code&gt;, &lt;code&gt;matchSessionMode()&lt;/code&gt; detects coordinator/normal mismatches and fixes the env variable at runtime.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tool execution
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;toolOrchestration.ts&lt;/code&gt; splits tool calls into two groups: concurrent-safe and non-concurrent. Concurrent-safe tools run in parallel, max concurrency 10 (&lt;code&gt;CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;code&gt;StreamingToolExecutor.ts&lt;/code&gt; starts executing tools as they come in from the stream. When a non-concurrent tool shows up, it waits for the previous concurrent batch to finish. If Bash errors out, it kills sibling subprocesses with &lt;code&gt;siblingAbortController&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Every tool call runs &lt;code&gt;executePreToolHooks&lt;/code&gt; and &lt;code&gt;executePostToolHooks&lt;/code&gt;. On error, &lt;code&gt;executePostToolUseFailureHooks&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  10. Skill system
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Sources
&lt;/h3&gt;

&lt;p&gt;Skills come from three places: disk-based (&lt;code&gt;.claude/skills/skill-name/SKILL.md&lt;/code&gt;), legacy &lt;code&gt;/commands/&lt;/code&gt;, and MCP servers.&lt;/p&gt;

&lt;p&gt;The disk-based format is mandatory: a single &lt;code&gt;.md&lt;/code&gt; file isn't supported, the &lt;code&gt;skill-name/SKILL.md&lt;/code&gt; structure is required. Conflicting skills are detected via &lt;code&gt;realpath()&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Frontmatter
&lt;/h3&gt;

&lt;p&gt;Supported fields: &lt;code&gt;description&lt;/code&gt;, &lt;code&gt;argument-hint&lt;/code&gt;, &lt;code&gt;arguments&lt;/code&gt;, &lt;code&gt;when_to_use&lt;/code&gt;, &lt;code&gt;version&lt;/code&gt;, &lt;code&gt;model&lt;/code&gt; (or &lt;code&gt;inherit&lt;/code&gt;), &lt;code&gt;allowed-tools&lt;/code&gt;, &lt;code&gt;disable-model-invocation&lt;/code&gt;, &lt;code&gt;user-invocable&lt;/code&gt;, &lt;code&gt;hooks&lt;/code&gt;, &lt;code&gt;context&lt;/code&gt; (&lt;code&gt;fork&lt;/code&gt;/&lt;code&gt;inline&lt;/code&gt;), &lt;code&gt;agent&lt;/code&gt;, &lt;code&gt;effort&lt;/code&gt;, &lt;code&gt;shell&lt;/code&gt;, &lt;code&gt;paths&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;${CLAUDE_SKILL_DIR}&lt;/code&gt; and &lt;code&gt;${CLAUDE_SESSION_ID}&lt;/code&gt; variables get injected into skill content.&lt;/p&gt;

&lt;h3&gt;
  
  
  Security
&lt;/h3&gt;

&lt;p&gt;Running &lt;code&gt;!&lt;/code&gt; shell commands is blocked for MCP-sourced skills. Only works for disk-based ones.&lt;/p&gt;

&lt;p&gt;Path traversal protection: &lt;code&gt;..&lt;/code&gt;, URL-encoded variants, and Unicode NFKC normalization attacks get rejected. File writes use &lt;code&gt;O_NOFOLLOW | O_CREAT | O_EXCL&lt;/code&gt; flags, mode &lt;code&gt;0o600&lt;/code&gt;, directories &lt;code&gt;0o700&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bundled skills
&lt;/h3&gt;

&lt;p&gt;In &lt;code&gt;src/skills/bundled/&lt;/code&gt;: &lt;code&gt;updateConfig&lt;/code&gt;, &lt;code&gt;keybindings&lt;/code&gt;, &lt;code&gt;verify&lt;/code&gt;, &lt;code&gt;debug&lt;/code&gt;, &lt;code&gt;loremIpsum&lt;/code&gt;, &lt;code&gt;skillify&lt;/code&gt;, &lt;code&gt;remember&lt;/code&gt;, &lt;code&gt;simplify&lt;/code&gt;, &lt;code&gt;batch&lt;/code&gt;, &lt;code&gt;stuck&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Behind feature flags: &lt;code&gt;dream&lt;/code&gt; (KAIROS/KAIROS_DREAM), &lt;code&gt;hunter&lt;/code&gt; (REVIEW_ARTIFACT), &lt;code&gt;loop&lt;/code&gt; (AGENT_TRIGGERS), &lt;code&gt;scheduleRemoteAgents&lt;/code&gt; (AGENT_TRIGGERS_REMOTE).&lt;/p&gt;




&lt;h2&gt;
  
  
  11. Plugin system
&lt;/h2&gt;

&lt;p&gt;There's a built-in plugin scaffold, but it's currently empty. The &lt;code&gt;initBuiltinPlugins()&lt;/code&gt; function doesn't register any plugins. Comment in the code: transition scaffold for "bundled skills that should be user-toggleable".&lt;/p&gt;

&lt;p&gt;Marketplace plugins use the &lt;code&gt;{name}@{marketplace}&lt;/code&gt; ID format. Stored in &lt;code&gt;settings.json&lt;/code&gt; as &lt;code&gt;enabledPlugins&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Plugins that get removed from the marketplace are silently deleted at session start via the &lt;code&gt;forceRemoveDeletedPlugins: true&lt;/code&gt; flag. No confirmation, no notification.&lt;/p&gt;




&lt;h2&gt;
  
  
  12. MCP server management
&lt;/h2&gt;

&lt;h3&gt;
  
  
  XAA (Cross-App Access)
&lt;/h3&gt;

&lt;p&gt;An enterprise feature. Gets MCP tokens without the browser approval screen. Two stages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;RFC 8693 Token Exchange: uses &lt;code&gt;id_token&lt;/code&gt; to get an ID-JAG (Identity Assertion Authorization Grant)&lt;/li&gt;
&lt;li&gt;RFC 7523 JWT Bearer Grant: uses the ID-JAG to get an &lt;code&gt;access_token&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Activated with &lt;code&gt;CLAUDE_CODE_ENABLE_XAA&lt;/code&gt;. IdP configuration comes from &lt;code&gt;settings.xaaIdp&lt;/code&gt;. Tokens get cached in the keychain.&lt;/p&gt;

&lt;h3&gt;
  
  
  Official MCP Registry
&lt;/h3&gt;

&lt;p&gt;A URL list is pulled fire-and-forget from &lt;code&gt;https://api.anthropic.com/mcp-registry/v0/servers?version=latest&amp;amp;visibility=commercial&lt;/code&gt;. It's for a security check: "is this MCP server official?"&lt;/p&gt;

&lt;h3&gt;
  
  
  VSCode SDK MCP
&lt;/h3&gt;

&lt;p&gt;The MCP server called &lt;code&gt;claude-vscode&lt;/code&gt; gets special treatment. &lt;code&gt;file_updated&lt;/code&gt; notifications go from Claude to VSCode; &lt;code&gt;log_event&lt;/code&gt; notifications come back from VSCode to Claude. Ant-only.&lt;/p&gt;

&lt;p&gt;On connect, GrowthBook gate values get pushed to VSCode: &lt;code&gt;tengu_vscode_review_upsell&lt;/code&gt;, &lt;code&gt;tengu_vscode_onboarding&lt;/code&gt;, &lt;code&gt;tengu_quiet_fern&lt;/code&gt;, &lt;code&gt;tengu_vscode_cc_auth&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Elicitation
&lt;/h3&gt;

&lt;p&gt;Handles &lt;code&gt;elicitation/create&lt;/code&gt; requests from the MCP 2025-03-26 spec. Two modes: &lt;code&gt;form&lt;/code&gt; (generate UI from JSON schema) and &lt;code&gt;url&lt;/code&gt; (open browser, wait for completion). The hook system is integrated.&lt;/p&gt;




&lt;h2&gt;
  
  
  13. IDE Bridge (Remote Control)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Requirements
&lt;/h3&gt;

&lt;p&gt;Needs all three: &lt;code&gt;feature('BRIDGE_MODE')&lt;/code&gt; build flag, &lt;code&gt;tengu_ccr_bridge&lt;/code&gt; GrowthBook gate, and a Claude.ai OAuth token. Doesn't work with API key, Bedrock, or Vertex.&lt;/p&gt;

&lt;h3&gt;
  
  
  Two implementations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;v1 (env-based): current standard&lt;/li&gt;
&lt;li&gt;v2 (env-less/REPL bridge): enabled via the &lt;code&gt;tengu_bridge_repl_v2&lt;/code&gt; gate&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;CSE-to-session shim: can translate &lt;code&gt;cse_*&lt;/code&gt; IDs to &lt;code&gt;session_*&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  JWT management
&lt;/h3&gt;

&lt;p&gt;Tokens get refreshed 5 minutes before expiry. Failed refreshes give up after 3 attempts, spaced 60 seconds apart. The &lt;code&gt;sk-ant-si-&lt;/code&gt; session-ingress prefix gets stripped before decoding.&lt;/p&gt;

&lt;h3&gt;
  
  
  CCR Auto Connect
&lt;/h3&gt;

&lt;p&gt;When the &lt;code&gt;tengu_cobalt_harbor&lt;/code&gt; gate is on, every session automatically connects to Remote Control. Can be disabled with &lt;code&gt;remoteControlAtStartup=false&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mirror Mode
&lt;/h3&gt;

&lt;p&gt;With &lt;code&gt;CLAUDE_CODE_CCR_MIRROR&lt;/code&gt; env or the &lt;code&gt;tengu_ccr_mirror&lt;/code&gt; gate, every local session opens an additional outbound-only CCR session.&lt;/p&gt;




&lt;h2&gt;
  
  
  14. Session Memory
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Triggering
&lt;/h3&gt;

&lt;p&gt;Both the &lt;code&gt;tengu_session_memory&lt;/code&gt; GrowthBook gate + &lt;code&gt;isAutoCompactEnabled()&lt;/code&gt; have to be on. Disabled in remote mode.&lt;/p&gt;

&lt;p&gt;Thresholds come from the &lt;code&gt;tengu_sm_config&lt;/code&gt; dynamic config: &lt;code&gt;minimumMessageTokensToInit&lt;/code&gt; (tokens needed before the first extraction), &lt;code&gt;minimumTokensBetweenUpdate&lt;/code&gt; (token increase between extractions), &lt;code&gt;toolCallsBetweenUpdates&lt;/code&gt; (tool call count between extractions).&lt;/p&gt;

&lt;p&gt;The token threshold is always required. It fires together with either the tool call threshold or a "no tool in the last assistant turn" condition.&lt;/p&gt;

&lt;h3&gt;
  
  
  How it works
&lt;/h3&gt;

&lt;p&gt;An isolated subagent starts via &lt;code&gt;runForkedAgent&lt;/code&gt;. It only has &lt;code&gt;FileEdit&lt;/code&gt; permission for the memory file. It doesn't pollute the main conversation.&lt;/p&gt;

&lt;p&gt;File: &lt;code&gt;~/.claude/session_memory/&amp;lt;session_id&amp;gt;/memory.md&lt;/code&gt;. Starts from a template.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;/summary&lt;/code&gt; command calls &lt;code&gt;manuallyExtractSessionMemory()&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  15. Memory directory (memdir)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Structure
&lt;/h3&gt;

&lt;p&gt;Four memory types: &lt;code&gt;user&lt;/code&gt; (about the user), &lt;code&gt;feedback&lt;/code&gt; (corrections and confirmations), &lt;code&gt;project&lt;/code&gt; (ongoing work), &lt;code&gt;reference&lt;/code&gt; (pointers to external sources). Each type has its own team/private scope rules.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;MEMORY.md&lt;/code&gt; entrypoint caps at 200 lines or 25,000 bytes. When exceeded, it's truncated and the overflow limit is flagged.&lt;/p&gt;

&lt;h3&gt;
  
  
  Relevance selection
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;findRelevantMemories()&lt;/code&gt; asks Sonnet for the current query (&lt;code&gt;sideQuery&lt;/code&gt;), picks up to 5 files. It deliberately skips reference documents for recently used tools.&lt;/p&gt;

&lt;h3&gt;
  
  
  Team memory
&lt;/h3&gt;

&lt;p&gt;Behind the &lt;code&gt;TEAMMEM&lt;/code&gt; feature flag. Separate path management via &lt;code&gt;teamMemPaths.ts&lt;/code&gt;. Path security is extensive: null bytes, URL-encoded traversal, Unicode NFKC normalization attacks, backslash injection. All get rejected (&lt;code&gt;PathTraversalError&lt;/code&gt;).&lt;/p&gt;




&lt;h2&gt;
  
  
  16. OAuth
&lt;/h2&gt;

&lt;p&gt;PKCE + Authorization Code Flow. Two parallel flows race at the same time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automatic: browser opens, waits for localhost callback&lt;/li&gt;
&lt;li&gt;Manual: user copies and pastes the code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whichever finishes first wins.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;skipBrowserOpen&lt;/code&gt; is for the SDK control protocol: when the &lt;code&gt;claude_authenticate&lt;/code&gt; command comes in, it lets the caller manage its own display. Both URLs get passed to &lt;code&gt;authURLHandler&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;After the token is obtained, &lt;code&gt;fetchProfileInfo()&lt;/code&gt; pulls the subscription type and rate limit tier.&lt;/p&gt;




&lt;h2&gt;
  
  
  17. Platform features
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Vim mode
&lt;/h3&gt;

&lt;p&gt;State machine between INSERT and NORMAL. In NORMAL mode, a full command parser: idle, operator, operatorCount, operatorTextObj, execute stages.&lt;/p&gt;

&lt;p&gt;Operators: &lt;code&gt;d&lt;/code&gt; (delete), &lt;code&gt;c&lt;/code&gt; (change), &lt;code&gt;y&lt;/code&gt; (yank). Motions: &lt;code&gt;h&lt;/code&gt;/&lt;code&gt;l&lt;/code&gt;/&lt;code&gt;j&lt;/code&gt;/&lt;code&gt;k&lt;/code&gt;, &lt;code&gt;w&lt;/code&gt;/&lt;code&gt;b&lt;/code&gt;/&lt;code&gt;e&lt;/code&gt;/&lt;code&gt;W&lt;/code&gt;/&lt;code&gt;B&lt;/code&gt;/&lt;code&gt;E&lt;/code&gt;, &lt;code&gt;0&lt;/code&gt;/&lt;code&gt;^&lt;/code&gt;/&lt;code&gt;$&lt;/code&gt;, &lt;code&gt;G&lt;/code&gt;, &lt;code&gt;gj&lt;/code&gt;/&lt;code&gt;gk&lt;/code&gt;. Text objects: &lt;code&gt;iw&lt;/code&gt;/&lt;code&gt;aw&lt;/code&gt;, quote/paren/bracket/brace pairs. Find: &lt;code&gt;f&lt;/code&gt;/&lt;code&gt;F&lt;/code&gt;/&lt;code&gt;t&lt;/code&gt;/&lt;code&gt;T&lt;/code&gt; + repeat.&lt;/p&gt;

&lt;p&gt;Dot-repeat is supported. Max count 10,000 (infinite loop protection).&lt;/p&gt;

&lt;h3&gt;
  
  
  Keybinding system
&lt;/h3&gt;

&lt;p&gt;20 different contexts: Global, Chat, Autocomplete, Settings, Confirmation, Tabs, Transcript, HistorySearch, Task, ThemePicker, Scroll, Help, Attachments, Footer, MessageSelector, MessageActions, DiffDialog, ModelPicker, Select, Plugin.&lt;/p&gt;

&lt;p&gt;Platform adaptations: &lt;code&gt;alt+v&lt;/code&gt; for image paste on Windows (&lt;code&gt;ctrl+v&lt;/code&gt; on Linux/macOS). Windows Terminal VT mode check: before Node 22.17.0 and Bun 1.2.23, &lt;code&gt;shift+tab&lt;/code&gt; doesn't work, &lt;code&gt;meta+m&lt;/code&gt; is the fallback.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ctrl+c&lt;/code&gt; and &lt;code&gt;ctrl+d&lt;/code&gt; can't be rebound. Protected by &lt;code&gt;reservedShortcuts.ts&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Bindings gated on feature flags: space push-to-talk (&lt;code&gt;VOICE_MODE&lt;/code&gt;), &lt;code&gt;shift+up&lt;/code&gt; message actions (&lt;code&gt;MESSAGE_ACTIONS&lt;/code&gt;), &lt;code&gt;ctrl+shift+b&lt;/code&gt; toggle brief (&lt;code&gt;KAIROS_BRIEF&lt;/code&gt;), &lt;code&gt;ctrl+shift+f&lt;/code&gt;/&lt;code&gt;p&lt;/code&gt; global search/quick open (&lt;code&gt;QUICK_SEARCH&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;User customization: &lt;code&gt;~/.claude/keybindings.json&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Voice mode
&lt;/h3&gt;

&lt;p&gt;Requires an Anthropic OAuth token. Doesn't work with API key, Bedrock, Vertex, or Foundry. The &lt;code&gt;voice_stream&lt;/code&gt; endpoint is only available on claude.ai.&lt;/p&gt;

&lt;p&gt;GrowthBook killswitch: &lt;code&gt;tengu_amber_quartz_disabled&lt;/code&gt;. On macOS, token check goes through the &lt;code&gt;security&lt;/code&gt; CLI (~20-50ms cold, subsequent calls cached).&lt;/p&gt;




&lt;h2&gt;
  
  
  18. Infrastructure
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Native TypeScript ports
&lt;/h3&gt;

&lt;p&gt;Pure TypeScript reimplementations of three modules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;yoga-layout&lt;/strong&gt;: Meta's Yoga flexbox engine (C++ to TS, ~2,500 lines). The subset Ink uses: &lt;code&gt;flex-direction&lt;/code&gt;, &lt;code&gt;grow&lt;/code&gt;/&lt;code&gt;shrink&lt;/code&gt;/&lt;code&gt;basis&lt;/code&gt;, &lt;code&gt;align-items&lt;/code&gt;, &lt;code&gt;justify-content&lt;/code&gt;, &lt;code&gt;margin&lt;/code&gt;/&lt;code&gt;padding&lt;/code&gt;/&lt;code&gt;border&lt;/code&gt;/&lt;code&gt;gap&lt;/code&gt;, position absolute/relative, measure functions. Wrap, baseline, &lt;code&gt;display:contents&lt;/code&gt; are also there, Ink just doesn't use them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;file-index&lt;/strong&gt;: nucleo (Helix editor's fuzzy finder, Rust to TS). nucleo-style scoring: &lt;code&gt;SCORE_MATCH=16&lt;/code&gt;, &lt;code&gt;BONUS_BOUNDARY=8&lt;/code&gt;, &lt;code&gt;BONUS_CAMEL=6&lt;/code&gt;. Test files take a 1.05x penalty. During async build, ready prefix searches work (incremental). Chunk size is time-based (4ms), adaptive to the machine.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;color-diff&lt;/strong&gt;: a color difference calculation module.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No native dependencies needed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Upstream proxy (CCR)
&lt;/h3&gt;

&lt;p&gt;Enterprise MITM proxy support. Startup sequence:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reads the token from &lt;code&gt;/run/ccr/session_token&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;On Linux, &lt;code&gt;prctl(PR_SET_DUMPABLE, 0)&lt;/code&gt; calls &lt;code&gt;libc.so.6&lt;/code&gt; via Bun FFI. Protects the heap against ptrace. Blocks the &lt;code&gt;gdb -p $PPID&lt;/code&gt; attack via prompt injection.&lt;/li&gt;
&lt;li&gt;Downloads the proxy CA cert and merges it with the system CA bundle&lt;/li&gt;
&lt;li&gt;Starts a local CONNECT-to-WebSocket relay&lt;/li&gt;
&lt;li&gt;Deletes the token file (it stays in heap)&lt;/li&gt;
&lt;li&gt;Injects &lt;code&gt;HTTPS_PROXY&lt;/code&gt;, &lt;code&gt;SSL_CERT_FILE&lt;/code&gt;, &lt;code&gt;NODE_EXTRA_CA_CERTS&lt;/code&gt; into child processes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;NO_PROXY: &lt;code&gt;anthropic.com&lt;/code&gt;, &lt;code&gt;github.com&lt;/code&gt;, &lt;code&gt;registry.npmjs.org&lt;/code&gt;, &lt;code&gt;pypi.org&lt;/code&gt;, etc.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CCR_UPSTREAM_PROXY_ENABLED&lt;/code&gt; is set server-side (because GrowthBook is cold in the container, the client check isn't trustworthy).&lt;/p&gt;

&lt;p&gt;Fail-open. A proxy setup error doesn't kill the session.&lt;/p&gt;

&lt;h3&gt;
  
  
  Migrations
&lt;/h3&gt;

&lt;p&gt;One-way, usually idempotent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;migrateSonnet45ToSonnet46&lt;/code&gt;: Pro/Max/Team from explicit model string to the &lt;code&gt;sonnet&lt;/code&gt; alias&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;migrateSonnet1mToSonnet45&lt;/code&gt;: &lt;code&gt;sonnet[1m]&lt;/code&gt; conversion (in 4.6, 1m was opened to a different group)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;migrateFennecToOpus&lt;/code&gt;: ant-only, &lt;code&gt;fennec-latest&lt;/code&gt; alias to &lt;code&gt;opus&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;migrateLegacyOpusToCurrent&lt;/code&gt;: explicit legacy Opus strings to the &lt;code&gt;opus&lt;/code&gt; alias&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;migrateOpusToOpus1m&lt;/code&gt;: on Max/Team Premium, &lt;code&gt;opus&lt;/code&gt; gets auto-upgraded to &lt;code&gt;opus[1m]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;resetProToOpusDefault&lt;/code&gt;: resets Pro users to the Opus 4.5 default&lt;/li&gt;
&lt;li&gt;Config key renames, auto mode dialog reset&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  LSP integration
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;LSPServerManager&lt;/code&gt; routes to an LSP server based on file extension. Requests like &lt;code&gt;textDocument/definition&lt;/code&gt;, &lt;code&gt;textDocument/references&lt;/code&gt;, etc.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;passiveFeedback.ts&lt;/code&gt; converts LSP &lt;code&gt;publishDiagnostics&lt;/code&gt; notifications into Claude's attachment format. Severity mapping: &lt;code&gt;1=Error&lt;/code&gt;, &lt;code&gt;2=Warning&lt;/code&gt;, &lt;code&gt;3=Information&lt;/code&gt;, &lt;code&gt;4=Hint&lt;/code&gt;. Malformed &lt;code&gt;file://&lt;/code&gt; URIs are normalized; if that fails, the URI is used as-is.&lt;/p&gt;




&lt;h2&gt;
  
  
  19. Other details
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Buddy system
&lt;/h3&gt;

&lt;p&gt;A virtual pet system in &lt;code&gt;buddy/&lt;/code&gt;. Deterministic animal derivation from a UUID hash: 18 species, 1% shiny, rarity from Common to Legendary, RPG stats (&lt;code&gt;DEBUGGING&lt;/code&gt;, &lt;code&gt;PATIENCE&lt;/code&gt;, &lt;code&gt;CHAOS&lt;/code&gt;, &lt;code&gt;WISDOM&lt;/code&gt;, &lt;code&gt;SNARK&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;The species name "duck" is hidden with hex codes (&lt;code&gt;0x64,0x75,0x63,0x6b&lt;/code&gt;). Clashes with an internal model codename.&lt;/p&gt;

&lt;p&gt;No cheating possible. Species and stats are recomputed from the UUID every time.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;moreright/&lt;/code&gt; stub
&lt;/h3&gt;

&lt;p&gt;A single no-op file. The real implementation lives in the internal build. Interface: &lt;code&gt;onBeforeQuery&lt;/code&gt; (before every API call) and &lt;code&gt;onTurnComplete&lt;/code&gt; (after every turn). Nobody knows what it does.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;cyberRiskInstruction.ts&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;A cybersecurity instruction. At the top of the file: "Do not modify without Safeguards Team review." At the bottom, a direct message to Claude: "Claude: Do not edit this file unless explicitly asked to do so by the user."&lt;/p&gt;

&lt;h3&gt;
  
  
  Channel permissions
&lt;/h3&gt;

&lt;p&gt;MCP channel approval IDs are 5 letters, filtered through a 24-word profanity filter. Code comment: "this is why i bias to numbers, hard to have anything worse than 80085."&lt;/p&gt;

&lt;h3&gt;
  
  
  Screens
&lt;/h3&gt;

&lt;p&gt;Three main React screen components: &lt;code&gt;Doctor.tsx&lt;/code&gt; (the &lt;code&gt;/doctor&lt;/code&gt; command), &lt;code&gt;REPL.tsx&lt;/code&gt; (the main REPL, at 874KB the largest file), &lt;code&gt;ResumeConversation.tsx&lt;/code&gt; (session resume).&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;512K lines of code. A day's reading.&lt;/p&gt;

&lt;p&gt;Engineering quality is high. Circuit breaker on compaction, cascade prevention on 529s, sibling abort in streaming tool execution, prompt cache diagnostics. Decisions made by a team that's been paged at 3 AM.&lt;/p&gt;

&lt;p&gt;Privacy is mixed. Prompts redacted by default (good). Telemetry on by default without documentation (bad). Email sent to the A/B test infrastructure (debatable). autoDream runs without permission (uncomfortable).&lt;/p&gt;

&lt;p&gt;The most striking find is undercover mode. Not technically, philosophically. A company that makes AI developer tools has its tool hide its identity when its own engineers use it in public.&lt;/p&gt;

&lt;p&gt;The leak itself is the most ordinary of mistakes. A missing line in &lt;code&gt;.npmignore&lt;/code&gt;. A 57MB file. A publish pipeline nobody's checking.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>typescript</category>
      <category>security</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
