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

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,22 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 2.2.1 - 2026-05-18
6
+
7
+ - Added `channel recover-workspace --source rpc --output-raw` to append raw JSON-RPC request and response history
8
+ to method-specific JSON files under the channel workspace `rpcCallHistory/` directory, with `eth_getLogs` split by event.
9
+ Indexed recovery appends to existing history; `--from-genesis` overwrites it with one full genesis-to-latest scan.
10
+
11
+ ## 2.2.0 - 2026-05-18
12
+
13
+ - Added `install --read-only` for channel-state read commands and commands that do not depend on channel state. This
14
+ mode installs only the bridge deployment, bridge ABI manifest, DApp deployment, and storage layout artifacts.
15
+ - Kept default `install` as full mode for proof-backed and channel-mutating commands, and made deployment artifact
16
+ validation mode-aware before command execution.
17
+ - Extended `help doctor` with per-command availability so read-only installs clearly report which commands are usable
18
+ and which commands still require full install.
19
+ - Added private-state CLI E2E coverage for the read-only install mode before the full install flow.
20
+
5
21
  ## 2.1.2 - 2026-05-15
6
22
 
7
23
  - Fixed wallet lifecycle recovery log lookups so account-specific registration and exit event scans
package/README.md CHANGED
@@ -4,16 +4,16 @@ Command-line client for the Tokamak private-state DApp.
4
4
 
5
5
  The full private-state DApp documentation is published with the repository:
6
6
 
7
- - https://github.com/tokamak-network/Tokamak-zk-EVM-contracts/tree/main/packages/apps/private-state/docs
7
+ - https://github.com/tokamak-network/Tokamak-zk-EVM-contracts/tree/main/docs/dapps/private-state
8
8
 
9
- ## Terminology And CEX Boundary
9
+ ## Terminology And Exchange Boundary
10
10
 
11
11
  This npm README uses the same terminology as the repository README:
12
12
 
13
13
  - `Tokamak Private App Channels`: Ethereum-settled, validity-proven execution domains for bridge-coupled DApps.
14
14
  - `private-state DApp`: the current reference DApp that programs confidential application state inside a channel.
15
15
  - `canonical Tokamak Network Token`: the L1 asset whose custody remains anchored on Ethereum.
16
- - `self-custody L1 wallet`: a user-controlled L1 account, not a centralized-exchange deposit address.
16
+ - `self-custody L1 wallet`: a user-controlled L1 account, not an exchange deposit address.
17
17
  - `L1-transparent bridge edge`: public bridge deposit and withdrawal transactions involving the canonical token.
18
18
  - `channel-local accounting balance`: liquid application balance inside a channel before or after note use.
19
19
  - `private-state note`: a channel-local application note, not an exchange-supported token or deposit asset.
@@ -22,10 +22,20 @@ This npm README uses the same terminology as the repository README:
22
22
  - `viewing key`: the note-receive private key used to decrypt note-delivery events for the registered note-receive public key.
23
23
  - `spending key`: the channel-bound L2 private key used to authorize proof-backed note use.
24
24
 
25
- Tokamak Private App Channels are not a centralized-exchange deposit network. CEX-facing token transfers and bridge
25
+ Tokamak Private App Channels are not an exchange deposit network. Exchange-facing token transfers and bridge
26
26
  entry or exit remain public L1 activity. Internal private-state note counterparty relationships and note provenance are
27
27
  not public by default and are not reconstructed by Tokamak on a user's behalf.
28
28
 
29
+ ## Address And Key-Safety Warnings
30
+
31
+ Do not use an exchange deposit address as a private-state wallet address. Private-state notes are not
32
+ supported exchange assets. Always withdraw TON to a self-custody L1 wallet before using a channel.
33
+
34
+ Bridge deposits and withdrawals are public L1 events. Internal note transfers are private by design and are not
35
+ automatically reconstructible by Tokamak, exchanges, or public observers.
36
+
37
+ This CLI does not send your spending key, wallet secret, or private note plaintext to Tokamak.
38
+
29
39
  ## Tokamak-Operated Mainnet Channels
30
40
 
31
41
  The table below lists private-state mainnet channels directly opened by Tokamak Network. Dates are
@@ -41,8 +51,8 @@ UTC.
41
51
  npm install -g @tokamak-private-dapps/private-state-cli
42
52
  ```
43
53
 
44
- Install the local Tokamak zk-EVM runtime workspace, Groth16 runtime workspace, and public private-state deployment
45
- artifacts:
54
+ Install the full local Tokamak zk-EVM runtime workspace, Groth16 runtime workspace, and public private-state deployment
55
+ artifacts needed by transaction-sending channel commands:
46
56
 
47
57
  ```bash
48
58
  private-state-cli install
@@ -61,6 +71,18 @@ selected Groth16 CLI package version.
61
71
  The Tokamak zk-EVM installer requires the selected CLI package to declare
62
72
  `tokamakZkEvm.compatibleBackendVersion` as a canonical major.minor version matching the selected package version.
63
73
 
