@tokamak-private-dapps/private-state-cli 2.1.1 → 2.1.2

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 CHANGED
@@ -2,6 +2,41 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 2.1.2 - 2026-05-15
6
+
7
+ - Fixed wallet lifecycle recovery log lookups so account-specific registration and exit event scans
8
+ use the same chunked `eth_getLogs` path as the rest of RPC workspace recovery.
9
+ - Removed synthetic wallet lifecycle epoch fallback creation; wallet recovery now requires
10
+ lifecycle registration events to be found in RPC log history instead of fabricating epochs from
11
+ current registration state.
12
+ - Fixed RPC log request pacing so every `eth_getLogs` call passes through a shared async limiter
13
+ instead of relying on a race-prone timestamp throttle during concurrent scans.
14
+ - Added progress output for the wallet lifecycle registered/exited event scans that run before
15
+ note-delivery recovery in `wallet recover-workspace`.
16
+ - Added `set rpc` for per-network RPC configuration with built-in `eth_getLogs` scan-limit tables
17
+ for Ankr, Chainstack, Chainnodes, QuickNode, and Alchemy, using 90% of the provider reference
18
+ request-rate values.
19
+ - Simplified note-mutating command post-processing so accepted note transactions wait for the
20
+ receipt block, then refresh channel and wallet workspaces from their recovery indexes instead of
21
+ manually applying note lifecycle metadata.
22
+ - Extended wallet recovery to persist public creation/spend linkage from commitment and
23
+ nullifier storage observations so raw evidence export can package stored metadata without
24
+ running its own log scan.
25
+ - Fixed raw evidence spend linkage so nullifier observation transactions are used as the spend
26
+ transition references and included with their transaction, receipt, and event evidence.
27
+ - Redesigned the bundled investigator GUI around purpose-first disclosure requests, an interactive
28
+ SVG note-linkage graph, node detail overlays, and Markdown ASCII-art linkage report export.
29
+ - Updated private-state documentation to reflect `set rpc` as the only CLI RPC configuration path
30
+ for ordinary bridge-facing and wallet commands.
31
+ - Split the CLI entrypoint into command dispatch modules and moved the shared runtime implementation
32
+ under `lib/runtime.mjs`; the published package now includes the `commands/` modules.
33
+ - Removed the `channel get-meta` workspace mirror lookup fallback so contract lookup errors surface
34
+ directly instead of being hidden behind `null` metadata.
35
+ - Changed `channel join` to derive the wallet lifecycle epoch from the accepted join receipt instead
36
+ of rescanning full account registration and exit history.
37
+ - Changed investigator numeric block filters to reject invalid values instead of silently treating
38
+ them as absent filters.
39
+
5
40
  ## 2.1.1 - 2026-05-14
6
41
 
7
42
  - Changed `channel recover-workspace --from-genesis` to move any existing local channel workspace
package/README.md CHANGED
@@ -264,8 +264,11 @@ private-state-cli investigator
264
264
  ```
265
265
 
266
266
  The command prints the bundled investigator HTML path and file URL, then opens the static browser GUI. Load the raw
267
- evidence ZIP in that GUI, apply the requested filters, and export a narrower user-consent disclosure ZIP. The GUI runs
268
- locally in the browser and does not send files over the network.
267
+ evidence ZIP in that GUI, choose the disclosure request type, inspect the interactive note-linkage graph, and export a
268
+ narrower user-consent disclosure ZIP. The graph view renders every matched note as a node and shows creation, spend,
269
+ and local note-to-note linkage edges when the raw bundle contains enough local evidence. The GUI can also export a
270
+ Markdown ASCII-art linkage report with a compact graph section and per-note detail sections. The GUI runs locally in the
271
+ browser and does not send files over the network.
269
272
 
270
273
  The investigator accepts current epoch-aware evidence bundles only. If a bundle was generated by an older CLI, rebuild
271
274
  the wallet workspace with `wallet recover-workspace` and export a new bundle with `wallet get-notes --export-evidence`.
@@ -273,7 +276,7 @@ the wallet workspace with `wallet recover-workspace` and export a new bundle wit
273
276
  Estimate live transaction costs before sending commands with:
274
277
 
275
278
  ```bash
