Skip to content

feat: accept and send cookies with JSON-RPC - #25162

Merged
alexghr merged 1 commit into
merge-train/spartanfrom
stack/feat-accept-and-send-cookies-with-json-rpc
Aug 16, 2026
Merged

feat: accept and send cookies with JSON-RPC#25162
alexghr merged 1 commit into
merge-train/spartanfrom
stack/feat-accept-and-send-cookies-with-json-rpc

Conversation

@alexghr

@alexghr alexghr commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Implements cookie support in the JSON-RPC clients. This is useful for load balancer session affinity.

This PR is stacked on top of #25159

Fix A-1628 A-1676

@alexghr
alexghr force-pushed the stack/feat-track-json-rpc-metrics branch from 60b2228 to 10007f1 Compare August 12, 2026 13:46
@alexghr
alexghr force-pushed the stack/feat-accept-and-send-cookies-with-json-rpc branch from e103355 to 69c6bb9 Compare August 12, 2026 13:46
if (!responseOk) {
throw new Error('HTTP ' + resp.statusCode);
}
throw new Error(`Failed to parse body as JSON. encoding: ${contentEncoding}, body: ${responseText!}`);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This error needs to be updated, right?

method: 'POST',
body: jsonStringify(body),
headers: { 'content-type': 'application/json', ...extraHeaders },
credentials: config.credentials ?? 'omit',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm missing where we set 'include' here for the json rpc client

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I didn't set this as a default. I'd rather the app do it.

Comment on lines +78 to +84
if (cookieJar) {
const setCookieHeaders = resp.headers['set-cookie'];
const cookies = typeof setCookieHeaders === 'string' ? [setCookieHeaders] : (setCookieHeaders ?? []);
for (const cookie of cookies) {
cookieJar.setCookie(cookie, requestUrl.href);
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm surprised this needs to be done manually tbh

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yep, undici only offers helpers to work with cookie headers but no cookie jar
https://undici.nodejs.org/api/Cookies

There's tough-cookie available but I'd rather not add a new dependency and let the app choose https://www.npmjs.com/package/tough-cookie

Comment on lines +27 to +36
rpcCorsAllowedOrigins: {
env: 'RPC_CORS_ALLOWED_ORIGINS',
description: 'Origins allowed to make credentialed cross-origin JSON RPC requests, separated by commas.',
parseEnv: (value: string) =>
value
.split(',')
.map(origin => origin.trim())
.filter(Boolean),
defaultValue: [],
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Where would we be using this? Or is this intended just for operators who'd be running a node to serve json-rpc to their app? I'm tempted to suggest deleting this in favor of just the "allow cors" below.

Or should this be handled by another layer in front of the node?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We can't send access-control-allow-origin: * when working with cookies so we need to reflect the origin header on request back to the browser. I thought maybe node operators might need to control for which origins to do that? We'd use continue using * but now instead of returning a literal * we just reflect back the origin header.

setCookie(cookie: string, url: string): void;
}

export function makeUndiciFetch(client: Dispatcher = new Agent(), cookieJar?: CookieJar): JsonRpcFetch {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think we are never setting the cookie jar, are we? Do we really care about this undici client supporting session cookies then?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

App concern. I'll add some docs!

Comment on lines +83 to +88
export function makeFetch(
retries: number[],
defaultNoRetry: boolean,
log?: Logger,
config: JsonRpcFetchConfig = {},
): JsonRpcFetch {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is makeFetch actually called by clients? Don't they go via createAztecNodeClient, which doesn't expose a way to set the credentials config?

body: unknown,
extraHeaders: Record<string, string> = {},
noRetry = false,
config: JsonRpcFetchConfig = {},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Since we're on it, could we add a maxBatchSize to client as well?

@alexghr
alexghr force-pushed the stack/feat-track-json-rpc-metrics branch from 10007f1 to 7824433 Compare August 14, 2026 08:11
@alexghr
alexghr force-pushed the stack/feat-accept-and-send-cookies-with-json-rpc branch 2 times, most recently from 5e3bd96 to 7049d54 Compare August 14, 2026 08:51
@alexghr
alexghr force-pushed the stack/feat-track-json-rpc-metrics branch from 7824433 to 3b7c6ea Compare August 14, 2026 08:51
@alexghr
alexghr force-pushed the stack/feat-accept-and-send-cookies-with-json-rpc branch from 7049d54 to be6077b Compare August 14, 2026 10:27
@alexghr
alexghr force-pushed the stack/feat-track-json-rpc-metrics branch from 3b7c6ea to 849195d Compare August 14, 2026 10:54
@alexghr
alexghr force-pushed the stack/feat-accept-and-send-cookies-with-json-rpc branch from be6077b to 83ff922 Compare August 14, 2026 10:56
@alexghr
alexghr force-pushed the stack/feat-track-json-rpc-metrics branch from 849195d to 36c1dc4 Compare August 14, 2026 12:10
@alexghr
alexghr force-pushed the stack/feat-accept-and-send-cookies-with-json-rpc branch from 83ff922 to c926fa8 Compare August 14, 2026 12:11
@alexghr

alexghr commented Aug 14, 2026

Copy link
Copy Markdown
Contributor Author

I dropped the backport-to-v5-next label because this contains breaking changes as it stands. I'll manually patch it in for v5

@alexghr
alexghr force-pushed the stack/feat-accept-and-send-cookies-with-json-rpc branch from c926fa8 to d5fdb5d Compare August 14, 2026 12:19
Base automatically changed from stack/feat-track-json-rpc-metrics to merge-train/spartan August 14, 2026 12:37
@alexghr
alexghr force-pushed the stack/feat-accept-and-send-cookies-with-json-rpc branch from d5fdb5d to f4a59f6 Compare August 14, 2026 12:42
@alexghr
alexghr force-pushed the stack/feat-accept-and-send-cookies-with-json-rpc branch from f4a59f6 to 4da81db Compare August 14, 2026 13:37
@alexghr
alexghr force-pushed the stack/feat-accept-and-send-cookies-with-json-rpc branch from 4da81db to f10ab76 Compare August 16, 2026 15:46
@alexghr
alexghr merged commit 429e9e4 into merge-train/spartan Aug 16, 2026
10 checks passed
@alexghr
alexghr deleted the stack/feat-accept-and-send-cookies-with-json-rpc branch August 16, 2026 20:01
aminsammara added a commit that referenced this pull request Aug 17, 2026
Promotes `v5-next` onto `v5` for the **v5.2.0** release.

Frozen at `ee5d2d367e` — the `v5-next` tip at cut time. Both
`merge-train/spartan-v5` and `merge-train/fairies-v5` are drained to
that same commit, so nothing staged is left behind.

### Testnet validation

This branch was cut at `a4db216abf`, which is byte-identical to the
`v5.2.0-nightly.20260815` tag — same commit, same tree
(`0b22572eace2419ede6bbab173514d6965d4e0d0`). That nightly's CI3 run is
green, its artifacts are published to npm and Docker Hub, and it has
been **running healthily on testnet since 2026-08-15**:

| Component | Image / reported version |
|---|---|
| validators, prover node, prover broker, prover agents (`testnet` ns) |
`aztecprotocol/aztec:5.2.0-nightly.20260815` |
| `v5.testnet.rpc.aztec-labs.com` | `nodeVersion=5.2.0-nightly.20260815`
|
| `canonical.testnet.rpc.aztec-labs.com` |
`nodeVersion=5.2.0-nightly.20260815` |

Chain advancing normally, no prunes or reorgs, no WARN/ERROR across node
pods since rollout.

The branch has since been fast-forwarded to `ee5d2d367e` to pick up
#25242 (configurable RPC server HTTP timeouts and CORS allowed-headers).
That is the only delta from the soaked tree — 8 files, +131/−10, no
nightly covers it yet. Its config defaults were checked against Node's
built-ins (`keepAliveTimeout` 5000 ms, `headersTimeout` 60000 ms) and
match exactly, and the CORS default path resolves to the same `cors()`
call as before, so a node that sets none of the new env vars behaves
identically. It touches no circuits, protocol contracts, or generated
constants.

### Manifest

`.release-please-manifest.json` reads `5.2.0` on this branch, which is
the released version — correct as-is, no change needed in this PR.
`v5-next` moves to `5.3.0` separately in #25240; this branch is frozen
and cannot pick that up, so the two can merge in either order.

`v5` is a strict ancestor of `v5-next` this cycle, so there was no
manifest conflict to pre-resolve.

### Scope

139 commits (86 non-merge, 23 PR-level) spanning 2026-07-14 to
2026-08-17.

| Area | Theme |
|---|---|
| `prover-node` / `prover-client` | Epoch-proving robustness:
retry-to-converge with failure declared only at submission-window
expiry, ticker-driven expiry, per-checkpoint post-mortem upload,
checkpoint-only re-proving, prune-induced fault handling (#24678,
#24982, #24983, #24990, #25027) |
| `pxe` | Sync performance: hash-pinned node read cache (#24969),
anchor-bounded tag log caching (#25074), note/event validation from
cached tx data (#25076), constrained tag sync (#24275), sender tagging
finalization from log blocks (#25045) |
| `p2p` / `validator-client` | Gossip tx validation no longer stalls
behind tx-pool finalization (#25148); startup fails when p2p fails to
start (#25177); slashing/proposing/health/`sendTx` gated on p2p
connectivity (#25185); duplicate time-sensitive proposal validation
removed (#25207); `ValidatedProposal` branding (#25222) |
| `ethereum` / `aztec-node` | L1 watchers poll `getLogs` instead of
`eth_newFilter` (#25176); block stream sync no longer resolves against
an earlier pass (#25206) |
| `stdlib` / `foundation` | Deserialization bounds hardening (#25026,
#25028, #25029, #25109); checkpoint block-shape and block-count
validation (#25229); JSON-RPC cookies (#25231) |
| `archiver` | Removed-block cleanup and ownership-checked tx-effect
deletes (#24765); L2→L1 witness from a single store snapshot (#24754) |
| `aztec` | Declarative deployment framework at `@aztec/aztec/deploy`
(#24685) |
| `slasher` | Own-validator slash-target warnings and metrics (#25058) |
| telemetry | JSON-RPC metrics (#25159) |
| JSON-RPC server | Configurable HTTP keep-alive / headers timeouts and
CORS allowed-headers, defaults preserving current behaviour (#25242) |
| toolchain | Noir bumped to `v1.0.0-beta.25` (#24907) |

**One breaking change**, inherited from the Noir bump: note types
declared directly inside a `contract` module must now be `pub`.
Everything else is additive or internal.

### Protocol constants

Built from source and compared against `v5` — a cache-free rebuild of
every protocol circuit with each ref's own nargo and bb, then
regeneration of `vk_tree.ts` and `protocol_contract_data.ts`:

- `vkTreeRoot` =
`0x2b3b6ea4412b9c8f6457a37f91a2870306f8641e07e16a49b68bda6f8bc02892` —
unchanged from v5.1.0
- `protocolContractsHash` =
`0x2c075866eafc88a1f6f9addc7e337c6e64e45d1cb7fd7c0d612ebcec72aab2ca` —
unchanged from v5.1.0

The Noir beta.24 → beta.25 bump does not reach the circuits: the release
build consumes the committed `pinned-build.tar.gz`, which is
bit-identical between v5.1.0 and this commit (blob `3bedcb1fd1…`), so
the protocol-circuit bytecode is frozen rather than recompiled. The 47
verification keys were recomputed locally from that pinned bytecode with
the cache disabled, and all 47 `check_pinned_vk` checks passed.
`protocolContractsHash` is likewise backed by the new
`pinned-protocol-contracts.tar.gz`, whose three artifacts were
byte-compared against the build. Both values were also confirmed inside
the published `@aztec/protocol-contracts` and
`@aztec/noir-protocol-circuits-types` packages for
`5.2.0-nightly.20260815`.

That makes v5.2.0 a drop-in upgrade against the current rollup rather
than a coordinated one.

✅ **Gate re-run against `ee5d2d367e` (the current head) and passed** —
both values reproduced exactly from a build at this commit, with
`vkTreeRoot` numerically evaluated rather than inferred.
`29556326ce..ee5d2d3` touches no `noir-projects/**`,
`l1-contracts/src/**`, `ConstantsGen.sol`, `constants.gen.ts` or
`constants.nr`. Scope of the check: it verifies that the pinned circuit
bytecode plus locally recomputed VKs agree with the pin — not that a
from-source recompile reproduces v5.1.0's bytecode.

<details>
<summary>Commits added after the original constants check at
2955632</summary>

`#25207` `#25222` `#25206` `#25185` `#25176` `#25163` `#25229` `#25159`
`#25162` `#25231` `#25224` `#25228` `#25230` `#25242` plus their merge
commits.
</details>

### Known gap

Migration notes carry entries under `## TBD` that arrived with the docs
baseline backport (#25017) and describe `next`-line changes not present
on the v5 line — protocol contracts removed from
`@aztec/noir-contracts.js`, and the `at(wallet)` → `withWallet(wallet)`
deprecation. Neither exists on this branch. There is also no `## 5.1.0`
heading, and the entries that ship in v5.2.0 sit under `## TBD` rather
than a version heading.

This ships in the release docs, so it is worth correcting on `v5-next`
and backporting to `v5` before the tag, rather than after.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants