Changelog
July 23, 2026 · View on GitHub
All notable changes to le-agent-sdk are documented here.
This project adheres to Semantic Versioning. Entries begin at 0.4.0; for earlier history see the commit log.
[0.4.2] - 2026-07-23
Security fix — upgrade recommended. The L402 amount/budget gate no longer pays an invoice whose amount can't be bounded when no max_amount_sats is configured. Previously an unset max short-circuited the whole check and paid anything — an unbounded, unaudited spend whenever a caller forgot to set a ceiling. Now an unknown/unbounded/non-positive amount is refused regardless of whether a max is set; a known positive amount with no max still pays (opt-out preserved).
[0.4.1] - 2026-07-17
Security fix. Hardens capability discovery against malformed events from a hostile or misbehaving relay — upgrading is recommended.
Fixed
discover()no longer aborts the entire batch when a single relay event is malformed. A capability event with an unparseablepricetag, a dict missing a committed field (pubkey/created_at/kind/tags/content), or a non-dict payload could each makediscover()raise and drop every capability in the response — so one hostile or misbehaving relay could DoS discovery for every agent. Each event is now authenticated and parsed independently: a malformed event is skipped and logged as a warning (fail closed, loudly) while the valid capabilities are still returned. Crypto-backend faults continue to propagate (fail closed, unchanged).
[0.4.0] - 2026-07-17
Security release. Fixes signature verification silently passing, two payment-budget bypasses, and unverified relay events — upgrading is recommended.
This release also replaces the package's crypto dependency, which is why it is a minor bump rather than a patch: see Dependencies below.
Dependencies
-
Replaces the
secp256k1dependency withcoincurve, which ships prebuilt wheels.secp256k1required a native build (libsecp256k1 plus a C toolchain) and failed to install on Windows entirely; even where a build was possible it frequently produced an install whereimport secp256k1failed, which is what the verification bug below turned into a silent security hole.coincurveprovides the same BIP-340 Schnorr primitives over the same curve with no build step.For most users this is transparent —
pip install le-agent-sdksimply starts working where it previously did not. Two things to be aware of:- If your project imported
secp256k1itself and relied on this package to pull it in, it is no longer installed transitively. Declare it directly. - The signature wire format is unchanged. Events signed by 0.3.x verify under 0.4.0 and vice versa; the curve, key encoding, and BIP-340 semantics are identical, only the binding differs. This is covered by cross-implementation tests against the .NET SDK and the BIP-340 published vectors.
- If your project imported
Fixed
- Fixes signature verification silently passing when the crypto backend is
unavailable — upgrade recommended.
NostrEvent.verify()returnedTruefor any event whose ID matched when the native secp256k1 library could not be imported. The event ID is a plain SHA-256 over public fields, so it is attacker-computable and proves nothing about authenticity — forged capability advertisements and forged attestations under any pubkey were accepted. Verification now raisesCryptoBackendUnavailableErrorinstead of passing. Because the old dependency could not be installed at all on some platforms, this affected real deployments rather than only misconfigured ones — and the dependency swap above removes the condition for nearly all of them. - Fixes
pay_and_access()ignoringmax_amount_sats, and budget checks being skipped for invoices whose amount could not be read — both allowed payments above the configured limit; upgrade recommended.pay_and_access()never consulted the limit at all, so a client constructed withmax_amount_sats=100would pay a 10,000,000-sat invoice. Separately, an invoice whose amount could not be determined was treated as "no limit applies" and paid. An amount that cannot be determined is now refused whenever a limit is configured. - Fixes the BOLT-11 amount parser reading an amount from the invoice data part.
The pattern was not anchored to the human-readable part, so an amountless (i.e.
unbounded) invoice whose data happened to contain
<digits><multiplier>1was reported as a small amount and passed the budget check. Amounts are now read only from the human-readable part, and rounded up rather than truncated so a budget check is never given an under-reported value. - Incoming relay events are now signature-verified before use.
discover(),get_attestations()andlisten_requests()passed raw relay JSON straight into the models. Relay lists are caller-configurable and results are merged across relays, so a single malicious relay could inject events attributed to any pubkey. Events failing verification are dropped and logged; other relays' results are unaffected. - The
User-Agentsent byL402ProducerClientreported0.1.0on every release since 0.1.0. It now tracks the package version.
Added
CryptoBackendUnavailableError, exported from the package root. SubclassesRuntimeError, so existingexcept RuntimeErrorhandlers continue to work.Secp256k1UnavailableErroris kept as an alias of it.- Cross-implementation wire-compatibility tests: events signed by the .NET SDK (via NBitcoin.Secp256k1) are committed as fixtures and verified on every run, alongside the BIP-340 published test vectors.
Changed
pay_and_access()accepts amax_amount_satsargument to override the instance-level limit for a single call, matchingaccess().
Upgrade notes
pip install le-agent-sdkno longer needs a C toolchain. If you previously installed build dependencies (libsecp256k1, build-essential, Visual C++ Build Tools) solely for this package, they are no longer required.- If the crypto backend is not importable in your environment, verification now
raises where it previously returned
True. Any code path that reads events from relays is affected. This is intentional: the previous result was not a weaker check, it was no check. - Callers relying on unknown-amount invoices being paid while
max_amount_satsis set will now seeValueError. Either set no limit (explicitly opting out of budget enforcement) or use invoices with an explicit amount.
Note on 0.3.3
An earlier cut of this work was staged as 0.3.3 and was never published to PyPI.
Its contents are released here as 0.4.0; no 0.3.3 artifact exists. The
Secp256k1UnavailableError name originated in that unreleased cut, so no released
version ever exported it — it is aliased anyway for anyone tracking the branch.