@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,29 @@
1
+ import type { CredentialSource } from "./credentials.ts";
2
+ /**
3
+ * The proof value for one NMTS key, base64url of 32 bytes.
4
+ *
5
+ * ⛔ NO POLICY HERE. Whether this run may build one is decided by `accountProofFor` below; keeping
6
+ * the arithmetic separate from the permission is what lets a test drive each without the other.
7
+ */
8
+ export declare function accountProof(code: string): Promise<string>;
9
+ /** A run's NMTS key together with where this machine got it from. */
10
+ export interface CodeInHand {
11
+ code: string;
12
+ source: CredentialSource;
13
+ }
14
+ /**
15
+ * The proof for this run — asked for, never assumed.
16
+ *
17
+ * ⛔ THE AGREEMENT IS `plain-env`, AND IT IS THE ONE THAT ALREADY COVERS THIS. Its words are
18
+ * exactly "use the NMTS key from a plain environment variable", which is what a run does
19
+ * when it turns `NMTS_ACCOUNT_CODE` into a value it sends. A sixth consent key is not the
20
+ * answer: `consent.ts` says in its header why the count is five and that adding to it is a
21
+ * decision rather than a tidy-up, and the bar it sets — undoable, costly, or the code somewhere
22
+ * that is not this tool's sealed file — is met by the existing key rather than by a new one.
23
+ *
24
+ * ⛔ ASKED HERE AND NOT ONLY WHERE THE CODE WAS READ. `code-access.ts` does require it when it
25
+ * reads that variable, and its own header says why a rule enforced at each call site has as
26
+ * many holes as there are call sites. This is the call site that SENDS something, so it asks
27
+ * for itself; an already-granted agreement costs a file read and no question.
28
+ */
29
+ export declare function accountProofFor(held: CodeInHand): Promise<string>;
@@ -1,4 +1,4 @@
1
- // Proving possession of the account code to a server that already trusts this machine's API key.
1
+ // Proving possession of the NMTS key to a server that already trusts this machine's API key.
2
2
  //
3
3
  // ⛔ WHY A SECOND PROOF EXISTS AT ALL. Three routes rebuild an account's disaster-recovery
4
4
  // artefacts — the dump every recovery list is assembled from, and the two records that say a
@@ -8,7 +8,7 @@
8
8
  //
9
9
  // ⛔ WHAT THE VALUE IS, AND WHY SENDING IT IS SAFE. It is `authSecret` — bytes [16,48) of the
10
10
  // derivation (NCF-3 §1), the SAME 32 bytes every sign-in sends, over TLS, for the server to
11
- // check against the argon2id verifier it stores. It is not the account code and it decrypts
11
+ // check against the argon2id verifier it stores. It is not the NMTS key and it decrypts
12
12
  // nothing: `dataKey`, the file keys, the file-list key and the wallet root are different slices
13
13
  // of the same output and none of them reach this or any other request. Deriving it is one-way,
14
14
  // so a server that holds it cannot work back to the code.
@@ -20,14 +20,14 @@
20
20
  // module returns a string, no caller stores it, and `api.ts` puts it in one header and nowhere
21
21
  // else — not a URL, not a message, not a log line.
22
22
  //
23
- // ⛔ THE ACCOUNT CODE ITSELF STAYS HERE. It is not an argument to anything, it is not in the
23
+ // ⛔ THE NMTS KEY ITSELF STAYS HERE. It is not an argument to anything, it is not in the
24
24
  // header, and the buffers the derivation produces are wiped on every path out — including the
25
25
  // failing one. The derivation output is not an account id: it is every key in the account.
26
26
  import { requireConsent } from "./consent.js";
27
27
  import { DERIVED, loadCrypto } from "./crypto.js";
28
28
  import { NmtsError } from "./errors.js";