276
- private-state-cli help transaction-fees --network mainnet --rpc-url <RPC_URL>
279
+ private-state-cli help transaction-fees --network mainnet
277
280
  ```
278
281
 
279
282
  `help transaction-fees` uses the measured gas data packaged in `assets/tx-fees.json`, the selected network's live fee data,
@@ -366,8 +369,9 @@ note-delivery events and rebuild the user's readable note view. Sharing it gives
366
369
  registered note-receive public key, but not spending authority.
367
370
 
368
371
  The spending key is the channel-bound L2 private key. It authorizes proof-backed use of the wallet identity. Commands
369
- that consume existing notes, such as `wallet transfer-notes` and `wallet redeem-notes`, need both the viewing key and
370
- the spending key because the CLI must first reconstruct the plaintext notes and then prove authorized use of them.
372
+ that create or consume notes, such as `wallet mint-notes`, `wallet transfer-notes`, and `wallet redeem-notes`, need both
373
+ the viewing key and the spending key because the CLI refreshes the readable note workspace after accepted note
374
+ transactions and then proves authorized note use when inputs are consumed.
371
375
 
372
376
  Key recovery is intentionally split. Recreating the viewing key requires the original L1 private key and the same channel
373
377
  context. Recreating the spending key requires the original L1 private key, the same channel context, and the same wallet
@@ -390,11 +394,40 @@ The CLI stores user workspaces under:
390
394
  Wallet backup metadata lives under the channel workspace. Viewing and spending private keys live as separate protected
391
395
  key files under `~/tokamak-private-channels/secrets/<network>/wallets/<wallet>/`.
392
396
 
393
- Bridge-facing commands accept optional `--rpc-url <URL>`. When `--rpc-url` is provided, the CLI stores it in
394
- `~/tokamak-private-channels/secrets/<network>/.env` as `RPC_URL=<URL>` with protected canonical secret permissions.
395
- When `--rpc-url` is omitted, the CLI reads `RPC_URL` from that file. The `anvil` network falls back to
396
- `http://127.0.0.1:8545` when no saved RPC URL exists. Canonical CLI secrets are checked on read: macOS/Linux uses
397
- `0600`, while Windows uses ACL repair and inspection when possible.
397
+ Configure the network RPC endpoint before bridge-facing or wallet recovery commands:
398
+
399
+ ```bash
400
+ private-state-cli set rpc --network mainnet --rpc-url <RPC_URL> --provider alchemy
401
+ ```
402
+
403
+ The CLI writes `~/tokamak-private-channels/workspace/<network>/rpc-config.env` and later commands read `RPC_URL`,
404
+ `LOG_CHUNK_SIZE`, `LOG_REQUESTS_PER_SECOND`, and `RPC_BLOCK_RANGE_CAP` from that file. Built-in provider limits are
405
+ set to 90% of the provider reference values: Ankr `27 calls/s, 3000 blocks`; Chainstack `22.5 calls/s, 100 blocks`;
406
+ Chainnodes `22.5 calls/s, 20000 blocks`; QuickNode `13.5 calls/s, 5 blocks`; Alchemy `7.497 calls/s, 10 blocks`.
407
+ If the provider is not listed, use
408
+ `--log-requests-per-second <N>` and `--block-range-cap <N>` instead of `--provider`.
409
+
410
+ ### Slow Workspace Recovery
411
+
412
+ `channel recover-workspace` and `wallet recover-workspace` scan on-chain logs with `eth_getLogs`. If recovery is
413
+ unexpectedly slow, first check the RPC scan limits saved by `set rpc`. The main speed factors are the provider's
414
+ `eth_getLogs` block range cap and the allowed log request rate. A small block range cap can turn the same channel scan
415
+ into thousands of RPC calls.
416
+
417
+ For example, an Alchemy free-tier-style cap of `10` blocks is much slower for long recovery scans than Ankr's built-in
418
+ `3000` block setting or Chainnodes' built-in `20000` block setting. When recovery is too slow, re-run `set rpc` with a
419
+ provider that supports a larger `eth_getLogs` block range cap, or provide explicit values:
420
+
421
+ ```bash
422
+ private-state-cli set rpc --network mainnet --rpc-url <RPC_URL> --provider ankr
423
+ private-state-cli set rpc --network mainnet --rpc-url <RPC_URL> --log-requests-per-second <N> --block-range-cap <N>
424
+ ```
425
+
426
+ If an RPC provider rejects the configured range or rate during recovery, run `set rpc` again with limits that match the
427
+ provider's documented `eth_getLogs` policy, then retry the recovery command.
428
+
429
+ Canonical CLI secrets are checked on read: macOS/Linux uses `0600`, while Windows uses ACL repair and inspection when
430
+ possible.
398
431
 
399
432
  ## LLM Agent Guidance
400
433
 
@@ -429,8 +462,9 @@ Operating rules:
429
462
  - A viewing key decrypts encrypted note-delivery events for the registered note-receive public key. A spending key is
