@tokamak-private-dapps/private-state-cli 1.1.1 → 1.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
@@ -1,5 +1,44 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.2.1 - 2026-05-11
4
+
5
+ - Changed pre-command automatic recovery from an RPC log request time estimate to a fixed
6
+ 7,200-block recovery delta budget.
7
+
8
+ ## 1.2.0 - 2026-05-08
9
+
10
+ - Added optional channel workspace mirror recovery. `channel recover-workspace` now accepts
11
+ `--source rpc|mirror`, with `rpc` remaining the default when `--source` is omitted.
12
+ - Added `channel set-workspace-mirror` so a channel leader can register the official workspace
13
+ mirror base URL stored in `BridgeCore`.
14
+ - Added mirror checkpoint validation that checks signed checkpoint metadata before downloading
15
+ bundles, then validates downloaded checkpoint or delta bundle contents against on-chain channel
16
+ metadata before replaying the remaining RPC log delta to the latest block.
17
+ - Documented the static server protocol for channel workspace mirrors.
18
+ - Removed `--source auto` from `channel recover-workspace`; recovery source is now either
19
+ `rpc` or `mirror`.
20
+ - Required `channel recover-workspace --from-genesis` to be paired with explicit `--source rpc`
21
+ so genesis replay cannot be requested accidentally through an omitted source.
22
+ - Restored pre-command workspace refresh for commands that require current local state, but limited
23
+ automatic refresh to saved recovery indexes. Automatic command preflight never replays from
24
+ genesis and points users to explicit `channel recover-workspace` or `wallet recover-workspace`
25
+ when a genesis rebuild is required.
26
+ - Restored received-note event-log refresh for `wallet get-notes`, `wallet transfer-notes`, and
27
+ `wallet redeem-notes`, limited to the saved wallet note recovery index.
28
+ - Reworked workspace mirror recovery around leader-signed checkpoint manifests and
29
+ delta bundles. When a local recovery index exists, the CLI prechecks the mirror checkpoint and
30
+ downloads only the matching delta bundle instead of a full workspace bundle.
31
+ - Removed the version segment from workspace mirror URLs and kept the protocol version only in
32
+ manifest and bundle metadata.
33
+ - Added `channel publish-workspace-mirror` to build static mirror files when the local workspace is
34
+ current and ahead of the registered mirror checkpoint.
35
+ - Added `channel publish-workspace-mirror --force` so a channel leader can repair an unreadable or
36
+ invalid remote mirror manifest by publishing a full checkpoint without using that manifest as a
37
+ delta base.
38
+ - Required mirror bundle `sizeBytes` and enforce it as the download limit before verifying bundle
39
+ contents.
40
+ - Kept streaming checkpoint or delta bundle download progress with an estimated remaining time.
41
+
3
42
  ## 1.1.1 - 2026-05-08
4
43
 
5
44
  - Added bridge deployment verification support for Etherscan-compatible explorers, including
package/README.md CHANGED
@@ -99,26 +99,37 @@ continues to print the same command list for shell compatibility.
99
99
 
100
100
  Workspace recovery commands use the saved recovery index by default. If the local workspace is missing, corrupted, or
101
101
  does not contain a usable index, `channel recover-workspace` and `wallet recover-workspace` stop with an explicit error instead of
102
- silently replaying logs from channel genesis. Use `--from-genesis` only when you intentionally want to rebuild from the
103
- channel creation block:
102
+ silently replaying logs from channel genesis. Use `--source rpc --from-genesis` only when you intentionally want to
103
+ rebuild channel workspace state from the channel creation block:
104
104
 
105
105
  ```bash
106
- private-state-cli channel recover-workspace --channel-name <CHANNEL> --network mainnet --from-genesis
106
+ private-state-cli channel recover-workspace --channel-name <CHANNEL> --network mainnet --source rpc --from-genesis
107
107
  private-state-cli wallet recover-workspace --channel-name <CHANNEL> --network mainnet --account <ACCOUNT> --from-genesis
108
108
  ```
109
109
 
110
110
  `channel create` is the exception: after the channel is created on-chain, the CLI initializes that new local workspace
