Releases: wasit-dev/Wasit
Release list
0.6.0
All three packages move to 0.6.0 together: @wasit-dev/core, @wasit-dev/cli and @wasit-dev/server.
The two x402 payment checks now prove what their names say: X402-06 verifies the settlement on-chain, and X402-07 forges only the signature. MPP-01 reads the newer form of the transfer event and stops blaming a target for a slow RPC. Node.js 22 is supported. No check was added or removed.
Results can change on upgrade
X402-06fails a target that serves without settling, reports a transaction that is not its settlement, or answers without aPAYMENT-RESPONSEheader.X402-07fails a target that decodes a payment without verifying its signature, or accepts a forged one with 201 or 204.MPP-01passes a payment to a muxed (M...) recipient that it used to miss, and gives no verdict instead of a FAIL when RPC stops advancing.
Against Stellar's official reference paywall (stellar/x402-stellar), this build passes 7/7, with X402-06 verified on-chain. If a run goes red on upgrade, the check got stronger, not the service worse.
Changed: X402-06 verifies settlement on-chain
It passed on any 2xx, which established that the target served the resource, not that the payment landed. It now reads the settlement from the PAYMENT-RESPONSE header and holds the reported transaction to the advertised terms on Stellar RPC, as MPP-01 does: one transfer from this run's payer to payTo, for amount of asset. Against a server built to serve without settling, and one that reports someone else's transaction, 0.5.0 passed and 0.6.0 fails both. A settlement_pending response is reconciled on chain, as the spec directs.
wasit test gains --rpc-url, and the MCP tool wasit_x402_test gains rpcUrl.
Fixed: X402-07 corrupts only the signature
It overwrote the tail of the base64 envelope, which broke XDR decoding, so a target that decoded the envelope and never verified the signature still refused it and passed. It now flips one byte of the client's Soroban authorization-entry signature and leaves the rest of the transaction intact. Against a server that decodes but never verifies, 0.5.0 passed and 0.6.0 fails. Acceptance is now any 2xx, not only 200.
Fixed: MPP-01 reads CAP-67 transfer events
Since CAP-67, a SEP-41 transfer to a muxed address emits its data as a map { amount, to_muxed_id } instead of a bare i128. MPP-01 read only the bare form, so a settlement to a muxed recipient would have been reported as no transfer at all. It now reads both and matches an advertised muxed recipient on the base account and the id. Checking this end to end showed the official @stellar/mpp charge server has the same gap: stellar/stellar-mpp-sdk#89.
Fixed: MPP-01 no longer blames the target for a slow RPC
The wait for the settled transaction is now measured in ledgers: it is reported missing only once RPC has closed ten more ledgers without it, and an RPC that stops advancing gives ERROR (harness) instead of a FAIL. When a target refuses the payment, the FAIL no longer says it "paid".
Changed: Node.js 22 is supported
All three packages declare "node": ">=22" instead of >=24. CI tests them on Node 22 and 24.
Fixed: wasit wallet status --role <role> exits 2 when it could not check that role
wasit wallet status --role x402 && deploy used to go ahead on a key that was never checked. With --role it now exits 2 whenever the role could not be checked; an unfunded account is an answer and exits 0.
Install
npx -y @wasit-dev/cli@0.6.0 --version
claude mcp add wasit -- npx -y @wasit-dev/server@0.6.0Testnet only. A passing result means the service behaved as the spec requires for the published checks; it is not a security audit.
0.5.0
All three packages move to 0.5.0 together: @wasit-dev/core, @wasit-dev/cli and @wasit-dev/server.
Two reporting fixes found by running Wasit against code it did not write, and one addition that brings the MCP server level with the CLI. No check was added or removed.
Results can change on upgrade
Only in two situations:
- A target that issues an x402 v1 challenge. See the first fix below.
- An MPP channel target that refuses a replay with a status other than 402 or 2xx. The verdict is still FAIL, but it no longer calls the refusal a double-spend.
Against a conformant v2 target nothing changes: Wasit's own x402 fixture passes 7/7 with this build, as it did with 0.4.0.
Fixed: x402 v1 challenges are read, and payment checks no longer fail when nothing was paid
Run with its operator's written authorization against a service that speaks x402 v1, 0.4.0 reported X402-02 FAIL, skipped X402-03–05, and reported X402-06 and X402-07 as FAIL although no payment was ever built or sent. X402-07 therefore read as a corrupted signature that was not rejected. docs/CHECKS.md already said a challenge that cannot be read has no verdict.
Now:
X402-02still fails, because theexactscheme on Stellar is defined for v2 only. The failure now says an x402 v1 challenge was found in the response body.X402-03–05inspect that body instead of being skipped.X402-04applies the v1 field names, andX402-05reports whether the network is a CAIP-2 identifier.X402-06andX402-07are skipped with the reason, for a v1 challenge or any challenge the payment client cannot read. Nothing is sent.
Seven new offline tests, each mutation-checked.
Fixed: MPP-12 and MPP-14 no longer call a refused replay a double-spend
Both reported any non-402 response as "accepted twice … This is a double-spend." The official MPP SDK's channel server on its main branch refuses replays with HTTP 500 (stellar/stellar-mpp-sdk#82), so Wasit claimed a double-spend while the server had in fact refused. The verdict stays FAIL, since the required status is 402, but the double-spend wording now appears only for a 2xx.
Added: wasit_x402_test accepts method, body and headers
The CLI has had --method, --body and --header since 0.1.0, but the MCP tool could only send a GET, so an agent could not test a paid POST endpoint. The same request shape now applies to every probe, including X402-06 and X402-07. A body sent with GET or HEAD is refused as a configuration error. Header values appear in the agent's transcript, so never pass a credential as a header; use the CLI for an endpoint that needs one.
Install
npx -y @wasit-dev/cli@0.5.0 --version
claude mcp add wasit -- npx -y @wasit-dev/server@0.5.0Testnet only. A passing result means the service behaved as the spec requires for the published checks; it is not a security audit.
Verified before publishing
- 120 offline tests pass;
npm run typecheckis clean. npm run verify:clean-install: the three tarballs install into an empty project,wasit-mcpcompletes an MCP handshake, and reports 0.5.0.- Full x402 suite against Wasit's own v2 fixture: 7/7.
Still planned
On-chain verification for X402-06, a signature-only corruption for X402-07, and exit code 2 from wasit wallet status --role on an unreadable key are now planned for 0.6.0. See the changelog.
Full changelog: v0.4.0...v0.5.0
0.4.0
[0.4.0] — 2026-09-17
d3-mcp-session.mp4
All three packages, versioned together as usual. Two correctness fixes, both in
how a run reports what it actually established. Neither adds a check; both
change what existing checks report in situations where the old answer was
wrong.
Fixed — a charge-mode payment client no longer breaks every later
channel-mode check. Mppx.create replaces globalThis.fetch with a wrapper
bound to one payment method unless polyfill: false is passed. Once MPP-01
created a charge client, every later channel-mode 402 in the same process was
refused by that wrapper with "No method found for challenges: stellar.channel.
Available: stellar.charge", reported as a defect in a target that was in fact
conformant. The CLI hid this because each invocation is a fresh process; the
MCP server did not, because one process answers many tool calls in a row. Only
the first payment-mode check in a process produced a trustworthy verdict.
fetchTarget now resolves the underlying fetch through mppx's own
Symbol.for("mppx.fetch.wrapper") tag, so a check observes the target rather
than whatever a previous check installed on the global. packages/core's
charge client also passes polyfill: false, which is necessary but not
sufficient on its own: one stale build or one new caller brings the leak back.
Regression test: test/unit/fetch-target-isolation.test.ts.
Changed — a failed setup is reported as no verdict, not as a defect.
MPP-11, MPP-12 and MPP-14 each need one correctly advancing commitment
accepted before they can probe anything. That submission used to be attempted
once, and a refusal was reported as FAIL. Two very different worlds produce that
refusal identically: a target that wrongly rejects valid vouchers, and a channel
whose cumulative moved between the challenge being issued and the credential
being submitted, because something else is paying through it. The submission is
now retried up to three times, each against a freshly issued challenge, so
ordinary contention clears on its own. When every attempt is refused the result
is ERROR with the new error kind setup, carrying all three refusals verbatim,
and the run exits 2 (no-verdict) instead of 1 (non-conformant).
Added — CheckSetupError and the setup error kind, exported from
@wasit-dev/core. errorKind in --json and in the MCP server's
structuredContent can now carry "setup" alongside unreachable,
configuration and harness. A consumer that switches exhaustively on that
field needs a branch for it.
Added — scripts/fixtures.sh, which starts, stops and reports on the four
local fixture servers so a full local run needs one terminal instead of four.
Also adds packages/core/test/fixtures/mpp-channel-refusing-server.ts, a target
that issues valid challenges and refuses every credential, which reproduces a
setup failure on demand rather than by racing two runs.
@wasit-dev/server@0.3.0
Fixes the version the MCP server reports in its initialize handshake. No tool, resource or result shape changed.
Install
npx -y @wasit-dev/server@0.3.0What changed in this package
- The server announced
0.1.0in the MCPinitializehandshake through two releases, because the version string was hardcoded. It now reads its own package manifest at runtime, the same waywasit --versiondoes. Until now a client could file a bug against a version that was never published. - Bumped to
@wasit-dev/core@^0.3.0, so the MCP server and the CLI always run the same check code rather than the server quietly trailing a version behind.
Compatibility
Node.js >=24, stdio transport. The four tools (wasit_x402_test, wasit_mpp_charge_test, wasit_mpp_channel_test, wasit_mpp_channel_test_with_close) and the wasit://checks resource are unchanged. structuredContent is unchanged since 0.2.0. wasit_mpp_channel_test_with_close is still registered only when the server is started with WASIT_ALLOW_DESTRUCTIVE=1 — an agent cannot call what it cannot see.
Known issues
Same nested Stellar SDK advisories as @wasit-dev/core — upstream peer ranges, no downstream fix. See SECURITY.md.
Links
@wasit-dev/core@0.3.0
Adds a wallet layer for setting up testnet payer keys, and makes every failure in it speak the same error taxonomy the rest of core already used. No breaking change to the check runners or toStructuredRun().
Install
npm install @wasit-dev/core@0.3.0What changed in this package
- New wallet helpers, exported by name: derive a public key from a secret, read XLM and USDC balances from Horizon, fund via Friendbot, open a USDC trustline, generate a commitment seed.
TESTNET_HORIZON_URLis deliberately not exported — it is an implementation detail, not a supported knob. - Wallet failures now throw
ConfigurationErrororTargetUnreachableErrorfromerrors.tsinstead of leaking raw Stellar SDK errors, with Horizon's result codes (op_underfunded,op_no_trust) folded into the message. Callers rendererror.messageand never inspect an SDK error type — which is what stopped the CLI and the dashboard reporting the same failure two different ways. - Horizon and Friendbot requests are bounded by a 15s timeout. Previously a stalled request had no way back.
- A malformed secret key — a truncated paste, a
G…public key in a secret's slot, a hex commitment seed — is rejected by name with a readable message, and the message never echoes the rejected value. index.tsuses explicit named exports rather thanexport *, so what is public API is decided here rather than by whatever a module happens to export.- First automated unit tests, covering the check pipeline, the error taxonomy, network handling and the wallet layer.
Compatibility
Node.js >=24, ESM only. Additive — nothing removed, no signature changed. Check functions still never reject; the new wallet functions do throw, which is the one contract that differs.
Known issues
A clean install resolves two copies of @stellar/stellar-sdk, and the nested older copy carries open axios advisories with no downstream fix available. The cause is the peer ranges declared by @stellar/mpp@0.7.1, not Wasit's own dependencies. Reported upstream; nothing in this release changes that chain. See SECURITY.md.
Links
@wasit-dev/cli@0.3.0
Running wasit with no arguments in a terminal now opens an interactive dashboard. Adds wasit wallet for setting up the testnet keys the check runners read from .env.
Install
npm install -g @wasit-dev/cli@0.3.0
wasitWhat changed in this package
- Interactive dashboard. A menu over the three check runners, a catalogue browser and a wallet screen, driven by arrow keys. A run shows a live elapsed timer and per-check progress, ends with total and average timing, and saves to
wasit-<protocol>-<timestamp>.jsonwiths— the same shape--jsonprints. A misconfigured run gets its own error screen rather than a red line under an otherwise-empty checklist. Piped or in CI,wasitstill prints help, so nothing that scripts it today changes behaviour. wasit wallet—status,create,fund. There is deliberately no--networkflag: Friendbot, the printed USDC issuer and the whole idea of a disposable generated key only make sense on testnet. XLM funding via Friendbot is fully automatic. USDC is not, and the command says so rather than pretending otherwise — the trustline is opened for you, but a balance needs one visit to Circle's faucet or a configuredWASIT_USDC_DISTRIBUTOR_SECRET, because no scriptable testnet USDC faucet exists for Stellar.wasit wallet status --role mpp-channelis now rejected up front.COMMITMENT_SECRET_HEXis a raw hex seed with no on-chain account, which the help text already said; the command accepted the role anyway and then crashed deriving an address for it.- A malformed key in
.envno longer kills the process. In the dashboard it used to escape as an unhandled rejection and terminate the process from under Ink's renderer, leaving the wallet screen frozen on its spinner. All paths now name the offending variable and exit 2, and never echo the rejected value. .envwritten from the dashboard is0600and atomic (temp file plus rename), so an interrupted write cannot truncate a file holding Stellar secrets, and an existing world-readable.envis tightened on the next write.- Friendbot's "this account already exists" response is no longer reported as "Funded: 10,000 XLM."
qunmounts Ink instead of callingprocess.exit, so the terminal gets its cursor back. Ctrl+C still exits 130 immediately, by design.
Compatibility
Node.js >=24. Requires @wasit-dev/core@^0.3.0, installed automatically. Exit codes are unchanged: 0 every check that ran conformed, 1 at least one conformance failure, 2 at least one check produced no verdict. A run with both still exits 1 — a real finding outranks a missing one.
Known issues
Same nested Stellar SDK advisories as @wasit-dev/core — upstream peer ranges, no downstream fix. See SECURITY.md.
Links
@wasit-dev/server@0.2.0
structuredContent is now core's toStructuredRun() verbatim rather than an object assembled here, so the CLI and an agent can no longer describe the same run in two different shapes. If you parse structuredContent, re-read it before upgrading: the field set is close, but it is no longer hand-built in this package.
Requires @wasit-dev/core@^0.2.0. The four tools, the destructive opt-in, and the wasit://checks resource are unchanged.
Full detail in CHANGELOG.md.
@wasit-dev/core@0.2.0
Adds the check catalogue and the structured-run reshape that the CLI's --json and the MCP server's structuredContent both consume, so no front end assembles its own result shape any more. CHECK_CATALOGUE and PROTOCOL_IDS are new exports, alongside toStructuredRun() and its StructuredRun / StructuredCheckResult types.
Runtime dependencies now match what the source actually imports: @x402/core, @x402/express, commander and dotenv are gone from dependencies. None were imported by packages/core/src — the first two are used only by the bundled fixtures, which are not published. express moves in as a devDependency; the x402 fixture imports it directly but it had never been declared anywhere, resolving only because npm hoisted it from a deeper transitive dependency.
Full detail in CHANGELOG.md.
@wasit-dev/cli@0.2.0
New wasit checks subcommand lists every check the tool can run, grouped by protocol and annotated with the subcommand that runs it, plus flags for negative, destructive and funds-spending checks. --protocol narrows it to one suite.
New --json on every runner — test, mpp-charge, mpp-channel and checks. A run emits outcome, per-status counts, and a results array carrying each check's id, name, status, detail, destructive flag, and errorKind when a check could not run. Human-readable output moves to stderr under --json, so stdout stays parseable and pipes cleanly into CI. --help also gains worked examples.
Dependencies cleaned up: @stellar/mpp, @stellar/stellar-sdk and mppx are dropped — the CLI never imported them — and the commander and dotenv ranges now name the versions the code is actually built and verified against rather than two majors behind.
Full detail in CHANGELOG.md.
@wasit-dev/server 0.1.2
README only — no code changes. @wasit-dev/server now ships a README.md (previously absent, so its npm listing page was empty). Also picks up a fresh build, so the published dist/ matches already-merged source changes.