29
29
  /**
30
- * The proof value for one account code, base64url of 32 bytes.
30
+ * The proof value for one NMTS key, base64url of 32 bytes.
31
31
  *
32
32
  * ⛔ NO POLICY HERE. Whether this run may build one is decided by `accountProofFor` below; keeping
33
33
  * the arithmetic separate from the permission is what lets a test drive each without the other.
@@ -40,7 +40,7 @@ export async function accountProof(code) {
40
40
  }
41
41
  catch {
42
42
  // ⛔ The engine's own message is not repeated: it can contain the input.
43
- throw new NmtsError("That is not a valid NMTS account code.", {
43
+ throw new NmtsError("That is not a valid NMTS key.", {
44
44
  exitCode: 2,
45
45
  nextStep: "Check for a mistyped or missing character. The last character is a check symbol.",
46
46
  });
@@ -60,7 +60,7 @@ export async function accountProof(code) {
60
60
  * The proof for this run — asked for, never assumed.
61
61
  *
62
62
  * ⛔ THE AGREEMENT IS `plain-env`, AND IT IS THE ONE THAT ALREADY COVERS THIS. Its words are
63
- * exactly "use the account code from a plain environment variable", which is what a run does
63
+ * exactly "use the NMTS key from a plain environment variable", which is what a run does
64
64
  * when it turns `NMTS_ACCOUNT_CODE` into a value it sends. A sixth consent key is not the
65
65
  * answer: `consent.ts` says in its header why the count is five and that adding to it is a
66
66
  * decision rather than a tidy-up, and the bar it sets — undoable, costly, or the code somewhere
@@ -0,0 +1,18 @@
1
+ export interface AccountIdentity {
2
+ /** Base64url of the 16 bytes the server knows this account by. Public. */
3
+ accountId: string;
4
+ /** The address other people use to share with this account. Public. */
5
+ publicCode: string;
6
+ /** The NMTS key as it is meant to be read, in groups. NOT printed by default. */
7
+ displayCode: string;
8
+ }
9
+ /**
10
+ * Check that a string is a real NMTS key.
11
+ *
12
+ * ⛔ This is the engine's own parser, which verifies the trailing check symbol. A typo therefore
13
+ * fails HERE, offline, instead of becoming a sign-in failure the person cannot tell apart from
14
+ * a wrong password, a network problem or a suspended account.
15
+ */
16
+ export declare function assertUsableCode(code: string): Promise<void>;
17
+ /** Derive the public facts about an account from its code. */
18
+ export declare function identityOf(code: string): Promise<AccountIdentity>;
package/dist/account.js CHANGED
@@ -1,4 +1,4 @@
1
- // What an account code says about itself, computed on this machine and nowhere else.
1
+ // What an NMTS key says about itself, computed on this machine and nowhere else.
2
2
  //
3
3
  // ⛔ EVERY VALUE HERE IS DERIVED LOCALLY. None of it is asked of a server, and none of it needs
4
4
  // one: that is the property the whole product rests on, and it is why `whoami` can answer
@@ -12,7 +12,7 @@
12
12
  // ⚠ TWO OTHER MODULES DERIVE `authSecret`, AND THEY ARE NAMED HERE SO THIS PARAGRAPH STAYS TRUE.
13
13
  // `registration.ts` builds it for the single call that CREATES an account, because the server
14
14
  // has to be given it once to store a verifier of it. `account-proof.ts` builds it for the three
15
- // recovery routes that ask a key to prove the account code as well — the same value a sign-in
15
+ // recovery routes that ask a key to prove the NMTS key as well — the same value a sign-in
16
16
  // sends, and its own header says why sending it is safe and what it can still do if it is
17
17
  // stolen. Neither returns it to anything else, and nothing here changed: this module still does
18
18
  // not hand it out. ⚠ `dataKey` is a different matter and is NOT returned by anything, here or
@@ -21,7 +21,7 @@
21
21
  import { DERIVED, loadCrypto } from "./crypto.js";
22
22
  import { NmtsError } from "./errors.js";