111
111
  by replaying from the channel's genesis block because no prior recovery index can exist for a new channel.
112
112
 
113
- `channel join` requires the channel workspace to have a usable recovery index and refreshes that workspace through the
114
- indexed path before submitting the registration transaction. For a channel that was created elsewhere, run
115
- `channel recover-workspace --from-genesis` once before joining; later joins and wallet commands resume from the saved
116
- index instead of silently replaying from genesis.
113
+ `channel join` refreshes stale channel workspace state through the saved recovery index before submitting the
114
+ registration transaction. For a channel that was created elsewhere, run `channel recover-workspace --source rpc --from-genesis`
115
+ once before joining, or recover from a registered workspace mirror; later joins and wallet commands resume from the
116
+ saved index instead of silently replaying from genesis.
117
117
 
118
118
  Wallet getter commands that need channel state, including `wallet get-meta`, `wallet get-channel-fund`, and
119
- `wallet get-notes`, follow the same indexed recovery rule before reading local or on-chain state. `wallet get-notes` also uses
120
- the wallet's saved note-receive scan index for encrypted note delivery logs. If either index is unusable, the command
121
- stops and asks the user to run the appropriate recovery command with `--from-genesis`.
119
+ `wallet get-notes`, refresh stale local workspaces through saved recovery indexes before reading state. `wallet get-notes`
120
+ also refreshes received-note logs through the saved wallet note recovery index. Automatic refresh never replays from
121
+ channel genesis and only runs when the recovery delta fits within the 7,200-block pre-command budget. If a saved index
122
+ is missing, unusable, or too far behind, the command stops and asks the user to run the appropriate recovery command
123
+ with `--from-genesis` explicitly when needed.
124
+
125
+ Channel leaders can optionally register a workspace mirror server so users can bootstrap recovery
126
+ from a signed checkpoint and download only the local-to-checkpoint delta when a local recovery index
127
+ already exists. The channel leader can build the static mirror files with
128
+ `channel publish-workspace-mirror` and then deploy the output directory to the registered mirror
129
+ host. If the existing mirror manifest is unreadable or invalid, the leader can use
130
+ `channel publish-workspace-mirror --force` to write a full checkpoint without trusting that remote
131
+ manifest as a delta base. The CLI protocol is documented at
132
+ https://github.com/tokamak-network/Tokamak-zk-EVM-contracts/blob/main/packages/apps/private-state/docs/channel-workspace-mirror-protocol.md.
122
133
 
123
134
  Back up a local wallet with:
124
135
 
@@ -134,7 +145,7 @@ run `channel recover-workspace` before using wallet commands that need channel s
134
145
 
135
146
  ```bash
136
147
  private-state-cli wallet import --input ./wallet-backup.zip
137
- private-state-cli channel recover-workspace --channel-name <CHANNEL> --network mainnet --from-genesis
148
+ private-state-cli channel recover-workspace --channel-name <CHANNEL> --network mainnet --source rpc --from-genesis
138
149
  ```
139
150
 
140
151
  For a wider backup that can run wallet commands immediately when the imported channel workspace cache is still
@@ -330,7 +341,7 @@ Suggested interaction flow:
330
341
  `wallet mint-notes`.
331
342
  7. For a private transfer, select available note IDs from `wallet get-notes`, find the recipient L2 address from
332
343
  `wallet get-meta`, then build `wallet transfer-notes`.
333
- 8. After transfer, guide the recipient to run `wallet get-notes` to recover received notes from event logs.
344
+ 8. After transfer, guide the recipient to run `wallet get-notes`; it refreshes received notes from the saved recovery index when the delta fits the 7,200-block pre-command budget. If the index is missing or too far behind, explain `wallet recover-workspace --from-genesis`.
334
345
 
335
346
  Example onboarding explanation for `channel join`:
336
347
 
@@ -69,10 +69,10 @@ export const PRIVATE_STATE_CLI_FIELD_CATALOG = Object.freeze({
69
69
  option: "--wallet",
70
70
  },
71
71
  output: {
72
- label: "Output ZIP",
72
+ label: "Output Path",
73
73
  type: "text",
74
- placeholder: "/path/to/wallet-export.zip",
75
- valueLabel: "<ZIP>",
74
+ placeholder: "/path/to/output",
75
+ valueLabel: "<PATH>",
76
76
  option: "--output",
77
77
  },
78
78
  input: {
@@ -157,10 +157,34 @@ export const PRIVATE_STATE_CLI_FIELD_CATALOG = Object.freeze({
157
157
  fromGenesis: {
158
158
  label: "Scan From Genesis",
159
159
  type: "checkbox",
160
- hint: "Ignore the local recovery index and replay channel logs from genesis.",
160
+ hint: "Requires --source rpc. Ignore the local recovery index and replay channel logs from genesis.",
161
161
  option: "--from-genesis",
162
162
  optional: true,
163
163
  },
164
+ source: {
165
+ label: "Recovery Source",
166
+ type: "select",
167
+ options: ["rpc", "mirror"],
168
+ valueLabel: "<rpc|mirror>",
169
+ hint: "Optional. Defaults to rpc. mirror validates the channel leader's checkpoint manifest and downloads only the needed checkpoint or delta bundle before RPC delta replay.",
170
+ option: "--source",
171
+ optional: true,
172
+ },
173
+ url: {
174
+ label: "Workspace Mirror URL",
175
+ type: "text",
176
+ placeholder: "https://mirror.example",
177
+ valueLabel: "<URL>",
178
+ hint: "Base URL for the channel workspace mirror protocol.",
179
+ option: "--url",
180
+ },
181
+ force: {
182
+ label: "Force",
183
+ type: "checkbox",
184
+ hint: "Ignore an unreadable or invalid existing mirror manifest and publish a full checkpoint instead of a delta.",
185
+ option: "--force",
186
+ optional: true,
187
+ },
164
188
  json: {
165
189
  label: "JSON Output",
166
190
  type: "checkbox",
@@ -280,15 +304,40 @@ export const PRIVATE_STATE_CLI_COMMANDS = Object.freeze([
280
304
  id: "channel-recover-workspace",
281
305
  display: "channel recover-workspace",
282
306
  description: "Rebuild the local channel workspace from bridge state.",
283
- fields: ["channelName", "network", "fromGenesis", "rpcUrl"],
284
- usage: "--channel-name, --network, optional --from-genesis, and optional --rpc-url",
307
+ fields: ["channelName", "network", "source", "fromGenesis", "rpcUrl"],
308
+ usage: "--channel-name, --network, optional --source, optional --from-genesis, and optional --rpc-url",
285
309
  help: [
286
- "By default, resumes RPC log scanning from the workspace recovery index when available",
310
+ "By default, --source rpc resumes RPC log scanning from the workspace recovery index when available",
311
+ "--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",
287
312
  "Fails instead of falling back to genesis when no usable recovery index exists",
288
- "Use --from-genesis to ignore the recovery index and replay logs from channel genesis",
313
+ "Use --source rpc --from-genesis to ignore the recovery index and replay logs from channel genesis",
289
314
  "Prints RPC log scan progress while rebuilding the workspace",
290
315
  ],
291
316
  },
317
+ {
318
+ id: "channel-set-workspace-mirror",
319
+ display: "channel set-workspace-mirror",
320
+ description: "Register or update the channel leader's workspace mirror base URL.",
321
+ fields: ["channelName", "network", "account", "url", "rpcUrl"],
322
+ usage: "--channel-name, --network, --account, --url, and optional --rpc-url",
323
+ help: [
324
+ "Only the on-chain channel leader can update the registered mirror URL",
325
+ "The URL points to a server implementing the private-state channel workspace mirror protocol",
326
+ ],
327
+ },
328
+ {
329
+ id: "channel-publish-workspace-mirror",
330
+ display: "channel publish-workspace-mirror",
331
+ description: "Build static workspace mirror files for the registered mirror URL.",
332
+ fields: ["channelName", "network", "account", "output", "force", "rpcUrl"],
333
+ usage: "--channel-name, --network, --account, --output, optional --force, and optional --rpc-url",
334
+ help: [
335
+ "Requires the local channel workspace to be current and ahead of the registered mirror checkpoint",
336
+ "--force ignores an unreadable or invalid existing mirror manifest and publishes a full checkpoint without a delta",
337
+ "Writes manifest.json, checkpoint.zip, and any needed delta bundle under the workspace mirror static path",
338
+ "Does not upload files to a remote server; deploy the output directory to the registered HTTPS mirror host",
339
+ ],
340
+ },
292
341
  {
293
342
  id: "channel-get-meta",
294
343
  display: "channel get-meta",
@@ -332,8 +381,8 @@ export const PRIVATE_STATE_CLI_COMMANDS = Object.freeze([
332
381
  fields: ["channelName", "network", "account", "walletSecretPath", "rpcUrl"],
333
382
  usage: "--channel-name, --network, --account, --wallet-secret-path, and optional --rpc-url",
334
383
  help: [
335
- "Requires a recovered channel workspace and refreshes it through the workspace recovery index before joining",
336
- "Run channel recover-workspace --from-genesis once if no usable local recovery index exists",
384
+ "Refreshes the local channel workspace through the saved recovery index before joining when the scan fits the 10 second pre-command budget",
385
+ "Fails instead of replaying from genesis; run channel recover-workspace --source rpc --from-genesis when a genesis rebuild is required",
337
386
  "--wallet-secret-path imports an existing source secret file into the protected wallet-local secret file",
338
387
  "Prints the immutable policy snapshot before first registration",
339
388
  ],
@@ -344,7 +393,7 @@ export const PRIVATE_STATE_CLI_COMMANDS = Object.freeze([
344
393
  description: "Check whether a wallet matches the on-chain channel registration.",
345
394
  fields: ["wallet", "network"],
346
395
  usage: "--wallet and --network",
347
- help: ["Refreshes channel state through the workspace recovery index before reading registration metadata"],
396
+ help: ["Refreshes the local channel workspace through the saved recovery index before reading registration metadata when the scan fits the 10 second pre-command budget"],
348
397
  },
349
398
  {
350
399
  id: "wallet-list",
@@ -384,6 +433,7 @@ export const PRIVATE_STATE_CLI_COMMANDS = Object.freeze([
384
433
  description: "Move bridged funds into the channel L2 accounting balance.",
385
434
  fields: ["wallet", "network", "amount"],
386
435
  usage: "--wallet, --network, and --amount",
436
+ help: ["Refreshes the local channel workspace through the saved recovery index before proving the deposit when the scan fits the 10 second pre-command budget"],
387
437
  },
388
438
  {
389
439
  id: "wallet-withdraw-channel",
@@ -391,6 +441,7 @@ export const PRIVATE_STATE_CLI_COMMANDS = Object.freeze([
391
441
  description: "Move channel L2 balance back into the shared bridge vault.",
392
442
  fields: ["wallet", "network", "amount"],
393
443
  usage: "--wallet, --network, and --amount",
444
+ help: ["Refreshes the local channel workspace through the saved recovery index before proving the withdrawal when the scan fits the 10 second pre-command budget"],
394
445
  },
395
446
  {
396
447
  id: "wallet-get-channel-fund",
@@ -398,7 +449,7 @@ export const PRIVATE_STATE_CLI_COMMANDS = Object.freeze([
398
449
  description: "Read the current channel L2 accounting balance.",
399
450
  fields: ["wallet", "network"],
400
451
  usage: "--wallet and --network",
401
- help: ["Refreshes channel state through the workspace recovery index before reading the L2 accounting balance"],
452
+ 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"],
402
453
  },
403
454
  {
404
455
  id: "channel-exit",
@@ -406,6 +457,7 @@ export const PRIVATE_STATE_CLI_COMMANDS = Object.freeze([
406
457
  description: "Exit a channel. Both the CLI and bridge contract require a zero channel balance.",
407
458
  fields: ["wallet", "network"],
408
459
  usage: "--wallet and --network",
460
+ help: ["Refreshes the local channel workspace through the saved recovery index before checking the channel balance when the scan fits the 10 second pre-command budget"],
409
461
  },
410
462
  {
411
463
  id: "wallet-mint-notes",
@@ -413,7 +465,10 @@ export const PRIVATE_STATE_CLI_COMMANDS = Object.freeze([
413
465
  description: "Mint one or two private-state notes from the wallet's channel balance.",
414
466
  fields: ["wallet", "network", "amounts", "txSubmitter"],
415
467
  usage: "--wallet, --network, --amounts, and optional --tx-submitter",
416
- help: ["Use --tx-submitter <ACCOUNT> to let a separate local L1 account pay gas for stronger transaction privacy"],
468
+ help: [
469
+ "Refreshes the local channel workspace through the saved recovery index before proving the mint when the scan fits the 10 second pre-command budget",
470
+ "Use --tx-submitter <ACCOUNT> to let a separate local L1 account pay gas for stronger transaction privacy",
471
+ ],
417
472
  },
418
473
  {
419
474
  id: "wallet-transfer-notes",
@@ -421,7 +476,10 @@ export const PRIVATE_STATE_CLI_COMMANDS = Object.freeze([
421
476
  description: "Spend input notes into the registered 1->1, 1->2, or 2->1 private transfer shapes.",
422
477
  fields: ["wallet", "network", "noteIds", "recipients", "amounts", "txSubmitter"],
423
478
  usage: "--wallet, --network, --note-ids, --recipients, --amounts, and optional --tx-submitter",
424
- help: ["Use --tx-submitter <ACCOUNT> to let a separate local L1 account pay gas for stronger transaction privacy"],
479
+ help: [
480
+ "Refreshes the local channel workspace and received-note logs through saved recovery indexes before proving the transfer when scans fit the 10 second pre-command budget",
481
+ "Use --tx-submitter <ACCOUNT> to let a separate local L1 account pay gas for stronger transaction privacy",
482
+ ],
425
483
  },
426
484
  {
427
485
  id: "wallet-redeem-notes",
@@ -429,17 +487,21 @@ export const PRIVATE_STATE_CLI_COMMANDS = Object.freeze([
429
487
  description: "Redeem one tracked note back into the wallet's channel balance.",
430
488
  fields: ["wallet", "network", "noteIds", "txSubmitter"],
431
489
  usage: "--wallet, --network, --note-ids, and optional --tx-submitter",
432
- help: ["Use --tx-submitter <ACCOUNT> to let a separate local L1 account pay gas for stronger transaction privacy"],
490
+ help: [
491
+ "Refreshes the local channel workspace and received-note logs through saved recovery indexes before proving the redeem when scans fit the 10 second pre-command budget",
492
+ "Use --tx-submitter <ACCOUNT> to let a separate local L1 account pay gas for stronger transaction privacy",
493
+ ],
433
494
  },
434
495
  {
435
496
  id: "wallet-get-notes",
436
497
  display: "wallet get-notes",
437
- description: "Show the wallet's tracked note state and refresh received notes.",
498
+ description: "Refresh received notes when the saved recovery index is recent, then show tracked note state.",
438
499
  fields: ["wallet", "network"],
439
500
  usage: "--wallet and --network",
440
501
  help: [
441
- "Refreshes channel state through the workspace recovery index before reading notes",
442
- "Refreshes received-note logs through the wallet note recovery index",
502
+ "Refreshes the local channel workspace through the saved recovery index before reading notes when the scan fits the 10 second pre-command budget",
503
+ "Refreshes received-note logs through the saved wallet note recovery index when the scan fits the 10 second pre-command budget",
504
+ "Fails instead of replaying from genesis; run wallet recover-workspace --from-genesis when a genesis rebuild is required",
443
505
  ],
444
506
  },
445
507
  ]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tokamak-private-dapps/private-state-cli",
3
- "version": "1.1.1",
3
+ "version": "1.2.1",
4
4
  "description": "Command-line client for the Tokamak private-state DApp.",
5
5
  "license": "MIT OR Apache-2.0",
6
6
  "author": "Tokamak Network",