agentic-wallet-mcp 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 MyEG Services Berhad
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -3,7 +3,8 @@
3
3
  An agent-facing **MCP server** that gives an AI agent a Zetrix wallet: it **proves identity**
4
4
  (x401), **pays** (x402), and **obtains verifiable credentials** (via the MBI issuer) — by
5
5
  orchestrating existing SDKs/MCPs. It is a thin composer: all heavy crypto and payment logic
6
- lives in the imported libraries.
6
+ lives in the imported libraries. No private key ever exists in this process — everything signs
7
+ through Wallet BE's HSM.
7
8
 
8
9
  - **x401** proof → [`x401-zetrix-client`](https://www.npmjs.com/package/x401-zetrix-client) (npm)
9
10
  - **x402** payment → [`x402-zetrix-client`](https://www.npmjs.com/package/x402-zetrix-client) (npm)
@@ -12,8 +13,6 @@ lives in the imported libraries.
12
13
  - **holder key custody + signing** → Wallet BE softHSM (`/wallet/hsm/*`)
13
14
  - **VC issuance** → MBI RS (`/v1/vc/pay/*`)
14
15
 
15
- See [`docs/INTEGRATION_GUIDE.md`](docs/INTEGRATION_GUIDE.md) for the full config reference, tool
16
- contracts, a live-verified usage flow, and a troubleshooting table for real-infra errors.
17
16
  [`docs/CONTRACT_RECONCILIATION.md`](docs/CONTRACT_RECONCILIATION.md) has the authoritative
18
17
  endpoint/contract map, [`docs/DEVELOPMENT_CHECKLIST.md`](docs/DEVELOPMENT_CHECKLIST.md) has status,
19
18
  and [`docs/USAGE_FLOW.md`](docs/USAGE_FLOW.md) has a full end-to-end prompt script (onboarding →
@@ -21,13 +20,13 @@ VC issuance → identity proof → pay-per-use).
21
20
 
22
21
  ## Tools
23
22
 
24
- | Tool | Does | Input |
25
- |---|---|---|
26
- | `wallet_status` | Report holder DID/address/network + client-supplied held VCs | `{ heldCredentials? }` |
27
- | `prove_identity` | Answer an x401 `PROOF-REQUEST` → return the `PROOF-RESPONSE` header to replay | `{ proofRequest, vc, revealAttribute?, issuerKeys? }` |
28
- | `pay_and_fetch` | Fetch a URL, auto-pay with x402 (self-pay via Wallet BE) on `402` | `{ url, method?, headers?, body? }` |
29
- | `subscribe_and_issue` | Build the signed payload → pay x402 → MBI issues → return the VC | `{ templateId, attributes, expirationDate? }` |
30
- | `create_holder_account` | Onboarding: create a new HSM account if `HOLDER_ADDRESS` isn't provisioned yet | `{ password, label?, purpose? }` |
23
+ | Tool | Does | Input | Output (shape) |
24
+ |---|---|---|---|
25
+ | `wallet_status` | Report holder DID/address/network + client-supplied held VCs | `{ heldCredentials? }` | `{ holderDid, zetrixAddress, network, credentials }` |
26
+ | `prove_identity` | Answer an x401 `PROOF-REQUEST` → return the `PROOF-RESPONSE` header to replay | `{ proofRequest, vc, revealAttribute?, issuerKeys? }` | `{ proofResponseHeader, verified, presentationId }` |
27
+ | `pay_and_fetch` | Fetch a URL, auto-pay with x402 (self-pay via Wallet BE) on `402` | `{ url, method?, headers?, body? }` | `{ status, body, paymentMade, amountPaid, amountPaidHuman, asset }` |
28
+ | `subscribe_and_issue` | Build the signed payload → pay x402 → MBI issues → return the VC | `{ templateId, attributes, expirationDate? }` | `{ issued, vcId, vc, txHash }` |
29
+ | `create_holder_account` | Onboarding: manually mint an additional/replacement HSM account (the MCP already auto-creates one at startup if `ZETRIX_ADDRESS` is omitted see Environment below) | `{ password, label?, purpose? }` | `{ zetrixAddress, holderDid, publicKeyHex, message }` |
31
30
 
32
31
  > The wallet **never persists VCs** — the client holds them and passes them in (e.g. `vc` on
33
32
  > `prove_identity`, `heldCredentials` on `wallet_status`). All Ed25519 signing goes through
@@ -35,8 +34,14 @@ VC issuance → identity proof → pay-per-use).
35
34
 
36
35
  > `create_holder_account` mints a **brand-new** keypair — Wallet BE's `/account/create` has no
37
36
  > way to provision a pre-chosen address. It returns the new `zetrixAddress`/`holderDid` for you
38
- > to paste into `HOLDER_ADDRESS`/`HOLDER_DID`/`HSM_PASSWORD` yourself; the tool never writes your
39
- > MCP config or restarts the server for you.
37
+ > to paste into `ZETRIX_ADDRESS`/`HSM_PASSWORD` yourself (`HOLDER_DID` is optional see
38
+ > Environment below); the tool never writes your MCP config or restarts the server for you.
39
+
40
+ > `revealAttribute` on `prove_identity` is optional and usually should stay that way. Omitted,
41
+ > it's derived automatically from the challenge's DCQL `credential_requirements` — each claim path
42
+ > is resolved against the presented VC's `credentialSubject` (e.g. a DCQL leaf name `agentName`
43
+ > resolves to the VC's actual nested path `agentIdentityCredential.agentName`). Only pass it
44
+ > explicitly to reveal a narrower or different set of claims than the challenge asked for.
40
45
 
41
46
  > `prove_identity` no longer takes a `bbsPublicKey` input at all. The OID4VP verifier
42
47
  > (`openid4vp-verifier-be`) checks each VC's own issuer-signed proof(s) against the
@@ -45,28 +50,35 @@ VC issuance → identity proof → pay-per-use).
45
50
  > resolves them itself: it reads the VC's `issuer` DID and each `proof[].verificationMethod`,