430
463
  the channel-bound L2 private key used to authorize note use. Do not describe either key as interchangeable with the
431
464
  other.
432
- - The network RPC URL is the endpoint used to read and write chain state. It can be supplied once with `--rpc-url`
433
- on a bridge-facing command, after which the CLI saves it under the selected network.
465
+ - The network RPC URL is the endpoint used to read and write chain state. It must be configured once with
466
+ `private-state-cli set rpc --network <NETWORK> --rpc-url <URL> --provider <PROVIDER>`, or with explicit
467
+ `--log-requests-per-second` and `--block-range-cap` values when the provider is not built in.
434
468
  - A workspace recovery index is the saved block pointer and state-root hash that lets the CLI resume log scanning
435
469
  without replaying the channel from its creation block. If it is missing, explain `--from-genesis` before using it
436
470
  because genesis replay can take much longer.
@@ -438,25 +472,32 @@ Operating rules:
438
472
  recovery can be very slow because it scans channel logs from the creation block. If a channel workspace mirror is
439
473
  available, try mirror-based recovery first, and use RPC genesis replay only when mirror recovery is unavailable or
440
474
  unsuitable.
475
+ - When `channel recover-workspace` or `wallet recover-workspace` is unexpectedly slow, first inspect the RPC provider
476
+ configured by `set rpc`. Explain that recovery speed is dominated by `eth_getLogs` block range cap and log request
477
+ rate. Suggest re-running `set rpc` with a provider that supports a larger block range cap, such as Ankr or Chainnodes
478
+ when appropriate, or with explicit `--log-requests-per-second` and `--block-range-cap` values from the provider's
479
+ documentation.
441
480
  - When a CLI command fails, read the error message and any printed `Try:` hints first. Prefer the corrective action
442
481
  suggested by the CLI before inventing a different recovery sequence.
443
482
  - When the user does not have a network RPC URL yet, explain that they need an Ethereum JSON-RPC endpoint for the
444
- selected network. They can obtain one from an infrastructure provider such as Alchemy, Infura, QuickNode, or from
445
- their own node. Ask the user to create or select the endpoint in that provider's UI, then paste only the endpoint URL
446
- into the CLI command that accepts `--rpc-url`; do not ask for provider account passwords, API dashboards, seed phrases,
447
- private keys, or wallet secrets.
483
+ selected network. They can obtain one from an infrastructure provider such as Alchemy, Ankr, Chainstack, Chainnodes,
484
+ QuickNode, or from their own node. Ask the user to create or select the endpoint in that provider's UI, then paste only
485
+ the endpoint URL into `private-state-cli set rpc`; do not ask for provider account passwords, API dashboards, seed
486
+ phrases, private keys, or wallet secrets.
448
487
  - When a user wants to join a channel, do not jump straight to `channel join`. Walk them through:
449
488
  1. choose the network and channel name
450
489
  2. run `private-state-cli install`
451
490
  3. run `private-state-cli help doctor`
452
491
  4. obtain or confirm a network RPC URL for the selected network
453
- 5. prepare a private key source file locally, without pasting the key into chat
454
- 6. run `account import --account <NAME> --network <NETWORK> --private-key-file <PATH>`
455
- 7. prepare a wallet secret source file locally, for example with `openssl rand -hex 32 > ./wallet-secret.txt`
456
- 8. inspect the channel with `channel get-meta` if it already exists, or create it with `channel create` if the user is
492
+ 5. run `set rpc --network <NETWORK> --rpc-url <URL> --provider <PROVIDER>`, or use explicit scan limits for an
493
+ unlisted provider
494
+ 6. prepare a private key source file locally, without pasting the key into chat
495
+ 7. run `account import --account <NAME> --network <NETWORK> --private-key-file <PATH>`
496
+ 8. prepare a wallet secret source file locally, for example with `openssl rand -hex 32 > ./wallet-secret.txt`
497
+ 9. inspect the channel with `channel get-meta` if it already exists, or create it with `channel create` if the user is
457
498
  the channel creator
458
- 9. explain the immutable policy warning printed by the CLI
459
- 10. run `channel join --channel-name <CHANNEL> --network <NETWORK> --account <ACCOUNT> --wallet-secret-path <PATH> --acknowledge-action-impact`
499
+ 10. explain the immutable policy warning printed by the CLI
500
+ 11. run `channel join --channel-name <CHANNEL> --network <NETWORK> --account <ACCOUNT> --wallet-secret-path <PATH> --acknowledge-action-impact`
460
501
  - Before executing any command for a user that requires an `--acknowledge-*` option, strongly warn the user in plain
