@muhaven/mcp 0.2.8 → 0.3.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/CHANGELOG.md +120 -0
- package/dist/broker.cjs +499 -6
- package/dist/broker.js +499 -6
- package/dist/index.cjs +832 -15
- package/dist/index.d.cts +301 -14
- package/dist/index.d.ts +301 -14
- package/dist/index.js +833 -16
- package/manifest.json +1 -1
- package/package.json +2 -1
package/dist/index.d.cts
CHANGED
|
@@ -9,9 +9,46 @@ import { z } from 'zod';
|
|
|
9
9
|
* (Windows). Each request is a single JSON object; each response is a
|
|
10
10
|
* single JSON object. No request pipelining, no streaming.
|
|
11
11
|
*
|
|
12
|
-
* **Protocol version 0.
|
|
13
|
-
*
|
|
14
|
-
*
|
|
12
|
+
* **Protocol version 0.5.0** — additive bump from 0.4.0 for Wave 5
|
|
13
|
+
* Option D Commit 3. Adds the PermissionValidator install lifecycle so
|
|
14
|
+
* the broker can compose a MODE.ENABLE UserOp on the first Path D buy
|
|
15
|
+
* after a freshly-minted Scoped session (installs the validator on-chain
|
|
16
|
+
* atomically with the inner call). Three additive surfaces:
|
|
17
|
+
* - `current_nonce` — read-side IPC verb. Broker does `eth_call` against
|
|
18
|
+
* the kernel's `currentNonce()` view via its configured chain RPC.
|
|
19
|
+
* MCP server compares the returned uint32 with the mirror's stored
|
|
20
|
+
* `validatorNonce`; mismatch surfaces as fallback `enable_sig_stale`
|
|
21
|
+
* (kernel nonce drifted between mint + first buy → stored enableSig
|
|
22
|
+
* is over a stale typed-data digest).
|
|
23
|
+
* - `notify_userop_landed` — write-side IPC verb. MCP server invokes
|
|
24
|
+
* this after receipt-arrival to hand the broker the on-chain
|
|
25
|
+
* `PermissionInstalled` receipt details. Broker posts the receipt
|
|
26
|
+
* to the backend's `validator-enabled` route over its configured
|
|
27
|
+
* HTTPS egress (R2 design call: broker carries
|
|
28
|
+
* `BROKER_CALLBACK_SERVICE_SECRET`; MCP carries the bundler + RPC
|
|
29
|
+
* URLs only). Exponential backoff retry 5s/15s/60s/5m, max 1h.
|
|
30
|
+
* The backend chain indexer is the authoritative source-of-truth
|
|
31
|
+
* (subscribes to `PermissionInstalled` events independently); this
|
|
32
|
+
* callback is the fast-path optimization.
|
|
33
|
+
* - `PolicySnapshotWire` gains optional `enableData`, `enableSig`, and
|
|
34
|
+
* `validatorNonce`. Backwards-compatible: snapshots without these
|
|
35
|
+
* are treated as already-enabled validator (the legacy posture
|
|
36
|
+
* before C3). New snapshots from C2+ frontends carry all three;
|
|
37
|
+
* broker plumbs them through to the MCP for the MODE.ENABLE wrap.
|
|
38
|
+
*
|
|
39
|
+
* **Threat-model relaxation (R2 design call)**: in C3 the broker gains
|
|
40
|
+
* limited outbound egress (chain RPC `eth_call` + HTTPS to the
|
|
41
|
+
* backend's `validator-enabled` route). The original zero-egress
|
|
42
|
+
* invariant (lethal-trifecta split) is narrowed: the broker still
|
|
43
|
+
* holds NO read-side secrets the network peer cares about (the chain
|
|
44
|
+
* RPC is public; the backend callback is gated on a shared secret the
|
|
45
|
+
* broker holds + asserts in-bound). Documented in
|
|
46
|
+
* `development/DEV_WAVE_5/DEV_LOG.md` C3 entry as a load-bearing
|
|
47
|
+
* decision the operator made at handoff.
|
|
48
|
+
*
|
|
49
|
+
* **Protocol version 0.4.0** (history) — Wave 5 Path D Slice 1. Adds
|
|
50
|
+
* the policy-snapshot subsystem so the broker can enforce scope +
|
|
51
|
+
* per-op spend cap BEFORE signing a UserOp:
|
|
15
52
|
* - `sign_userop` — like `sign_hash` but carries the structured inner
|
|
16
53
|
* call (target + callData) so the broker validates against the active
|
|
17
54
|
* policy snapshot before delegating to the signer. The MCP server
|
|
@@ -75,7 +112,7 @@ import { z } from 'zod';
|
|
|
75
112
|
* - Requests are size-capped (`maxRequestBytes`) — a malformed peer
|
|
76
113
|
* cannot exhaust broker memory by sending an unbounded JSON blob.
|
|
77
114
|
*/
|
|
78
|
-
declare const BROKER_PROTOCOL_VERSION = "0.
|
|
115
|
+
declare const BROKER_PROTOCOL_VERSION = "0.5.0";
|
|
79
116
|
interface BrokerHelloRequest {
|
|
80
117
|
readonly type: 'hello';
|
|
81
118
|
}
|
|
@@ -171,6 +208,60 @@ interface BrokerClearPolicySnapshotRequest {
|
|
|
171
208
|
interface BrokerGetActiveSessionIdRequest {
|
|
172
209
|
readonly type: 'get_active_session_id';
|
|
173
210
|
}
|
|
211
|
+
/**
|
|
212
|
+
* Wave 5 Option D Commit 3 — read the kernel's `currentNonce()` view
|
|
213
|
+
* via the broker's chain RPC. The MCP server uses this to gate the
|
|
214
|
+
* MODE.ENABLE composition path: mirror's stored `validatorNonce` must
|
|
215
|
+
* still match the on-chain nonce (the enableSig was signed over the
|
|
216
|
+
* typed-data digest that pinned the nonce; if the kernel's nonce has
|
|
217
|
+
* advanced since mint, the digest no longer matches and the on-chain
|
|
218
|
+
* validator rejects the install).
|
|
219
|
+
*
|
|
220
|
+
* Returns a uint32. Broker daemon enforces the range; MCP-side caller
|
|
221
|
+
* compares to the mirror's stored value as a numeric equality check.
|
|
222
|
+
*/
|
|
223
|
+
interface BrokerCurrentNonceRequest {
|
|
224
|
+
readonly type: 'current_nonce';
|
|
225
|
+
/** Kernel/smart-account address. The kernel's `currentNonce()` is
|
|
226
|
+
* a view-only method on the account itself (not the EntryPoint). */
|
|
227
|
+
readonly accountAddress: `0x${string}`;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Wave 5 Option D Commit 3 — MCP server notifies broker that a
|
|
231
|
+
* MODE.ENABLE UserOp has settled on-chain. Broker forwards the receipt
|
|
232
|
+
* to the backend's `validator-enabled` route over HTTPS (using its
|
|
233
|
+
* pre-configured `BROKER_CALLBACK_SERVICE_SECRET` + `BROKER_BACKEND_BASE_URL`).
|
|
234
|
+
*
|
|
235
|
+
* The IPC return is FIRE-AND-FORGET: the broker queues the callback +
|
|
236
|
+
* acks immediately. The retry loop (5s/15s/60s/5m, max 1h)
|
|
237
|
+
* runs in the broker's background; failures are operator-visible via
|
|
238
|
+
* the daemon's log channel only (the MCP server does not block on
|
|
239
|
+
* the broker's egress).
|
|
240
|
+
*
|
|
241
|
+
* Idempotent on the chain-indexer side — the backend route re-verifies
|
|
242
|
+
* the receipt; the chain indexer is the authoritative source of truth.
|
|
243
|
+
*/
|
|
244
|
+
interface BrokerNotifyUseropLandedRequest {
|
|
245
|
+
readonly type: 'notify_userop_landed';
|
|
246
|
+
readonly sessionId: string;
|
|
247
|
+
/** Address of the kernel that owns the installed PermissionValidator.
|
|
248
|
+
* Backend uses (account_address, permission_id) to look up the mirror
|
|
249
|
+
* row to flip. */
|
|
250
|
+
readonly accountAddress: `0x${string}`;
|
|
251
|
+
/** 0x-prefixed 4-byte permissionId, lower-cased. */
|
|
252
|
+
readonly permissionId: `0x${string}`;
|
|
253
|
+
/** Tx hash carrying the install. Backend re-verifies via
|
|
254
|
+
* `eth_getTransactionReceipt` before flipping the enum. */
|
|
255
|
+
readonly txHash: `0x${string}`;
|
|
256
|
+
/** Block number containing the receipt — telemetry / forensics. */
|
|
257
|
+
readonly blockNumber: number;
|
|
258
|
+
/** Log index of the matched `SelectorSet` install event within the
|
|
259
|
+
* tx's logs array. INFORMATIONAL only — the backend re-scans the FULL
|
|
260
|
+
* receipt and matches by permissionId, so a wrong/0 logIndex is
|
|
261
|
+
* harmless (the deployed kernel emits `SelectorSet`, not
|
|
262
|
+
* `PermissionInstalled`, for enable-mode installs). */
|
|
263
|
+
readonly logIndex: number;
|
|
264
|
+
}
|
|
174
265
|
/**
|
|
175
266
|
* Per-selector enforcement rule. The broker matches `innerCall`'s
|
|
176
267
|
* selector against `selector`, then — if `capArgIndex` is not null —
|
|
@@ -274,6 +365,31 @@ interface PolicySnapshotWire {
|
|
|
274
365
|
* (additive optional field, no protocol version bump).
|
|
275
366
|
*/
|
|
276
367
|
readonly permissionId?: `0x${string}`;
|
|
368
|
+
/**
|
|
369
|
+
* Wave 5 Option D Commit 3 — install material for the MODE.ENABLE
|
|
370
|
+
* UserOp. `enableData` is the validator-install payload built by the
|
|
371
|
+
* frontend at mint time (`permissionValidator.getEnableData(...)`).
|
|
372
|
+
* Variable size — real-world Wave 5 policy sets produce ~30KB hex.
|
|
373
|
+
* Optional in the wire shape; absence = treat as already-enabled
|
|
374
|
+
* (legacy posture; MCP composes MODE.DEFAULT directly).
|
|
375
|
+
*/
|
|
376
|
+
readonly enableData?: `0x${string}`;
|
|
377
|
+
/**
|
|
378
|
+
* Wave 5 Option D Commit 3 — WebAuthn-shaped passkey signature over
|
|
379
|
+
* `getPluginsEnableTypedData(...)`. 256-1024 bytes hex; ZeroDev's
|
|
380
|
+
* passkey-validator emits a structured envelope (NOT a bare 65-byte
|
|
381
|
+
* ECDSA). Optional; paired with `enableData` (both present or both
|
|
382
|
+
* absent — broker daemon enforces; see `parsePolicySnapshot`).
|
|
383
|
+
*/
|
|
384
|
+
readonly enableSig?: `0x${string}`;
|
|
385
|
+
/**
|
|
386
|
+
* Wave 5 Option D Commit 3 — `currentNonce()` value the kernel
|
|
387
|
+
* returned at mint time. The MCP server's broker pre-check compares
|
|
388
|
+
* this to the LIVE on-chain nonce (via the broker's new
|
|
389
|
+
* `current_nonce` IPC verb) — mismatch surfaces as fallback
|
|
390
|
+
* `enable_sig_stale`. Optional; uint32 range.
|
|
391
|
+
*/
|
|
392
|
+
readonly validatorNonce?: number;
|
|
277
393
|
}
|
|
278
394
|
interface BrokerHelloResponse {
|
|
279
395
|
readonly type: 'hello';
|
|
@@ -371,14 +487,39 @@ interface BrokerGetActiveSessionIdResponse {
|
|
|
371
487
|
* in either case. */
|
|
372
488
|
readonly sessionId: string | null;
|
|
373
489
|
}
|
|
490
|
+
interface BrokerCurrentNonceResponse {
|
|
491
|
+
readonly type: 'current_nonce';
|
|
492
|
+
/** uint32 returned by the kernel's `currentNonce()` view. */
|
|
493
|
+
readonly nonce: number;
|
|
494
|
+
/** Mirror of the request — defends against the broker accidentally
|
|
495
|
+
* serving a cached response for a different account. The caller
|
|
496
|
+
* compares this to its request input before trusting `nonce`. */
|
|
497
|
+
readonly accountAddress: `0x${string}`;
|
|
498
|
+
}
|
|
499
|
+
interface BrokerNotifyUseropLandedResponse {
|
|
500
|
+
readonly type: 'notify_userop_landed';
|
|
501
|
+
/** Always `true` once the callback is queued. The broker queues
|
|
502
|
+
* fire-and-forget; failures surface in the broker's log channel
|
|
503
|
+
* only. */
|
|
504
|
+
readonly queued: true;
|
|
505
|
+
readonly sessionId: string;
|
|
506
|
+
}
|
|
374
507
|
interface BrokerErrorResponse {
|
|
375
508
|
readonly type: 'error';
|
|
376
509
|
readonly code: BrokerErrorCode;
|
|
377
510
|
readonly message: string;
|
|
378
511
|
}
|
|
379
|
-
type BrokerErrorCode = 'invalid_request' | 'payload_too_large' | 'unsupported_type' | 'internal' | 'forbidden' | 'keystore_unavailable' | 'session_key_unavailable' | 'no_active_snapshot' | 'policy_violation' | 'scope_violation' | 'max_spend_exceeded' | 'rate_limited'
|
|
380
|
-
|
|
381
|
-
|
|
512
|
+
type BrokerErrorCode = 'invalid_request' | 'payload_too_large' | 'unsupported_type' | 'internal' | 'forbidden' | 'keystore_unavailable' | 'session_key_unavailable' | 'no_active_snapshot' | 'policy_violation' | 'scope_violation' | 'max_spend_exceeded' | 'rate_limited'
|
|
513
|
+
/** Broker's chain RPC `eth_call` failed (network, malformed reply,
|
|
514
|
+
* RPC error). MCP-side caller falls back to Path C with a clear
|
|
515
|
+
* "broker chain RPC unreachable" remediation message. */
|
|
516
|
+
| 'chain_rpc_failed'
|
|
517
|
+
/** Broker hasn't been configured with the callback secret or backend
|
|
518
|
+
* base URL — `notify_userop_landed` is a no-op IPC verb in this
|
|
519
|
+
* posture (the chain indexer remains the safety net). */
|
|
520
|
+
| 'callback_unconfigured';
|
|
521
|
+
type BrokerRequest = BrokerHelloRequest | BrokerSignHashRequest | BrokerStoreJwtRequest | BrokerGetJwtRequest | BrokerClearJwtRequest | BrokerSignUserOpRequest | BrokerStorePolicySnapshotRequest | BrokerGetPolicySnapshotRequest | BrokerClearPolicySnapshotRequest | BrokerGetActiveSessionIdRequest | BrokerCurrentNonceRequest | BrokerNotifyUseropLandedRequest;
|
|
522
|
+
type BrokerResponse = BrokerHelloResponse | BrokerSignHashResponse | BrokerStoreJwtResponse | BrokerGetJwtResponse | BrokerClearJwtResponse | BrokerSignUserOpResponse | BrokerStorePolicySnapshotResponse | BrokerGetPolicySnapshotResponse | BrokerClearPolicySnapshotResponse | BrokerGetActiveSessionIdResponse | BrokerCurrentNonceResponse | BrokerNotifyUseropLandedResponse | BrokerErrorResponse;
|
|
382
523
|
/**
|
|
383
524
|
* Parse a single-line request payload. Returns either the validated
|
|
384
525
|
* request or a structured error — the daemon converts errors to a
|
|
@@ -508,6 +649,15 @@ declare class BrokerClient {
|
|
|
508
649
|
getPolicySnapshot(sessionId: string): Promise<BrokerGetPolicySnapshotResponse>;
|
|
509
650
|
clearPolicySnapshot(sessionId: string): Promise<BrokerClearPolicySnapshotResponse>;
|
|
510
651
|
getActiveSessionId(): Promise<BrokerGetActiveSessionIdResponse>;
|
|
652
|
+
currentNonce(accountAddress: `0x${string}`): Promise<BrokerCurrentNonceResponse>;
|
|
653
|
+
notifyUseropLanded(args: {
|
|
654
|
+
sessionId: string;
|
|
655
|
+
accountAddress: `0x${string}`;
|
|
656
|
+
permissionId: `0x${string}`;
|
|
657
|
+
txHash: `0x${string}`;
|
|
658
|
+
blockNumber: number;
|
|
659
|
+
logIndex: number;
|
|
660
|
+
}): Promise<BrokerNotifyUseropLandedResponse>;
|
|
511
661
|
/**
|
|
512
662
|
* Detect whether the running daemon speaks Path D (protocol 0.4.0+).
|
|
513
663
|
* Wraps `hello()` with a semver-gte comparison so the MCP tool layer
|
|
@@ -618,6 +768,7 @@ declare class BackendClient {
|
|
|
618
768
|
private buildUrl;
|
|
619
769
|
private exchangeWithRetry;
|
|
620
770
|
private exchange;
|
|
771
|
+
private runFetch;
|
|
621
772
|
}
|
|
622
773
|
|
|
623
774
|
/**
|
|
@@ -1275,6 +1426,38 @@ interface BrokerRuntimeConfig {
|
|
|
1275
1426
|
backendBaseUrl: string;
|
|
1276
1427
|
/** Effective dashboard URL paired with backendBaseUrl. */
|
|
1277
1428
|
dashboardBaseUrl: string;
|
|
1429
|
+
/**
|
|
1430
|
+
* Wave 5 Option D Commit 3 — chain RPC URL the broker uses to read
|
|
1431
|
+
* `kernel.currentNonce()` via `eth_call` during the MODE.ENABLE
|
|
1432
|
+
* pre-check. Optional: when undefined, `current_nonce` IPC returns
|
|
1433
|
+
* `chain_rpc_failed`. Defaults from `MUHAVEN_BROKER_RPC_URL` (broker-
|
|
1434
|
+
* specific) with fallback to `MUHAVEN_BUNDLER_URL` (shared with the
|
|
1435
|
+
* MCP server; ZeroDev bundlers also serve `eth_call`). Operator can
|
|
1436
|
+
* pin a dedicated read-only RPC via `MUHAVEN_BROKER_RPC_URL` to keep
|
|
1437
|
+
* the broker's egress surface narrower than the MCP server's.
|
|
1438
|
+
*
|
|
1439
|
+
* NOTE: this is the first network-egress capability the broker has
|
|
1440
|
+
* been given — see protocol.ts JSDoc on the threat-model relaxation.
|
|
1441
|
+
*/
|
|
1442
|
+
chainRpcUrl?: string;
|
|
1443
|
+
/**
|
|
1444
|
+
* Wave 5 Option D Commit 3 — service secret the broker uses to POST
|
|
1445
|
+
* to the backend's `validator-enabled` route. Optional: when
|
|
1446
|
+
* undefined, `notify_userop_landed` returns `callback_unconfigured`
|
|
1447
|
+
* (chain indexer is still the authoritative safety net).
|
|
1448
|
+
*
|
|
1449
|
+
* Sourced from `BROKER_CALLBACK_SERVICE_SECRET`. Held by the broker
|
|
1450
|
+
* (not the MCP server) to keep the secret outside the LLM-controlled
|
|
1451
|
+
* subprocess.
|
|
1452
|
+
*/
|
|
1453
|
+
callbackServiceSecret?: string;
|
|
1454
|
+
/**
|
|
1455
|
+
* Wave 5 Option D Commit 3 — `Origin` header the broker stamps on
|
|
1456
|
+
* outbound bundler / RPC calls. ZeroDev bundlers reject bare Node
|
|
1457
|
+
* fetches with 403 (per [[feedback-zerodev-bundler-origin-header]]).
|
|
1458
|
+
* Defaults to `dashboardBaseUrl`.
|
|
1459
|
+
*/
|
|
1460
|
+
outboundOriginHeader?: string;
|
|
1278
1461
|
}
|
|
1279
1462
|
/**
|
|
1280
1463
|
* Compute the default IPC endpoint for the broker. POSIX: a socket file
|
|
@@ -1560,17 +1743,114 @@ interface IPolicyStore {
|
|
|
1560
1743
|
init?(): Promise<void>;
|
|
1561
1744
|
}
|
|
1562
1745
|
|
|
1746
|
+
interface OutboundConfig {
|
|
1747
|
+
readonly chainRpcUrl?: string;
|
|
1748
|
+
readonly backendBaseUrl: string;
|
|
1749
|
+
readonly callbackServiceSecret?: string;
|
|
1750
|
+
/** Origin header stamped on outbound bundler / RPC calls. */
|
|
1751
|
+
readonly outboundOriginHeader: string;
|
|
1752
|
+
/** Per-fetch timeout in ms. Defaults to 15s. */
|
|
1753
|
+
readonly fetchTimeoutMs?: number;
|
|
1754
|
+
/**
|
|
1755
|
+
* Injectable fetch impl for tests. Defaults to global `fetch`. The
|
|
1756
|
+
* shape matches the standard Fetch API — no node-specific quirks.
|
|
1757
|
+
*/
|
|
1758
|
+
readonly fetchImpl?: typeof fetch;
|
|
1759
|
+
/**
|
|
1760
|
+
* Setter/clearer for the retry timer. Tests inject a fake-timer
|
|
1761
|
+
* pair so they can advance virtual time without `await new
|
|
1762
|
+
* Promise(setTimeout)`. Production passes Node's globals.
|
|
1763
|
+
*/
|
|
1764
|
+
readonly setTimeout?: typeof setTimeout;
|
|
1765
|
+
readonly clearTimeout?: typeof clearTimeout;
|
|
1766
|
+
}
|
|
1767
|
+
declare class BrokerOutbound {
|
|
1768
|
+
private readonly config;
|
|
1769
|
+
private readonly log;
|
|
1770
|
+
private readonly fetchImpl;
|
|
1771
|
+
private readonly fetchTimeoutMs;
|
|
1772
|
+
private readonly setTimeoutImpl;
|
|
1773
|
+
private readonly clearTimeoutImpl;
|
|
1774
|
+
/**
|
|
1775
|
+
* Wave 5 Option D Commit 3 (multi-agent review SecEng-MED-3) —
|
|
1776
|
+
* in-process dedup of `notify_userop_landed` callbacks. Map of
|
|
1777
|
+
* `<sessionId>:<txHash>` → the in-flight retry loop's Promise.
|
|
1778
|
+
* Repeated IPC calls with the same key fold into the existing
|
|
1779
|
+
* loop instead of spawning a parallel POST. Defends against a
|
|
1780
|
+
* local-socket peer flooding the broker with replay attempts +
|
|
1781
|
+
* caps the retry-budget waste at one loop per real install.
|
|
1782
|
+
*/
|
|
1783
|
+
private readonly inflightCallbacks;
|
|
1784
|
+
constructor(config: OutboundConfig, log?: (level: 'info' | 'warn' | 'error', msg: string, meta?: Record<string, unknown>) => void);
|
|
1785
|
+
/**
|
|
1786
|
+
* Read the kernel's `currentNonce()` view via `eth_call` against the
|
|
1787
|
+
* configured chain RPC. Returns a uint32. Throws `ChainRpcError` when
|
|
1788
|
+
* unconfigured / network failed / RPC returned non-decodable bytes.
|
|
1789
|
+
*/
|
|
1790
|
+
currentNonce(accountAddress: `0x${string}`): Promise<number>;
|
|
1791
|
+
/**
|
|
1792
|
+
* Whether the callback path is wired (both secret + backend URL set).
|
|
1793
|
+
* The `notify_userop_landed` daemon handler checks this and returns
|
|
1794
|
+
* `callback_unconfigured` when false so the operator sees the gap.
|
|
1795
|
+
*/
|
|
1796
|
+
isCallbackConfigured(): boolean;
|
|
1797
|
+
/**
|
|
1798
|
+
* Queue a `validator-enabled` callback POST to the backend. Returns
|
|
1799
|
+
* immediately; the retry loop runs in the background (5s / 15s / 60s
|
|
1800
|
+
* / 5m, max 1h elapsed). Failures are logged but do NOT propagate to
|
|
1801
|
+
* the IPC caller — the chain indexer is the authoritative safety
|
|
1802
|
+
* net.
|
|
1803
|
+
*
|
|
1804
|
+
* Idempotency: every POST carries an `Idempotency-Key` header
|
|
1805
|
+
* `<sessionId>:validator-enabled`. The backend route is no-op if the
|
|
1806
|
+
* mirror row's `enable_status` is already `'enabled'` (because the
|
|
1807
|
+
* chain indexer raced ahead).
|
|
1808
|
+
*
|
|
1809
|
+
* Returns a Promise resolved when the loop terminates (success or
|
|
1810
|
+
* max-elapsed). Callers don't need to await; tests use it for
|
|
1811
|
+
* deterministic assertions.
|
|
1812
|
+
*/
|
|
1813
|
+
enqueueValidatorEnabledCallback(args: {
|
|
1814
|
+
readonly sessionId: string;
|
|
1815
|
+
readonly userId?: string;
|
|
1816
|
+
readonly accountAddress: `0x${string}`;
|
|
1817
|
+
readonly permissionId: `0x${string}`;
|
|
1818
|
+
readonly txHash: `0x${string}`;
|
|
1819
|
+
readonly blockNumber: number;
|
|
1820
|
+
readonly logIndex: number;
|
|
1821
|
+
}): Promise<{
|
|
1822
|
+
ok: boolean;
|
|
1823
|
+
attempts: number;
|
|
1824
|
+
lastError?: string;
|
|
1825
|
+
}>;
|
|
1826
|
+
private runCallbackLoop;
|
|
1827
|
+
private postCallback;
|
|
1828
|
+
private sleep;
|
|
1829
|
+
}
|
|
1830
|
+
|
|
1563
1831
|
/**
|
|
1564
1832
|
* `muhaven-broker` daemon — the single-purpose process that holds the
|
|
1565
|
-
* session-key private half AND the device-flow JWT, exposing
|
|
1566
|
-
* primitives for `sign_hash` + `store_jwt` / `get_jwt` / `clear_jwt
|
|
1833
|
+
* session-key private half AND the device-flow JWT, exposing IPC
|
|
1834
|
+
* primitives for `sign_hash` + `store_jwt` / `get_jwt` / `clear_jwt` +
|
|
1835
|
+
* the Wave 5 Path D policy + UserOp signing surface.
|
|
1567
1836
|
*
|
|
1568
1837
|
* Design constraints (ranked):
|
|
1569
1838
|
* 1. Never speak TCP. Reachable only via local socket / named pipe.
|
|
1570
|
-
* 2.
|
|
1571
|
-
*
|
|
1572
|
-
*
|
|
1573
|
-
*
|
|
1839
|
+
* 2. **Outbound network egress is NARROW (Wave 5 Option D Commit 3
|
|
1840
|
+
* relaxation, operator-approved at handoff)**. The broker has
|
|
1841
|
+
* EXACTLY two outbound channels — both via the `BrokerOutbound`
|
|
1842
|
+
* module (`./outbound.ts`):
|
|
1843
|
+
* (a) chain RPC `eth_call` to the configured `MUHAVEN_BROKER_RPC_URL`
|
|
1844
|
+
* (fallback `MUHAVEN_BUNDLER_URL`) for `kernel.currentNonce()`
|
|
1845
|
+
* reads, gating the MODE.ENABLE pre-check;
|
|
1846
|
+
* (b) HTTPS POST to the backend's `validator-enabled` route,
|
|
1847
|
+
* gated by `BROKER_CALLBACK_SERVICE_SECRET`.
|
|
1848
|
+
* NO other outbound capability. NO bundler `eth_sendUserOperation`,
|
|
1849
|
+
* NO `eth_call` against non-`currentNonce` selectors, NO non-
|
|
1850
|
+
* backend HTTPS destinations. The JWT keystore + the IPC-mediated
|
|
1851
|
+
* `sign_hash` / `sign_userop` paths remain zero-egress.
|
|
1852
|
+
* See `./protocol.ts` JSDoc (top) for the threat-model rationale
|
|
1853
|
+
* + `./outbound.ts` JSDoc (top) for the per-channel contracts.
|
|
1574
1854
|
* 3. Peer access is enforced by filesystem permissions on POSIX (parent
|
|
1575
1855
|
* dir 0700 / socket file 0600). Windows named pipe inherits the
|
|
1576
1856
|
* creating user's ACL by default.
|
|
@@ -1595,6 +1875,12 @@ interface BrokerDaemonOptions {
|
|
|
1595
1875
|
* verbs added in Wave 5 Path D Slice 1 (protocol 0.4.0).
|
|
1596
1876
|
*/
|
|
1597
1877
|
policyStore?: IPolicyStore;
|
|
1878
|
+
/**
|
|
1879
|
+
* Wave 5 Option D Commit 3 — inject the outbound module for tests.
|
|
1880
|
+
* Production builds one from `config.chainRpcUrl` +
|
|
1881
|
+
* `config.callbackServiceSecret` at constructor time.
|
|
1882
|
+
*/
|
|
1883
|
+
outbound?: BrokerOutbound;
|
|
1598
1884
|
/** Override for the connection-handler logger; defaults to silent. */
|
|
1599
1885
|
logger?: (event: BrokerLogEvent) => void;
|
|
1600
1886
|
}
|
|
@@ -1634,7 +1920,7 @@ interface HandleBrokerRequestOptions {
|
|
|
1634
1920
|
*/
|
|
1635
1921
|
pid?: number;
|
|
1636
1922
|
}
|
|
1637
|
-
declare function handleBrokerRequest(req: BrokerRequest, signer: ISigner, keystore: IKeystore, nowSec?: () => number, options?: HandleBrokerRequestOptions, policyStore?: IPolicyStore): Promise<BrokerResponse>;
|
|
1923
|
+
declare function handleBrokerRequest(req: BrokerRequest, signer: ISigner, keystore: IKeystore, nowSec?: () => number, options?: HandleBrokerRequestOptions, policyStore?: IPolicyStore, outbound?: BrokerOutbound): Promise<BrokerResponse>;
|
|
1638
1924
|
declare class BrokerDaemon {
|
|
1639
1925
|
private readonly server;
|
|
1640
1926
|
private readonly signer;
|
|
@@ -1642,6 +1928,7 @@ declare class BrokerDaemon {
|
|
|
1642
1928
|
private readonly config;
|
|
1643
1929
|
private keystore;
|
|
1644
1930
|
private readonly policyStore;
|
|
1931
|
+
private readonly outbound;
|
|
1645
1932
|
/**
|
|
1646
1933
|
* Whether a session-key private half is actually loaded. `false` =
|
|
1647
1934
|
* daemon booted in read-only posture (no `MUHAVEN_BROKER_SESSION_KEY`
|