Skip to content

feat(tanstack): server issues the CDN segment token, so regionalized stores engage - #524

Merged
JonasJesus42 merged 1 commit into
mainfrom
feat/cseg-server-issued
Sep 1, 2026
Merged

feat(tanstack): server issues the CDN segment token, so regionalized stores engage#524
JonasJesus42 merged 1 commit into
mainfrom
feat/cseg-server-issued

Conversation

@JonasJesus42

@JonasJesus42 JonasJesus42 commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #523. That PR made the feature arrive with a version bump — but on a real store it turned out to
be permanently inert, and this fixes why.

What we saw in production

montecarlo, on 7.57, with the marker being sent correctly:

request:  __cseg=desktop.68bf898c7544
response: X-Cache-Segment: desktop|sc=1|r=RJ     ← the worker's actual segment
          Cdn-Cache-Control: no-store

The client sent device. The worker recomputed device + salesChannel + region and refused. Fail-closed did
exactly its job — the feature just never did anything.

Why the client could never get it right

dimension source client can see?
device user-agent
salesChannel / VTEX regionId cookie vtex_segment
region via CF geo request.cf.regionCode server only

The r=RJ above is a Cloudflare region code, not a VTEX regionId — it comes from request.cf, which the
browser never sees. So on any regionalized store, a client-derived token could not match. And regionalized is
the normal case, not the exception.

The fix: invert who issues the token

The server computes it from the segment it already built, publishes it to the page, the client echoes it back.

  • segmentToken folds dimensions in instead of refusing them. Sales channel, region, geo and a site's own
    custom fields are what the key must distinguish — not reasons to give up on caching. loggedIn remains a
    hard refusal: a personalized response never belongs in a shared entry, however precise the key.
  • The token is a hash. regionId can contain the previous separator (v2.XXXX), values would need
    escaping, and there is no reason to publish a visitor's region in a URL that lands in logs.
  • requestSegmentDescriptor folds in __cf_geo, which buildSegment does not carry — it comes from
    buildGeoCacheParam. Both read the same helpers, so the URL cannot distinguish less than the Worker's key.
  • CdnSegmentMarker publishes it through the RequestContext bag — the same server-to-client path
    DraftPreviewIndicator already uses. Per-request via AsyncLocalStorage; a module global here would be the
    layout-cache race all over again.

Verification is unchanged: the worker recomputes and compares. The marker is never trusted.

Verified end to end

Built site, no site-level config (src/start.ts absent, no cdnCacheControl):

worker issues  1q6wwk1
SSR publishes  window.__DECO_CSEG="1q6wwk1"
client echoes  ?__cseg=1q6wwk1
response       CDN-Cache-Control: public, max-age=1800

Fail-closed on that same build:

case result
server-issued token public, max-age=1800
no marker no-store
forged token no-store
another device's token no-store
bot UA no-store
login cookie no-store
HTML document no-store

2592 tests passing. Typecheck clean.

One end-to-end geo case is covered by unit test rather than through fetch: injectGeoCookies rebuilds the
Request, and new Request(req, init) only carries cf through on workerd — under Node it is dropped. Noted
in the test file.

Trade-off worth knowing

More dimensions in the URL means more entries to warm. A store spread across many regions will see a lower hit
rate in front of the Worker than one concentrated in a few.

That is a smaller ceiling, not a regression: a miss in front of the Worker falls through to the Worker's own
cache — which is already segmented — not to VTEX. The floor is exactly today's behaviour.

🤖 Generated with Claude Code


Summary by cubic

Switches the CDN segment marker from a client-derived token to one the server issues, so regionalized stores actually engage. Previously the client built the token from the user agent and refused any segment richer than device, but region is resolved from request.cf on the server — a client token never matched on a regionalized store and the feature stayed inert.

What changed

  • segmentToken now hashes the full segment descriptor (device, sales channel, region, geo, custom dimensions) instead of refusing those dimensions; loggedIn still yields no token.
  • The worker entry publishes the token per-request via the RequestContext bag; CdnSegmentMarker writes it to the page and decoServerFnFetch echoes it back on /_serverFn URLs.
  • The token is a hash rather than readable fields, since VTEX regionId can contain the old separator and a visitor's region should not appear in logged URLs.
  • Verification is unchanged: the worker recomputes the segment and only relaxes no-store on an exact match, so stale or forged markers are harmless.

Trade-off

  • The fuller URL key means more entries to warm; a store spread across regions sees a lower hit rate, but misses fall through to the Worker's own segmented cache, so the worst case is today's behavior.