74
+ For read-only channel recovery, channel metadata lookup, wallet recovery, wallet metadata lookup, bridge balance
75
+ lookup, bridge deposit, bridge withdrawal, and local helper commands, install only the read-only artifact subset:
76
+
77
+ ```bash
78
+ private-state-cli install --read-only
79
+ ```
80
+
81
+ Read-only install materializes only `bridge.<chainId>.json`, `bridge-abi-manifest.<chainId>.json`,
82
+ `deployment.<chainId>.latest.json`, and `storage-layout.<chainId>.latest.json`. It does not install the Tokamak
83
+ zk-EVM runtime, Groth16 runtime, Groth16 zkey, callable DApp ABI, or DApp registration artifact, so commands that
84
+ create or mutate channel state require a later full `private-state-cli install`.
85
+
64
86
  `install` downloads public deployment artifacts from the configured artifact index. It does not read repository-local
65
87
  `deployment/` outputs by default. Repository development workflows that need local anvil artifacts can opt in explicitly:
66
88
 
@@ -147,8 +169,8 @@ Static warning scope:
147
169
  | `wallet redeem-notes` | L1 submitter, input nullifier, accounting update, root update | Consumes notes | Prior path by which the note was received |
148
170
  | `wallet withdraw-channel` | L1 submitter, registered L2 address, amount, channel id, accounting update | No direct note spend | Prior private-state note path behind the liquid balance |
149
171
 
150
- `account deposit-bridge` and `account withdraw-bridge` also print a centralized-exchange address warning. Do not use a
151
- centralized-exchange controlled address as a self-custody bridge source or as the direct bridge withdrawal target
172
+ `account deposit-bridge` and `account withdraw-bridge` also print an exchange-controlled address warning. Do not use an
173
+ exchange-controlled address as a self-custody bridge source or as the direct bridge withdrawal target
152
174
  unless the user explicitly understands the compliance implications. Prefer a self-custody L1 wallet.
153
175
 
154
176
  Workspace recovery commands use saved recovery indexes by default. If the local channel workspace is missing,
@@ -170,6 +192,12 @@ run is interrupted, the next non-`--from-genesis` RPC recovery resumes from the
170
192
  can also start from that local checkpoint: it uses a matching delta bundle when one is available, otherwise a newer
171
193
  verified full mirror checkpoint replaces the local checkpoint before RPC catch-up.
172
194
 
195
+ Use `channel recover-workspace --source rpc --output-raw` when you need to preserve the raw JSON-RPC request and
196
+ response history for inspection. The CLI appends calls to method-specific JSON files, and splits `eth_getLogs` into
197
+ event-specific files such as `eth_getLogs.CurrentRootVectorObserved.json`, under
198
+ `~/tokamak-private-channels/workspace/<network>/<channel>/channel/rpcCallHistory/`. Indexed recovery appends to the
199
+ existing history, while `--from-genesis` overwrites it with one full genesis-to-latest scan.
200
+
173
201
  `channel create` is the exception: after the channel is created on-chain, the CLI initializes that new local workspace
174
202
  by replaying from the channel's genesis block because no prior recovery index can exist for a new channel.
175
203
 
@@ -208,7 +236,7 @@ already exists. The channel leader can build the static mirror files with
208
236
  host. If the existing mirror manifest is unreadable or invalid, the leader can use
209
237
  `channel publish-workspace-mirror --force` to write a full checkpoint without trusting that remote
210
238
  manifest as a delta base. The CLI protocol is documented at
211
- https://github.com/tokamak-network/Tokamak-zk-EVM-contracts/blob/main/packages/apps/private-state/docs/channel-workspace-mirror-protocol.md.
239
+ https://github.com/tokamak-network/Tokamak-zk-EVM-contracts/blob/main/docs/dapps/private-state/channel-workspace-mirror-protocol.md.
212
240
 
213
241
  Back up a local wallet with:
214
242
 
@@ -309,9 +337,11 @@ Channel policy warning:
309
337
  a new channel; it does not rewrite the policy of an already-created channel.
310
338
 
311
339
  `private-state-cli help doctor` reports the CLI package version, dependency versions recorded by the last
312
- `private-state-cli install`, selected proof backend runtime versions, current dependency versions through `tokamak-l2js`, and Tokamak zk-EVM runtime
313
- install mode, Docker mode, CUDA runtime metadata, live `nvidia-smi` and Docker GPU probe results, and Groth16
314
- runtime health. The doctor check fails when the Tokamak Docker `useGpus` metadata does not match the live GPU probes.
340
+ `private-state-cli install`, selected proof backend runtime versions when full mode was installed, current dependency
341
+ versions through `tokamak-l2js`, Tokamak zk-EVM runtime install mode, Docker mode, CUDA runtime metadata, live
342
+ `nvidia-smi` and Docker GPU probe results, Groth16 runtime health, deployment artifact readiness, and per-command
343
+ availability. In read-only install mode, proof runtime checks are skipped and proof-backed or channel-mutating
344
+ commands are reported unavailable until full install is completed.
315
345
 
316
346
  Local helper commands:
317
347
 
@@ -561,8 +591,9 @@ command.
561
591
 
562
592
  ## Artifacts
563
593
 
564
- Proof-backed commands require installed bridge, DApp, and Groth16 artifacts. Run `private-state-cli install` before
565
- using bridge-facing commands on a new machine.
594
+ Proof-backed and channel-mutating commands require full installed bridge, DApp, and Groth16 artifacts. Run
595
+ `private-state-cli install` before creating, joining, exiting, or mutating channels on a new machine. Channel-state read
596
+ commands and commands unrelated to channel state can run after `private-state-cli install --read-only`.
566
597
 
567
598
  Channel balance commands such as `wallet deposit-channel` and `wallet withdraw-channel` use the installed Groth16 runtime workspace
568
599
  directly. Proof generation writes to the fixed workspace paths under `~/tokamak-private-channels/groth16/proof`; the CLI
@@ -579,11 +610,16 @@ Release order matters for npm publication. `@tokamak-private-dapps/common-librar
579
610
 
580
611
  It installs the `private-state-cli` terminal command and the local files needed by that command.
581
612
  It does not install bridge contracts, app contracts, or local deployment outputs. The `private-state-cli install`
582
- command provisions the local Tokamak zk-EVM and Groth16 runtime workspaces used by proof-backed commands.
613
+ command defaults to full mode, which provisions local Tokamak zk-EVM and Groth16 runtime workspaces used by
614
+ proof-backed commands. `private-state-cli install --read-only` installs only the public bridge and private-state DApp
615
+ artifacts needed by channel-state read commands and commands that do not depend on channel state.
583
616
 
584
617
  ### When should I run `private-state-cli install`?
585
618
 
586
- Run it once on a new machine, or after public bridge, DApp, Groth16, or Tokamak zk-EVM runtime artifacts are updated.
619
+ Run full install once on a machine that will create, join, exit, or mutate channels. Run read-only install on a machine
620
+ that only needs channel recovery, wallet recovery, metadata lookup, bridge balance lookup, bridge deposit or withdrawal,
621
+ and local import/export/helper commands. Re-run the relevant mode after public bridge, DApp, Groth16, or Tokamak zk-EVM
622
+ runtime artifacts are updated.
587
623
 
588
624
  ### Does this package publish private user data?
589
625
 
@@ -10,7 +10,6 @@ import {
10
10
  handleDepositBridge,
11
11
  handleWithdrawBridge,
12
12
  loadExplicitCommandRuntime,
13
- prepareDeploymentArtifacts,
14
13
  } from "../lib/runtime.mjs";
15
14
 
16
15
  export const accountCommands = Object.freeze({
@@ -24,20 +23,17 @@ export const accountCommands = Object.freeze({
24
23
  },
25
24
  "account-get-bridge-fund": async (args) => {
26
25
  assertAccountGetBridgeFundArgs(args);
27
- const { network, provider } = loadExplicitCommandRuntime(args);
28
- await prepareDeploymentArtifacts(network.chainId);
26
+ const { provider } = loadExplicitCommandRuntime(args, { prepareArtifacts: true });
29
27
  await handleAccountGetBridgeFund({ args, provider });
30
28
  },
31
29
  "account-deposit-bridge": async (args) => {
32
30
  assertDepositBridgeArgs(args);
33
- const { network, provider } = loadExplicitCommandRuntime(args);
34
- await prepareDeploymentArtifacts(network.chainId);
31
+ const { network, provider } = loadExplicitCommandRuntime(args, { prepareArtifacts: true });
35
32
  await handleDepositBridge({ args, network, provider });
36
33
  },
37
34
  "account-withdraw-bridge": async (args) => {
38
35
  assertWithdrawBridgeArgs(args);
39
- const { network, provider } = loadExplicitCommandRuntime(args);
40
- await prepareDeploymentArtifacts(network.chainId);
36
+ const { network, provider } = loadExplicitCommandRuntime(args, { prepareArtifacts: true });
41
37
  await handleWithdrawBridge({ args, network, provider });
42
38
  },
43
39
  });
@@ -16,51 +16,43 @@ import {
16
16
  handleWorkspaceInit,
17
17
  loadExplicitCommandRuntime,
18
18
  loadWalletCommandRuntime,
19
- prepareDeploymentArtifacts,
20
19
  } from "../lib/runtime.mjs";
21
20
 
22
21
  export const channelCommands = Object.freeze({
23
22
  "channel-create": async (args) => {
24
23
  assertCreateChannelArgs(args);
25
- const { network, provider } = loadExplicitCommandRuntime(args);
26
- await prepareDeploymentArtifacts(network.chainId);
24
+ const { network, provider } = loadExplicitCommandRuntime(args, { prepareArtifacts: true });
27
25
  await handleChannelCreate({ args, network, provider });
28
26
  },
29
27
  "channel-recover-workspace": async (args) => {
30
28
  assertRecoverWorkspaceArgs(args);
31
- const { network, provider, rpcUrl } = loadExplicitCommandRuntime(args, { staticNetwork: true });
29
+ const { network, provider, rpcUrl } = loadExplicitCommandRuntime(args, { staticNetwork: true, prepareArtifacts: true });
32
30
  await assertProviderChainIdMatchesNetwork({ provider, network, rpcUrl });
33
- await prepareDeploymentArtifacts(network.chainId);
34
31
  await handleWorkspaceInit({ args, network, provider });
35
32
  },
36
33
  "channel-get-meta": async (args) => {
37
34
  assertGetChannelArgs(args);
38
- const { network, provider } = loadExplicitCommandRuntime(args);
39
- await prepareDeploymentArtifacts(network.chainId);
35
+ const { network, provider } = loadExplicitCommandRuntime(args, { prepareArtifacts: true });
40
36
  await handleGetChannel({ args, network, provider });
41
37
  },
42
38
  "channel-set-workspace-mirror": async (args) => {
43
39
  assertSetWorkspaceMirrorArgs(args);
44
- const { network, provider } = loadExplicitCommandRuntime(args);
45
- await prepareDeploymentArtifacts(network.chainId);
40
+ const { network, provider } = loadExplicitCommandRuntime(args, { prepareArtifacts: true });
46
41
  await handleSetChannelWorkspaceMirror({ args, network, provider });
47
42
  },
48
43
  "channel-publish-workspace-mirror": async (args) => {
49
44
  assertPublishWorkspaceMirrorArgs(args);
50
- const { network, provider } = loadExplicitCommandRuntime(args);
51
- await prepareDeploymentArtifacts(network.chainId);
45
+ const { network, provider } = loadExplicitCommandRuntime(args, { prepareArtifacts: true });
52
46
  await handlePublishChannelWorkspaceMirror({ args, network, provider });
53
47
  },
54
48
  "channel-join": async (args) => {
55
49
  assertJoinChannelArgs(args);
56
- const { network, provider, rpcUrl } = loadExplicitCommandRuntime(args);
57
- await prepareDeploymentArtifacts(network.chainId);
50
+ const { network, provider, rpcUrl } = loadExplicitCommandRuntime(args, { prepareArtifacts: true });
58
51
  await handleJoinChannel({ args, network, provider, rpcUrl });
59
52
  },
60
53
  "channel-exit": async (args) => {
61
54
  assertExitChannelArgs(args);
62
- const { network, provider } = loadWalletCommandRuntime(args);
63
- await prepareDeploymentArtifacts(network.chainId);
55
+ const { provider } = loadWalletCommandRuntime(args, { prepareArtifacts: true });
64
56
  await handleExitChannel({ args, provider });
65
57
  },
66
58
  });
@@ -8,32 +8,27 @@ import {
8
8
  handleTransferNotes,
9
9
  handleWalletGetNotes,
10
10
  loadWalletCommandRuntime,
11
- prepareDeploymentArtifacts,
12
11
  } from "../lib/runtime.mjs";
13
12
 
14
13
  export const notesCommands = Object.freeze({
15
14
  "wallet-mint-notes": async (args) => {
16
15
  assertMintNotesArgs(args);
17
- const { network, provider } = loadWalletCommandRuntime(args);
18
- await prepareDeploymentArtifacts(network.chainId);
16
+ const { provider } = loadWalletCommandRuntime(args, { prepareArtifacts: true });
19
17
  await handleMintNotes({ args, provider });
20
18
  },
21
19
  "wallet-redeem-notes": async (args) => {
22
20
  assertRedeemNotesArgs(args);
23
- const { network, provider } = loadWalletCommandRuntime(args);
24
- await prepareDeploymentArtifacts(network.chainId);
21
+ const { provider } = loadWalletCommandRuntime(args, { prepareArtifacts: true });
25
22
  await handleRedeemNotes({ args, provider });
26
23
  },
27
24
  "wallet-get-notes": async (args) => {
28
25
  assertWalletGetNotesArgs(args);
29
- const { network, provider } = loadWalletCommandRuntime(args);
30
- await prepareDeploymentArtifacts(network.chainId);
26
+ const { provider } = loadWalletCommandRuntime(args, { prepareArtifacts: true });
31
27
  await handleWalletGetNotes({ args, provider });
32
28
  },
33
29
  "wallet-transfer-notes": async (args) => {
34
30
  assertTransferNotesArgs(args);
35
- const { network, provider } = loadWalletCommandRuntime(args);
36
- await prepareDeploymentArtifacts(network.chainId);
31
+ const { provider } = loadWalletCommandRuntime(args, { prepareArtifacts: true });
37
32
  await handleTransferNotes({ args, provider });
38
33
  },
39
34
  });
@@ -20,7 +20,6 @@ import {
20
20
  handleWalletImportKey,
21
21
  loadExplicitCommandRuntime,
22
22
  loadWalletCommandRuntime,
23
- prepareDeploymentArtifacts,
24
23
  } from "../lib/runtime.mjs";
25
24
 
26
25
  export const walletCommands = Object.freeze({
@@ -54,33 +53,28 @@ export const walletCommands = Object.freeze({
54
53
  },
55
54
  "wallet-recover-workspace": async (args) => {
56
55
  assertRecoverWalletArgs(args);
57
- const { network, provider, rpcUrl } = loadExplicitCommandRuntime(args, { staticNetwork: true });
56
+ const { network, provider, rpcUrl } = loadExplicitCommandRuntime(args, { staticNetwork: true, prepareArtifacts: true });
58
57
  await assertProviderChainIdMatchesNetwork({ provider, network, rpcUrl });
59
- await prepareDeploymentArtifacts(network.chainId);
60
58
  await handleRecoverWallet({ args, network, provider, rpcUrl });
61
59
  },
62
60
  "wallet-get-meta": async (args) => {
63
61
  assertWalletGetMetaArgs(args);
64
- const { network, provider } = loadWalletCommandRuntime(args);
65
- await prepareDeploymentArtifacts(network.chainId);
62
+ const { provider } = loadWalletCommandRuntime(args, { prepareArtifacts: true });
66
63
  await handleWalletGetMeta({ args, provider });
67
64
  },
68
65
  "wallet-get-channel-fund": async (args) => {
69
66
  assertWalletGetChannelFundArgs(args);
70
- const { network, provider } = loadWalletCommandRuntime(args);
71
- await prepareDeploymentArtifacts(network.chainId);
67
+ const { provider } = loadWalletCommandRuntime(args, { prepareArtifacts: true });
72
68
  await handleWalletGetChannelFund({ args, provider });
73
69
  },
74
70
  "wallet-deposit-channel": async (args) => {
75
71
  assertWalletChannelMoveArgs(args, "wallet-deposit-channel");
76
- const { network, provider } = loadWalletCommandRuntime(args);
77
- await prepareDeploymentArtifacts(network.chainId);
72
+ const { provider } = loadWalletCommandRuntime(args, { prepareArtifacts: true });
78
73
  await handleGrothVaultMove({ args, provider, direction: "deposit" });
79
74
  },
80
75
  "wallet-withdraw-channel": async (args) => {
81
76
  assertWalletChannelMoveArgs(args, "wallet-withdraw-channel");
82
- const { network, provider } = loadWalletCommandRuntime(args);
83
- await prepareDeploymentArtifacts(network.chainId);
77
+ const { provider } = loadWalletCommandRuntime(args, { prepareArtifacts: true });
84
78
  await handleGrothVaultMove({ args, provider, direction: "withdraw" });
85
79
  },
86
80
  });
@@ -188,6 +188,13 @@ export const PRIVATE_STATE_CLI_FIELD_CATALOG = Object.freeze({
188
188
  valueLabel: "<VERSION>",
189
189
  optional: true,
190
190
  },
191
+ readOnly: {
192
+ label: "Read-Only Install",
193
+ type: "checkbox",
194
+ hint: "Install only artifacts needed by channel-state read commands and commands that do not depend on channel state.",
195
+ option: "--read-only",
196
+ optional: true,
197
+ },
191
198
  fromGenesis: {
192
199
  label: "Scan From Genesis",
193
200
  type: "checkbox",
@@ -195,6 +202,13 @@ export const PRIVATE_STATE_CLI_FIELD_CATALOG = Object.freeze({
195
202
  option: "--from-genesis",
196
203
  optional: true,
197
204
  },
205
+ outputRaw: {
206
+ label: "Output Raw RPC History",
207
+ type: "checkbox",
208
+ hint: "With channel recover-workspace --source rpc, preserve raw JSON-RPC request and response history under the channel workspace.",
209
+ option: "--output-raw",
210
+ optional: true,
211
+ },
198
212
  source: {
199
213
  label: "Recovery Source",
200
214
  type: "select",
@@ -239,7 +253,7 @@ const ACTION_IMPACT_HELP = Object.freeze({
239
253
  acknowledgement: "Requires --acknowledge-action-impact after the user reviews the action-impact warning.",
240
254
  illegalUse: "The command must not be used for money laundering, sanctions evasion, terrorist financing, illegal gambling, criminal-proceeds concealment, or regulatory evasion.",
241
255
  secretRecovery: "Losing wallet secrets, viewing keys, or spending keys can prevent note discovery or note use; the CLI cannot recover lost secrets.",
242
- cexAddress: "Do not use a centralized-exchange controlled address as a self-custody bridge source or direct bridge withdrawal target.",
256
+ exchangeControlledAddress: "Do not use an exchange-controlled address as a self-custody bridge source or direct bridge withdrawal target.",
243
257
  policy: "The user must review the channel policy snapshot before accepting channel-bound actions.",
244
258
  provenance: "Public observers cannot reconstruct private note counterparty relationships or note provenance from public contract state alone.",
245
259
  });
@@ -247,10 +261,12 @@ const ACTION_IMPACT_HELP = Object.freeze({
247
261
  export const PRIVATE_STATE_CLI_COMMANDS = Object.freeze([
248
262
  {
249
263
  id: "install",
250
- description: "Install the Tokamak zk-EVM CLI runtime, Groth16 runtime, and private-state deployment artifacts.",
251
- fields: ["docker", "includeLocalArtifacts", "groth16CliVersion", "tokamakZkEvmCliVersion"],
252
- usage: "optional --docker, --include-local-artifacts, --groth16-cli-version, and --tokamak-zk-evm-cli-version",
264
+ description: "Install private-state CLI runtime artifacts in full or read-only mode.",
265
+ fields: ["readOnly", "docker", "includeLocalArtifacts", "groth16CliVersion", "tokamakZkEvmCliVersion"],
266
+ usage: "optional --read-only, --docker, --include-local-artifacts, --groth16-cli-version, and --tokamak-zk-evm-cli-version",
253
267
  help: [
268
+ "Default full mode installs proof runtimes and all deployment artifacts needed by transaction-sending commands",
269
+ "--read-only installs only artifacts needed by channel-state read commands and commands unrelated to channel state",
254
270
  "Version options install exact CLI package versions; omitted versions resolve to npm registry latest",
255
271
  "Use --docker on Linux to forward Docker mode to the Tokamak zk-EVM and Groth16 runtimes",
256
272
  "Use --include-local-artifacts to also install local deployment/ artifacts from the current working directory",
@@ -295,11 +311,12 @@ export const PRIVATE_STATE_CLI_COMMANDS = Object.freeze([
295
311
  {
296
312
  id: "help-doctor",
297
313
  display: "help doctor",
298
- description: "Check private-state CLI package versions, runtime install state, Docker mode, CUDA mode, and deployment artifacts.",
314
+ description: "Check private-state CLI package versions, install state, deployment artifacts, and command availability.",
299
315
  fields: ["gpu", "json"],
300
316
  usage: "optional --gpu and optional --json",
301
317
  help: [
302
318
  "Prints a concise human-readable table by default; use --json for the full machine-readable report",
319
+ "Reports whether each command is usable with the current read-only or full install state",
303
320
  "Use --gpu to run live NVIDIA/Docker GPU probes",
304
321
  ],
305
322
  },
@@ -352,6 +369,7 @@ export const PRIVATE_STATE_CLI_COMMANDS = Object.freeze([
352
369
  id: "account-get-bridge-fund",
353
370
  display: "account get-bridge-fund",
354
371
  description: "Read the local account's current shared bridge vault balance.",
372
+ installMode: "read-only",
355
373
  fields: ["network", "account"],
356
374
  usage: "--network, --account",
357
375
  },
@@ -359,6 +377,7 @@ export const PRIVATE_STATE_CLI_COMMANDS = Object.freeze([
359
377
  id: "channel-create",
360
378
  display: "channel create",
361
379
  description: "Create a bridge channel and initialize its workspace.",
380
+ installMode: "full",
362
381
  fields: ["channelName", "joinToll", "network", "account"],
363
382
  usage: "--channel-name, --join-toll, --network, --account",
364
383
  help: [
@@ -370,8 +389,9 @@ export const PRIVATE_STATE_CLI_COMMANDS = Object.freeze([
370
389
  id: "channel-recover-workspace",
371
390
  display: "channel recover-workspace",
372
391
  description: "Rebuild the local channel workspace from bridge state.",
373
- fields: ["channelName", "network", "source", "fromGenesis"],
374
- usage: "--channel-name, --network, optional --source, optional --from-genesis",
392
+ installMode: "read-only",
393
+ fields: ["channelName", "network", "source", "fromGenesis", "outputRaw"],
394
+ usage: "--channel-name, --network, optional --source, optional --from-genesis, optional --output-raw",
375
395
  help: [
376
396
  "By default, --source rpc resumes RPC log scanning from the workspace recovery index when available",
377
397
  "--source mirror validates the channel leader's registered checkpoint manifest, downloads only the needed checkpoint or delta bundle, and then replays RPC logs to latest",
@@ -379,6 +399,7 @@ export const PRIVATE_STATE_CLI_COMMANDS = Object.freeze([
379
399
  "Mirror recovery uses a matching delta bundle when available; otherwise a newer verified full checkpoint replaces the local checkpoint before RPC catch-up",
380
400
  "Fails instead of falling back to genesis when no usable recovery index exists",
381
401
  "Use --source rpc --from-genesis to ignore the recovery index and replay logs from channel genesis",
402
+ "--output-raw with --source rpc appends raw JSON-RPC request and response history to method-specific JSON files under the channel workspace rpcCallHistory directory; eth_getLogs is split by event",
382
403
  "--from-genesis moves the existing local channel workspace to workspace-rebuild-backups before writing the current-format workspace; local secrets are preserved",
383
404
  "Prints RPC log scan progress while rebuilding the workspace",
384
405
  ],
@@ -387,6 +408,7 @@ export const PRIVATE_STATE_CLI_COMMANDS = Object.freeze([
387
408
  id: "channel-set-workspace-mirror",
388
409
  display: "channel set-workspace-mirror",
389
410
  description: "Register or update the channel leader's workspace mirror base URL.",
411
+ installMode: "full",
390
412
  fields: ["channelName", "network", "account", "url"],
391
413
  usage: "--channel-name, --network, --account, --url",
392
414
  help: [
@@ -398,6 +420,7 @@ export const PRIVATE_STATE_CLI_COMMANDS = Object.freeze([
398
420
  id: "channel-publish-workspace-mirror",
399
421
  display: "channel publish-workspace-mirror",
400
422
  description: "Build static workspace mirror files for the registered mirror URL.",
423
+ installMode: "read-only",
401
424
  fields: ["channelName", "network", "account", "output", "force"],
402
425
  usage: "--channel-name, --network, --account, --output, optional --force",
403
426
  help: [
@@ -411,6 +434,7 @@ export const PRIVATE_STATE_CLI_COMMANDS = Object.freeze([
411
434
  id: "channel-get-meta",
412
435
  display: "channel get-meta",
413
436
  description: "Read channel existence, manager, vault, toll, refund schedule, and immutable policy snapshot.",
437
+ installMode: "read-only",
414
438
  fields: ["channelName", "network"],
415
439
  usage: "--channel-name, --network",
416
440
  },
@@ -418,12 +442,13 @@ export const PRIVATE_STATE_CLI_COMMANDS = Object.freeze([
418
442
  id: "account-deposit-bridge",
419
443
  display: "account deposit-bridge",
420
444
  description: "Deposit canonical tokens into the shared bridge vault.",
445
+ installMode: "read-only",
421
446
  fields: ["amount", "network", "account", "acknowledgeActionImpact"],
422
447
  usage: "--amount, --network, --account, --acknowledge-action-impact",
423
448
  help: [
424
449
  "Action impact: emits public L1 approval and bridge funding events that expose the local L1 account, bridge vault, amount, and transaction hashes.",
425
450
  "Private note state is not changed by this command.",
426
- ACTION_IMPACT_HELP.cexAddress,
451
+ ACTION_IMPACT_HELP.exchangeControlledAddress,
427
452
  ACTION_IMPACT_HELP.illegalUse,
428
453
  ACTION_IMPACT_HELP.acknowledgement,
429
454
  ],
@@ -432,12 +457,13 @@ export const PRIVATE_STATE_CLI_COMMANDS = Object.freeze([
432
457
  id: "account-withdraw-bridge",
433
458
  display: "account withdraw-bridge",
434
459
  description: "Withdraw tokens from the shared bridge vault back to the wallet.",
460
+ installMode: "read-only",
435
461
  fields: ["amount", "network", "account", "acknowledgeActionImpact"],
436
462
  usage: "--amount, --network, --account, --acknowledge-action-impact",
437
463
  help: [
438
464
  "Action impact: emits a public L1 bridge withdrawal event that exposes the local L1 recipient, bridge vault, amount, and transaction hash.",
439
465
  "Private note state is not changed by this command; prior note provenance is not public by default.",
440
- ACTION_IMPACT_HELP.cexAddress,
466
+ ACTION_IMPACT_HELP.exchangeControlledAddress,
441
467
  ACTION_IMPACT_HELP.illegalUse,
442
468
  ACTION_IMPACT_HELP.acknowledgement,
443
469
  ],
@@ -446,6 +472,7 @@ export const PRIVATE_STATE_CLI_COMMANDS = Object.freeze([
446
472
  id: "wallet-recover-workspace",
447
473
  display: "wallet recover-workspace",
448
474
  description: "Rebuild a recoverable local wallet from on-chain channel state.",
475
+ installMode: "read-only",
449
476
  fields: ["channelName", "network", "account", "fromGenesis"],
450
477
  usage: "--channel-name, --network, --account, optional --from-genesis",
451
478
  help: [
@@ -462,6 +489,7 @@ export const PRIVATE_STATE_CLI_COMMANDS = Object.freeze([
462
489
  id: "channel-join",
463
490
  display: "channel join",
464
491
  description: "Pay the channel join toll and bind a wallet to a channel-specific L2 identity.",
492
+ installMode: "full",
465
493
  fields: ["channelName", "network", "account", "walletSecretPath", "acknowledgeActionImpact"],
466
494
  usage: "--channel-name, --network, --account, --wallet-secret-path, --acknowledge-action-impact",
467
495
  help: [
@@ -481,6 +509,7 @@ export const PRIVATE_STATE_CLI_COMMANDS = Object.freeze([
481
509
  id: "wallet-get-meta",
482
510
  display: "wallet get-meta",
483
511
  description: "Check whether a wallet matches the on-chain channel registration.",
512
+ installMode: "read-only",
484
513
  fields: ["wallet", "network"],
485
514
  usage: "--wallet and --network",
486
515
  help: [
@@ -550,6 +579,7 @@ export const PRIVATE_STATE_CLI_COMMANDS = Object.freeze([
550
579
  id: "wallet-deposit-channel",
551
580
  display: "wallet deposit-channel",
552
581
  description: "Move bridged funds into the channel L2 accounting balance.",
582
+ installMode: "full",
553
583
  fields: ["wallet", "network", "amount", "acknowledgeActionImpact"],
554
584
  usage: "--wallet, --network, --amount, and --acknowledge-action-impact",
555
585
  help: [
@@ -566,6 +596,7 @@ export const PRIVATE_STATE_CLI_COMMANDS = Object.freeze([
566
596
  id: "wallet-withdraw-channel",
567
597
  display: "wallet withdraw-channel",
568
598
  description: "Move channel L2 balance back into the shared bridge vault.",
599
+ installMode: "full",
569
600
  fields: ["wallet", "network", "amount", "acknowledgeActionImpact"],
570
601
  usage: "--wallet, --network, --amount, and --acknowledge-action-impact",
571
602
  help: [
@@ -583,6 +614,7 @@ export const PRIVATE_STATE_CLI_COMMANDS = Object.freeze([
583
614
  id: "wallet-get-channel-fund",
584
615
  display: "wallet get-channel-fund",
585
616
  description: "Read the current channel L2 accounting balance.",
617
+ installMode: "read-only",
586
618
  fields: ["wallet", "network"],
587
619
  usage: "--wallet and --network",
588
620
  help: ["Refreshes the local channel workspace through the saved recovery index before reading the L2 accounting balance when the scan fits the 10 second pre-command budget"],
@@ -591,6 +623,7 @@ export const PRIVATE_STATE_CLI_COMMANDS = Object.freeze([
591
623
  id: "channel-exit",
592
624
  display: "channel exit",
593
625
  description: "Exit a channel. Both the CLI and bridge contract require a zero channel balance.",
626
+ installMode: "full",
594
627
  fields: ["wallet", "network"],
595
628
  usage: "--wallet and --network",
596
629
  help: [
@@ -602,6 +635,7 @@ export const PRIVATE_STATE_CLI_COMMANDS = Object.freeze([
602
635
  id: "wallet-mint-notes",
603
636
  display: "wallet mint-notes",
604
637
  description: "Mint one or two private-state notes from the wallet's channel balance.",
638
+ installMode: "full",
605
639
  fields: ["wallet", "network", "amounts", "acknowledgeActionImpact", "txSubmitter"],
606
640
  usage: "--wallet, --network, --amounts, --acknowledge-action-impact, and optional --tx-submitter",
607
641
  help: [
@@ -621,6 +655,7 @@ export const PRIVATE_STATE_CLI_COMMANDS = Object.freeze([
621
655
  id: "wallet-transfer-notes",
622
656
  display: "wallet transfer-notes",
623
657
  description: "Spend input notes into the registered 1->1, 1->2, or 2->1 private transfer shapes.",
658
+ installMode: "full",
624
659
  fields: ["wallet", "network", "noteIds", "recipients", "amounts", "acknowledgeActionImpact", "txSubmitter"],
625
660
  usage: "--wallet, --network, --note-ids, --recipients, --amounts, --acknowledge-action-impact, and optional --tx-submitter",
626
661
  help: [
@@ -639,6 +674,7 @@ export const PRIVATE_STATE_CLI_COMMANDS = Object.freeze([
639
674
  id: "wallet-redeem-notes",
640
675
  display: "wallet redeem-notes",
641
676
  description: "Redeem one tracked note back into the wallet's channel balance.",
677
+ installMode: "full",
642
678
  fields: ["wallet", "network", "noteIds", "acknowledgeActionImpact", "txSubmitter"],
643
679
  usage: "--wallet, --network, --note-ids, --acknowledge-action-impact, and optional --tx-submitter",
644
680
  help: [
@@ -657,6 +693,7 @@ export const PRIVATE_STATE_CLI_COMMANDS = Object.freeze([
657
693
  id: "wallet-get-notes",
658
694
  display: "wallet get-notes",
659
695
  description: "Refresh received notes when the saved recovery index is recent, then show tracked note state.",
696
+ installMode: "read-only",
660
697
  fields: ["wallet", "network", "exportEvidence", "acknowledgeFullNotePlaintextExport"],
661
698
  usage: "--wallet, --network, optional --export-evidence, and optional --acknowledge-full-note-plaintext-export",
662
699
  help: [
@@ -669,6 +706,10 @@ export const PRIVATE_STATE_CLI_COMMANDS = Object.freeze([
669
706
  },
670
707
  ]);
671
708
 
709
+ export function privateStateCliCommandInstallMode(command) {
710
+ return command.installMode ?? "none";
711
+ }
712
+
672
713
  export function privateStateCliCommandDisplay(command) {
673
714
  return command.display ?? command.id;
674
715
  }
@@ -698,7 +739,7 @@ export function privateStateCliCommandSynopsis(command) {
698
739
  return null;
699
740
  }
700
741
  const valueLabel = field.valueLabel ?? field.placeholderLabel ?? `<${field.label?.toUpperCase().replace(/\s+/g, "_") ?? "VALUE"}>`;
701
- const option = field.type === "checkbox" || fieldKey === "fromGenesis"
742
+ const option = field.type === "checkbox"
702
743
  ? field.option
703
744
  : `${field.option} ${valueLabel}`;
704
745
  return field.optional || optionalFields.has(fieldKey) ? `[${option}]` : option;