23
23
  /**
24
- * Check that a string is a real account code.
24
+ * Check that a string is a real NMTS key.
25
25
  *
26
26
  * ⛔ This is the engine's own parser, which verifies the trailing check symbol. A typo therefore
27
27
  * fails HERE, offline, instead of becoming a sign-in failure the person cannot tell apart from
@@ -34,7 +34,7 @@ export async function assertUsableCode(code) {
34
34
  }
35
35
  catch {
36
36
  // ⛔ The engine's own message is not repeated: it can contain the input.
37
- throw new NmtsError("That is not a valid NMTS account code.", {
37
+ throw new NmtsError("That is not a valid NMTS key.", {
38
38
  exitCode: 2,
39
39
  nextStep: "Check for a mistyped or missing character. The last character is a check symbol.",
40
40
  });
@@ -48,7 +48,7 @@ export async function identityOf(code) {
48
48
  bytes = glue.account_code_parse(code);
49
49
  }
50
50
  catch {
51
- throw new NmtsError("That is not a valid NMTS account code.", {
51
+ throw new NmtsError("That is not a valid NMTS key.", {
52
52
  exitCode: 2,
53
53
  nextStep: "Check for a mistyped or missing character. The last character is a check symbol.",
54
54
  });
@@ -0,0 +1,71 @@
1
+ /** The hosts this version knows by name. Anything else is reported as unrecognised, never guessed. */
2
+ export type HostId = "claude-code" | "codex" | "opencode" | "hermes" | "openclaw";
3
+ /** What a host calls itself where a person would see it. */
4
+ export declare const HOST_NAMES: Readonly<Record<HostId, string>>;
5
+ export interface HostSighting {
6
+ id: HostId;
7
+ /**
8
+ * `parent` — this host is on the other end of the pipe. `ancestor` — this host is somewhere above
9
+ * us, possibly several processes up. ⛔ Never collapse the two: the second is what an inherited
10
+ * environment variable can honestly claim, and treating it as the first is how a tool running
11
+ * inside two agents names the wrong one.
12
+ */
13
+ relation: "parent" | "ancestor";
14
+ /** The marker that was found, named so the reader can check it. */
15
+ by: string;
16
+ /** The host's own version where the signal carried a usable one. */
17
+ version: string | null;
18
+ }
19
+ /**
20
+ * Does this host wash the environment when it starts an MCP server?
21
+ *
22
+ * ⛔ MEASURED FROM EACH PROJECT'S SOURCE, not from behaviour we hope for:
23
+ * · Codex `env_clear()` then an eleven-name allow-list (`rmcp-client/src/utils.rs`).
24
+ * · Hermes an eight-name `_SAFE_ENV_KEYS` (`tools/mcp_tool.py`), documented as deliberate.
25
+ * · OpenClaw the MCP SDK's own six-name default (`agents/mcp-stdio-transport.ts`).
26
+ * · Claude Code and opencode pass the parent environment through.
27
+ * ⚠ The two that pass it through do so BY CHOICE — the SDK default they build on is the same
28
+ * six names. This says what they do today, and nothing about what they promise.
29
+ */
30
+ export declare const WASHES_ENVIRONMENT: Readonly<Record<HostId, boolean>>;
31
+ /**
32
+ * Every host whose marker is in this environment.
33
+ *
34
+ * ⛔ A LIST, NOT AN ANSWER. Markers accumulate: an agent inside an agent leaves both, and that was
35
+ * seen while measuring (`OPENCODE=1` and `CLAUDECODE=1` arrived together). Returning the first
36
+ * match would name whichever host this function happens to test first.
37
+ */
38
+ export declare function hostsInEnvironment(env?: NodeJS.ProcessEnv): HostSighting[];
39
+ /** What an MCP client sends about itself in `initialize`. Only these two fields are ever read. */
40
+ export interface ClientInfo {
41
+ name?: unknown;
42
+ version?: unknown;
43
+ }
44
+ /**
45
+ * Who is on the other end of this MCP pipe.
46
+ *
47
+ * ⛔ `mcp` IS NOT A HOST. It is the Python MCP SDK's default name, so every client built on that
48
+ * SDK that does not set its own arrives calling itself that — Hermes among them. Reading it as
49
+ * a host name would put a confident wrong name on the screen, which is worse than an empty one.
50
+ */
51
+ export declare function hostFromClientInfo(info: ClientInfo | undefined): HostSighting | null;
52
+ /**
53
+ * Hermes, and only on Linux.
54
+ *
55
+ * ⛔ IT IS THE ONLY HOST WITH NO MARKER OF ITS OWN IN EITHER OTHER PLACE. It washes the environment
56
+ * and it never sets `clientInfo`, so what is left is the shape of the process that started us:
57
+ * on POSIX it inserts a watchdog written in Python between itself and the server. Windows gets
58
+ * no wrapper and therefore no signal — `null` there is "not measurable", not "not Hermes".
59
+ */
60
+ export declare function hermesFromParent(readParentCommand?: () => string | null): HostSighting | null;
61
+ /** The parent's command line on Linux, or `null` anywhere the question cannot be asked. */
62
+ export declare function readLinuxParentCommand(): string | null;
63
+ /**
64
+ * The one thing an agent has to know before it plans anything: can a secret reach this process
65
+ * through the environment at all?
66
+ *
67
+ * Returns the hosts that would have washed it away, or an empty list when nothing here washes.
68
+ */
69
+ export declare function washingHosts(sightings: readonly HostSighting[]): HostId[];
70
+ /** One sighting in the words the `env` command prints. */
71
+ export declare function describeSighting(s: HostSighting): string;
@@ -0,0 +1,169 @@
1
+ // Which agent is running this tool, measured rather than assumed.
2
+ //
3
+ // ⛔ WHY IT MATTERS AND IS NOT COSMETIC. Three of the five hosts below CLEAR THE ENVIRONMENT before
4
+ // they start an MCP server and put back only a fixed allow-list. `NMTS_ACCOUNT_CODE`,
5
+ // `NMTS_ACCOUNT_CODE_FILE` and the passphrase variable are not on any of those lists, so under
6
+ // Codex, Hermes and OpenClaw they DO NOT ARRIVE — however carefully the person exported them.
7
+ // That is a fact worth saying before the first upload rather than after it.
8
+ //
9
+ // ⛔ TWO DIFFERENT QUESTIONS, AND THEY HAVE DIFFERENT ANSWERS.
10
+ // · An environment marker says "one of my ancestors is this host". It is inherited by every
11
+ // descendant, so an agent that starts a shell that starts another agent leaves both markers
12
+ // standing. It never proves who started US.
13
+ // · MCP `initialize` carries `clientInfo`, which comes from the process on the other end of
14
+ // this pipe. That one IS the direct parent, and it survives the washing above because it
15
+ // travels over the protocol rather than in the environment.
16
+ // So the two are reported separately and never merged into one confident answer.
17
+ //
18
+ // ⛔ NOTHING HERE IS A SECURITY BOUNDARY. Every marker below can be set by anything, and one of
19
+ // them (Codex's `CODEX_PERMISSION_PROFILE`) says so in its own source comment. This tells an
20
+ // agent what to expect; it never decides what is allowed.
21
+ //
22
+ // ⚠ MEASURED 2026-08-31 against Claude Code 2.1.251, Codex 0.151.0, opencode 1.18.25,
23
+ // Hermes 0.20.6 and OpenClaw 2026.8.1 — each from that project's own source or documentation.
24
+ // A host that changes its markers will simply stop being recognised, which is the safe direction:
25
+ // an unrecognised host reports nothing rather than reporting the wrong name.
26
+ import { readFileSync } from "node:fs";
27
+ import { platform } from "node:os";
28
+ /** What a host calls itself where a person would see it. */
29
+ export const HOST_NAMES = {
30
+ "claude-code": "Claude Code",
31
+ codex: "Codex",
32
+ opencode: "opencode",
33
+ hermes: "Hermes",
34
+ openclaw: "OpenClaw",
35
+ };
36
+ /**
37
+ * Does this host wash the environment when it starts an MCP server?
38
+ *
39
+ * ⛔ MEASURED FROM EACH PROJECT'S SOURCE, not from behaviour we hope for:
40
+ * · Codex `env_clear()` then an eleven-name allow-list (`rmcp-client/src/utils.rs`).
41
+ * · Hermes an eight-name `_SAFE_ENV_KEYS` (`tools/mcp_tool.py`), documented as deliberate.
42
+ * · OpenClaw the MCP SDK's own six-name default (`agents/mcp-stdio-transport.ts`).
43
+ * · Claude Code and opencode pass the parent environment through.
44
+ * ⚠ The two that pass it through do so BY CHOICE — the SDK default they build on is the same
45
+ * six names. This says what they do today, and nothing about what they promise.
46
+ */
47
+ export const WASHES_ENVIRONMENT = {
48
+ "claude-code": false,
49
+ codex: true,
50
+ opencode: false,
51
+ hermes: true,
52
+ openclaw: true,
53
+ };
54
+ /**
55
+ * Claude Code writes its version into `AI_AGENT` with the dots turned into hyphens
56
+ * (`claude-code_2-1-251_harness`). Reading it back is worth doing only when the value still starts
57
+ * with that host's own prefix — Claude Code leaves an outer host's `AI_AGENT` alone, so any other
58
+ * shape belongs to somebody else and its middle field is not a Claude Code version.
59
+ */
60
+ function claudeVersionFrom(aiAgent) {
61
+ if (aiAgent === undefined || !aiAgent.startsWith("claude-code_"))
62
+ return null;
63
+ const middle = aiAgent.split("_")[1];
64
+ if (middle === undefined || !/^[0-9]+(-[0-9]+)*$/.test(middle))
65
+ return null;
66
+ return middle.replaceAll("-", ".");
67
+ }
68
+ /**
69
+ * Every host whose marker is in this environment.
70
+ *
71
+ * ⛔ A LIST, NOT AN ANSWER. Markers accumulate: an agent inside an agent leaves both, and that was
72
+ * seen while measuring (`OPENCODE=1` and `CLAUDECODE=1` arrived together). Returning the first
73
+ * match would name whichever host this function happens to test first.
74
+ */
75
+ export function hostsInEnvironment(env = process.env) {
76
+ const out = [];
77
+ const seen = (id, by, version = null) => out.push({ id, relation: "ancestor", by, version });
78
+ if (env["CLAUDECODE"] === "1")
79
+ seen("claude-code", "CLAUDECODE=1", claudeVersionFrom(env["AI_AGENT"]));
80
+ // ⛔ `CODEX_SANDBOX` is NOT here: Codex sets it only when a sandbox is in use, so its absence
81
+ // means nothing at all. These two are set for every child of the shell tool.
82
+ if ((env["CODEX_THREAD_ID"] ?? "") !== "")
83
+ seen("codex", "CODEX_THREAD_ID");
84
+ else if ((env["CODEX_SESSION_ID"] ?? "") !== "")
85
+ seen("codex", "CODEX_SESSION_ID");
86
+ if (env["OPENCODE"] === "1")
87
+ seen("opencode", "OPENCODE=1");
88
+ if (env["HERMES_AGENT"] === "true")
89
+ seen("hermes", "HERMES_AGENT=true");
90
+ else if ((env["HERMES_SESSION_ID"] ?? "") !== "")
91
+ seen("hermes", "HERMES_SESSION_ID");
92
+ if (env["OPENCLAW_CLI"] === "1")
93
+ seen("openclaw", "OPENCLAW_CLI=1");
94
+ else if ((env["OPENCLAW_SHELL"] ?? "") !== "")
95
+ seen("openclaw", "OPENCLAW_SHELL");
96
+ // ⛔ `AI_AGENT` on its own is deliberately not read. Claude Code and Hermes both write it with
97
+ // `${AI_AGENT:-…}`, so it holds the OUTERMOST host's name and answers a question nobody asked.
98
+ return out;
99
+ }
100
+ /**
101
+ * Who is on the other end of this MCP pipe.
102
+ *
103
+ * ⛔ `mcp` IS NOT A HOST. It is the Python MCP SDK's default name, so every client built on that
104
+ * SDK that does not set its own arrives calling itself that — Hermes among them. Reading it as
105
+ * a host name would put a confident wrong name on the screen, which is worse than an empty one.
106
+ */
107
+ export function hostFromClientInfo(info) {
108
+ const name = typeof info?.name === "string" ? info.name : "";
109
+ if (name === "")
110
+ return null;
111
+ const version = typeof info?.version === "string" && info.version !== "" ? info.version : null;
112
+ const at = (id, useVersion) => ({ id, relation: "parent", by: `clientInfo.name=${name}`, version: useVersion ? version : null });
113
+ if (name === "claude-code")
114
+ return at("claude-code", true);
115
+ // The crate that speaks MCP is versioned separately from the Codex release, so its number is not
116
+ // the version a person would recognise and is left out rather than shown as one.
117
+ if (name === "codex-mcp-client")
118
+ return at("codex", false);
119
+ if (name === "opencode")
120
+ return at("opencode", true);
121
+ // ⛔ Pinned to "0.0.0" in OpenClaw's main path, so the field is present and meaningless.
122
+ if (name.startsWith("openclaw-"))
123
+ return at("openclaw", false);
124
+ return null;
125
+ }
126
+ /**
127
+ * Hermes, and only on Linux.
128
+ *
129
+ * ⛔ IT IS THE ONLY HOST WITH NO MARKER OF ITS OWN IN EITHER OTHER PLACE. It washes the environment
130
+ * and it never sets `clientInfo`, so what is left is the shape of the process that started us:
131
+ * on POSIX it inserts a watchdog written in Python between itself and the server. Windows gets
132
+ * no wrapper and therefore no signal — `null` there is "not measurable", not "not Hermes".
133
+ */
134
+ export function hermesFromParent(readParentCommand = readLinuxParentCommand) {
135
+ const command = readParentCommand();
136
+ if (command === null || !command.includes("mcp_stdio_watchdog.py"))
137
+ return null;
138
+ return { id: "hermes", relation: "parent", by: "the parent process runs mcp_stdio_watchdog.py", version: null };
139
+ }
140
+ /** The parent's command line on Linux, or `null` anywhere the question cannot be asked. */
141
+ export function readLinuxParentCommand() {
142
+ if (platform() !== "linux")
143
+ return null;
144
+ try {
145
+ return readFileSync(`/proc/${process.ppid}/cmdline`, "utf8").replaceAll("\0", " ").trim();
146
+ }
147
+ catch {
148
+ return null;
149
+ }
150
+ }
151
+ /**
152
+ * The one thing an agent has to know before it plans anything: can a secret reach this process
153
+ * through the environment at all?
154
+ *
155
+ * Returns the hosts that would have washed it away, or an empty list when nothing here washes.
156
+ */
157
+ export function washingHosts(sightings) {
158
+ const ids = new Set();
159
+ for (const s of sightings)
160
+ if (WASHES_ENVIRONMENT[s.id])
161
+ ids.add(s.id);
162
+ return [...ids];
163
+ }
164
+ /** One sighting in the words the `env` command prints. */
165
+ export function describeSighting(s) {
166
+ const version = s.version === null ? "" : ` ${s.version}`;
167
+ const relation = s.relation === "parent" ? "started this server" : "is running somewhere above this process";
168
+ return `${HOST_NAMES[s.id]}${version} ${relation} (${s.by})`;
169
+ }
@@ -0,0 +1,2 @@
1
+ /** What a caller does next about a refusal, when the tool knows something the message does not. */
2
+ export declare function adviseFor(code: string): string | null;
@@ -0,0 +1,210 @@
1
+ // What a caller does next about a refusal the server explained.
2
+ //
3
+ // ⛔ IT LIVES BESIDE `api.ts` RATHER THAN INSIDE IT. This is one table with one job — a code in, a
4
+ // sentence out — and it had grown to be most of the file it sat in, which is why the request
5
+ // path there had no room left to learn anything new. Splitting on this line leaves each file
6
+ // readable as one thing.
7
+ //
8
+ // ⛔ `check:advice` READS THIS FILE'S SWITCH and compares it, as a set, against every code
9
+ // `api/src/error.rs` can send. A code in neither this table nor that gate's SILENT list is not
10
+ // a decision, it is an oversight — so moving this table means moving what the gate reads, and
11
+ // both moved together.
12
+ //
13
+ // ⛔ EACH SENTENCE NAMES ONE NEXT STEP, OR SAYS THERE IS NONE. "Unauthorized" tells an agent
14
+ // nothing it can act on; "this key was revoked — make a new one" tells it whether to retry, to
15
+ // ask a person, or to stop. Several entries exist only to stop an agent from retrying the one
16
+ // remedy that cannot possibly work.
17
+ /** What a caller does next about a refusal, when the tool knows something the message does not. */
18
+ export function adviseFor(code) {
19
+ switch (code) {
20
+ case "CLEARANCE_REQUIRED":
21
+ case "TURNSTILE_FAILED":
22
+ return ("This account needs a human check, which a command-line tool cannot pass. An API key " +
23
+ "made on the account screen is what waives it — put it in NMTS_API_KEY. If that screen " +
24
+ "has no place to make one, this server does not have API keys switched on.");
25
+ case "UNAUTHORIZED":
26
+ return "The credential is missing or expired. Check NMTS_API_KEY, or make a new key.";
27
+ // ⛔ Each of these says something different on purpose, because the remedies are different
28
+ // and a program that cannot tell them apart will retry the one thing that cannot work.
29
+ case "SESSION_REVOKED":
30
+ return "That is a browser session somebody ended from another device, not an API key. A key is what a program should carry; NMTS_API_KEY is where it goes.";
31
+ case "API_KEY_REVOKED":
32
+ return "Somebody revoked this key. It will not start working again — make a new one.";
33
+ case "API_KEY_EXPIRED":
34
+ return "This key reached the end of the lifetime it was given. Make a new one.";
35
+ case "API_KEY_SCOPE":
36
+ return ("The key is valid and was not given permission for this. Nothing here will succeed with " +
37
+ "it — a key with the right permissions has to be made on the account screen.");
38
+ case "API_KEY_MALFORMED":
39
+ return ("What was sent is not a well-formed key. Check that the whole string was copied, with " +
40
+ "no quotes or line break — it is one line of exactly 65 characters.");
41
+ case "ACCOUNT_CODE_NOT_A_CREDENTIAL":
42
+ return ("That was an NMTS key, not an API key. An NMTS key never goes to the server; it stays " +
43
+ "on this machine and opens the files. Put the NMTS key in NMTS_ACCOUNT_CODE and the API key in " +
44
+ "NMTS_API_KEY.");
45
+ case "AGENT_VERIFY_REQUIRED":
46
+ return ("This was refused because nothing has checked lately that a person is behind this " +
47
+ "account's key. Ask the person to run `nmts verify` and to follow what it prints — it " +
48
+ "gives them a code to type at a browser, and nothing here can pass that check for them.");
49
+ case "SUPPORT_DUPLICATE":
50
+ return ("The same message reached the developer within the last day, so this one was not filed " +
51
+ "twice — nothing was lost. Sending it again will be refused again: say something the " +
52
+ "first message did not, or read the reply on the thread that is already open.");
53
+ // ⛔ THE REFUSAL IS CORRECT AND THERE IS NOTHING HERE TO WORK AROUND. Accepting terms is a
54
+ // person reading a document and agreeing to it; a program doing it for them would be
55
+ // signing on somebody else's behalf, and this tool holds an API key, not a person. So the
56
+ // only thing missing was the advice — without it an agent gets a bare 403 and starts
57
+ // trying credentials, which is the one thing that cannot be the cause.
58
+ // ⭐ 2026-09-06: the person no longer needs a browser for it. `nmts accept-terms`
59
+ // is refused in mode auto and asks them to type the version, so the advice names it — an
60
+ // agent still cannot run it, and the sentence says so.
61
+ //
62
+ // ⚠ It does not say WHICH requests are refused. The server gates some and not others (reading
63
+ // and deleting are not gated today), that line has moved twice, and a sentence here naming
64
+ // the list would be a copy of it that nothing keeps true.
65
+ case "TERMS_ACCEPTANCE_REQUIRED":
66
+ return ("This account has not accepted the terms now in force, and the server refuses this " +
67
+ "request until it does. Nothing an agent runs can accept them. Ask the person to run " +
68
+ "`nmts accept-terms` themselves (it is refused in mode auto), or to accept on the " +
69
+ "account screen at nmts.me. Other requests may still work in the meantime.");
70
+ // ⛔ A KEY IS NOT ENOUGH HERE AND NEVER WILL BE. These routes rebuild what makes the account
71
+ // recoverable without NMTS, and the owner's rule is that the code is re-entered for them.
72
+ // An agent that reads this as "my key is wrong" starts making new keys, which is the one
73
+ // remedy that cannot work.
74
+ case "ACCOUNT_PROOF_REQUIRED":
75
+ return ("This request needs proof of the NMTS key as well as the API key, and what was sent was " +
76
+ "missing or did not match. Check that the NMTS key this machine is holding belongs to the " +
77
+ "same account as the API key. Wrong attempts are counted, and three of them lock these " +
78
+ "routes for a while.");
79
+ case "ACCOUNT_BANNED":
80
+ return "This account is suspended. Nothing here will succeed until that is lifted.";
81
+ // ── Getting to the starting line ──────────────────────────────────────────────────────────
82
+ case "ACCOUNT_EXISTS":
83
+ return "An account already exists for that. Use the one you have rather than making another.";
84
+ case "ALPHA_NOT_OPEN":
85
+ return ("This build asks the server for a channel it does not open. This is not something to " +
86
+ "retry or to fix with a different credential — use a release build.");
87
+ case "API_KEY_CAP":
88
+ return ("The account holds as many live keys as it is allowed. Nothing here can raise the limit: " +
89
+ "the person has to revoke a key they no longer use, on the account screen at nmts.me.");
90
+ case "API_KEY_CHANNEL":
91
+ return ("This account is enrolled on a preview build, and keys are not issued while it is. Ask " +
92
+ "the person to leave the preview on the account screen, then make the key.");
93
+ case "INVALID_CREDENTIALS":
94
+ return ("The server did not accept what was sent. For an API key: check that key. For `key new`: the " +
95
+ "server checked the proof derived from the NMTS key, and it did not match a registered " +
96
+ "account — the NMTS key itself never goes to the server.");
97
+ case "LOCKED_OUT":
98
+ return ("Too many failed attempts, so this is shut for a while. Retrying now makes it longer, " +
99
+ "not shorter. The refusal carries the moment it lifts; wait for it.");
100
+ case "RATE_LIMITED":
101
+ return ("Too many requests too quickly. Wait and send fewer — the refusal carries how long. This " +
102
+ "is not a credential problem, so changing keys will not help.");
103
+ case "SURFACE_MISMATCH":
104
+ return ("This account acts through a different build than the one calling. The refusal names " +
105
+ "which; nothing on this machine can change it, and the person switches it at nmts.me.");
106
+ // ── The terms ─────────────────────────────────────────────────────────────────────────────
107
+ case "TERMS_VERSION_MISMATCH":
108
+ return ("The versions sent are not the ones in force; the refusal carries the ones that are. " +
109
+ "This is a stale copy, not a refusal to serve — read the current versions and send those.");
110
+ case "TERMS_NOT_IN_FORCE":
111
+ return ("There is nothing to accept, so accepting cannot be what is missing. This is a server " +
112
+ "condition; report it rather than retrying.");
113
+ // ── Credits and the free trial ────────────────────────────────────────────────────────────
114
+ case "CREDIT_FILE_CAP":
115
+ return ("One file may cost at most the published cap in credits, and this one costs more. The " +
116
+ "refusal carries both numbers. Splitting the file is the way through; more credits is not.");
117
+ case "CREDIT_DAILY_CAP":
118
+ return ("The account has spent its allowance for today. The refusal carries the cap and what is " +
119
+ "spent. Waiting for the day to turn is the only remedy — buying credits does not lift it.");
120
+ // ⛔ FOUR CODES, FOUR REMEDIES, and that is why the server did not send one. They are cleared
121
+ // by four different acts — name an account of your own · name a different one · send less ·
122
+ // send some — and a retry loop that could not tell them apart would do the wrong one.
123
+ case "CREDIT_TRANSFER_OUTSIDE_FAMILY":
124
+ return ("Credits move between one person's own accounts — the account they made and every account " +
125
+ "under it — and nowhere else. The recipient named is not one of them, or does not exist: " +
126
+ "the server answers both the same way, so this cannot be used to find out which " +
127
+ "identifiers are real. Nothing was moved, and no credential changes this.");
128
+ case "CREDIT_TRANSFER_SELF":
129
+ return "The recipient named is the account sending. Name another account of the same family. Nothing was moved.";
130
+ case "CREDIT_TRANSFER_INSUFFICIENT":
131
+ return ("The sending account cannot cover that amount; the refusal carries what was needed and " +
132
+ "what it can spend. Nothing was moved and nothing is held. Send the smaller number, or " +
133
+ "get credits into that account first (`nmts trial`, or a funded wallet).");
134
+ case "CREDIT_TRANSFER_ZERO":
135
+ return "The transfer named no credits. Send a whole number above zero; nothing was moved.";
136
+ case "TRIAL_CLOSED":
137
+ return "The free trial is not open at all right now. Credits have to come from a funded wallet.";
138
+ case "TRIAL_FULL":
139
+ return "This week's free-trial places are taken. Applying again this week cannot succeed; next week can.";
140
+ case "TRIAL_ALREADY":
141
+ return "This account already took the free trial this week. It comes round weekly, not once.";
142
+ case "TRIAL_HELD":
143
+ return "Free-trial applications are paused pending review. Retrying does not move it.";
144
+ case "TRIAL_LINE_CAPPED":
145
+ return ("This internet connection has taken its share of this week's places today — the limit is " +
146
+ "on the connection, not on the account, so another account here hits it too.");
147
+ // ── Storage, the chain, and what is safe to retry ─────────────────────────────────────────
148
+ // ⛔ THE THREE OUTCOMES ARE DIFFERENT AND AN AGENT MUST NOT COLLAPSE THEM. Refused means it did
149
+ // not happen. Failed means it did not finish. Uncertain means nobody knows — and that is the
150
+ // one where retrying blindly can spend money twice.
151
+ case "CHAIN_REQUEST_REFUSED":
152
+ return ("The storage service refused the request itself, so nothing was spent and nothing was " +
153
+ "stored. Retrying the same request will be refused the same way.");
154
+ case "CHAIN_REGISTER_FAILED":
155
+ return "Registering the storage did not go through. Nothing is stored; the upload can be tried again.";
156
+ case "CHAIN_CERTIFY_FAILED":
157
+ return ("The bytes went out but the storage was never certified, so the file is not safely stored. " +
158
+ "Try the upload again.");
159
+ case "CHAIN_UNCERTAIN":
160
+ return ("⛔ Nobody knows whether the storage was registered. Do NOT simply retry: doing so can pay " +
161
+ "twice for the same file. Run `nmts ls` first and see whether the file is there.");
162
+ case "CHAIN_SPEND_CAP":
163
+ return ("The service has stopped spending on storage for today. This is not about this account " +
164
+ "and no credential or credit changes it. Try tomorrow.");
165
+ case "CHAIN_DELETE_FAILED":
166
+ return ("The storage could not be released. The file's record is gone from this side either way, " +
167
+ "so nothing here is stuck — the storage runs out on its own when its time is up.");
168
+ case "RELEASE_NOT_SPONSORED":
169
+ return ("This file's storage was not paid for with credits, so it is not the server's to release. " +
170
+ "Storage bought from a wallet is released by that wallet.");
171
+ case "SPONSORED_STATE":
172
+ return ("The upload is not at the step that call belongs to — the steps have an order and one was " +
173
+ "skipped or already done. Start the upload again rather than repeating this call.");
174
+ // ── Two callers, one drive ────────────────────────────────────────────────────────────────
175
+ case "MANIFEST_TOO_LARGE":
176
+ return ("The sealed file list is over the server's ceiling (16 MiB), so this save was refused and " +
177
+ "nothing changed. The list is stored as a small index plus chunks, and the index holds one " +
178
+ "short row per chunk — so an account reaches this only with a list that is not in chunks. " +
179
+ "Save once more to convert it; the ceiling is then the account's chunk allowance, which " +
180
+ "grows with confirmed files (details.allowed on MANIFEST_CHUNKS_EXCEEDED).");
181
+ // ── The chunked file list (NCF-3 §6.3) ────────────────────────────────────────────────────
182
+ case "MANIFEST_CHUNK_HASH":
183
+ return ("The name in the path is not the SHA-256 of the `ct` string being sent, so the server " +
184
+ "refused to store it under that name. Hash the base64url text exactly as it travels, not " +
185
+ "the bytes it decodes to, and send it under that name.");
186
+ case "MANIFEST_CHUNK_TOO_LARGE":
187
+ return ("One chunk of the file list is over the server's 4 MiB ceiling of sealed bytes. Pack " +
188
+ "fewer entries per chunk — the format's own bound is on the plaintext, so a chunk built " +
189
+ "to it is always under this — and send the same index again.");
190
+ case "MANIFEST_CHUNK_MISSING":
191
+ return ("The index names chunks this account has not stored. The refusal lists them in " +
192
+ "details.missing: write exactly those chunks, then send the same index again. Nothing " +
193
+ "changed and the version did not move.");
194
+ case "MANIFEST_CHUNKS_EXCEEDED":
195
+ return ("The account is at its chunk allowance (details.allowed). It is a base allowance plus " +
196
+ "one chunk for every details.per_verified_items files the storage network has confirmed, " +
197
+ "so it grows as files are stored; deleting files or packing the list into fewer chunks is " +
198
+ "what brings this write inside it.");
199
+ case "VERSION_CONFLICT":
200
+ return ("Something else changed the drive since this was read. Nothing is lost and nothing is " +
201
+ "wrong with the credential: read the current state and apply the change to that.");
202
+ case "ERASE_BLOCKED":
203
+ return ("The account cannot be erased while retained records still point at it. This will not " +
204
+ "clear by retrying; the records have their own retention and it has to run out.");
205
+ case "CREDITS_SHORT":
206
+ return "The account does not have enough credits for this upload.";
207
+ default:
208
+ return null;
209
+ }
210
+ }