Written for commit dd24327. Summary will update on new commits.

Review in cubic

…stores engage

The first version had the client derive the token from what it could observe
(`navigator.userAgent`), and rejected any segment richer than device. That is
why it engaged on exactly one kind of site: one whose segment reduces to device
alone.

A regionalized VTEX store — most of them — segments on region too, and region
comes from `request.cf.regionCode`, which exists only on the server. Observed
on a live site: the client sent `__cseg=desktop.<build>` while the worker
recomputed `desktop|sc=1|r=RJ`, so the marker never matched and the feature was
permanently inert. Fail-closed did its job; the feature just never did anything.

Invert it: the server computes the token from the segment it already built,
publishes it to the page, and the client echoes it back.

- `segmentToken` now folds sales channel, region, geo and a site's own custom
  dimensions INTO the token instead of refusing them. Those are what the key
  must distinguish, not reasons to give up on caching. `loggedIn` is still a
  hard refusal — a personalized response never belongs in a shared entry, no
  matter how precise the key.
- The token is a hash: `regionId` can contain the old separator (`v2.XXXX`),
  values would need escaping, and there is no reason to publish a visitor's
  region in a URL that ends up in logs.
- `requestSegmentDescriptor` folds in `__cf_geo`, which `buildSegment` does not
  carry — it comes from `buildGeoCacheParam`. Both now read the same helpers so
  the URL cannot distinguish less than the Worker's key does.
- `CdnSegmentMarker` publishes it via the RequestContext bag, the same
  server-to-client path `DraftPreviewIndicator` uses. Per-request through
  AsyncLocalStorage — a module global here would be the layout-cache race again.

Verification is unchanged and still by recomputation, so the marker is never
trusted.

Verified end to end on a built site with no site-level config: the worker issued
`1q6wwk1`, the SSR published it, the client echoed it, and the response came
back `public, max-age=1800`. Fail-closed intact on the same build — no marker,
forged token, another device's token, bot UA and login cookie all `no-store`,
HTML documents `no-store`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@JonasJesus42
JonasJesus42 requested a review from a team September 1, 2026 16:02

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 issues found across 7 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/tanstack/src/sdk/cdnSegment.ts">

<violation number="1" location="packages/tanstack/src/sdk/cdnSegment.ts:70">
P1: When two segment descriptors collide in this 32-bit hash, `cdnCacheableServerFn` accepts the same `__cseg` marker for both requests, allowing a front cache to serve one region or custom-dimension response to another. Use a collision-resistant digest, making token generation and verification asynchronous if necessary.</violation>
</file>

<file name="packages/tanstack/src/hooks/CdnSegmentMarker.tsx">

<violation number="1" location="packages/tanstack/src/hooks/CdnSegmentMarker.tsx:24">
P2: When SSR publishes a token, the client hydration tree omits this server-rendered `<script>` because the browser `RequestContext` bag is empty. React therefore reports a hydration mismatch and can discard the SSR tree; read `window.__DECO_CSEG` on the client and render the same script node, as `DraftPreviewIndicator` does.</violation>
</file>

<file name="packages/tanstack/src/sdk/workerEntry.test.ts">

<violation number="1" location="packages/tanstack/src/sdk/workerEntry.test.ts:828">
P2: The geo regression test no longer exercises the leak it claims to guard. The old test drove a geo-keyed site through the worker and asserted `CDN-Cache-Control: no-store`; the replacement only asserts that `segmentToken` returns different hashes for two hand-written strings (`desktop|geo=BR|RJ` vs `desktop|geo=BR|SP`). Any hash differentiates distinct inputs, so this passes even if `requestSegmentDescriptor` dropped the geo dimension entirely — exactly the cross-region data leak this PR is meant to prevent — and it never runs the server-issue / client-echo / verify loop. Since regionalized pricing/stock is the security-critical path here, add a test whose token comes from the worker's real descriptor (e.g. build `hashSegment(buildSegment(req))` + `|geo=...` and send it against a geo-keyed worker, or factor `requestSegmentDescriptor` out so the test and production share it).</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

if (!segmentDescriptor) return null;

return `${seg.device}.${buildHash}`;
return djb2Hex(`${segmentDescriptor}|${buildHash}`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: When two segment descriptors collide in this 32-bit hash, cdnCacheableServerFn accepts the same __cseg marker for both requests, allowing a front cache to serve one region or custom-dimension response to another. Use a collision-resistant digest, making token generation and verification asynchronous if necessary.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/tanstack/src/sdk/cdnSegment.ts, line 70:

<comment>When two segment descriptors collide in this 32-bit hash, `cdnCacheableServerFn` accepts the same `__cseg` marker for both requests, allowing a front cache to serve one region or custom-dimension response to another. Use a collision-resistant digest, making token generation and verification asynchronous if necessary.</comment>

<file context>
@@ -1,74 +1,71 @@
+  if (!segmentDescriptor) return null;
 
-  return `${seg.device}.${buildHash}`;
+  return djb2Hex(`${segmentDescriptor}|${buildHash}`);
 }
</file context>

import { CSEG_BAG_KEY, CSEG_GLOBAL } from "../sdk/cdnSegment";

export function CdnSegmentMarker() {
const token = RequestContext.getBag<string>(CSEG_BAG_KEY);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: When SSR publishes a token, the client hydration tree omits this server-rendered <script> because the browser RequestContext bag is empty. React therefore reports a hydration mismatch and can discard the SSR tree; read window.__DECO_CSEG on the client and render the same script node, as DraftPreviewIndicator does.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/tanstack/src/hooks/CdnSegmentMarker.tsx, line 24:

<comment>When SSR publishes a token, the client hydration tree omits this server-rendered `<script>` because the browser `RequestContext` bag is empty. React therefore reports a hydration mismatch and can discard the SSR tree; read `window.__DECO_CSEG` on the client and render the same script node, as `DraftPreviewIndicator` does.</comment>

<file context>
@@ -0,0 +1,35 @@
+import { CSEG_BAG_KEY, CSEG_GLOBAL } from "../sdk/cdnSegment";
+
+export function CdnSegmentMarker() {
+  const token = RequestContext.getBag<string>(CSEG_BAG_KEY);
+  if (!token) return null;
+  return (
</file context>

it("never lets two regions share a token", () => {
// The leak this guards: RJ and SP resolving to the same colo must not
// share an entry, since their regionalized pricing and stock differ.
expect(tok("desktop|geo=BR|RJ")).not.toBe(tok("desktop|geo=BR|SP"));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The geo regression test no longer exercises the leak it claims to guard. The old test drove a geo-keyed site through the worker and asserted CDN-Cache-Control: no-store; the replacement only asserts that segmentToken returns different hashes for two hand-written strings (desktop|geo=BR|RJ vs desktop|geo=BR|SP). Any hash differentiates distinct inputs, so this passes even if requestSegmentDescriptor dropped the geo dimension entirely — exactly the cross-region data leak this PR is meant to prevent — and it never runs the server-issue / client-echo / verify loop. Since regionalized pricing/stock is the security-critical path here, add a test whose token comes from the worker's real descriptor (e.g. build hashSegment(buildSegment(req)) + |geo=... and send it against a geo-keyed worker, or factor requestSegmentDescriptor out so the test and production share it).

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/tanstack/src/sdk/workerEntry.test.ts, line 828:

<comment>The geo regression test no longer exercises the leak it claims to guard. The old test drove a geo-keyed site through the worker and asserted `CDN-Cache-Control: no-store`; the replacement only asserts that `segmentToken` returns different hashes for two hand-written strings (`desktop|geo=BR|RJ` vs `desktop|geo=BR|SP`). Any hash differentiates distinct inputs, so this passes even if `requestSegmentDescriptor` dropped the geo dimension entirely — exactly the cross-region data leak this PR is meant to prevent — and it never runs the server-issue / client-echo / verify loop. Since regionalized pricing/stock is the security-critical path here, add a test whose token comes from the worker's real descriptor (e.g. build `hashSegment(buildSegment(req))` + `|geo=...` and send it against a geo-keyed worker, or factor `requestSegmentDescriptor` out so the test and production share it).</comment>

<file context>
@@ -774,72 +778,69 @@ describe('cdnCacheControl: "serverfn-segment"', () => {
+  it("never lets two regions share a token", () => {
+    // The leak this guards: RJ and SP resolving to the same colo must not
+    // share an entry, since their regionalized pricing and stock differ.
+    expect(tok("desktop|geo=BR|RJ")).not.toBe(tok("desktop|geo=BR|SP"));
   });
 
</file context>

@JonasJesus42
JonasJesus42 merged commit 7d56b97 into main Sep 1, 2026
1 check passed
@JonasJesus42
JonasJesus42 deleted the feat/cseg-server-issued branch September 1, 2026 16:13
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown

🎉 This PR is included in version 7.58.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant