Skip to content

Commit b3e5ad5

Browse files
raahulrahlclaude
andcommitted
docs(release): notes for 2026.20.3
Covers everything since 2026.20.2: x402 v2 migration closing four payment-bypass bug classes, three resolved server bugs (context_id, cancel race, DID admission), private skills surface, operator- extensible x402 EVM chains, parquetjs removal, ~90 Dependabot patches, README + PAYMENT.md teaching-voice rewrites, ty diagnostics to zero. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent b3dc1d9 commit b3e5ad5

1 file changed

Lines changed: 323 additions & 0 deletions

File tree

release-notes/2026.20.3.txt

Lines changed: 323 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,323 @@
1+
Release: x402 v2 Hardening, Private Skills, Multi-Chain Payments
2+
Version: 2026.20.3
3+
Date: May 13, 2026
4+
5+
OVERVIEW
6+
--------
7+
This release is a security-and-correctness pass with two operator-facing
8+
features riding along. On the security side, the x402 payment middleware
9+
was rewritten on top of the x402 SDK v2 — that single migration closes
10+
four payment-bypass bug classes (body-parse fail-open, replay, unverified
11+
signatures, skipped balance check) and clears every high-severity x402
12+
entry from bugs/known-issues.md. Three additional server bug classes are
13+
closed in this release: context_id parsing now rejects malformed UUIDs
14+
with JSON-RPC -32602 instead of fabricating new contexts; task cancel is
15+
now CAS-protected and no longer mis-reports state under a worker race;
16+
and a new optional `auth.allowed_dids` admission allowlist gates which
17+
Hydra-registered DIDs can reach handlers at all. The frontend drops
18+
parquetjs (removing 130+ transitive packages and two unpatched Apache
19+
Thrift advisories), and roughly 90 Dependabot alerts are patched via
20+
lock refresh. On the feature side, the x402 middleware is now
21+
operator-extensible to any EVM chain via `extra_networks` (SKALE Europa
22+
ships as the worked example), and agents can expose a second skill
23+
surface — `private_skills` — gated by Hydra auth + a DID allowlist, for
24+
operators whose skill descriptions are themselves the commercial product.
25+
Documentation got substantial work: PAYMENT.md and PRIVATE_SKILLS.md
26+
were rewritten in the README's teaching voice; the README itself was
27+
slimmed to a transformers-style layout and now leads with the Trade
28+
Compliance OS pitch.
29+
30+
PROBLEM SOLVED
31+
--------------
32+
Before this release:
33+
- The x402 payment middleware on v0.2.1 carried four payment-bypass
34+
shapes: a bare `except Exception` swallowed body-parse errors and
35+
fell through to the handler; there was no nonce store so signed
36+
payloads could be replayed indefinitely; signature verification was
37+
a stub method that never actually ran EIP-712 recovery; and the
38+
balance-check path had a fall-through branch that allowed requests
39+
when the asset contract had no code on chain. Combined, these were
40+
the four high-severity x402 entries in bugs/known-issues.md.
41+
- `_parse_context_id` in the A2A task manager logged a warning and
42+
generated a fresh UUID whenever a client sent a malformed
43+
`context_id`. The caller thought they were continuing conversation
44+
X and silently started a new isolated one; the old context was
45+
orphaned in storage, and an attacker could cheaply inflate storage
46+
by sending unbounded distinct garbage values.
47+
- The cancel_task handler read state, compared to terminal_states,
48+
then called `scheduler.cancel_task` with no atomic guarantee between
49+
the two steps. A worker that completed mid-cancel left the handler
50+
cancelling an already-terminal task and returning a misleading
51+
response that didn't reflect the actual final state.
52+
- After Hydra introspection + DID signature verification, any
53+
Hydra-registered DID could call any agent. There was no admission
54+
layer above auth: in multi-tenant or shared-Hydra deployments, a
55+
third party could `hydra create oauth2-client` with their own DID,
56+
obtain a valid token, and burn the agent's compute / LLM budget.
57+
- Skill descriptions on `/.well-known/agent.json` leaked the
58+
operator's product menu to the public. Operators whose skill
59+
catalogue IS the commercial product (compliance, legal, regulated
60+
workflows) had no way to expose skills to authorized partners
61+
without exposing them to the world.
62+
- x402 v2 ships built-in pricing for Base mainnet and Base Sepolia
63+
only. Operators wanting SKALE / Polygon / Avalanche / any other EVM
64+
chain had to choose between forking the SDK or shipping dead code.
65+
Four prior PRs (#486, #496, #507, #528) attempted patches that the
66+
v2 migration invalidated.
67+
- The frontend depended on parquetjs (inherited unmodified from
68+
huggingface/chat-ui) for an HF-internal dataset-export endpoint
69+
Bindu doesn't run. parquetjs pulled in Apache Thrift with two
70+
unpatched high-severity advisories (path traversal / response
71+
splitting in <=0.22.0, uncontrolled recursion in <0.23.0).
72+
- ~90 Dependabot alerts had accumulated across the Python and JS
73+
dependency trees.
74+
- The TruffleHog pre-commit hook ran with `--results=verified,unknown`,
75+
giving a 7% signal-to-noise ratio (2 verified hits out of 27 in a
76+
historical scan — the rest were docker-compose DSNs, .env.example
77+
files, config samples). Every PR went red on this check; reviewers
78+
learned to ignore the alarm.
79+
- `ty` had accumulated 71 diagnostics since the last cleanup, so the
80+
pre-commit ty hook failed for every commit on main.
81+
82+
After this release:
83+
- x402 middleware delegates verification, settlement, and balance
84+
checks to `x402ResourceServer` / `HTTPFacilitatorClient`. The
85+
body-parse handler is a narrow `except (JSONDecodeError,
86+
UnicodeDecodeError)` returning 402; the new `nonce_store.py` claims
87+
`(network, asset, nonce)` BEFORE the facilitator round-trip via
88+
Redis SETNX (with InMemoryNonceStore fallback for dev); signature
89+
verification runs full EIP-712 recovery on the facilitator; the
90+
balance-check fall-through is removed. Real-facilitator smoke
91+
confirms forged signatures are rejected with
92+
`invalid_exact_evm_signature` before the handler runs.
93+
- Malformed `context_id` now raises `MalformedContextIdError` from
94+
`_parse_context_id`; send_message and stream_message catch it and
95+
return a JSON-RPC -32602 ("Invalid params") response echoing the
96+
offending value. No storage write, no orphaned context, no
97+
amplification primitive.
98+
- The Storage ABC gains `update_task_state_if(task_id, from_state,
99+
to_state) -> bool`. Postgres implements it as a single conditional
100+
`UPDATE ... WHERE state = :from RETURNING id`; the in-memory backend
101+
implements it as a compare-and-swap with no inter-await yield.
102+
cancel_task CAS-claims the transition before signaling the
103+
scheduler — on a CAS miss it re-loads and reports the actual
104+
post-race state.
105+
- New `AuthSettings.allowed_dids: list[str] | None = None`. When None
106+
(default), behavior is unchanged. When set, the Hydra middleware
107+
runs an admission check after signature verification and rejects
108+
unlisted DIDs with HTTP 403 before any handler is reached.
109+
- New `private_skills` and `allowed_dids` keys on AgentManifest. When
110+
configured, a second endpoint `/agent/private.json` returns the
111+
merged catalog (public + private skills) only to DIDs on the
112+
allowlist; opt-in only, the route isn't registered when unused.
113+
- New `X402Settings.extra_networks` config surface: a Pydantic dict
114+
keyed on friendly chain slug carrying CAIP-2, asset address, and
115+
EIP-712 domain metadata. SKALE Europa ships as the default entry
116+
matching `facilitator.x402.fi`'s `/supported` response. Operators
117+
add chains by extending the dict — no Bindu code change required.
118+
- parquetjs and the unused `/admin/export` route are gone from the
119+
frontend. 130+ transitive packages and both Thrift advisories drop
120+
out of the lockfile.
121+
- pypdf, cryptography, cdp-sdk, pynacl, web3, ip-address, and several
122+
transitive bumps (aiohttp, urllib3, GitPython, Mako, mistune,
123+
langchain-core, python-multipart) land via direct + lock-refresh
124+
upgrades.
125+
- TruffleHog now gates only on verified hits. Unverified hits log as
126+
warnings but don't fail the build. Signal-to-noise jumps from 7% to
127+
100% on a historical scan of this repo.
128+
- ty diagnostics are at zero. Several were real (variance issues on
129+
`List[…]` parameters narrowed to `Sequence` / `Mapping`); the rest
130+
are documented exercising-error-paths in tests or runtime patterns
131+
ty doesn't see through.
132+
133+
HEADLINE FEATURES
134+
-----------------
135+
Operator-extensible EVM chains for x402
136+
Add any EVM chain by extending one Pydantic dict. Each entry registers
137+
a money parser scoped to its CAIP-2 so the SDK's built-in Base parser
138+
still wins for Base. SKALE Europa ships as the default extra network,
139+
matching `facilitator.x402.fi`'s advertised bridged-USDC asset
140+
(decimals=6, name="USDC", EIP-712 version="2") exactly.
141+
142+
Private skill catalogs gated by DID allowlist
143+
Two new optional keys on AgentManifest — `private_skills` and
144+
`allowed_dids` — expose a second endpoint at `/agent/private.json`.
145+
The Hydra middleware fronts it like any non-public route; the handler
146+
adds an allowlist check that rejects authenticated-but-unlisted DIDs
147+
with 403. Closes the IP-leak shape (#538) with auth gating rather than
148+
encryption-at-rest; the JWE/E2E option remains as a Phase 2 if a real
149+
customer demands "operator doesn't trust the server itself" semantics.
150+
151+
SECURITY
152+
--------
153+
- x402 SDK >=2.3.0,<3 (was ==0.2.1). Clears the x402 SDK CVE and
154+
replaces the manual middleware with the v2 ResourceServer pattern.
155+
- All four high-severity x402 bug classes are closed and removed from
156+
bugs/known-issues.md. Bindu Core's High count goes from 4 to 0.
157+
- Three medium-severity server bug classes are closed:
158+
`context-id-silent-fallback`, `task-cancel-check-then-act-race`,
159+
`did-admission-control-missing`.
160+
- DID admission control is now configurable (`auth.allowed_dids`).
161+
Operators running shared Hydra or multi-tenant deployments can
162+
restrict which DIDs reach handlers even when Hydra would issue them
163+
a valid token.
164+
- parquetjs removed: 130+ transitive packages drop out, including the
165+
two unpatched Apache Thrift advisories.
166+
- pynacl ==1.5.0 → >=1.6.2,<2 (libsodium incomplete-disallowed-inputs).
167+
- web3 ==7.13.0 → >=7.15.0,<8 (SSRF via CCIP Read OffchainLookup).
168+
- cryptography ==44.0.2 → >=46.0.5,<47.
169+
- pypdf <6 → >=6.10.2,<7 (clears 44 alerts via lock refresh).
170+
- cdp-sdk ==0.21.0 → >=1.45.0,<2 (frees the cryptography pin).
171+
- Frontend: ip-address ^9.0.5 → ^10.1.1 (Address6 XSS); cookie ^0.7.0
172+
override; minimatch ^9.0.7 override scoped to typescript-estree.
173+
- TruffleHog pre-commit hook tightened to `--results=verified` so the
174+
scanner stays usable.
175+
176+
PERFORMANCE IMPACT
177+
------------------
178+
- x402 v2 middleware: replay short-circuits at the nonce store before
179+
any facilitator round-trip, so duplicate-payment requests are now
180+
cheaper to reject than they were to accept under v0.2.1.
181+
- Admission control: a Python `in` check on a list. Operators with
182+
long allowlists may want to set a pattern-match variant in a future
183+
release (Option B in the design sketch); exact-match is shipping
184+
first because it covers the common case.
185+
- CAS cancel: one extra UPDATE on Postgres, one extra dict read on
186+
in-memory storage. Both inside the existing request span.
187+
- The Storage ABC change is additive: existing backends gain one new
188+
method but their hot paths are unchanged.
189+
190+
TESTING
191+
-------
192+
- 993 unit tests passing, 3 skipped, 12 warnings, runtime ~3.3s on
193+
this machine. ruff clean. ty: 0 diagnostics.
194+
- 7 new admission tests in
195+
tests/unit/server/middleware/test_hydra_admission.py covering the
196+
default admit-all behavior, exact-match hits, misses, the deny-all
197+
empty-list posture, missing client_did, and a 403-doesn't-forward
198+
integration check.
199+
- 3 new storage CAS tests in tests/unit/server/storage/
200+
test_memory_storage.py (CAS success, state mismatch, missing task).
201+
- 2 new task handler tests covering the cancel race scenario:
202+
CAS-success happy path; CAS-failure does NOT signal the scheduler
203+
and reports the actual post-race state.
204+
- 7 new context-id tests in tests/unit/server/test_task_manager.py
205+
covering all four `_parse_context_id` branches plus the
206+
`_create_error_response` code-override path.
207+
- 1 new regression test for `send_message` returning -32602 on
208+
malformed context_id.
209+
- 8 new tests for the private-skills endpoint exercising the auth +
210+
allowlist gate end-to-end with a stub middleware mirroring how
211+
Hydra middleware stamps request.state.user.
212+
- 11 new tests for `extra_networks` covering schema validation, money
213+
parser registration, non-default-decimals (WETH 18-decimals), and
214+
fall-through behavior.
215+
- 10 new nonce-store tests covering key construction, replay
216+
rejection, TTL expiry, and concurrent claim races.
217+
- 9 new x402 middleware tests exercising the full v2 dispatch path
218+
with a mocked ResourceServer.
219+
- 2 opt-in live-facilitator smoke tests at
220+
tests/integration/x402/test_skale_facilitator_supported.py (gated
221+
on X402_NETWORK_TESTS=1) confirm the shipped SKALE Europa default
222+
matches facilitator.x402.fi's `/supported` response.
223+
224+
KNOWN ISSUES
225+
------------
226+
- `bugs/known-issues.md::skale-facilitator-cert-expired` (low, ops):
227+
the only public x402 facilitator advertising SKALE chains is
228+
`facilitator.x402.fi`, whose TLS certificate is currently expired.
229+
Bindu's defaults still point at Coinbase's facilitator; SKALE
230+
operators set `X402__FACILITATOR_URL` themselves and either accept
231+
the cert error or front the upstream with a reverse proxy. This is
232+
not a code bug on the Bindu side — see the linked entry for the
233+
recommended production posture (self-host `x402-facilitator`).
234+
- `bugs/known-issues.md::authz-scope-check-behind-optional-flag`
235+
(medium, security): the scope-check flag (`auth.require_permissions`)
236+
defaults to False and authorization disappears when it's not flipped
237+
on. Deferred to the planned Ory Kratos migration rather than patched
238+
in place — the entry stays in known-issues.md.
239+
- `bugs/known-issues.md::no-rate-limit-or-quota-per-caller` (medium):
240+
no per-DID quota or body-size limit on the Starlette app. Operators
241+
exposing Bindu directly on the public internet should add their own
242+
rate limiting at the proxy until this lands natively.
243+
- The historical Auth0 client_secret values committed in Oct 2025 at
244+
e28e3417 are still reachable via `git log` (removed from HEAD but
245+
the blob persists). Rotation is an operator action, not a code fix.
246+
247+
FILES CHANGED
248+
-------------
249+
Since 2026.20.2: 74 files changed, +5366 insertions, −3946 deletions.
250+
Headline groupings:
251+
252+
Security & correctness:
253+
- bindu/server/middleware/auth/hydra.py (admission helper + check)
254+
- bindu/server/handlers/task_handlers.py (CAS-based cancel)
255+
- bindu/server/handlers/message_handlers.py (malformed context_id
256+
→ -32602)
257+
- bindu/server/task_manager.py (raise instead of fallback;
258+
_create_error_response gains optional code override)
259+
- bindu/server/errors.py (new — MalformedContextIdError)
260+
- bindu/server/storage/base.py (abstract update_task_state_if)
261+
- bindu/server/storage/memory_storage.py (in-memory CAS)
262+
- bindu/server/storage/postgres_storage.py (conditional UPDATE CAS)
263+
- bindu/settings.py (new auth.allowed_dids + x402.extra_networks)
264+
- bugs/known-issues.md (six resolved entries removed + counters)
265+
- bugs/core/2026-05-12-x402-v2-migration-hardening.md (new postmortem)
266+
267+
x402 v2 migration:
268+
- bindu/server/middleware/x402/ (full rewrite around v2 ResourceServer)
269+
- bindu/server/middleware/x402/nonce_store.py (new — Redis SETNX +
270+
in-memory fallback)
271+
- bindu/server/applications.py (CAIP-2 normalisation, ExactEvmServerScheme
272+
money parser registration for extra networks)
273+
- tests/unit/server/middleware/x402/* (30 new tests)
274+
- tests/integration/x402/test_skale_facilitator_supported.py (gated)
275+
- pyproject.toml (x402 0.2.1 → 2.3.0+; pydantic-settings as direct dep)
276+
277+
Private skills:
278+
- bindu/common/models.py (AgentManifest gains private_skills,
279+
allowed_dids)
280+
- bindu/penguin/manifest.py, bindufy.py, config_validator.py
281+
- bindu/server/endpoints/private_agent_card.py (new)
282+
- bindu/server/endpoints/__init__.py
283+
- bindu/server/applications.py (conditional route registration)
284+
- tests/unit/server/endpoints/test_private_agent_card.py (new, 8 tests)
285+
- examples/private_skills_agent/ (new — ACME Compliance walkthrough)
286+
- docs/PRIVATE_SKILLS.md (new — teaching-voice doc)
287+
288+
Dependency hygiene:
289+
- pyproject.toml (pypdf, cryptography, cdp-sdk, pynacl, web3 bumps)
290+
- uv.lock (refreshed; ~90 Python alerts cleared)
291+
- frontend/package.json + lockfile (ip-address, cookie, minimatch;
292+
parquetjs + @types/parquetjs dropped)
293+
- frontend/src/routes/admin/export/ (deleted)
294+
- frontend/src/hooks.server.ts (deprecated PARQUET_EXPORT_SECRET
295+
fallback removed)
296+
297+
Documentation:
298+
- README.md (slimmed to transformers-style; maintainer avatars;
299+
multi-chain x402 row; Trade Compliance OS pitch)
300+
- docs/PAYMENT.md (rewritten in teaching voice; end-to-end paywall
301+
walkthrough with success + failure paths against a mock facilitator)
302+
- docs/PRIVATE_SKILLS.md (new)
303+
304+
CI / type cleanup:
305+
- .pre-commit-config.yaml (trufflehog gates on verified hits only)
306+
- bindu/common/protocol/types.py, several handler files (ty: ignore
307+
on intentional patterns; real type narrowing where applicable)
308+
- tests/conftest_stubs.py (deleted — was shadowing real opentelemetry
309+
and x402 installs)
310+
311+
Tests (cumulative new):
312+
- ~80 new unit + integration tests across admission, CAS storage,
313+
context-id parsing, private skills endpoint, x402 nonce store,
314+
x402 middleware v2 dispatch, and extra_networks.
315+
316+
CONTRIBUTORS
317+
------------
318+
Lead maintainer:
319+
- Raahul Dutta (16 commits since 2026.20.2)
320+
321+
Pair-programming assist:
322+
- Claude Opus 4.7 (1M context) — co-author on every commit in this
323+
release.

0 commit comments

Comments
 (0)