461
502
  language about what that acknowledgement means and ask for explicit confirmation. Do not add
462
503
  `--acknowledge-action-impact` or `--acknowledge-full-note-plaintext-export` on the user's behalf until they confirm.
@@ -413,16 +413,22 @@
413
413
  <h2>Command Guide</h2>
414
414
  <div class="box">
415
415
  <ol class="guide-list">
416
+ <li>
417
+ <span class="guide-label">Configure RPC:</span> <code>set rpc</code>
418
+ <div class="guide-example">
419
+ <code>set rpc --network 'sepolia' --rpc-url '__RPC_URL__' --provider 'alchemy'</code>
420
+ </div>
421
+ </li>
416
422
  <li>
417
423
  <span class="guide-label">Join a channel:</span> <code>channel join</code>
418
424
  <div class="guide-example">
419
- <code>channel join --channel-name 'my-private-channel' --network 'sepolia' --account 'my-account' --wallet-secret-path '/path/to/wallet-secret' --rpc-url '__RPC_URL__'</code>
425
+ <code>channel join --channel-name 'my-private-channel' --network 'sepolia' --account 'my-account' --wallet-secret-path '/path/to/wallet-secret'</code>
420
426
  </div>
421
427
  </li>
422
428
  <li>
423
429
  <span class="guide-label">Create notes:</span> <code>account deposit-bridge</code> -&gt; <code>wallet deposit-channel</code> -&gt; <code>wallet mint-notes</code>
424
430
  <div class="guide-example">
425
- <code>account deposit-bridge --amount '100' --network 'sepolia' --account 'my-account' --rpc-url '__RPC_URL__'</code><br />
431
+ <code>account deposit-bridge --amount '100' --network 'sepolia' --account 'my-account'</code><br />
426
432
  <code>wallet deposit-channel --wallet 'my-private-channel-0xYourL1Address' --network 'sepolia' --amount '100'</code><br />
427
433
  <code>wallet mint-notes --wallet 'my-private-channel-0xYourL1Address' --network 'sepolia' --amounts '[&quot;50&quot;,&quot;50&quot;,&quot;0&quot;]'</code>
428
434
  </div>
@@ -438,7 +444,7 @@
438
444
  <div class="guide-example">
439
445
  <code>wallet redeem-notes --wallet 'my-private-channel-0xYourL1Address' --network 'sepolia' --note-ids '[&quot;0xNoteIdA&quot;]'</code><br />
440
446
  <code>wallet withdraw-channel --wallet 'my-private-channel-0xYourL1Address' --network 'sepolia' --amount '25'</code><br />
441
- <code>account withdraw-bridge --amount '25' --network 'sepolia' --account 'my-account' --rpc-url '__RPC_URL__'</code>
447
+ <code>account withdraw-bridge --amount '25' --network 'sepolia' --account 'my-account'</code>
442
448
  </div>
443
449
  </li>
444
450
  </ol>
@@ -1420,7 +1426,7 @@
1420
1426
  ].forEach((fieldKey) => {
1421
1427
  memoryFieldsEl.appendChild(createField(fieldKey));
1422
1428
  });
1423
- memoryStatusEl.textContent = "Use account import --private-key-file before signing commands. Optional --rpc-url is saved as the network RPC_URL for later commands.";
1429
+ memoryStatusEl.textContent = "Use set rpc once before bridge-facing commands, then use account import --private-key-file before signing commands.";
1424
1430
  }
1425
1431
 
1426
1432
  function renderCommandSelect() {
@@ -1480,7 +1486,7 @@
1480
1486
  : "",
1481
1487
  ].join("");
1482
1488
  warningEl.textContent = missing.length === 0
1483
- ? `Ready. If --rpc-url is omitted, the CLI uses the saved network RPC_URL.${workspaceWarnings}`
1489
+ ? `Ready. Bridge-facing commands use the saved network RPC configuration.${workspaceWarnings}`
1484
1490
  : `Missing inputs: ${missing.map((fieldKey) => fieldCatalog[fieldKey].label).join(", ")}.${workspaceWarnings}`;
1485
1491
  }
1486
1492
 