46
51
  > resolves the issuer's DID document via the Zetrix ZID resolver, and matches the BBS+
47
52
  > (`publicKeyMultibase`) and Ed25519 (`publicKeyHex`) verification methods referenced by the VC's
48
- > own proofs. See `docs/CONTRACT_RECONCILIATION.md §8` for the full trace through the verifier's
53
+ > own proofs. `issuerKeys` is still accepted as a manual escape hatch if the resolver is ever
54
+ > unreachable. See `docs/CONTRACT_RECONCILIATION.md §8` for the full trace through the verifier's
49
55
  > source that led to this.
50
56
 
51
- ## Install & build
57
+ ## Install
58
+
59
+ Run it directly via `npx` — no install step needed (see "Configuring in Claude Desktop / Claude
60
+ Code" below for wiring it into an MCP client):
52
61
 
53
62
  ```bash
54
- npm install
55
- npm test # 85 tests
56
- npm run build # tsc + esbuild → dist/server-bundle.cjs (the bin)
63
+ npx agentic-wallet-mcp
57
64
  ```
58
65
 
59
- Node 18 (built-in `fetch`). `x401-zetrix-client` is consumed as a normal npm dependency; the
60
- `bin` runs the **esbuild bundle** (`dist/server-bundle.cjs`), which inlines it.
66
+ Or install [`agentic-wallet-mcp`](https://www.npmjs.com/package/agentic-wallet-mcp) directly:
67
+
68
+ ```bash
69
+ npm i agentic-wallet-mcp
70
+ ```
71
+
72
+ Node ≥ 18 required (built-in `fetch`).
61
73
 
62
74
  ## Environment
63
75
 
64
76
  | Variable | Required | Description |
65
77
  |---|---|---|
66
- | `ZETRIX_NETWORK` | yes | `zetrix:testnet` or `zetrix:mainnet` — also selects the default `WALLET_BE_URL`/`MBI_BASE_URL`/`OID4VP_BASE_URL` below |
67
- | `HOLDER_ADDRESS` | yes | Holder Zetrix address (the HSM account) |
68
- | `HOLDER_DID` | yes | Holder DID |
69
- | `HSM_PASSWORD` | no* | HSM password (may instead be supplied per tool-call) |
78
+ | `ZETRIX_NETWORK` | yes | `zetrix:testnet` or `zetrix:mainnet` — also selects the default `WALLET_BE_URL`/`MBI_BASE_URL`/`OID4VP_BASE_URL`/`ZID_RESOLVER_BASE_URL` below |
79
+ | `HSM_PASSWORD` | yes* | HSM password |
80
+ | `ZETRIX_ADDRESS` | no | Holder Zetrix address (the HSM account). Omit on first run — see "Onboarding" below |
81
+ | `HOLDER_DID` | no | Holder DID. Omit and the MCP derives it automatically — see "Onboarding" below |
70
82
  | `WALLET_BE_URL` | no | Wallet BE base URL override (HSM `/wallet/hsm/sign-blob`) — auto-derived from `ZETRIX_NETWORK` when not set |
71
83
  | `MBI_BASE_URL` | no | MBI RS base URL override (`/v1/vc/pay/apply`) — auto-derived from `ZETRIX_NETWORK` when not set |
72
84
  | `OID4VP_BASE_URL` | no | OID4VP verifier base URL override — auto-derived from `ZETRIX_NETWORK` by the x401 SDK when not set (BT-2368) |
@@ -76,6 +88,27 @@ Node ≥ 18 (built-in `fetch`). `x401-zetrix-client` is consumed as a normal npm
76
88
 
77
89
  \* sensitive — never logged.
78
90
 
91
+ ### Onboarding: two ways to set up your holder identity
92
+
93
+ `ZETRIX_ADDRESS` and `HOLDER_DID` are both optional — the MCP resolves your holder identity at
94
+ startup, in one of two ways:
95
+
96
+ 1. **First-time user — only `HSM_PASSWORD` set.** The MCP creates a brand-new HSM account on
97
+ Wallet BE (`POST /wallet/hsm/account/create`) and derives the DID from the returned public
98
+ key. It logs the new `ZETRIX_ADDRESS` (and `HOLDER_DID`) to stderr on startup — copy it into
99
+ your MCP config for next time, since nothing is persisted to disk between runs (env vars only
100
+ load once, at process start).
101
+ 2. **Existing user — `ZETRIX_ADDRESS` + `HSM_PASSWORD` set, `HOLDER_DID` optional.** The MCP
102
+ always self-signs the address via the existing `POST /wallet/hsm/sign-message` call and
103
+ derives the DID from the `publicKey` the response carries — no separate lookup endpoint
104
+ needed. A supplied `HOLDER_DID` is never trusted blindly: it's compared against this derived
105
+ value, and if they don't match, the derived (correct) one wins — the mismatch is logged to
106
+ stderr so you know to fix your config. Omit `HOLDER_DID` entirely and the derived value is
107
+ just used directly.
108
+
109
+ Either way, `wallet_status` always reports the resolved `zetrixAddress`/`holderDid` for the
110
+ running session, so you can confirm what the MCP resolved to at any time.
111
+
79
112
  \*\* **strongly recommended before pointing this wallet at mainnet/real funds.** `pay_and_fetch`
80
113
  and `subscribe_and_issue` auto-pay whatever `maxAmountRequired` a remote server's 402 challenge
81
114
  demands, with no built-in ceiling — a prompt-injected or misled agent calling either tool against
@@ -84,18 +117,20 @@ balance. `MAX_PAYMENT_AMOUNT` is a hard, code-enforced cap that holds regardless
84
117
  calling agent decides. Once set, it becomes an allowlist: an asset with no entry and no `"*"`
85
118
  fallback is **denied**, not passed through uncapped.
86
119
 
87
- You don't need to look up or fill in `WALLET_BE_URL`/`MBI_BASE_URL`/`OID4VP_BASE_URL` yourself —
88
- just pick `zetrix:testnet` or `zetrix:mainnet` for `ZETRIX_NETWORK` and the MCP (and the x401 SDK
89
- it wires up) uses the built-in default for that network:
120
+ You don't need to look up or fill in `WALLET_BE_URL`/`MBI_BASE_URL`/`OID4VP_BASE_URL`/
121
+ `ZID_RESOLVER_BASE_URL` yourself — just pick `zetrix:testnet` or `zetrix:mainnet` for
122
+ `ZETRIX_NETWORK` and the MCP (and the x401 SDK it wires up) uses the built-in default for that
123
+ network:
90
124
 
91
- | Network | `WALLET_BE_URL` default | `MBI_BASE_URL` default | `OID4VP_BASE_URL` default |
92
- |---|---|---|---|
93
- | `zetrix:testnet` | `https://wallet-api.myegdev.com/server` | `https://mbi-vc.myegdev.com` | `https://zid-oid4vp-sandbox.zetrix.com/api` |
94
- | `zetrix:mainnet` | `https://wallet-api.zetrix.com/server` | `https://mbi-vc.zetrix.com` | `https://zid-oid4vp.zetrix.com/api` |
125
+ | Network | `WALLET_BE_URL` default | `MBI_BASE_URL` default | `OID4VP_BASE_URL` default | `ZID_RESOLVER_BASE_URL` default |
126
+ |---|---|---|---|---|
127
+ | `zetrix:testnet` | `https://wallet-api.myegdev.com/server` | `https://mbi-vc.myegdev.com` | `https://zid-oid4vp-sandbox.zetrix.com/api` | `https://zid-resolver-sandbox.zetrix.com` |
128
+ | `zetrix:mainnet` | `https://wallet-api.zetrix.com/server` | `https://mbi-vc.zetrix.com` | `https://zid-oid4vp.zetrix.com/api` | `https://zid-resolver.zetrix.com` |
95
129
 
96
- Only set `WALLET_BE_URL`/`MBI_BASE_URL`/`OID4VP_BASE_URL` explicitly if you run your own instance
97
- of that service instead of the default one — an explicit value always wins over the network
98
- default.
130
+ Only set any of the four explicitly if you run your own instance of that service instead of the
131
+ default one — an explicit value always wins over the network default. The `*.myegdev.com`
132
+ (testnet) hosts are internal — unreachable without VPN; see "Network reachability &
133
+ troubleshooting" below if any of them time out.
99
134
 
100
135
  That's the complete list — no VC-MCP subprocess, no BaaS gateway key, no manually-configured
101
136
  BBS+ key to set up (BT-2357 replaced that whole path — see `docs/CONTRACT_RECONCILIATION.md §8`).
@@ -116,24 +151,32 @@ the `<...>` placeholders (don't commit a filled copy; `mcp.local.json` is gitign
116
151
  {
117
152
  "mcpServers": {
118
153
  "agentic-wallet": {
119
- "command": "node",
120
- "args": ["/absolute/path/to/zetrix-agentic-wallet/dist/server-bundle.cjs"],
154
+ "command": "npx",
155
+ "args": ["-y", "agentic-wallet-mcp"],
121
156
  "env": {
122
157
  "ZETRIX_NETWORK": "zetrix:testnet",
123
- "HOLDER_ADDRESS": "ZTX3...",
124
- "HOLDER_DID": "did:zid:...",
125
- "HSM_PASSWORD": "your-hsm-password"
158
+ "HSM_PASSWORD": "your-hsm-password",
159
+ "ZETRIX_ADDRESS": "ZTX3...",
160
+ "HOLDER_DID": "did:zid:..."
126
161
  }
127
162
  }
128
163
  }
129
164
  }
130
165
  ```
131
166
 
167
+ > First run, no account yet? Omit `ZETRIX_ADDRESS` (and `HOLDER_DID`) entirely — the MCP creates
168
+ > one for you at startup and logs it to stderr; copy it back into `env` for next time. See
169
+ > "Onboarding" under Environment above.
170
+
171
+ > Working on this repo locally instead of the published package? Point `command`/`args` at the
172
+ > local build directly: `"command": "node"`, `"args": ["/absolute/path/to/zetrix-agentic-wallet/dist/server-bundle.cjs"]`.
173
+
132
174
  > Prefer environment/secret managers over inline secrets for `HSM_PASSWORD` in production.
133
175
  >
134
- > Only add `WALLET_BE_URL` / `MBI_BASE_URL` / `OID4VP_BASE_URL` to the `env` block if you run your
135
- > own instance of that service — otherwise leave them out and the MCP (and the x401 SDK) use the
136
- > default for whichever `ZETRIX_NETWORK` you picked (see the table above).
176
+ > Only add `WALLET_BE_URL` / `MBI_BASE_URL` / `OID4VP_BASE_URL` / `ZID_RESOLVER_BASE_URL` to the
177
+ > `env` block if you run your own instance of that service — otherwise leave them out and the MCP
178
+ > (and the x401 SDK) use the default for whichever `ZETRIX_NETWORK` you picked (see the table
179
+ > above).
137
180
 
138
181
  ## Example prompts
139
182
 
@@ -145,21 +188,61 @@ the `<...>` placeholders (don't commit a filled copy; `mcp.local.json` is gitign
145
188
 
146
189
  For the full ordered script (onboarding → check → issue → prove → pay), see [`docs/USAGE_FLOW.md`](docs/USAGE_FLOW.md).
147
190
 
148
- ## Status
149
-
150
- Milestone 2 (WBS 4.1–4.5) is code-complete **and live-verified end-to-end against the real
151
- sandbox** — not just unit-tested. `subscribe_and_issue` issued a real VC and settled a real
152
- payment; `prove_identity` got a real OID4VP verifier to return `VERIFIED`; `pay_and_fetch`
153
- completed a real x402 payment against `ms-zetrix` (via `ms-public-proxy`) and got real chain data
154
- back. This closed out the remaining G-checks from `docs/CONTRACT_RECONCILIATION.md §7`: the
155
- OID4VP `ResponseWrapper`/wallet-auth submit contract (G6), the MBI `data` hex form (G3), and the
156
- DCQL→`revealAttribute` mapping (G4, now VC-aware resolves DCQL claim paths against the
157
- presented VC's actual nested structure). See
158
- [`docs/INTEGRATION_GUIDE.md`](docs/INTEGRATION_GUIDE.md)its "Live verification record" section
159
- has the exact evidence (VC ids, tx hashes, presentation ids), and its "Network reachability &
160
- troubleshooting" section covers every real-infra error hit along the way and its fix.
161
-
162
- One known item remains open, tracked as a follow-up (not a blocker): `verifiedClaims` came back
163
- empty on a `VERIFIED` proof (likely a verifier echo behavior, not a wallet bug). Also unconfirmed:
164
- Wallet BE's `/account/create` `publicKeyHex` format (raw vs Zetrix `b001…`-encoded
165
- `deriveHolderDid` handles both, but only one is real).
191
+ ## End-to-end usage flow
192
+
193
+ Full narrative version with example prompts: [`docs/USAGE_FLOW.md`](docs/USAGE_FLOW.md).
194
+
195
+ **Step 0 — onboarding (once).** Only if `ZETRIX_ADDRESS` isn't set yet: the MCP creates an HSM
196
+ account automatically at startup from `HSM_PASSWORD` alone (see "Onboarding" under Environment
197
+ above) copy the logged `zetrixAddress` into your MCP config for next time. Alternatively, call
198
+ `create_holder_account { password }` manually and paste the returned `zetrixAddress` in yourself;
199
+ either way, restart the server afterward (env vars load once, at process start).
200
+
201
+ **Phase 1 — `wallet_status` — pre-check.** Pass any VCs the caller already holds via
202
+ `heldCredentials`; the response tells you whether the agent-identity credential you need is
203
+ already there. Skip to Phase 3 if so.
204
+
205
+ **Phase 2 `subscribe_and_issue` VC issuance (✅ live-verified).**
206
+ `{ templateId, attributes }` holder-signs the payload via Wallet BE MBI's x402 `402`
207
+ self-pay MBI settles **and** issues the VC in one call. **Hold onto the returned `vc`** it's
208
+ what you pass into every future `prove_identity` call.
209
+
210
+ **Phase 3 — `prove_identity` — x401 identity proof (✅ live-verified, `VERIFIED`).**
211
+ `{ proofRequest, vc }` (omit `revealAttribute`/`issuerKeys` — both now resolve automatically).
212
+ Internally: fetch the OID4VP presentation definition → derive the BBS+ selective-disclosure VP
213
+ via MBI (`/vp/ext/create` + `/vp/ext/submit`, `includeVp: true`) → resolve the issuer's
214
+ verification keys via the ZID resolver → submit to the verifier with wallet-auth headers →
215
+ package the signed result as a `PROOF-RESPONSE`. Replaying that header back to the original
216
+ resource server happens outside this MCP, in whatever drove the conversation. See "The
217
+ `PROOF-REQUEST` header" section in [`docs/USAGE_FLOW.md`](docs/USAGE_FLOW.md) for the exact wire
218
+ structure and field semantics.
219
+
220
+ **Phase 4 — `pay_and_fetch` — pay-per-use (✅ live-verified).**
221
+ `{ url, method?, headers?, body? }` → fetch → on `402`, self-pay → retry with `X-PAYMENT`.
222
+ Independent of Phases 2/3 — no VC or identity proof involved, just a fresh payment per call.
223
+
224
+ ## Network reachability & troubleshooting
225
+
226
+ Several dependencies sit behind a corporate VPN or a CDN edge. When something that worked before
227
+ suddenly times out or 403s, check this table before assuming a code regression — in every case
228
+ observed so far, the code was correct and the network/edge state had changed.
229
+
230
+ | Symptom | Cause | Fix / status |
231
+ |---|---|---|
232
+ | `Wallet BE /wallet/hsm/sign-blob request failed <- fetch failed <- UND_ERR_CONNECT_TIMEOUT` (or same for `mbi-vc.myegdev.com`) | `*.myegdev.com` hosts are internal — unreachable without VPN | Connect/reconnect your VPN, then retry. Verify first: `curl -I https://wallet-api.myegdev.com/server` |
233
+ | `VP derivation failed <- ZID resolver HTTP 403 ... cf-mitigated: challenge` | ZID resolver (`zid-resolver-sandbox.zetrix.com`) was behind a Cloudflare **managed challenge** that blocked plain server-to-server `fetch` | **Fixed 2026-07-17** — DevOps allowlisted server-to-server access; the resolver now returns `200` directly (verified: `curl https://zid-resolver-sandbox.zetrix.com/1.0/identifiers/<did>` → real DID document, keys byte-identical to a browser-fetched copy). If it ever regresses, `prove_identity`'s `issuerKeys` input is the fallback — fetch the DID document via a real browser (it clears the JS challenge) and pass its `verificationMethod` entries' `publicKeyMultibase` (BBS+) / `publicKeyHex` (Ed25519) directly. |
234
+ | `OID4VP backend returned a malformed presentation definition` | Historical SDK bug: the live sandbox's `GET /v1/presentation/{id}` response has no `expires_at` field, but the SDK guard required one | **Fixed** in `x401-zetrix-client` — `expiresAt` is now optional on `PresentationDefinition`. If you see this, you're on a stale cached `npx` install — clear it (`npx clear-npx-cache` or bump the version) to pick up the current published `agentic-wallet-mcp`. |
235
+ | `SUBMIT_FAILED: OID4VP backend returned 401 ... Missing X-Wallet-Public-Key header` | Historical SDK gap: `POST /v1/presentation/submit` requires wallet-auth headers (`X-Wallet-Public-Key` / `X-Wallet-Signed-Data`, holder signs their own address) that the SDK didn't send | **Fixed** — `X401Wallet` now accepts an injected `submitAuth` provider and the wallet wires it automatically; nothing to configure. |
236
+ | `MBI /vp/ext/submit did not return vp` | MBI's `includeVp` opt-in (BT-2357) not deployed on the target instance | Confirmed deployed on the sandbox (`mbi-vc.myegdev.com`). If you see this against a different MBI instance, that instance needs the same rollout. |
237
+
238
+ ## Security notes
239
+
240
+ - **No plaintext private key ever exists in this process.** All Ed25519 signing routes through
241
+ Wallet BE's HSM (`/wallet/hsm/*`); `walletCfg.privateKey` is always `''`.
242
+ - **`MAX_PAYMENT_AMOUNT` is the real control against unbounded auto-spend** — see Environment
243
+ above. Without it, `pay_and_fetch`/`subscribe_and_issue` will pay whatever a server's `402`
244
+ challenge demands, up to the HSM account's balance. This was raised in code review (finding
245
+ AWL-M01) specifically because an agent-side "confirm before paying" step is not a real security
246
+ boundary: the same prompt injection or bad instruction that drove the call in the first place
247
+ could just as easily drive the confirmation.
248
+