CLAUDE LABJP
MCP — Support for the 2026-07-28 spec is rolling out across Claude. The protocol moves from bidirectional and stateful to request/response, so MCP servers can now live on serverless and edge infrastructureEXTENSIONS — Three official extensions have landed: MCP Apps for server-rendered UI, Tasks for async and long-running work, and Enterprise Managed Auth for IdP-based org-wide provisioningADOPTION — MCP passed 400 million monthly SDK downloads, roughly 4x growth this year, settling into its role as the standard way to connect agents to applicationsQUOTA — Today, August 19, is the last day of the 50 percent weekly usage boost for Claude Code subscribers. If you have long agent runs queued, this is the windowPRICING — Claude Sonnet 5's introductory rate of $2 per million input tokens and $10 output ends August 31; standard pricing of $3 and $15 takes over on September 1, twelve days outFIX — A bug where MCP v2 connections endlessly reopened subscriptions against servers with fixed timeouts is resolved, and a forward_user_identity setting was added for user attributionMCP — Support for the 2026-07-28 spec is rolling out across Claude. The protocol moves from bidirectional and stateful to request/response, so MCP servers can now live on serverless and edge infrastructureEXTENSIONS — Three official extensions have landed: MCP Apps for server-rendered UI, Tasks for async and long-running work, and Enterprise Managed Auth for IdP-based org-wide provisioningADOPTION — MCP passed 400 million monthly SDK downloads, roughly 4x growth this year, settling into its role as the standard way to connect agents to applicationsQUOTA — Today, August 19, is the last day of the 50 percent weekly usage boost for Claude Code subscribers. If you have long agent runs queued, this is the windowPRICING — Claude Sonnet 5's introductory rate of $2 per million input tokens and $10 output ends August 31; standard pricing of $3 and $15 takes over on September 1, twelve days outFIX — A bug where MCP v2 connections endlessly reopened subscriptions against servers with fixed timeouts is resolved, and a forward_user_identity setting was added for user attribution
Articles/Claude Code
Claude Code/2026-08-05Advanced

Passing the Request, Not the Secret — Where Sandbox Credential Masking Works and Where Substitution Breaks

Claude Code's sandbox credential masking lets processes read sentinel values while a proxy swaps in the real secret at send time. I rebuilt it as a minimal proxy and measured which auth schemes survive the swap, which break, and what happens when the secret rides in the body.

Claude Code225sandbox9security15credentials3automation101

Premium Article

When I narrowed the sandbox's secret read surface with sandbox.credentials denyRead a while back, one loose end kept bothering me.

Denying reads is a strong control. But any job that legitimately calls an external API still needs the real token somewhere. Deny can hide the secrets a job does not need — it cannot protect the one secret the job actually uses.

Then the August 5, 2026 changelog landed with mode: "mask" for sandbox credentials on Linux and WSL, and one sentence stopped me: commands inside the sandbox read a sentinel copy of the value, and the sandbox proxy swaps in the real value at send time.

Not hiding the secret. Handing the process a decoy, then swapping in the original at the exit.

How far that design actually protects you is not something the docs alone can answer. So I rebuilt the core of the mechanism — the byte-level swap — as a minimal proxy and pushed on its boundaries myself.

From "Can't Read It" to "Reads a Decoy"

Deny and mask protect different things.

Deny controls exposure. It hides ~/.aws/credentials and unrelated environment variables from the sandbox — the same instinct as tightening filesystem isolation from the write surface: shrink what can be touched at all.

Mask protects the next layer. Even a secret the job legitimately uses is never visible to the process. What the process reads is a harmless string like SBX_SENTINEL_...; the real value only exists in the request for the instant it passes through the egress proxy.

The implication is significant. If prompt injection or malicious code runs inside the sandbox, all it can exfiltrate is the sentinel — a string with no authority anywhere in the outside world.

And yet: a mechanism defined as "replace bytes at send time" must have cases it structurally cannot cover. To find them, I wrote the smallest proxy that could.

Rebuilding the Mechanism in Miniature

My test environment is Python 3.10.12 in a Linux sandbox. Real implementations involve TLS handling and more, but here I isolate the one core operation — replacing sentinel bytes on the wire with the real value — over a plaintext path.

The naive version replaces within each received chunk and forwards it:

# Naive: substitute the sentinel per received chunk
def handle(client_sock):
    up = socket.create_connection((UPSTREAM_HOST, UPSTREAM_PORT))
    client_sock.settimeout(0.5)
    try:
        while True:
            chunk = client_sock.recv(8192)
            if not chunk:
                break
            # Only sentinels fully contained in one chunk get replaced
            up.sendall(chunk.replace(SENTINEL, REAL_VALUE))
    except socket.timeout:
        pass

It appears to work. But it has a classic stream-processing hole: the moment a sentinel straddles two recv chunks, neither chunk contains the complete pattern, and the substitution silently misses.

The fix is a rolling buffer that always retains a tail of length len(sentinel) - 1:

# Rolling buffer: catches sentinels split across chunk boundaries
S = SENTINEL          # bytes
keep = len(S) - 1     # longest prefix that could straddle a boundary
 
def handle(client_sock):
    up = socket.create_connection((UPSTREAM_HOST, UPSTREAM_PORT))
    client_sock.settimeout(0.5)
    buf = b""
    try:
        while True:
            chunk = client_sock.recv(8192)
            if not chunk:
                break
            buf += chunk
            buf = buf.replace(S, REAL_VALUE)
            if len(buf) > keep:
                # Hold back the last `keep` bytes until the next chunk arrives
                up.sendall(buf[:-keep])
                buf = buf[-keep:]
    except socket.timeout:
        pass
    if buf:
        # Final flush: replace anything left in the tail before sending
        up.sendall(buf.replace(S, REAL_VALUE))

Error handling is trimmed for clarity, but the two essentials are the carried-over tail and the final flush. Drop either one and you get a bug that only fires when a sentinel lands at the end of a request — the kind that is miserable to reproduce.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
Learn to classify auth schemes into ones sentinel substitution can protect and ones it structurally cannot, backed by base64 and HMAC measurements
See why per-chunk replacement silently leaks sentinels that straddle recv boundaries, with reusable naive vs. rolling-buffer proxy code
Learn why a secret carried in the request body throws off the Content-Length declaration — a longer real value truncates into a JSONDecodeError, a shorter one blocks upstream for 3.003 seconds — plus the length-correction code and a five-step rollout checklist
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

Related Articles

Claude Code2026-08-04
Tightening Filesystem Isolation Separately from the Network — Collect the Paths, Then Squeeze the Write Surface
Claude Code v2.1.216 lets you control filesystem isolation independently from network isolation. Before tightening anything, I traced what a real job actually touches, split reads from writes, and measured how stable the path set is across repeated runs. The numbers changed how I wrote the allowlist.
Claude Code2026-06-25
Your Sandbox Can Run the Code but Shouldn't Read Your Credentials — Shrinking the Secret-Read Surface with sandbox.credentials
Claude Code's sandbox can still read ~/.aws/credentials and token env vars by default. Using sandbox.credentials (v2.1.187+), here is how I tightened the secret-read surface of unattended runs at the OS level, with config and verification you can reuse.
Claude Code2026-08-09
One Transient 401 Replaced My Long-Lived Token — Giving Credentials a Provenance Field
A shared credential file can lose its long-lived token to a single transient 401. I injected exactly one 401 into a 24-worker fleet, measured the blast radius, and compared a provenance guard against full isolation.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →