@needmoretruth/nmts-cli 0.17.2 → 0.34.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.
Files changed (419) hide show
  1. package/AGENTS.md +124 -516
  2. package/CHANGELOG.md +98 -0
  3. package/README.ko.md +374 -467
  4. package/README.md +384 -465
  5. package/dist/account-proof.d.ts +29 -0
  6. package/dist/account-proof.js +6 -6
  7. package/dist/account.d.ts +18 -0
  8. package/dist/account.js +5 -5
  9. package/dist/agent-host.d.ts +71 -0
  10. package/dist/agent-host.js +169 -0
  11. package/dist/api-advice.d.ts +2 -0
  12. package/dist/api-advice.js +210 -0
  13. package/dist/api-key.d.ts +79 -0
  14. package/dist/api-key.js +7 -7
  15. package/dist/api-refusal.d.ts +14 -0
  16. package/dist/api-refusal.js +8 -0
  17. package/dist/api.d.ts +114 -0
  18. package/dist/api.js +59 -162
  19. package/dist/args.d.ts +160 -0
  20. package/dist/args.js +118 -30
  21. package/dist/artifact-about.d.ts +49 -0
  22. package/dist/autonomy.d.ts +39 -0
  23. package/dist/autonomy.js +110 -32
  24. package/dist/code-access.d.ts +29 -0
  25. package/dist/code-access.js +3 -3
  26. package/dist/code-vault.d.ts +38 -0
  27. package/dist/code-vault.js +8 -8
  28. package/dist/collision.d.ts +67 -0
  29. package/dist/collision.js +1 -1
  30. package/dist/commands/accept-terms.d.ts +11 -0
  31. package/dist/commands/accept-terms.js +106 -0
  32. package/dist/commands/balance.d.ts +7 -0
  33. package/dist/commands/balance.js +54 -1
  34. package/dist/commands/create-code-file.d.ts +24 -0
  35. package/dist/commands/create-code-file.js +93 -0
  36. package/dist/commands/create-link.d.ts +10 -0
  37. package/dist/commands/create-link.js +232 -0
  38. package/dist/commands/create.d.ts +20 -0
  39. package/dist/commands/create.js +43 -102
  40. package/dist/commands/credits.d.ts +9 -0
  41. package/dist/commands/credits.js +110 -0
  42. package/dist/commands/delete-account.d.ts +11 -0
  43. package/dist/commands/delete-account.js +63 -0
  44. package/dist/commands/deposit.d.ts +7 -0
  45. package/dist/commands/deposit.js +85 -0
  46. package/dist/commands/devices.d.ts +12 -0
  47. package/dist/commands/devices.js +144 -0
  48. package/dist/commands/documents.d.ts +35 -0
  49. package/dist/commands/documents.js +212 -0
  50. package/dist/commands/env.d.ts +5 -0
  51. package/dist/commands/env.js +10 -2
  52. package/dist/commands/erase.d.ts +13 -0
  53. package/dist/commands/erase.js +193 -0
  54. package/dist/commands/expiring.d.ts +19 -0
  55. package/dist/commands/extend.d.ts +33 -0
  56. package/dist/commands/extend.js +54 -15
  57. package/dist/commands/get.d.ts +18 -0
  58. package/dist/commands/key-manage.d.ts +11 -0
  59. package/dist/commands/key-manage.js +162 -0
  60. package/dist/commands/key.d.ts +28 -0
  61. package/dist/commands/key.js +249 -0
  62. package/dist/commands/kit.d.ts +11 -0
  63. package/dist/commands/kit.js +7 -7
  64. package/dist/commands/listfile.d.ts +13 -0
  65. package/dist/commands/listfile.js +12 -5
  66. package/dist/commands/login.d.ts +18 -0
  67. package/dist/commands/login.js +19 -19
  68. package/dist/commands/logout.d.ts +1 -0
  69. package/dist/commands/logout.js +1 -1
  70. package/dist/commands/losses.d.ts +12 -0
  71. package/dist/commands/losses.js +180 -0
  72. package/dist/commands/ls.d.ts +23 -0
  73. package/dist/commands/marks-dispatch.d.ts +2 -0
  74. package/dist/commands/marks-dispatch.js +51 -0
  75. package/dist/commands/marks.d.ts +46 -0
  76. package/dist/commands/marks.js +80 -0
  77. package/dist/commands/mcp.d.ts +24 -0
  78. package/dist/commands/mcp.js +70 -5
  79. package/dist/commands/mode.d.ts +8 -0
  80. package/dist/commands/mode.js +60 -24
  81. package/dist/commands/on-collision.d.ts +6 -0
  82. package/dist/commands/organise.d.ts +46 -0
  83. package/dist/commands/padding.d.ts +7 -0
  84. package/dist/commands/padding.js +114 -0
  85. package/dist/commands/public-code.d.ts +9 -0
  86. package/dist/commands/public-code.js +8 -8
  87. package/dist/commands/pull.d.ts +27 -0
  88. package/dist/commands/push-wallet.d.ts +19 -0
  89. package/dist/commands/push-wallet.js +69 -0
  90. package/dist/commands/push.d.ts +47 -0
  91. package/dist/commands/push.js +37 -7
  92. package/dist/commands/put-wallet.d.ts +77 -0
  93. package/dist/commands/put-wallet.js +316 -0
  94. package/dist/commands/put.d.ts +56 -0
  95. package/dist/commands/put.js +53 -6
  96. package/dist/commands/rebuild.d.ts +18 -0
  97. package/dist/commands/rebuild.js +108 -10
  98. package/dist/commands/receive.d.ts +24 -0
  99. package/dist/commands/recovery-dispatch.d.ts +2 -0
  100. package/dist/commands/recovery-dispatch.js +34 -0
  101. package/dist/commands/recovery-list.d.ts +11 -0
  102. package/dist/commands/recovery-list.js +6 -6
  103. package/dist/commands/recovery.d.ts +25 -0
  104. package/dist/commands/recovery.js +2 -2
  105. package/dist/commands/rollback.d.ts +9 -0
  106. package/dist/commands/rollback.js +118 -0
  107. package/dist/commands/s3.d.ts +23 -0
  108. package/dist/commands/s3.js +6 -6
  109. package/dist/commands/settings.d.ts +5 -0
  110. package/dist/commands/settings.js +21 -6
  111. package/dist/commands/share-dispatch.d.ts +2 -0
  112. package/dist/commands/share-dispatch.js +46 -0
  113. package/dist/commands/share.d.ts +14 -0
  114. package/dist/commands/share.js +13 -3
  115. package/dist/commands/shares-sent.d.ts +18 -0
  116. package/dist/commands/shares-sent.js +95 -0
  117. package/dist/commands/support.d.ts +24 -0
  118. package/dist/commands/support.js +321 -0
  119. package/dist/commands/sweep.d.ts +11 -0
  120. package/dist/commands/tip.d.ts +11 -0
  121. package/dist/commands/tip.js +86 -0
  122. package/dist/commands/trash.d.ts +17 -0
  123. package/dist/commands/trash.js +1 -1
  124. package/dist/commands/trial.d.ts +7 -0
  125. package/dist/commands/trial.js +16 -18
  126. package/dist/commands/unlock.d.ts +16 -0
  127. package/dist/commands/unlock.js +159 -0
  128. package/dist/commands/update.d.ts +42 -0
  129. package/dist/commands/update.js +1 -1
  130. package/dist/commands/usage.d.ts +8 -0
  131. package/dist/commands/verify.d.ts +18 -0
  132. package/dist/commands/verify.js +10 -6
  133. package/dist/commands/wallet-activity.d.ts +26 -0
  134. package/dist/commands/wallet-activity.js +117 -0
  135. package/dist/commands/wallet-donate.d.ts +26 -0
  136. package/dist/commands/wallet-donate.js +163 -0
  137. package/dist/commands/wallet-hall.d.ts +35 -0
  138. package/dist/commands/wallet-hall.js +214 -0
  139. package/dist/commands/wallet-send.d.ts +22 -0
  140. package/dist/commands/wallet-send.js +153 -0
  141. package/dist/commands/wallet-storage-ops.d.ts +24 -0
  142. package/dist/commands/wallet-storage-ops.js +215 -0
  143. package/dist/commands/wallet-storage.d.ts +18 -0
  144. package/dist/commands/wallet-storage.js +95 -0
  145. package/dist/commands/wallet-swap.d.ts +32 -0
  146. package/dist/commands/wallet-swap.js +287 -0
  147. package/dist/commands/wallet.d.ts +36 -0
  148. package/dist/commands/wallet.js +57 -15
  149. package/dist/commands/whoami.d.ts +10 -0
  150. package/dist/commands/whoami.js +24 -6
  151. package/dist/consent.d.ts +140 -0
  152. package/dist/consent.js +89 -30
  153. package/dist/credentials.d.ts +146 -0
  154. package/dist/credentials.js +10 -10
  155. package/dist/crypto-surface.d.ts +197 -0
  156. package/dist/crypto.d.ts +81 -0
  157. package/dist/crypto.js +13 -0
  158. package/dist/deposit.d.ts +25 -0
  159. package/dist/deposit.js +78 -0
  160. package/dist/download-part.d.ts +40 -0
  161. package/dist/download-sink.d.ts +76 -0
  162. package/dist/download.d.ts +79 -0
  163. package/dist/download.js +3 -3
  164. package/dist/drive-paths.d.ts +73 -0
  165. package/dist/environment.d.ts +62 -0
  166. package/dist/environment.js +29 -9
  167. package/dist/errors.d.ts +22 -0
  168. package/dist/errors.js +3 -3
  169. package/dist/exit.d.ts +44 -0
  170. package/dist/expiry.d.ts +58 -0
  171. package/dist/extend-budget.d.ts +35 -0
  172. package/dist/extend-budget.js +81 -0
  173. package/dist/extend-chain.d.ts +43 -0
  174. package/dist/extend-chain.js +40 -3
  175. package/dist/extend-plan.d.ts +148 -0
  176. package/dist/extend-plan.js +1 -1
  177. package/dist/gate.d.ts +18 -0
  178. package/dist/gate.js +100 -0
  179. package/dist/guards.d.ts +2 -0
  180. package/dist/help.d.ts +20 -0
  181. package/dist/help.js +199 -33
  182. package/dist/human-check.d.ts +23 -0
  183. package/dist/index.d.ts +42 -0
  184. package/dist/index.js +49 -0
  185. package/dist/item-trash.d.ts +1 -0
  186. package/dist/kit-file.d.ts +56 -0
  187. package/dist/kit-file.js +10 -10
  188. package/dist/list-file.d.ts +88 -0
  189. package/dist/list-file.js +90 -7
  190. package/dist/list-view-find.d.ts +15 -0
  191. package/dist/list-view-order.d.ts +32 -0
  192. package/dist/main.d.ts +12 -0
  193. package/dist/main.js +112 -113
  194. package/dist/manifest-chunk-cache.d.ts +14 -0
  195. package/dist/manifest-chunk-cache.js +103 -0
  196. package/dist/manifest-chunk-flow.d.ts +81 -0
  197. package/dist/manifest-chunk-flow.js +307 -0
  198. package/dist/manifest-create.d.ts +23 -0
  199. package/dist/manifest-create.js +26 -30
  200. package/dist/manifest-write.d.ts +161 -0
  201. package/dist/manifest-write.js +42 -33
  202. package/dist/manifest.d.ts +82 -0
  203. package/dist/manifest.js +69 -21
  204. package/dist/mark-render.d.ts +26 -0
  205. package/dist/mcp-args.d.ts +17 -0
  206. package/dist/mcp-ask.d.ts +43 -0
  207. package/dist/mcp-ask.js +100 -0
  208. package/dist/mcp-tools/context.d.ts +46 -0
  209. package/dist/mcp-tools/credits.d.ts +3 -0
  210. package/dist/mcp-tools/credits.js +61 -0
  211. package/dist/mcp-tools/files.d.ts +3 -0
  212. package/dist/mcp-tools/files.js +34 -0
  213. package/dist/mcp-tools/organise.d.ts +3 -0
  214. package/dist/mcp-tools/organise.js +82 -2
  215. package/dist/mcp-tools/reads.d.ts +3 -0
  216. package/dist/mcp-tools/reads.js +132 -4
  217. package/dist/mcp-tools/share.d.ts +3 -0
  218. package/dist/mcp-tools/share.js +26 -7
  219. package/dist/mcp-tools/support.d.ts +3 -0
  220. package/dist/mcp-tools/support.js +140 -0
  221. package/dist/mcp-tools/tiers.d.ts +31 -0
  222. package/dist/mcp-tools/tiers.js +147 -0
  223. package/dist/mcp.d.ts +80 -0
  224. package/dist/mcp.js +110 -9
  225. package/dist/net-retry.d.ts +38 -0
  226. package/dist/network.d.ts +10 -0
  227. package/dist/notice.d.ts +7 -0
  228. package/dist/notice.js +7 -7
  229. package/dist/product.d.ts +29 -0
  230. package/dist/product.js +1 -1
  231. package/dist/progress.d.ts +37 -0
  232. package/dist/prompt.d.ts +31 -0
  233. package/dist/prompt.js +1 -1
  234. package/dist/rebuild-key-check.d.ts +26 -0
  235. package/dist/rebuild-key-check.js +90 -0
  236. package/dist/rebuild.d.ts +87 -0
  237. package/dist/rebuild.js +26 -4
  238. package/dist/recovery-assemble.d.ts +40 -0
  239. package/dist/recovery-assemble.js +1 -1
  240. package/dist/recovery-build.d.ts +40 -0
  241. package/dist/recovery-map-file.d.ts +53 -0
  242. package/dist/recovery-map-file.js +4 -4
  243. package/dist/recovery-map.d.ts +197 -0
  244. package/dist/recovery-release.d.ts +104 -0
  245. package/dist/recovery-seq.d.ts +10 -0
  246. package/dist/recovery-source.d.ts +50 -0
  247. package/dist/redact.d.ts +70 -0
  248. package/dist/redact.js +212 -0
  249. package/dist/registration.d.ts +30 -0
  250. package/dist/registration.js +5 -5
  251. package/dist/risk.d.ts +380 -0
  252. package/dist/risk.js +206 -0
  253. package/dist/run-log.d.ts +78 -0
  254. package/dist/run-log.js +301 -0
  255. package/dist/s3/listing.d.ts +47 -0
  256. package/dist/s3/multipart.d.ts +21 -0
  257. package/dist/s3/response-sink.d.ts +8 -0
  258. package/dist/s3/same-file.d.ts +54 -0
  259. package/dist/s3/server.d.ts +58 -0
  260. package/dist/s3/server.js +2 -2
  261. package/dist/s3/sigv4.d.ts +46 -0
  262. package/dist/s3/sigv4.js +1 -1
  263. package/dist/s3/staging.d.ts +10 -0
  264. package/dist/s3/xml.d.ts +28 -0
  265. package/dist/safe-path.d.ts +41 -0
  266. package/dist/seal.d.ts +120 -0
  267. package/dist/secret-reader.d.ts +28 -0
  268. package/dist/server.d.ts +9 -0
  269. package/dist/session.d.ts +32 -0
  270. package/dist/session.js +1 -1
  271. package/dist/setup-questions.d.ts +7 -0
  272. package/dist/share.d.ts +108 -0
  273. package/dist/share.js +1 -1
  274. package/dist/shared/lib/crypto/size-padding.d.ts +55 -0
  275. package/dist/shared/lib/crypto/size-padding.js +5 -0
  276. package/dist/shared/lib/drive/manifest-chunks.d.ts +95 -0
  277. package/dist/shared/lib/drive/manifest-chunks.js +252 -0
  278. package/dist/shared/lib/drive/manifest-codec.d.ts +214 -0
  279. package/dist/shared/lib/drive/manifest-codec.js +7 -7
  280. package/dist/shared/lib/drive/manifest-index.d.ts +98 -0
  281. package/dist/shared/lib/drive/manifest-ops.d.ts +167 -0
  282. package/dist/shared/lib/drive/manifest-ops.js +9 -4
  283. package/dist/shared/lib/drive/manifest-pack.d.ts +65 -0
  284. package/dist/shared/lib/drive/manifest-pack.js +287 -0
  285. package/dist/shared/lib/drive/manifest-settings.d.ts +113 -0
  286. package/dist/shared/lib/drive/manifest-settings.js +87 -6
  287. package/dist/shared/lib/drive/name-conflict.d.ts +76 -0
  288. package/dist/shared/lib/drive/rebuild-verify.d.ts +73 -0
  289. package/dist/shared/lib/drive/rebuild-verify.js +112 -0
  290. package/dist/shared/lib/drive/unique-name.d.ts +9 -0
  291. package/dist/shared/lib/drive/zstd.d.ts +27 -0
  292. package/dist/shared/lib/drive/zstd.js +74 -0
  293. package/dist/shared/lib/extend/epochs.d.ts +205 -0
  294. package/dist/shared/lib/net/retry-budget.d.ts +79 -0
  295. package/dist/shared/lib/share/shared-file-info.d.ts +21 -0
  296. package/dist/shared/lib/storage-control/chain.d.ts +57 -0
  297. package/dist/shared/lib/storage-control/chain.js +82 -0
  298. package/dist/shared/lib/storage-control/plan.d.ts +40 -0
  299. package/dist/shared/lib/storage-control/plan.js +72 -0
  300. package/dist/shared/lib/storage-control/reuse.d.ts +72 -0
  301. package/dist/shared/lib/storage-control/reuse.js +95 -0
  302. package/dist/shared/lib/storage-network.d.ts +25 -0
  303. package/dist/shared/lib/upload/part-plan.d.ts +24 -0
  304. package/dist/shared/lib/wallet/activity.d.ts +138 -0
  305. package/dist/shared/lib/wallet/activity.js +208 -0
  306. package/dist/shared/lib/wallet/send-rules.d.ts +68 -0
  307. package/dist/shared/lib/wallet/send-rules.js +116 -0
  308. package/dist/shared/lib/wallet/swap-rules.d.ts +115 -0
  309. package/dist/shared/lib/wallet/swap-rules.js +199 -0
  310. package/dist/shared/lib/wallet/tip.d.ts +14 -0
  311. package/dist/shared/lib/wallet/tip.js +34 -0
  312. package/dist/shared/lib/wallet/venue-ids.d.ts +23 -0
  313. package/dist/shared/lib/wallet/venue-ids.js +53 -0
  314. package/dist/standing-tip.d.ts +24 -0
  315. package/dist/standing-tip.js +52 -0
  316. package/dist/stdout.d.ts +38 -0
  317. package/dist/storage-control-chain.d.ts +38 -0
  318. package/dist/storage-control-chain.js +78 -0
  319. package/dist/sui-rpc.d.ts +15 -0
  320. package/dist/sui-rpc.js +59 -0
  321. package/dist/support-attach.d.ts +24 -0
  322. package/dist/support-attach.js +72 -0
  323. package/dist/support-copy.d.ts +42 -0
  324. package/dist/support-copy.js +109 -0
  325. package/dist/support-wire.d.ts +49 -0
  326. package/dist/support-wire.js +194 -0
  327. package/dist/trash-sweep.d.ts +55 -0
  328. package/dist/units.d.ts +2 -0
  329. package/dist/update-check.d.ts +50 -0
  330. package/dist/update-source.d.ts +63 -0
  331. package/dist/upload-api.d.ts +3 -0
  332. package/dist/upload-file.d.ts +97 -0
  333. package/dist/upload-file.js +3 -2
  334. package/dist/upload-price.d.ts +50 -0
  335. package/dist/upload-steps.d.ts +26 -0
  336. package/dist/upload-steps.js +29 -9
  337. package/dist/upload-store.d.ts +182 -0
  338. package/dist/upload-wallet-chain.d.ts +54 -0
  339. package/dist/upload-wallet-chain.js +203 -0
  340. package/dist/upload-wallet-plan.d.ts +128 -0
  341. package/dist/upload-wallet-plan.js +184 -0
  342. package/dist/upload-wallet.d.ts +24 -0
  343. package/dist/upload-wallet.js +208 -0
  344. package/dist/upload-wire.d.ts +244 -0
  345. package/dist/upload.d.ts +18 -0
  346. package/dist/upload.js +10 -0
  347. package/dist/usage-report.d.ts +35 -0
  348. package/dist/wallet-activity-chain.d.ts +5 -0
  349. package/dist/wallet-activity-chain.js +31 -0
  350. package/dist/wallet-chain.d.ts +19 -0
  351. package/dist/wallet-chain.js +2 -2
  352. package/dist/wallet-grant.d.ts +50 -0
  353. package/dist/wallet-grant.js +218 -0
  354. package/dist/wallet-send-chain.d.ts +24 -0
  355. package/dist/wallet-send-chain.js +49 -0
  356. package/dist/wallet-sign.d.ts +133 -0
  357. package/dist/wallet-sign.js +317 -0
  358. package/dist/wallet-storage-chain.d.ts +3 -0
  359. package/dist/wallet-storage-chain.js +20 -0
  360. package/dist/wallet-swap-chain.d.ts +52 -0
  361. package/dist/wallet-swap-chain.js +186 -0
  362. package/dist/wallet-swap-quote.d.ts +22 -0
  363. package/dist/wallet-swap-quote.js +126 -0
  364. package/dist/wallet.d.ts +135 -0
  365. package/dist/wallet.js +5 -5
  366. package/dist/walrus-write.d.ts +56 -0
  367. package/dist/walrus-write.js +5 -4
  368. package/dist/walrus.d.ts +86 -0
  369. package/dist/walrus.js +48 -10
  370. package/dist/zstd-node.d.ts +11 -0
  371. package/dist/zstd-node.js +71 -0
  372. package/docs/commands/accept-terms.md +15 -0
  373. package/docs/commands/create.md +17 -0
  374. package/docs/commands/credits.md +34 -0
  375. package/docs/commands/delete-account.md +14 -0
  376. package/docs/commands/deposit.md +27 -0
  377. package/docs/commands/devices.md +9 -0
  378. package/docs/commands/documents.md +10 -0
  379. package/docs/commands/env.md +21 -0
  380. package/docs/commands/extend.md +27 -0
  381. package/docs/commands/folders.md +10 -0
  382. package/docs/commands/get.md +14 -0
  383. package/docs/commands/key.md +14 -0
  384. package/docs/commands/login.md +23 -0
  385. package/docs/commands/logout.md +11 -0
  386. package/docs/commands/losses.md +13 -0
  387. package/docs/commands/ls.md +16 -0
  388. package/docs/commands/marks.md +9 -0
  389. package/docs/commands/mcp.md +33 -0
  390. package/docs/commands/mode.md +29 -0
  391. package/docs/commands/on-collision.md +13 -0
  392. package/docs/commands/padding.md +13 -0
  393. package/docs/commands/public-code.md +9 -0
  394. package/docs/commands/pull.md +9 -0
  395. package/docs/commands/push.md +12 -0
  396. package/docs/commands/put.md +36 -0
  397. package/docs/commands/rebuild.md +13 -0
  398. package/docs/commands/recovery.md +18 -0
  399. package/docs/commands/s3.md +17 -0
  400. package/docs/commands/share.md +15 -0
  401. package/docs/commands/support.md +16 -0
  402. package/docs/commands/tip.md +21 -0
  403. package/docs/commands/trash.md +25 -0
  404. package/docs/commands/trial.md +12 -0
  405. package/docs/commands/unlock.md +25 -0
  406. package/docs/commands/update.md +14 -0
  407. package/docs/commands/usage.md +14 -0
  408. package/docs/commands/verify.md +18 -0
  409. package/docs/commands/wallet.md +67 -0
  410. package/docs/commands/whoami.md +12 -0
  411. package/package.json +67 -8
  412. package/server.json +28 -0
  413. package/vendor/nmts-crypto/nmts_crypto_wasm.d.ts +23 -2
  414. package/vendor/nmts-crypto/nmts_crypto_wasm.js +44 -2
  415. package/vendor/nmts-crypto/nmts_crypto_wasm_bg.wasm +0 -0
  416. package/vendor/nmts-crypto/nmts_crypto_wasm_bg.wasm.d.ts +1 -0
  417. package/vendor/nmts-crypto/package.json +1 -1
  418. package/dist/commands/consent.js +0 -77
  419. package/dist/extend-sign.js +0 -130
@@ -0,0 +1,38 @@
1
+ /**
2
+ * How long a command goes on trying before it reports the failure.
3
+ *
4
+ * ⛔ SHORTER THAN ANYTHING ELSE, BECAUSE SOMEBODY TYPED THIS AND IS LOOKING AT IT. The long budget
5
+ * is for work nobody is watching -- a page uploading in the background, which has money already
6
+ * spent on it and resumes where it stopped. A terminal that sits silent is indistinguishable
7
+ * from one that has hung, an agent has a deadline of its own, and running the command again
8
+ * costs nothing. What this has to cover is a link that blinks, not a server that is down.
9
+ */
10
+ export declare const CLI_RETRY_BUDGET_MS: number;
11
+ /** Told before each wait, so a terminal can say the tool is waiting rather than stuck. */
12
+ export type WaitReporter = (info: {
13
+ attempt: number;
14
+ waitMs: number;
15
+ error: unknown;
16
+ }) => void;
17
+ export interface KeepTryingOptions {
18
+ /** Whether this failure is worth repeating. ⛔ Required: see the header. */
19
+ readonly retryable: (error: unknown) => boolean;
20
+ readonly onWait?: WaitReporter;
21
+ readonly signal?: AbortSignal;
22
+ /** How long to keep trying, in ms. Omit for {@link CLI_RETRY_BUDGET_MS}. */
23
+ readonly budgetMs?: number;
24
+ /** Injected so a test runs a flapping link in milliseconds rather than in minutes. */
25
+ readonly now?: () => number;
26
+ readonly random?: () => number;
27
+ readonly sleep?: (ms: number) => Promise<void>;
28
+ }
29
+ /** Run `step` until it succeeds or the budget is spent. Rejects with the LAST error. */
30
+ export declare function keepTrying<T>(step: () => Promise<T>, options: KeepTryingOptions): Promise<T>;
31
+ /**
32
+ * Is this failure one that asking again could fix?
33
+ *
34
+ * ⛔ A REFUSAL IS NOT A BLIP. The server saying no -- wrong key, no credits, not found -- is an
35
+ * answer, and repeating it spends the budget to hear it again later. Only the shapes that mean
36
+ * "nobody answered" or "not right now" come back here as true.
37
+ */
38
+ export declare function isTransient(error: unknown, status?: number): boolean;
@@ -0,0 +1,10 @@
1
+ export declare const NETWORKS: readonly ["mainnet", "testnet"];
2
+ export type Network = (typeof NETWORKS)[number];
3
+ export declare const NETWORK_ENV_VAR = "NMTS_NETWORK";
4
+ /**
5
+ * Decide the network for this run.
6
+ *
7
+ * `explicit` beats the environment, which beats the one inference this function is willing to
8
+ * make: the live server is mainnet. Anything else and it refuses.
9
+ */
10
+ export declare function resolveNetwork(server: string, explicit?: string | undefined): Network;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * The text shown before an NMTS key is stored on this machine.
3
+ *
4
+ * Every sentence here is a measured fact about how NMTS works, not a prediction and not a
5
+ * comparison. If any of it stops being true, this text is wrong and must change with the code.
6
+ */
7
+ export declare function firstRunNotice(): string;
package/dist/notice.js CHANGED
@@ -1,6 +1,6 @@
1
- // The one thing this tool says before it keeps an account code on a machine.
1
+ // The one thing this tool says before it keeps an NMTS key on a machine.
2
2
  //
3
- // ⛔ WHY IT EXISTS. Handing an agent your account code is handing it the vault: in NMTS every key
3
+ // ⛔ WHY IT EXISTS. Handing an agent your NMTS key is handing it the vault: in NMTS every key
4
4
  // is derived from that one code — the file keys and the wallet both — so an agent that leaks it
5
5
  // has leaked everything at once, and the account cannot be re-keyed because the account IS the
6
6
  // code. The owner asked for this to be said out loud (2026-08-23).
@@ -17,16 +17,16 @@
17
17
  // the moment the decision is made — when the code is first written to this machine.
18
18
  import { PRODUCT_NAME } from "./product.js";
19
19
  /**
20
- * The text shown before an account code is stored on this machine.
20
+ * The text shown before an NMTS key is stored on this machine.
21
21
  *
22
22
  * Every sentence here is a measured fact about how NMTS works, not a prediction and not a
23
23
  * comparison. If any of it stops being true, this text is wrong and must change with the code.
24
24
  */
25
25
  export function firstRunNotice() {
26
26
  return [
27
- `${PRODUCT_NAME} is about to keep your account code on this machine.`,
27
+ `${PRODUCT_NAME} is about to keep your NMTS key on this machine.`,
28
28
  ``,
29
- ` Your account code is the only key to your account. The keys that encrypt`,
29
+ ` Your NMTS key is the only key to your account. The keys that encrypt`,
30
30
  ` your files and the keys to your wallet are all derived from it.`,
31
31
  ``,
32
32
  ` Keeping it here is what lets this tool work while you are away. It also`,
@@ -35,8 +35,8 @@ export function firstRunNotice() {
35
35
  ``,
36
36
  ` If it leaves this machine — in a log line, in a prompt, in a repository —`,
37
37
  ` whoever holds it can read every file in the account and spend from the`,
38
- ` wallet. NMTS cannot undo that: requests made with your code cannot be`,
39
- ` told apart from your own, and the code cannot be changed while keeping`,
38
+ ` wallet. NMTS cannot undo that: requests made with your NMTS key cannot be`,
39
+ ` told apart from your own, and it cannot be changed while keeping`,
40
40
  ` the account.`,
41
41
  ``,
42
42
  ` Give an agent an account you would be willing to lose.`,
@@ -0,0 +1,29 @@
1
+ /** What a person reads, and what the tool calls itself. Owner-settled 2026-08-23. */
2
+ export declare const PRODUCT_NAME = "nmts";
3
+ /** What an agent types. The same word: there is nothing to translate between the two. */
4
+ export declare const BINARY_NAME = "nmts";
5
+ /**
6
+ * This build's version.
7
+ *
8
+ * ⛔ Kept here rather than read from `package.json`: the published build has no package.json
9
+ * beside it. Here rather than in `main.ts` because the MCP server has to say it too, and a
10
+ * command importing the entry point is a cycle waiting to bite.
11
+ */
12
+ export declare const VERSION = "0.34.1";
13
+ /** Where the product lives, for messages that need to send somebody somewhere real. */
14
+ export declare const HOME_URL = "https://nmts.me";
15
+ /** The source, so a person holding only the built program can find what it was built from. */
16
+ export declare const SOURCE_URL = "https://github.com/needmoretruth/nmts-cli";
17
+ /** Who holds the copyright. One place, because it is also the answer to "who can license this". */
18
+ export declare const COPYRIGHT = "Copyright 2026 needmoretruth";
19
+ /**
20
+ * Where to write about a fault, a confusing message, or anything that got in the way.
21
+ *
22
+ * ⛔ IT IS IN THE TOOL AND NOT ONLY IN THE README, because the moment somebody wants to report
23
+ * something is the moment it went wrong -- and that is exactly when nobody goes looking for a
24
+ * web page. The smallest annoyance is worth an email; most of them are cheap to fix and
25
+ * invisible from here.
26
+ */
27
+ export declare const SUPPORT_EMAIL = "nmts@nmts.me";
28
+ /** The document in the source repository that is written for an agent rather than for a person. */
29
+ export declare const AGENTS_DOC = "AGENTS.md";
package/dist/product.js CHANGED
@@ -18,7 +18,7 @@ export const BINARY_NAME = "nmts";
18
18
  * beside it. Here rather than in `main.ts` because the MCP server has to say it too, and a
19
19
  * command importing the entry point is a cycle waiting to bite.
20
20
  */
21
- export const VERSION = "0.17.2";
21
+ export const VERSION = "0.34.1";
22
22
  /** Where the product lives, for messages that need to send somebody somewhere real. */
23
23
  export const HOME_URL = "https://nmts.me";
24
24
  /** The source, so a person holding only the built program can find what it was built from. */
@@ -0,0 +1,37 @@
1
+ /** Where a report goes. Split out so a test can drive it without a terminal. */
2
+ export interface ProgressSink {
3
+ write(text: string): void;
4
+ /** True for a terminal that can rewrite its last line. */
5
+ interactive: boolean;
6
+ }
7
+ export declare function stderrSink(): ProgressSink;
8
+ /** A reporter that says nothing. What `--json` gets, and what a test gets by default. */
9
+ export declare function silentSink(): ProgressSink;
10
+ /**
11
+ * Report progress through one phase.
12
+ *
13
+ * On a terminal the line rewrites in place, to a tenth of a percent — fine enough that a large
14
+ * upload visibly moves rather than appearing to hang. Into a pipe it prints a plain line every ten
15
+ * percent, which is legible in a log and small enough not to bury the result.
16
+ */
17
+ export declare class Progress {
18
+ private readonly sink;
19
+ private readonly label;
20
+ private lastPrinted;
21
+ private dirty;
22
+ constructor(sink: ProgressSink, label: string);
23
+ update(done: number, total: number): void;
24
+ /** Finish the line, so whatever prints next starts clean. */
25
+ done(): void;
26
+ }
27
+ /**
28
+ * A `fetch` that counts the bytes of the request body as they go out.
29
+ *
30
+ * ⛔ THE BODY IS RE-WRAPPED, NOT RE-READ. The bytes are handed to the request as a stream that
31
+ * reports each chunk on its way past, so nothing is copied and the count is what actually left
32
+ * rather than what was queued.
33
+ *
34
+ * ⚠ `duplex: "half"` is required by the fetch specification for a streaming body and Node enforces
35
+ * it. Without it the request throws before a single byte is sent.
36
+ */
37
+ export declare function countingFetch(onSent: (sent: number, total: number) => void, chunkBytes?: number): (url: RequestInfo, init?: RequestInit) => Promise<Response>;
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Hold the terminal in raw mode across a RUN of prompts.
3
+ *
4
+ * ⛔ WITHOUT THIS, THE GAP BETWEEN TWO PROMPTS ECHOES. `promptSecret` sets raw mode for its own
5
+ * question and restores it afterwards, which was right while this tool asked one thing. `login`
6
+ * now asks three in a row with work in between (the code is checked before a passphrase is
7
+ * asked for), and in that gap the terminal is back in line mode: it echoes what is typed, and
8
+ * its line discipline holds whole lines that the next prompt then never sees. Measured through
9
+ * a pty: a paste of code + passphrase + confirmation ended in "Cancelled" with both
10
+ * passphrases printed on the screen.
11
+ *
12
+ * ⚠ Restoring is what the `finally` is for, including on a throw — a process that exits leaving a
13
+ * terminal in raw mode leaves the person's shell unusable.
14
+ */
15
+ export declare function holdTerminal<T>(body: () => Promise<T>): Promise<T>;
16
+ export declare function stdinIsATerminal(): boolean;
17
+ /** Ask for a secret on the terminal, echoing nothing. Ctrl-C and Ctrl-D abandon the prompt. */
18
+ export declare function promptSecret(question: string, envVar: string): Promise<string>;
19
+ /**
20
+ * Ask a question whose answer is not a secret, and echo what is typed.
21
+ *
22
+ * ⛔ NOT FOR SECRETS. `promptSecret` exists for those: it turns echo off and wipes what it read.
23
+ * This one is for a choice, where seeing what you typed is the point.
24
+ *
25
+ * ⛔ IT MUST NOT BE CALLED INSIDE `holdTerminal`. That puts the terminal in raw mode, where
26
+ * readline gets characters one at a time and no line ever arrives.
27
+ *
28
+ * ⚠ Returns the empty string when there is no terminal, so callers can treat "nobody was there"
29
+ * as "nothing was chosen" rather than as an answer. A setup script must not hang here.
30
+ */
31
+ export declare function promptLine(question: string): Promise<string>;
package/dist/prompt.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // Reading a secret from a terminal without putting it on the screen.
2
2
  //
3
- // ⛔ WHY NOT `readline`. It echoes what is typed, and an account code on screen ends up in
3
+ // ⛔ WHY NOT `readline`. It echoes what is typed, and an NMTS key on screen ends up in
4
4
  // scrollback, in a screen share, and in the terminal recording some agents keep. The usual
5
5
  // workaround — swapping `process.stdout.write` for a muted one while the question is open — is
6
6
  // a global mutation in a process that may be writing other output, and it stays broken if the
@@ -0,0 +1,26 @@
1
+ import { type CryptoGlue } from "./crypto.ts";
2
+ import type { SourceItem } from "./rebuild.ts";
3
+ import { type PairVerdict } from "./shared/lib/drive/rebuild-verify.ts";
4
+ import type { ReadOptions } from "./walrus.ts";
5
+ export interface KeyCheckInput {
6
+ server: string;
7
+ apiKey: string;
8
+ /** The NMTS key, used once to derive the data key that opens the wrapped file keys. */
9
+ accountCode: string;
10
+ /** Which chain's aggregators hold the bytes — `mainnet` or `testnet`. */
11
+ chain: string;
12
+ read?: ReadOptions | undefined;
13
+ crypt: CryptoGlue;
14
+ }
15
+ /**
16
+ * A checker for this account, plus the way to forget the key it holds.
17
+ *
18
+ * ⛔ THE DATA KEY IS DERIVED ONCE, NOT PER FILE. Deriving it is a 64 MiB Argon2id pass; doing that
19
+ * per row would turn a thousand-file account into an hour of key derivation. It is held for the
20
+ * length of one rebuild and wiped by `done()`, which every path out of the caller reaches.
21
+ */
22
+ export interface AccountKeyCheck {
23
+ check(item: SourceItem): Promise<PairVerdict>;
24
+ done(): void;
25
+ }
26
+ export declare function accountKeyCheck(input: KeyCheckInput): AccountKeyCheck;
@@ -0,0 +1,90 @@
1
+ // Showing that one rebuilt row's key opens that row's own bytes.
2
+ //
3
+ // The RULE — why an unchecked pair is a defect and why an unknown answer is never a pass — is
4
+ // written once in `shared/lib/drive/rebuild-verify.ts`, which the browser and this tool share
5
+ // byte-for-byte. This file is only HOW this tool answers the question for one row.
6
+ //
7
+ // ⛔ IT READS 72 BYTES PER FILE, NOT A FILE. NCF-3 §4.2 puts a key commitment in the part header
8
+ // and `StreamDecryptor` checks it in constant time before any chunk is touched, so constructing
9
+ // a decryptor over the header ALONE is the whole answer — no ciphertext, no tag, no chunk. The
10
+ // read is the ordinary aggregator read `nmts get` uses, with a byte range on it.
11
+ //
12
+ // ⛔ NOTHING NEW IS ASKED OF THE SERVER. `GET /v1/items/{id}/parts` is the descriptor a download
13
+ // already fetches; it is asked here WITHOUT `?for=download`, because nothing is being
14
+ // downloaded and the counter behind that flag is for bytes people actually take.
15
+ //
16
+ // ⛔ A FAILURE IS A VERDICT, NOT A REFUSAL. A dead aggregator, a part on a storage network this
17
+ // build cannot read, a row the server describes strangely: each one leaves that file's key off
18
+ // the rebuilt list and says why. None of them may stop the rebuild — an account left un-rebuilt
19
+ // is worse than one rebuilt with a few keys withheld, and both are far better than a list that
20
+ // seals a pairing nobody checked.
21
+ import { request } from "./api.js";
22
+ import { AAD, DERIVED } from "./crypto.js";
23
+ import { asParts, fetchPart } from "./download-part.js";
24
+ import { NCF3_HEADER_BYTES } from "./shared/lib/drive/rebuild-verify.js";
25
+ export function accountKeyCheck(input) {
26
+ const { crypt } = input;
27
+ const [from, to] = DERIVED.dataKey;
28
+ const derived = crypt.kdf_derive(crypt.account_code_parse(input.accountCode));
29
+ const dataKey = derived.slice(from, to);
30
+ // ⛔ The whole derivation — every other key this account has — is wiped immediately. Only the
31
+ // 32 bytes that open wrapped file keys are kept, and only until `done()`.
32
+ derived.fill(0);
33
+ return {
34
+ check: (item) => checkOne(input, dataKey, item),
35
+ done: () => dataKey.fill(0),
36
+ };
37
+ }
38
+ async function checkOne(input, dataKey, item) {
39
+ const wrapped = item.dekWrapped;
40
+ // Answered by the shared layer already; kept so this function is total on its own input.
41
+ if (wrapped === undefined || wrapped === "")
42
+ return { ok: false, reason: "no-key" };
43
+ let header;
44
+ try {
45
+ // ⚠ The address is written out as a literal because the gate that compares this tool's
46
+ // addresses against the server's registered routes reads literals.
47
+ const described = asParts(await request(input.server, `/v1/items/${encodeURIComponent(item.id)}/parts`, {
48
+ token: input.apiKey,
49
+ }));
50
+ // ⛔ FOUND BY ITS NUMBER, NOT BY ITS POSITION. The order the rows arrive in is the server's
51
+ // choice, and not trusting the server's arrangement is the entire point of this check.
52
+ const first = described.parts.find((p) => p.part_index === 0);
53
+ if (first === undefined)
54
+ return { ok: false, reason: "no-parts" };
55
+ header = await fetchPart(first, input.chain, {
56
+ ...(input.read ?? {}),
57
+ range: { start: 0, end: NCF3_HEADER_BYTES },
58
+ });
59
+ }
60
+ catch {
61
+ // Every way the bytes did not arrive. ⛔ Not `wrong-key`: reporting a pair as wrong because an
62
+ // aggregator was down would tell somebody their file is unopenable when it is not.
63
+ return { ok: false, reason: "unreadable" };
64
+ }
65
+ if (header.length < NCF3_HEADER_BYTES)
66
+ return { ok: false, reason: "unreadable" };
67
+ let dek;
68
+ try {
69
+ dek = input.crypt.envelope_open(dataKey, new TextEncoder().encode(AAD.dekWrap), Buffer.from(wrapped, "base64url"));
70
+ }
71
+ catch {
72
+ // The wrapped key does not open under this account's data key at all — so whatever it is, it
73
+ // is not this account's key for this file.
74
+ return { ok: false, reason: "wrong-key" };
75
+ }
76
+ let opener;
77
+ try {
78
+ opener = new input.crypt.StreamDecryptor(dek, header.subarray(0, NCF3_HEADER_BYTES));
79
+ }
80
+ catch {
81
+ // The key commitment in the header did not match this key: the pair is WRONG, not unproven.
82
+ return { ok: false, reason: "wrong-key" };
83
+ }
84
+ finally {
85
+ dek.fill(0);
86
+ }
87
+ // Nothing is fed to it — the header was the question and it has been answered.
88
+ opener.free();
89
+ return { ok: true };
90
+ }
@@ -0,0 +1,87 @@
1
+ import type { ManifestEntry } from "./shared/lib/drive/manifest-codec.ts";
2
+ import { type PairingVerdicts, type PairVerdict, type UnverifiedPair } from "./shared/lib/drive/rebuild-verify.ts";
3
+ /**
4
+ * Stand-in name for a file the server can no longer name. Deliberately not a sentence and
5
+ * deliberately language-neutral: it sits in the field a person's own file names sit in, and the
6
+ * only thing to do with it is type over it.
7
+ */
8
+ export declare function placeholderName(id: string): string;
9
+ /** One stored file, as the server still knows it. */
10
+ export interface SourceItem {
11
+ id: string;
12
+ size: number;
13
+ createdAt: number;
14
+ updatedAt: number;
15
+ /** Present when the row is in the trash. */
16
+ deletedAt?: number;
17
+ /** The wrapped file key. Absent on a row committed before the server kept one. */
18
+ dekWrapped?: string;
19
+ /** The sealed whole-file hash, for verifying a download. Absent on older rows. */
20
+ contentHashCt?: string;
21
+ }
22
+ export interface RebuiltList {
23
+ /** The entries to seal, in the order the listings returned them. */
24
+ entries: ManifestEntry[];
25
+ /** How many came back live. */
26
+ live: number;
27
+ /** How many came back from the trash — still restorable, still stored, still charged for. */
28
+ trashed: number;
29
+ /**
30
+ * How many carry no wrapped key. They are still worth an entry — the row, the size and the
31
+ * dates are real — but nothing on any device can open their bytes, and saying so is the whole
32
+ * point of counting them.
33
+ */
34
+ keyless: number;
35
+ /** How many keys were shown to open their own file. Only these entries carry a key. */
36
+ verified: number;
37
+ /**
38
+ * Every file whose key was not shown to belong to it, with why, in listing order.
39
+ *
40
+ * ⛔ NOT A FAILURE ON ITS OWN. One unreadable aggregator is a file whose key is withheld until
41
+ * the next rebuild; the whole account failing is a different thing, and the command tells
42
+ * them apart rather than treating a bad afternoon on the network as a wrong key set.
43
+ */
44
+ unverified: readonly UnverifiedPair[];
45
+ /**
46
+ * Rows the server says it holds that this rebuild has no entry for, or null when that could not
47
+ * be checked. Not a failure: something thrown away before the restore window closed is exactly
48
+ * this, and so is a file trashed while the listings were being read.
49
+ */
50
+ unaccounted: number | null;
51
+ }
52
+ /**
53
+ * Turn the server's rows into entries.
54
+ *
55
+ * ⛔ EVERY REBUILT FILE SITS AT THE TOP OF THE DRIVE, because the server has no folder or parent
56
+ * left to report and inventing one would be a guess presented as a memory.
57
+ *
58
+ * ⛔ AND NO TWO OF THEM SHARE A NAME. A placeholder is the first characters of an id, so two ids
59
+ * can produce one name; in a drive addressed by path, two entries with the same path is a
60
+ * lookup this tool refuses rather than resolves — a file nobody can fetch. Numbering the second
61
+ * one costs nothing and the person is going to rename both anyway.
62
+ */
63
+ export declare function entriesFrom(items: readonly SourceItem[], verdicts: PairingVerdicts): ManifestEntry[];
64
+ export interface RebuildInput {
65
+ server: string;
66
+ apiKey: string;
67
+ /** Called as pages arrive, so a large account is not a silent wait. */
68
+ onProgress?: (read: number) => void;
69
+ /**
70
+ * Show that ONE row's key opens ONE row's own first sealed part — `rebuild-key-check.ts`.
71
+ *
72
+ * ⛔ REQUIRED, AND NOT OPTIONAL FOR A REASON. An optional check is a check some caller skips, and
73
+ * a wrong pairing is invisible when it is written: the entry looks ordinary, seals into the
74
+ * list, and is only found years later as a file that will not open.
75
+ */
76
+ verify(item: SourceItem): Promise<PairVerdict>;
77
+ /** Ticks while the headers are being read, so the second half is not a silent wait either. */
78
+ onVerifyProgress?: (checked: number, total: number) => void;
79
+ }
80
+ /**
81
+ * Read the whole account and work out the list it would be sealed as. Writes nothing.
82
+ *
83
+ * ⛔ THE TRASH IS READ SECOND AND WINS A TIE. An item thrown away between the two listings comes
84
+ * back in both; the trashed row is the newer truth, and taking the live one would resurrect
85
+ * something the person had just thrown away.
86
+ */
87
+ export declare function rebuildFromServer(input: RebuildInput): Promise<RebuiltList>;
package/dist/rebuild.js CHANGED
@@ -21,6 +21,14 @@
21
21
  // would then agree those files do not exist — while the account goes on paying for them. An
22
22
  // account left un-rebuilt is recoverable; a short list written over nothing is not.
23
23
  //
24
+ // ⛔ AND NO PAIR IS WRITTEN DOWN UNCHECKED. A row's wrapped key is sealed under a fixed separator,
25
+ // not under the row's id, so every one of an account's keys opens under the same account key —
26
+ // which means a server that returned row A's key beside row B's id would produce a list whose
27
+ // pairs are wrong and that nothing on this side would notice. Each key is therefore tried
28
+ // against its own row's first sealed part (`rebuild-key-check.ts`) before the pair is sealed. A
29
+ // pair that does not open keeps its entry and loses its key: the entry is what says the file was
30
+ // there, and a key written beside the wrong file is a claim this tool cannot make.
31
+ //
24
32
  // ⛔ THE TRASH IS PART OF IT. Trashed files are still stored, still charged for and still
25
33
  // restorable, so a list built from the live rows alone would empty the trash of an account that
26
34
  // rebuilt. ⚠ The server's trash view ends at the restore window: something thrown away longer
@@ -29,6 +37,7 @@
29
37
  import { request } from "./api.js";
30
38
  import { NmtsError } from "./errors.js";
31
39
  import { KIND_FILE } from "./shared/lib/drive/manifest-index.js";
40
+ import { mayCarryKey, verifyKeyPairings, } from "./shared/lib/drive/rebuild-verify.js";
32
41
  import { uniqueFileName } from "./shared/lib/drive/unique-name.js";
33
42
  /**
34
43
  * How many pages of one listing a rebuild will read.
@@ -206,11 +215,15 @@ async function serverRowIds(base, apiKey) {
206
215
  * lookup this tool refuses rather than resolves — a file nobody can fetch. Numbering the second
207
216
  * one costs nothing and the person is going to rename both anyway.
208
217
  */
209
- export function entriesFrom(items) {
218
+ export function entriesFrom(items, verdicts) {
210
219
  const taken = new Set();
211
220
  return items.map((item) => {
212
221
  const name = uniqueFileName(placeholderName(item.id), taken);
213
222
  taken.add(name);
223
+ // ⛔ THE KEY AND THE HASH TRAVEL TOGETHER OR NOT AT ALL. Both were filed beside this id by the
224
+ // server, so a swap that moved one moved the other; keeping the hash without its key would
225
+ // only move the failure somewhere less informative than here.
226
+ const paired = mayCarryKey(item.id, verdicts);
214
227
  return {
215
228
  id: item.id,
216
229
  parentId: null,
@@ -220,8 +233,8 @@ export function entriesFrom(items) {
220
233
  createdAt: item.createdAt,
221
234
  updatedAt: item.updatedAt,
222
235
  ...(item.deletedAt === undefined ? {} : { deletedAt: item.deletedAt }),
223
- ...(item.dekWrapped === undefined ? {} : { dekWrapped: item.dekWrapped }),
224
- ...(item.contentHashCt === undefined ? {} : { contentHashCt: item.contentHashCt }),
236
+ ...(!paired || item.dekWrapped === undefined ? {} : { dekWrapped: item.dekWrapped }),
237
+ ...(!paired || item.contentHashCt === undefined ? {} : { contentHashCt: item.contentHashCt }),
225
238
  };
226
239
  });
227
240
  }
@@ -242,7 +255,14 @@ export async function rebuildFromServer(input) {
242
255
  for (const item of trashed)
243
256
  byId.set(item.id, item);
244
257
  const items = [...byId.values()];
245
- const entries = entriesFrom(items);
258
+ // BEFORE ANY PAIR IS WRITTEN DOWN. Once a pairing is sealed it is carried by every later edit
259
+ // and copied into the recovery list, so this is the last moment it can still be checked.
260
+ const verdicts = await verifyKeyPairings({
261
+ rows: items,
262
+ openFirstPartHeader: (item) => input.verify(item),
263
+ ...(input.onVerifyProgress === undefined ? {} : { onProgress: input.onVerifyProgress }),
264
+ });
265
+ const entries = entriesFrom(items, verdicts);
246
266
  const held = await serverRowIds(input.server, input.apiKey);
247
267
  const unaccounted = held === null ? null : [...held].filter((id) => !byId.has(id)).length;
248
268
  return {
@@ -250,6 +270,8 @@ export async function rebuildFromServer(input) {
250
270
  live: items.filter((item) => item.deletedAt === undefined).length,
251
271
  trashed: items.filter((item) => item.deletedAt !== undefined).length,
252
272
  keyless: items.filter((item) => item.dekWrapped === undefined).length,
273
+ verified: verdicts.verified.size,
274
+ unverified: verdicts.unverified,
253
275
  unaccounted,
254
276
  };
255
277
  }
@@ -0,0 +1,40 @@
1
+ import { type BuiltRecoveryList } from "./recovery-build.ts";
2
+ import { type Session } from "./session.ts";
3
+ export interface AssembleOptions {
4
+ server?: string | undefined;
5
+ network?: string | undefined;
6
+ /** Ticks while the account is being read, so a large one does not look frozen. */
7
+ onProgress?: ((loaded: number) => void) | undefined;
8
+ }
9
+ export interface AssembledRecoveryList {
10
+ session: Session;
11
+ /** base64url of the 32-byte account proof. ⛔ Held for this run's requests and nothing else. */
12
+ proof: string;
13
+ built: BuiltRecoveryList;
14
+ /** This list's own version number. */
15
+ seq: number;
16
+ /** RFC3339, stamped before the account was read. What the server is told. */
17
+ capturedAt: string;
18
+ /** The `.nmtsmap` document and the name to offer it under. */
19
+ file: {
20
+ filename: string;
21
+ content: string;
22
+ };
23
+ }
24
+ export declare function assembleRecoveryList(options?: AssembleOptions): Promise<AssembledRecoveryList>;
25
+ /**
26
+ * Tell the server a list was written, and what number it carries.
27
+ *
28
+ * ⛔ THE FILE FIRST, THE RECORD SECOND — so this takes the path that already exists. The record is
29
+ * what makes an account screen say "your list is up to date"; saying that before the file
30
+ * exists would be a claim about something that may never have been written.
31
+ *
32
+ * ⛔ `kind: "local"` AND NO BLOB ID. This tool keeps the list on the person's own disk and writes
33
+ * no copy to the storage network, and the server refuses a blob id alongside `local` for
34
+ * exactly that reason: a recorded address nothing was written to would let a screen advertise
35
+ * a copy that does not exist.
36
+ *
37
+ * ⛔ THE ATTEMPT IS REMEMBERED WHETHER OR NOT IT LANDS. See `recovery-seq.ts`: a number the server
38
+ * refused must not be the number the next run offers again, or the refusal repeats for ever.
39
+ */
40
+ export declare function recordRecoveryList(assembled: AssembledRecoveryList, destination: string): Promise<void>;
@@ -2,7 +2,7 @@
2
2
  //
3
3
  // ⛔ ONE PLACE, BECAUSE THE TWO COMMANDS MUST NOT DISAGREE. `recovery-list` writes the sealed list
4
4
  // as a file of its own; `kit` writes the same document inside the one file that also carries the
5
- // account code. If those were assembled twice, one of them would eventually be built from a
5
+ // NMTS key. If those were assembled twice, one of them would eventually be built from a
6
6
  // check the other had dropped — and the difference would only show up in a recovery.
7
7
  //
8
8
  // ⛔ THE CAPTURE TIME IS STAMPED BEFORE ANYTHING IS READ. The server keeps it to decide which files
@@ -0,0 +1,40 @@
1
+ import { type CryptoGlue } from "./crypto.ts";
2
+ import { type RecoveryDocMetaDraft, type RecoveryListDoc } from "./recovery-map.ts";
3
+ import { type SourceItem } from "./recovery-source.ts";
4
+ import type { ManifestEntry } from "./shared/lib/drive/manifest-codec.ts";
5
+ /** Path of the account root. Every file sits at this or below it. */
6
+ export declare const ROOT_PATH = "/";
7
+ /** Folder names, root-down, as the `/a/b` string the format stores. Empty means the root. */
8
+ export declare function pathString(names: readonly string[]): string;
9
+ export interface BuildRecoveryListInput {
10
+ crypt: CryptoGlue;
11
+ /** ⛔ BORROWED, NOT KEPT. The caller derived it and the caller wipes it. */
12
+ dataKey: Uint8Array;
13
+ accountId: string;
14
+ /** The account's own sealed file list, opened. */
15
+ entries: readonly ManifestEntry[];
16
+ /** Every live stored file, as the server described it. */
17
+ source: readonly SourceItem[];
18
+ /** This list's own version. Higher is newer. */
19
+ seq: number;
20
+ /** RFC3339, stamped by the caller before the walk began. */
21
+ generatedAt: string;
22
+ meta: RecoveryDocMetaDraft;
23
+ }
24
+ export interface BuiltRecoveryList {
25
+ /** The sealed list — base64url of one NCF-3 envelope. */
26
+ sealed: string;
27
+ /** The document that was sealed, so a caller can read what it says without opening anything. */
28
+ doc: RecoveryListDoc;
29
+ fileCount: number;
30
+ totalBytes: number;
31
+ /**
32
+ * Live files the sealed file list names that the dump did not return.
33
+ *
34
+ * ⛔ NOT A REFUSAL — a short list beats none, and the ordinary cause is a file whose upload never
35
+ * committed. But a caller must not report the account as covered while this is not empty.
36
+ */
37
+ missingFromSource: readonly string[];
38
+ }
39
+ /** Build and seal the account's recovery list. Throws on any discrepancy; there is no partial list. */
40
+ export declare function buildRecoveryList(input: BuildRecoveryListInput): BuiltRecoveryList;
@@ -0,0 +1,53 @@
1
+ import { type ArtifactAbout } from "./artifact-about.ts";
2
+ /** Wrapper format identifier, checked on read before anything is attempted. */
3
+ export declare const MAP_FILE_FORMAT = "nmts-recovery-map";
4
+ /**
5
+ * Wrapper version — the SHELL's version, independent of the NRM version inside.
6
+ *
7
+ * ⛔ NOT RAISED FOR ANYTHING THIS TOOL ADDS. `MAX_WRAPPER_VERSION` in the standalone program is a
8
+ * CEILING: a shell numbered higher than a build knows is refused outright, unread. So a bump is
9
+ * a wall in front of every reader already in somebody's hands, never a courtesy.
10
+ */
11
+ export declare const MAP_FILE_VERSION = 2;
12
+ /** Filename extension. Deliberately not the file-list copy's: the two must not be confusable. */
13
+ export declare const MAP_FILE_EXTENSION = "nmtsmap";
14
+ /** The version to stamp for a document declaring `nrm`. */
15
+ export declare function minimumToolVersion(nrm: number): string;
16
+ /** The on-disk document. */
17
+ export interface RecoveryMapFile {
18
+ format: typeof MAP_FILE_FORMAT;
19
+ version: typeof MAP_FILE_VERSION;
20
+ /** NRM version of the sealed document. Read it before parsing what is inside. */
21
+ nrm: number;
22
+ /** Which list this is. Higher wins during a recovery. Version-independent. */
23
+ seq: number;
24
+ /** RFC3339 capture time, copied from inside the sealed document. */
25
+ generated_at: string;
26
+ /** Public account id, so a person holding several files knows which is which. */
27
+ account_id: string;
28
+ /** The sealed list: base64url of one NCF-3 envelope under `nmts/v3/recovery-map`. */
29
+ sealed: string;
30
+ min_tool: string;
31
+ note: string[];
32
+ about: ArtifactAbout;
33
+ }
34
+ export interface BuildMapFileInput {
35
+ accountId: string;
36
+ seq: number;
37
+ generatedAt: string;
38
+ /**
39
+ * NRM version of the document actually sealed.
40
+ *
41
+ * ⛔ NOT A CONSTANT. From NRM-3 on the number a document declares depends on what is IN it, so a
42
+ * wrapper stating the newest version this build knows would mislabel every ordinary file —
43
+ * and a recovery program refuses a document claiming a version it does not know without
44
+ * reading a byte of it.
45
+ */
46
+ nrm: number;
47
+ sealed: string;
48
+ }
49
+ /** Build the on-disk document and the filename to offer it under. */
50
+ export declare function buildRecoveryMapFile(input: BuildMapFileInput): {
51
+ filename: string;
52
+ content: string;
53
+ };