@@ -0,0 +1,43 @@
1
+ import {
2
+ assertAccountGetBridgeFundArgs,
3
+ assertAccountGetL1AddressArgs,
4
+ assertAccountImportArgs,
5
+ assertDepositBridgeArgs,
6
+ assertWithdrawBridgeArgs,
7
+ handleAccountGetBridgeFund,
8
+ handleAccountGetL1Address,
9
+ handleAccountImport,
10
+ handleDepositBridge,
11
+ handleWithdrawBridge,
12
+ loadExplicitCommandRuntime,
13
+ prepareDeploymentArtifacts,
14
+ } from "../lib/runtime.mjs";
15
+
16
+ export const accountCommands = Object.freeze({
17
+ "account-get-l1-address": async (args) => {
18
+ assertAccountGetL1AddressArgs(args);
19
+ handleAccountGetL1Address({ args });
20
+ },
21
+ "account-import": async (args) => {
22
+ assertAccountImportArgs(args);
23
+ handleAccountImport({ args });
24
+ },
25
+ "account-get-bridge-fund": async (args) => {
26
+ assertAccountGetBridgeFundArgs(args);
27
+ const { network, provider } = loadExplicitCommandRuntime(args);
28
+ await prepareDeploymentArtifacts(network.chainId);
29
+ await handleAccountGetBridgeFund({ args, provider });
30
+ },
31
+ "account-deposit-bridge": async (args) => {
32
+ assertDepositBridgeArgs(args);
33
+ const { network, provider } = loadExplicitCommandRuntime(args);
34
+ await prepareDeploymentArtifacts(network.chainId);
35
+ await handleDepositBridge({ args, network, provider });
36
+ },
37
+ "account-withdraw-bridge": async (args) => {
38
+ assertWithdrawBridgeArgs(args);
39
+ const { network, provider } = loadExplicitCommandRuntime(args);
40
+ await prepareDeploymentArtifacts(network.chainId);
41
+ await handleWithdrawBridge({ args, network, provider });
42
+ },
43
+ });
@@ -0,0 +1,66 @@
1
+ import {
2
+ assertCreateChannelArgs,
3
+ assertExitChannelArgs,
4
+ assertGetChannelArgs,
5
+ assertJoinChannelArgs,
6
+ assertProviderChainIdMatchesNetwork,
7
+ assertPublishWorkspaceMirrorArgs,
8
+ assertRecoverWorkspaceArgs,
9
+ assertSetWorkspaceMirrorArgs,
10
+ handleChannelCreate,
11
+ handleExitChannel,
12
+ handleGetChannel,
13
+ handleJoinChannel,
14
+ handlePublishChannelWorkspaceMirror,
15
+ handleSetChannelWorkspaceMirror,
16
+ handleWorkspaceInit,
17
+ loadExplicitCommandRuntime,
18
+ loadWalletCommandRuntime,
19
+ prepareDeploymentArtifacts,
20
+ } from "../lib/runtime.mjs";
21
+
22
+ export const channelCommands = Object.freeze({
23
+ "channel-create": async (args) => {
24
+ assertCreateChannelArgs(args);
25
+ const { network, provider } = loadExplicitCommandRuntime(args);
26
+ await prepareDeploymentArtifacts(network.chainId);
27
+ await handleChannelCreate({ args, network, provider });
28
+ },
29
+ "channel-recover-workspace": async (args) => {
30
+ assertRecoverWorkspaceArgs(args);
31
+ const { network, provider, rpcUrl } = loadExplicitCommandRuntime(args, { staticNetwork: true });
32
+ await assertProviderChainIdMatchesNetwork({ provider, network, rpcUrl });
33
+ await prepareDeploymentArtifacts(network.chainId);
34
+ await handleWorkspaceInit({ args, network, provider });
35
+ },
36
+ "channel-get-meta": async (args) => {
37
+ assertGetChannelArgs(args);
38
+ const { network, provider } = loadExplicitCommandRuntime(args);
39
+ await prepareDeploymentArtifacts(network.chainId);
40
+ await handleGetChannel({ args, network, provider });
41
+ },
42
+ "channel-set-workspace-mirror": async (args) => {
43
+ assertSetWorkspaceMirrorArgs(args);
44
+ const { network, provider } = loadExplicitCommandRuntime(args);
45
+ await prepareDeploymentArtifacts(network.chainId);
46
+ await handleSetChannelWorkspaceMirror({ args, network, provider });
47
+ },
48
+ "channel-publish-workspace-mirror": async (args) => {
49
+ assertPublishWorkspaceMirrorArgs(args);
50
+ const { network, provider } = loadExplicitCommandRuntime(args);
51
+ await prepareDeploymentArtifacts(network.chainId);
52
+ await handlePublishChannelWorkspaceMirror({ args, network, provider });
53
+ },
54
+ "channel-join": async (args) => {
55
+ assertJoinChannelArgs(args);
56
+ const { network, provider, rpcUrl } = loadExplicitCommandRuntime(args);
57
+ await prepareDeploymentArtifacts(network.chainId);
58
+ await handleJoinChannel({ args, network, provider, rpcUrl });
59
+ },
60
+ "channel-exit": async (args) => {
61
+ assertExitChannelArgs(args);
62
+ const { network, provider } = loadWalletCommandRuntime(args);
63
+ await prepareDeploymentArtifacts(network.chainId);
64
+ await handleExitChannel({ args, provider });
65
+ },
66
+ });
@@ -0,0 +1,62 @@
1
+ import {
2
+ assertHelpCommandsArgs,
3
+ assertVersionArgs,
4
+ configureOutput,
5
+ formatCliErrorForDisplay,
6
+ parseArgs,
7
+ printHelp,
8
+ printVersion,
9
+ } from "../lib/runtime.mjs";
10
+ import { accountCommands } from "./account.mjs";
11
+ import { channelCommands } from "./channel.mjs";
12
+ import { investigatorCommands } from "./investigator.mjs";
13
+ import { notesCommands } from "./notes.mjs";
14
+ import { systemCommands } from "./system.mjs";
15
+ import { walletCommands } from "./wallet.mjs";
16
+
17
+ const COMMANDS = Object.freeze({
18
+ ...systemCommands,
19
+ ...investigatorCommands,
20
+ ...accountCommands,
21
+ ...channelCommands,
22
+ ...walletCommands,
23
+ ...notesCommands,
24
+ });
25
+
26
+ export async function runPrivateStateCli(argv) {
27
+ let args = {};
28
+ try {
29
+ args = parseArgs(argv);
30
+ configureOutput(args);
31
+
32
+ if (args.version !== undefined) {
33
+ assertVersionArgs(args);
34
+ printVersion();
35
+ return;
36
+ }
37
+
38
+ if (args.help || !args.command) {
39
+ printHelp();
40
+ return;
41
+ }
42
+
43
+ if (args.command === "help-commands") {
44
+ assertHelpCommandsArgs(args);
45
+ printHelp();
46
+ return;
47
+ }
48
+
49
+ const command = COMMANDS[args.command];
50
+ if (!command) {
51
+ throw new Error(`Unsupported command: ${args.command}`);
52
+ }
53
+ await command(args);
54
+ } catch (error) {
55
+ console.error(formatCliErrorForDisplay(error, args));
56
+ process.exitCode = 1;
57
+ }
58
+ }
59
+
60
+ export function privateStateCliDispatchTable() {
61
+ return COMMANDS;
62
+ }
@@ -0,0 +1,11 @@
1
+ import {
2
+ assertInvestigatorArgs,
3
+ handleInvestigator,
4
+ } from "../lib/runtime.mjs";
5
+
6
+ export const investigatorCommands = Object.freeze({
7
+ investigator: async (args) => {
8
+ assertInvestigatorArgs(args);
9
+ handleInvestigator();
10
+ },
11
+ });
@@ -0,0 +1,39 @@
1
+ import {
2
+ assertMintNotesArgs,
3
+ assertRedeemNotesArgs,
4
+ assertTransferNotesArgs,
5
+ assertWalletGetNotesArgs,
6
+ handleMintNotes,
7
+ handleRedeemNotes,
8
+ handleTransferNotes,
9
+ handleWalletGetNotes,
10
+ loadWalletCommandRuntime,
11
+ prepareDeploymentArtifacts,
12
+ } from "../lib/runtime.mjs";
13
+
14
+ export const notesCommands = Object.freeze({
15
+ "wallet-mint-notes": async (args) => {
16
+ assertMintNotesArgs(args);
17
+ const { network, provider } = loadWalletCommandRuntime(args);
18
+ await prepareDeploymentArtifacts(network.chainId);
19
+ await handleMintNotes({ args, provider });
20
+ },
21
+ "wallet-redeem-notes": async (args) => {
22
+ assertRedeemNotesArgs(args);
23
+ const { network, provider } = loadWalletCommandRuntime(args);
24
+ await prepareDeploymentArtifacts(network.chainId);
25
+ await handleRedeemNotes({ args, provider });
26
+ },
27
+ "wallet-get-notes": async (args) => {
28
+ assertWalletGetNotesArgs(args);
29
+ const { network, provider } = loadWalletCommandRuntime(args);
30
+ await prepareDeploymentArtifacts(network.chainId);
31
+ await handleWalletGetNotes({ args, provider });
32
+ },
33
+ "wallet-transfer-notes": async (args) => {
34
+ assertTransferNotesArgs(args);
35
+ const { network, provider } = loadWalletCommandRuntime(args);
36
+ await prepareDeploymentArtifacts(network.chainId);
37
+ await handleTransferNotes({ args, provider });
38
+ },
39
+ });
@@ -0,0 +1,49 @@
1
+ import {
2
+ assertDoctorArgs,
3
+ assertGuideArgs,
4
+ assertInstallZkEvmArgs,
5
+ assertSetRpcArgs,
6
+ assertTransactionFeesArgs,
7
+ assertUninstallArgs,
8
+ assertUpdateArgs,
9
+ handleDoctor,
10
+ handleGuide,
11
+ handleInstallZkEvm,
12
+ handleSetRpc,
13
+ handleTransactionFees,
14
+ handleUninstall,
15
+ handleUpdate,
16
+ loadExplicitCommandRuntime,
17
+ } from "../lib/runtime.mjs";
18
+
19
+ export const systemCommands = Object.freeze({
20
+ install: async (args) => {
21
+ assertInstallZkEvmArgs(args);
22
+ await handleInstallZkEvm({ args });
23
+ },
24
+ uninstall: async (args) => {
25
+ assertUninstallArgs(args);
26
+ await handleUninstall();
27
+ },
28
+ "set-rpc": async (args) => {
29
+ assertSetRpcArgs(args);
30
+ await handleSetRpc({ args });
31
+ },
32
+ "help-update": async (args) => {
33
+ assertUpdateArgs(args);
34
+ await handleUpdate();
35
+ },
36
+ "help-doctor": async (args) => {
37
+ assertDoctorArgs(args);
38
+ await handleDoctor({ args });
39
+ },
40
+ "help-guide": async (args) => {
41
+ assertGuideArgs(args);
42
+ await handleGuide({ args });
43
+ },
44
+ "help-transaction-fees": async (args) => {
45
+ assertTransactionFeesArgs(args);
46
+ const { network, provider, rpcUrl } = loadExplicitCommandRuntime(args);
47
+ await handleTransactionFees({ network, provider, rpcUrl });
48
+ },
49
+ });
@@ -0,0 +1,86 @@
1
+ import {
2
+ assertProviderChainIdMatchesNetwork,
3
+ assertListLocalWalletsArgs,
4
+ assertRecoverWalletArgs,
5
+ assertWalletChannelMoveArgs,
6
+ assertWalletExportBackupArgs,
7
+ assertWalletExportKeyArgs,
8
+ assertWalletGetChannelFundArgs,
9
+ assertWalletGetMetaArgs,
10
+ assertWalletImportBackupArgs,
11
+ assertWalletImportKeyArgs,
12
+ handleGrothVaultMove,
13
+ handleListLocalWallets,
14
+ handleRecoverWallet,
15
+ handleWalletExportBackup,
16
+ handleWalletExportKey,
17
+ handleWalletGetChannelFund,
18
+ handleWalletGetMeta,
19
+ handleWalletImportBackup,
20
+ handleWalletImportKey,
21
+ loadExplicitCommandRuntime,
22
+ loadWalletCommandRuntime,
23
+ prepareDeploymentArtifacts,
24
+ } from "../lib/runtime.mjs";
25
+
26
+ export const walletCommands = Object.freeze({
27
+ "wallet-list": async (args) => {
28
+ assertListLocalWalletsArgs(args);
29
+ handleListLocalWallets({ args });
30
+ },
31
+ "wallet-export-backup": async (args) => {
32
+ assertWalletExportBackupArgs(args);
33
+ handleWalletExportBackup({ args });
34
+ },
35
+ "wallet-export-viewing-key": async (args) => {
36
+ assertWalletExportKeyArgs(args, "wallet-export-viewing-key");
37
+ handleWalletExportKey({ args, keyKind: "viewing" });
38
+ },
39
+ "wallet-export-spending-key": async (args) => {
40
+ assertWalletExportKeyArgs(args, "wallet-export-spending-key");
41
+ handleWalletExportKey({ args, keyKind: "spending" });
42
+ },
43
+ "wallet-import-backup": async (args) => {
44
+ assertWalletImportBackupArgs(args);
45
+ handleWalletImportBackup({ args });
46
+ },
47
+ "wallet-import-viewing-key": async (args) => {
48
+ assertWalletImportKeyArgs(args, "wallet-import-viewing-key");
49
+ handleWalletImportKey({ args, keyKind: "viewing" });
50
+ },
51
+ "wallet-import-spending-key": async (args) => {
52
+ assertWalletImportKeyArgs(args, "wallet-import-spending-key");
53
+ handleWalletImportKey({ args, keyKind: "spending" });
54
+ },
55
+ "wallet-recover-workspace": async (args) => {
56
+ assertRecoverWalletArgs(args);
57
+ const { network, provider, rpcUrl } = loadExplicitCommandRuntime(args, { staticNetwork: true });
58
+ await assertProviderChainIdMatchesNetwork({ provider, network, rpcUrl });
59
+ await prepareDeploymentArtifacts(network.chainId);
60
+ await handleRecoverWallet({ args, network, provider, rpcUrl });
61
+ },
62
+ "wallet-get-meta": async (args) => {
63
+ assertWalletGetMetaArgs(args);
64
+ const { network, provider } = loadWalletCommandRuntime(args);
65
+ await prepareDeploymentArtifacts(network.chainId);
66
+ await handleWalletGetMeta({ args, provider });
67
+ },
68
+ "wallet-get-channel-fund": async (args) => {
69
+ assertWalletGetChannelFundArgs(args);
70
+ const { network, provider } = loadWalletCommandRuntime(args);
71
+ await prepareDeploymentArtifacts(network.chainId);
72
+ await handleWalletGetChannelFund({ args, provider });
73
+ },
74
+ "wallet-deposit-channel": async (args) => {
75
+ assertWalletChannelMoveArgs(args, "wallet-deposit-channel");
76
+ const { network, provider } = loadWalletCommandRuntime(args);
77
+ await prepareDeploymentArtifacts(network.chainId);
78
+ await handleGrothVaultMove({ args, provider, direction: "deposit" });
79
+ },
80
+ "wallet-withdraw-channel": async (args) => {
81
+ assertWalletChannelMoveArgs(args, "wallet-withdraw-channel");
82
+ const { network, provider } = loadWalletCommandRuntime(args);
83
+ await prepareDeploymentArtifacts(network.chainId);
84
+ await handleGrothVaultMove({ args, provider, direction: "withdraw" });
85
+ },
86
+ });
@@ -10,13 +10,13 @@ private-state-cli wallet get-notes \
10
10
  --acknowledge-full-note-plaintext-export
11
11
  ```
12
12
 
13
- Open `index.html` in a modern browser, load the raw ZIP, select a filter scope, and build a narrower
14
- user-consent disclosure ZIP. From an installed CLI package, `private-state-cli investigator` prints the bundled
15
- HTML path and opens it in the default browser.
13
+ Open `index.html` in a modern browser, load the raw ZIP, choose the disclosure request type, inspect the graph, and
14
+ build a narrower user-consent disclosure ZIP. From an installed CLI package, `private-state-cli investigator` prints
15
+ the bundled HTML path and opens it in the default browser.
16
16
 
17
17
  The tool does not run a server and does not send files over the network. It reads the selected ZIP in
18
- the browser and writes a new ZIP with selected note records plus directly referenced transaction,
19
- receipt, and event files.
18
+ the browser. It can write a new ZIP with selected note records plus directly referenced transaction,
19
+ receipt, and event files, and it can export a Markdown ASCII-art linkage report.
20
20
 
21
21
  The raw evidence bundle contains plaintext for all locally known notes. Do not submit the raw bundle
22
22
  as an exchange or auditor package unless full wallet-history disclosure is intended. Use the
@@ -26,7 +26,17 @@ The investigator accepts current epoch-aware evidence bundles only. Supported no
26
26
  `wallets/<wallet>/epochs/<epoch-id>/notes/` inside the ZIP. If a bundle uses an older layout, rebuild the local wallet
27
27
  workspace with `wallet recover-workspace` and export a new evidence ZIP with `wallet get-notes --export-evidence`.
28
28
 
29
- ## Supported Filtering
29
+ ## Supported Investigation Views
30
+
31
+ - purpose-first request presets for full graph view, specific note receipt, specific note use, transaction linkage,
32
+ period receipts, and counterparty subsets
33
+ - an interactive SVG note-linkage graph where every matched note is a node
34
+ - graph edges for external note creation, external note spend, and locally recoverable note-to-note linkage
35
+ - node detail overlays showing commitment, nullifier, value, status, creation reference, spend reference, direction, and
36
+ available counterparty metadata
37
+ - a Markdown ASCII-art report with a compact graph section and separate note detail sections
38
+
39
+ ## Supported Filtering Inputs
30
40
 
31
41
  - note commitment or nullifier
32
42
  - creation transaction or spend transaction