@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.
- package/AGENTS.md +124 -516
- package/CHANGELOG.md +98 -0
- package/README.ko.md +374 -467
- package/README.md +384 -465
- package/dist/account-proof.d.ts +29 -0
- package/dist/account-proof.js +6 -6
- package/dist/account.d.ts +18 -0
- package/dist/account.js +5 -5
- package/dist/agent-host.d.ts +71 -0
- package/dist/agent-host.js +169 -0
- package/dist/api-advice.d.ts +2 -0
- package/dist/api-advice.js +210 -0
- package/dist/api-key.d.ts +79 -0
- package/dist/api-key.js +7 -7
- package/dist/api-refusal.d.ts +14 -0
- package/dist/api-refusal.js +8 -0
- package/dist/api.d.ts +114 -0
- package/dist/api.js +59 -162
- package/dist/args.d.ts +160 -0
- package/dist/args.js +118 -30
- package/dist/artifact-about.d.ts +49 -0
- package/dist/autonomy.d.ts +39 -0
- package/dist/autonomy.js +110 -32
- package/dist/code-access.d.ts +29 -0
- package/dist/code-access.js +3 -3
- package/dist/code-vault.d.ts +38 -0
- package/dist/code-vault.js +8 -8
- package/dist/collision.d.ts +67 -0
- package/dist/collision.js +1 -1
- package/dist/commands/accept-terms.d.ts +11 -0
- package/dist/commands/accept-terms.js +106 -0
- package/dist/commands/balance.d.ts +7 -0
- package/dist/commands/balance.js +54 -1
- package/dist/commands/create-code-file.d.ts +24 -0
- package/dist/commands/create-code-file.js +93 -0
- package/dist/commands/create-link.d.ts +10 -0
- package/dist/commands/create-link.js +232 -0
- package/dist/commands/create.d.ts +20 -0
- package/dist/commands/create.js +43 -102
- package/dist/commands/credits.d.ts +9 -0
- package/dist/commands/credits.js +110 -0
- package/dist/commands/delete-account.d.ts +11 -0
- package/dist/commands/delete-account.js +63 -0
- package/dist/commands/deposit.d.ts +7 -0
- package/dist/commands/deposit.js +85 -0
- package/dist/commands/devices.d.ts +12 -0
- package/dist/commands/devices.js +144 -0
- package/dist/commands/documents.d.ts +35 -0
- package/dist/commands/documents.js +212 -0
- package/dist/commands/env.d.ts +5 -0
- package/dist/commands/env.js +10 -2
- package/dist/commands/erase.d.ts +13 -0
- package/dist/commands/erase.js +193 -0
- package/dist/commands/expiring.d.ts +19 -0
- package/dist/commands/extend.d.ts +33 -0
- package/dist/commands/extend.js +54 -15
- package/dist/commands/get.d.ts +18 -0
- package/dist/commands/key-manage.d.ts +11 -0
- package/dist/commands/key-manage.js +162 -0
- package/dist/commands/key.d.ts +28 -0
- package/dist/commands/key.js +249 -0
- package/dist/commands/kit.d.ts +11 -0
- package/dist/commands/kit.js +7 -7
- package/dist/commands/listfile.d.ts +13 -0
- package/dist/commands/listfile.js +12 -5
- package/dist/commands/login.d.ts +18 -0
- package/dist/commands/login.js +19 -19
- package/dist/commands/logout.d.ts +1 -0
- package/dist/commands/logout.js +1 -1
- package/dist/commands/losses.d.ts +12 -0
- package/dist/commands/losses.js +180 -0
- package/dist/commands/ls.d.ts +23 -0
- package/dist/commands/marks-dispatch.d.ts +2 -0
- package/dist/commands/marks-dispatch.js +51 -0
- package/dist/commands/marks.d.ts +46 -0
- package/dist/commands/marks.js +80 -0
- package/dist/commands/mcp.d.ts +24 -0
- package/dist/commands/mcp.js +70 -5
- package/dist/commands/mode.d.ts +8 -0
- package/dist/commands/mode.js +60 -24
- package/dist/commands/on-collision.d.ts +6 -0
- package/dist/commands/organise.d.ts +46 -0
- package/dist/commands/padding.d.ts +7 -0
- package/dist/commands/padding.js +114 -0
- package/dist/commands/public-code.d.ts +9 -0
- package/dist/commands/public-code.js +8 -8
- package/dist/commands/pull.d.ts +27 -0
- package/dist/commands/push-wallet.d.ts +19 -0
- package/dist/commands/push-wallet.js +69 -0
- package/dist/commands/push.d.ts +47 -0
- package/dist/commands/push.js +37 -7
- package/dist/commands/put-wallet.d.ts +77 -0
- package/dist/commands/put-wallet.js +316 -0
- package/dist/commands/put.d.ts +56 -0
- package/dist/commands/put.js +53 -6
- package/dist/commands/rebuild.d.ts +18 -0
- package/dist/commands/rebuild.js +108 -10
- package/dist/commands/receive.d.ts +24 -0
- package/dist/commands/recovery-dispatch.d.ts +2 -0
- package/dist/commands/recovery-dispatch.js +34 -0
- package/dist/commands/recovery-list.d.ts +11 -0
- package/dist/commands/recovery-list.js +6 -6
- package/dist/commands/recovery.d.ts +25 -0
- package/dist/commands/recovery.js +2 -2
- package/dist/commands/rollback.d.ts +9 -0
- package/dist/commands/rollback.js +118 -0
- package/dist/commands/s3.d.ts +23 -0
- package/dist/commands/s3.js +6 -6
- package/dist/commands/settings.d.ts +5 -0
- package/dist/commands/settings.js +21 -6
- package/dist/commands/share-dispatch.d.ts +2 -0
- package/dist/commands/share-dispatch.js +46 -0
- package/dist/commands/share.d.ts +14 -0
- package/dist/commands/share.js +13 -3
- package/dist/commands/shares-sent.d.ts +18 -0
- package/dist/commands/shares-sent.js +95 -0
- package/dist/commands/support.d.ts +24 -0
- package/dist/commands/support.js +321 -0
- package/dist/commands/sweep.d.ts +11 -0
- package/dist/commands/tip.d.ts +11 -0
- package/dist/commands/tip.js +86 -0
- package/dist/commands/trash.d.ts +17 -0
- package/dist/commands/trash.js +1 -1
- package/dist/commands/trial.d.ts +7 -0
- package/dist/commands/trial.js +16 -18
- package/dist/commands/unlock.d.ts +16 -0
- package/dist/commands/unlock.js +159 -0
- package/dist/commands/update.d.ts +42 -0
- package/dist/commands/update.js +1 -1
- package/dist/commands/usage.d.ts +8 -0
- package/dist/commands/verify.d.ts +18 -0
- package/dist/commands/verify.js +10 -6
- package/dist/commands/wallet-activity.d.ts +26 -0
- package/dist/commands/wallet-activity.js +117 -0
- package/dist/commands/wallet-donate.d.ts +26 -0
- package/dist/commands/wallet-donate.js +163 -0
- package/dist/commands/wallet-hall.d.ts +35 -0
- package/dist/commands/wallet-hall.js +214 -0
- package/dist/commands/wallet-send.d.ts +22 -0
- package/dist/commands/wallet-send.js +153 -0
- package/dist/commands/wallet-storage-ops.d.ts +24 -0
- package/dist/commands/wallet-storage-ops.js +215 -0
- package/dist/commands/wallet-storage.d.ts +18 -0
- package/dist/commands/wallet-storage.js +95 -0
- package/dist/commands/wallet-swap.d.ts +32 -0
- package/dist/commands/wallet-swap.js +287 -0
- package/dist/commands/wallet.d.ts +36 -0
- package/dist/commands/wallet.js +57 -15
- package/dist/commands/whoami.d.ts +10 -0
- package/dist/commands/whoami.js +24 -6
- package/dist/consent.d.ts +140 -0
- package/dist/consent.js +89 -30
- package/dist/credentials.d.ts +146 -0
- package/dist/credentials.js +10 -10
- package/dist/crypto-surface.d.ts +197 -0
- package/dist/crypto.d.ts +81 -0
- package/dist/crypto.js +13 -0
- package/dist/deposit.d.ts +25 -0
- package/dist/deposit.js +78 -0
- package/dist/download-part.d.ts +40 -0
- package/dist/download-sink.d.ts +76 -0
- package/dist/download.d.ts +79 -0
- package/dist/download.js +3 -3
- package/dist/drive-paths.d.ts +73 -0
- package/dist/environment.d.ts +62 -0
- package/dist/environment.js +29 -9
- package/dist/errors.d.ts +22 -0
- package/dist/errors.js +3 -3
- package/dist/exit.d.ts +44 -0
- package/dist/expiry.d.ts +58 -0
- package/dist/extend-budget.d.ts +35 -0
- package/dist/extend-budget.js +81 -0
- package/dist/extend-chain.d.ts +43 -0
- package/dist/extend-chain.js +40 -3
- package/dist/extend-plan.d.ts +148 -0
- package/dist/extend-plan.js +1 -1
- package/dist/gate.d.ts +18 -0
- package/dist/gate.js +100 -0
- package/dist/guards.d.ts +2 -0
- package/dist/help.d.ts +20 -0
- package/dist/help.js +199 -33
- package/dist/human-check.d.ts +23 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.js +49 -0
- package/dist/item-trash.d.ts +1 -0
- package/dist/kit-file.d.ts +56 -0
- package/dist/kit-file.js +10 -10
- package/dist/list-file.d.ts +88 -0
- package/dist/list-file.js +90 -7
- package/dist/list-view-find.d.ts +15 -0
- package/dist/list-view-order.d.ts +32 -0
- package/dist/main.d.ts +12 -0
- package/dist/main.js +112 -113
- package/dist/manifest-chunk-cache.d.ts +14 -0
- package/dist/manifest-chunk-cache.js +103 -0
- package/dist/manifest-chunk-flow.d.ts +81 -0
- package/dist/manifest-chunk-flow.js +307 -0
- package/dist/manifest-create.d.ts +23 -0
- package/dist/manifest-create.js +26 -30
- package/dist/manifest-write.d.ts +161 -0
- package/dist/manifest-write.js +42 -33
- package/dist/manifest.d.ts +82 -0
- package/dist/manifest.js +69 -21
- package/dist/mark-render.d.ts +26 -0
- package/dist/mcp-args.d.ts +17 -0
- package/dist/mcp-ask.d.ts +43 -0
- package/dist/mcp-ask.js +100 -0
- package/dist/mcp-tools/context.d.ts +46 -0
- package/dist/mcp-tools/credits.d.ts +3 -0
- package/dist/mcp-tools/credits.js +61 -0
- package/dist/mcp-tools/files.d.ts +3 -0
- package/dist/mcp-tools/files.js +34 -0
- package/dist/mcp-tools/organise.d.ts +3 -0
- package/dist/mcp-tools/organise.js +82 -2
- package/dist/mcp-tools/reads.d.ts +3 -0
- package/dist/mcp-tools/reads.js +132 -4
- package/dist/mcp-tools/share.d.ts +3 -0
- package/dist/mcp-tools/share.js +26 -7
- package/dist/mcp-tools/support.d.ts +3 -0
- package/dist/mcp-tools/support.js +140 -0
- package/dist/mcp-tools/tiers.d.ts +31 -0
- package/dist/mcp-tools/tiers.js +147 -0
- package/dist/mcp.d.ts +80 -0
- package/dist/mcp.js +110 -9
- package/dist/net-retry.d.ts +38 -0
- package/dist/network.d.ts +10 -0
- package/dist/notice.d.ts +7 -0
- package/dist/notice.js +7 -7
- package/dist/product.d.ts +29 -0
- package/dist/product.js +1 -1
- package/dist/progress.d.ts +37 -0
- package/dist/prompt.d.ts +31 -0
- package/dist/prompt.js +1 -1
- package/dist/rebuild-key-check.d.ts +26 -0
- package/dist/rebuild-key-check.js +90 -0
- package/dist/rebuild.d.ts +87 -0
- package/dist/rebuild.js +26 -4
- package/dist/recovery-assemble.d.ts +40 -0
- package/dist/recovery-assemble.js +1 -1
- package/dist/recovery-build.d.ts +40 -0
- package/dist/recovery-map-file.d.ts +53 -0
- package/dist/recovery-map-file.js +4 -4
- package/dist/recovery-map.d.ts +197 -0
- package/dist/recovery-release.d.ts +104 -0
- package/dist/recovery-seq.d.ts +10 -0
- package/dist/recovery-source.d.ts +50 -0
- package/dist/redact.d.ts +70 -0
- package/dist/redact.js +212 -0
- package/dist/registration.d.ts +30 -0
- package/dist/registration.js +5 -5
- package/dist/risk.d.ts +380 -0
- package/dist/risk.js +206 -0
- package/dist/run-log.d.ts +78 -0
- package/dist/run-log.js +301 -0
- package/dist/s3/listing.d.ts +47 -0
- package/dist/s3/multipart.d.ts +21 -0
- package/dist/s3/response-sink.d.ts +8 -0
- package/dist/s3/same-file.d.ts +54 -0
- package/dist/s3/server.d.ts +58 -0
- package/dist/s3/server.js +2 -2
- package/dist/s3/sigv4.d.ts +46 -0
- package/dist/s3/sigv4.js +1 -1
- package/dist/s3/staging.d.ts +10 -0
- package/dist/s3/xml.d.ts +28 -0
- package/dist/safe-path.d.ts +41 -0
- package/dist/seal.d.ts +120 -0
- package/dist/secret-reader.d.ts +28 -0
- package/dist/server.d.ts +9 -0
- package/dist/session.d.ts +32 -0
- package/dist/session.js +1 -1
- package/dist/setup-questions.d.ts +7 -0
- package/dist/share.d.ts +108 -0
- package/dist/share.js +1 -1
- package/dist/shared/lib/crypto/size-padding.d.ts +55 -0
- package/dist/shared/lib/crypto/size-padding.js +5 -0
- package/dist/shared/lib/drive/manifest-chunks.d.ts +95 -0
- package/dist/shared/lib/drive/manifest-chunks.js +252 -0
- package/dist/shared/lib/drive/manifest-codec.d.ts +214 -0
- package/dist/shared/lib/drive/manifest-codec.js +7 -7
- package/dist/shared/lib/drive/manifest-index.d.ts +98 -0
- package/dist/shared/lib/drive/manifest-ops.d.ts +167 -0
- package/dist/shared/lib/drive/manifest-ops.js +9 -4
- package/dist/shared/lib/drive/manifest-pack.d.ts +65 -0
- package/dist/shared/lib/drive/manifest-pack.js +287 -0
- package/dist/shared/lib/drive/manifest-settings.d.ts +113 -0
- package/dist/shared/lib/drive/manifest-settings.js +87 -6
- package/dist/shared/lib/drive/name-conflict.d.ts +76 -0
- package/dist/shared/lib/drive/rebuild-verify.d.ts +73 -0
- package/dist/shared/lib/drive/rebuild-verify.js +112 -0
- package/dist/shared/lib/drive/unique-name.d.ts +9 -0
- package/dist/shared/lib/drive/zstd.d.ts +27 -0
- package/dist/shared/lib/drive/zstd.js +74 -0
- package/dist/shared/lib/extend/epochs.d.ts +205 -0
- package/dist/shared/lib/net/retry-budget.d.ts +79 -0
- package/dist/shared/lib/share/shared-file-info.d.ts +21 -0
- package/dist/shared/lib/storage-control/chain.d.ts +57 -0
- package/dist/shared/lib/storage-control/chain.js +82 -0
- package/dist/shared/lib/storage-control/plan.d.ts +40 -0
- package/dist/shared/lib/storage-control/plan.js +72 -0
- package/dist/shared/lib/storage-control/reuse.d.ts +72 -0
- package/dist/shared/lib/storage-control/reuse.js +95 -0
- package/dist/shared/lib/storage-network.d.ts +25 -0
- package/dist/shared/lib/upload/part-plan.d.ts +24 -0
- package/dist/shared/lib/wallet/activity.d.ts +138 -0
- package/dist/shared/lib/wallet/activity.js +208 -0
- package/dist/shared/lib/wallet/send-rules.d.ts +68 -0
- package/dist/shared/lib/wallet/send-rules.js +116 -0
- package/dist/shared/lib/wallet/swap-rules.d.ts +115 -0
- package/dist/shared/lib/wallet/swap-rules.js +199 -0
- package/dist/shared/lib/wallet/tip.d.ts +14 -0
- package/dist/shared/lib/wallet/tip.js +34 -0
- package/dist/shared/lib/wallet/venue-ids.d.ts +23 -0
- package/dist/shared/lib/wallet/venue-ids.js +53 -0
- package/dist/standing-tip.d.ts +24 -0
- package/dist/standing-tip.js +52 -0
- package/dist/stdout.d.ts +38 -0
- package/dist/storage-control-chain.d.ts +38 -0
- package/dist/storage-control-chain.js +78 -0
- package/dist/sui-rpc.d.ts +15 -0
- package/dist/sui-rpc.js +59 -0
- package/dist/support-attach.d.ts +24 -0
- package/dist/support-attach.js +72 -0
- package/dist/support-copy.d.ts +42 -0
- package/dist/support-copy.js +109 -0
- package/dist/support-wire.d.ts +49 -0
- package/dist/support-wire.js +194 -0
- package/dist/trash-sweep.d.ts +55 -0
- package/dist/units.d.ts +2 -0
- package/dist/update-check.d.ts +50 -0
- package/dist/update-source.d.ts +63 -0
- package/dist/upload-api.d.ts +3 -0
- package/dist/upload-file.d.ts +97 -0
- package/dist/upload-file.js +3 -2
- package/dist/upload-price.d.ts +50 -0
- package/dist/upload-steps.d.ts +26 -0
- package/dist/upload-steps.js +29 -9
- package/dist/upload-store.d.ts +182 -0
- package/dist/upload-wallet-chain.d.ts +54 -0
- package/dist/upload-wallet-chain.js +203 -0
- package/dist/upload-wallet-plan.d.ts +128 -0
- package/dist/upload-wallet-plan.js +184 -0
- package/dist/upload-wallet.d.ts +24 -0
- package/dist/upload-wallet.js +208 -0
- package/dist/upload-wire.d.ts +244 -0
- package/dist/upload.d.ts +18 -0
- package/dist/upload.js +10 -0
- package/dist/usage-report.d.ts +35 -0
- package/dist/wallet-activity-chain.d.ts +5 -0
- package/dist/wallet-activity-chain.js +31 -0
- package/dist/wallet-chain.d.ts +19 -0
- package/dist/wallet-chain.js +2 -2
- package/dist/wallet-grant.d.ts +50 -0
- package/dist/wallet-grant.js +218 -0
- package/dist/wallet-send-chain.d.ts +24 -0
- package/dist/wallet-send-chain.js +49 -0
- package/dist/wallet-sign.d.ts +133 -0
- package/dist/wallet-sign.js +317 -0
- package/dist/wallet-storage-chain.d.ts +3 -0
- package/dist/wallet-storage-chain.js +20 -0
- package/dist/wallet-swap-chain.d.ts +52 -0
- package/dist/wallet-swap-chain.js +186 -0
- package/dist/wallet-swap-quote.d.ts +22 -0
- package/dist/wallet-swap-quote.js +126 -0
- package/dist/wallet.d.ts +135 -0
- package/dist/wallet.js +5 -5
- package/dist/walrus-write.d.ts +56 -0
- package/dist/walrus-write.js +5 -4
- package/dist/walrus.d.ts +86 -0
- package/dist/walrus.js +48 -10
- package/dist/zstd-node.d.ts +11 -0
- package/dist/zstd-node.js +71 -0
- package/docs/commands/accept-terms.md +15 -0
- package/docs/commands/create.md +17 -0
- package/docs/commands/credits.md +34 -0
- package/docs/commands/delete-account.md +14 -0
- package/docs/commands/deposit.md +27 -0
- package/docs/commands/devices.md +9 -0
- package/docs/commands/documents.md +10 -0
- package/docs/commands/env.md +21 -0
- package/docs/commands/extend.md +27 -0
- package/docs/commands/folders.md +10 -0
- package/docs/commands/get.md +14 -0
- package/docs/commands/key.md +14 -0
- package/docs/commands/login.md +23 -0
- package/docs/commands/logout.md +11 -0
- package/docs/commands/losses.md +13 -0
- package/docs/commands/ls.md +16 -0
- package/docs/commands/marks.md +9 -0
- package/docs/commands/mcp.md +33 -0
- package/docs/commands/mode.md +29 -0
- package/docs/commands/on-collision.md +13 -0
- package/docs/commands/padding.md +13 -0
- package/docs/commands/public-code.md +9 -0
- package/docs/commands/pull.md +9 -0
- package/docs/commands/push.md +12 -0
- package/docs/commands/put.md +36 -0
- package/docs/commands/rebuild.md +13 -0
- package/docs/commands/recovery.md +18 -0
- package/docs/commands/s3.md +17 -0
- package/docs/commands/share.md +15 -0
- package/docs/commands/support.md +16 -0
- package/docs/commands/tip.md +21 -0
- package/docs/commands/trash.md +25 -0
- package/docs/commands/trial.md +12 -0
- package/docs/commands/unlock.md +25 -0
- package/docs/commands/update.md +14 -0
- package/docs/commands/usage.md +14 -0
- package/docs/commands/verify.md +18 -0
- package/docs/commands/wallet.md +67 -0
- package/docs/commands/whoami.md +12 -0
- package/package.json +67 -8
- package/server.json +28 -0
- package/vendor/nmts-crypto/nmts_crypto_wasm.d.ts +23 -2
- package/vendor/nmts-crypto/nmts_crypto_wasm.js +44 -2
- package/vendor/nmts-crypto/nmts_crypto_wasm_bg.wasm +0 -0
- package/vendor/nmts-crypto/nmts_crypto_wasm_bg.wasm.d.ts +1 -0
- package/vendor/nmts-crypto/package.json +1 -1
- package/dist/commands/consent.js +0 -77
- package/dist/extend-sign.js +0 -130
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
// ⛔ NOTHING SECRET IN THE HEADER, AND THAT INCLUDES SIZES. No file name, no count, no total. A
|
|
15
15
|
// leaked wrapper must not say what is inside it, or even how much.
|
|
16
16
|
//
|
|
17
|
-
// ⛔ THE
|
|
17
|
+
// ⛔ THE NMTS KEY IS NOT IN IT. This file plus the code is the account; the artefact that DOES
|
|
18
18
|
// carry both is the recovery kit, and it says so about itself in every language it is written.
|
|
19
19
|
//
|
|
20
20
|
// ⚠ WHAT DIFFERS FROM THE BROWSER'S COPY, and none of it is a format difference:
|
|
@@ -79,13 +79,13 @@ export function minimumToolVersion(nrm) {
|
|
|
79
79
|
const NOTE = [
|
|
80
80
|
`This file is a recovery list from NMTS (nmts.me). Encrypted inside is the record of where ` +
|
|
81
81
|
`this account's files are kept on the public storage network (Walrus).`,
|
|
82
|
-
`It opens only with the
|
|
82
|
+
`It opens only with the NMTS key this list was made for. File names and counts are not ` +
|
|
83
83
|
`visible from the outside.`,
|
|
84
84
|
`The original file list lives on the NMTS server. If the server can no longer be reached, this ` +
|
|
85
|
-
`list together with the
|
|
85
|
+
`list together with the NMTS key carries the information needed to find the files still ` +
|
|
86
86
|
`on the storage network and open them.`,
|
|
87
87
|
`A list with a higher number in its filename is newer — keep the newest. Store this file ` +
|
|
88
|
-
`somewhere other than the
|
|
88
|
+
`somewhere other than the NMTS key. The program that reads it is at ` +
|
|
89
89
|
`github.com/needmoretruth/nmts-recovery.`,
|
|
90
90
|
];
|
|
91
91
|
/** Build the on-disk document and the filename to offer it under. */
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import type { ArtifactAbout } from "./artifact-about.ts";
|
|
2
|
+
import { NmtsError } from "./errors.ts";
|
|
3
|
+
/** The newest NRM version this writer knows how to emit. */
|
|
4
|
+
export declare const NRM_VERSION_LATEST = 4;
|
|
5
|
+
/** The first NRM version in which every part carries `part_index`. */
|
|
6
|
+
export declare const NRM_VERSION_WITH_PART_INDEX = 2;
|
|
7
|
+
/** The first NRM version in which a quilt placement may be `{ identifier }` alone. */
|
|
8
|
+
export declare const NRM_VERSION_WITH_OWN_QUILT = 3;
|
|
9
|
+
/** The first NRM version in which a part may carry `padded_len`. */
|
|
10
|
+
export declare const NRM_VERSION_WITH_PADDING = 4;
|
|
11
|
+
/** Practical ceiling from RECOVERY-MANIFEST.md §1 — beyond this the format needs chunk framing. */
|
|
12
|
+
export declare const MANIFEST_ITEM_SOFT_CAP = 100000;
|
|
13
|
+
/** One stored piece of a file, in order. */
|
|
14
|
+
export interface ManifestPart {
|
|
15
|
+
/**
|
|
16
|
+
* Where this part belongs: 0 for the first, and the position it must be concatenated at
|
|
17
|
+
* thereafter. Required from NRM-2.
|
|
18
|
+
*
|
|
19
|
+
* It is written down because array order alone cannot be CHECKED. A reader holds each fetched
|
|
20
|
+
* part's 72-byte NCF-3 header, which carries the index sealed under the file key, so with this
|
|
21
|
+
* field it can compare three things that must agree: the position it is writing at, what the
|
|
22
|
+
* list says belongs there, and what the bytes themselves say they are.
|
|
23
|
+
*/
|
|
24
|
+
part_index: number;
|
|
25
|
+
/** Blob id holding this part's stream, in `network`'s own naming. */
|
|
26
|
+
blob_id?: string;
|
|
27
|
+
/** The REAL bytes this part contributes to the file. */
|
|
28
|
+
plaintext_len: number;
|
|
29
|
+
/**
|
|
30
|
+
* What the stored stream's header DECLARES, when the part was padded and that is larger.
|
|
31
|
+
* Absent means it was not padded. New in NRM-4.
|
|
32
|
+
*
|
|
33
|
+
* ⛔ THE TWO NUMBERS STAY APART so that "the parts sum to exactly `size`" keeps its exact
|
|
34
|
+
* strength. Folded into one, the check softens to "at least", which accepts any size below
|
|
35
|
+
* the real one: the file comes back short and nothing says so.
|
|
36
|
+
*/
|
|
37
|
+
padded_len?: number;
|
|
38
|
+
/** On-chain blob object, when the uploading client captured it. Omitted, never null. */
|
|
39
|
+
sui_object_id?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Which storage network holds `blob_id` — a NAME (`"walrus"`), not a code.
|
|
42
|
+
*
|
|
43
|
+
* A word rather than a number because whoever parses this may be doing so years from now with
|
|
44
|
+
* none of our code beside them, and a bare `1` is not something a stranger can look up.
|
|
45
|
+
*/
|
|
46
|
+
network?: string;
|
|
47
|
+
}
|
|
48
|
+
/** As a writer hands one in: the position is the value, so `part_index` is filled by the encoder. */
|
|
49
|
+
export interface ManifestPartInput extends Omit<ManifestPart, "part_index"> {
|
|
50
|
+
part_index?: number;
|
|
51
|
+
}
|
|
52
|
+
/** Quilt placement naming a quilt anywhere on the network. */
|
|
53
|
+
export interface ManifestQuiltAbsolute {
|
|
54
|
+
quilt_blob_id: string;
|
|
55
|
+
patch_id: string;
|
|
56
|
+
identifier?: undefined;
|
|
57
|
+
}
|
|
58
|
+
/** The own-quilt form (NRM-3): "the quilt this document was read out of". */
|
|
59
|
+
export interface ManifestQuiltOwn {
|
|
60
|
+
identifier: string;
|
|
61
|
+
quilt_blob_id?: undefined;
|
|
62
|
+
patch_id?: undefined;
|
|
63
|
+
}
|
|
64
|
+
export type ManifestQuilt = ManifestQuiltAbsolute | ManifestQuiltOwn;
|
|
65
|
+
/** Which form a placement is. One narrowing point, so "exactly one of the two" is decided here. */
|
|
66
|
+
export declare function isOwnQuilt(quilt: ManifestQuilt): quilt is ManifestQuiltOwn;
|
|
67
|
+
/** One recoverable file. */
|
|
68
|
+
export interface ManifestItem {
|
|
69
|
+
id: string;
|
|
70
|
+
name: string;
|
|
71
|
+
/** Logical folder path, e.g. `/photos/2026`. Root is `/`. */
|
|
72
|
+
path: string;
|
|
73
|
+
size: number;
|
|
74
|
+
/** The file key, base64url of 32 RAW bytes — the list IS the recovery key store. */
|
|
75
|
+
dek: string;
|
|
76
|
+
kind: "file";
|
|
77
|
+
/**
|
|
78
|
+
* When the file was created and last changed, RFC3339.
|
|
79
|
+
*
|
|
80
|
+
* ⚠ THE ONLY VALUES IN AN ITEM THAT NOTHING CHECKS. Everything else is either sealed under the
|
|
81
|
+
* account key or constrained arithmetically by something that is; these two are simply what
|
|
82
|
+
* the storage layer said. A reader may STAMP them onto restored files and must not order,
|
|
83
|
+
* compare or decide anything with them.
|
|
84
|
+
*/
|
|
85
|
+
created_at?: string;
|
|
86
|
+
updated_at?: string;
|
|
87
|
+
/** sha256 of the whole plaintext, base64url of 32 RAW bytes. Omitted when unrecorded. */
|
|
88
|
+
content_hash?: string;
|
|
89
|
+
parts: ManifestPart[];
|
|
90
|
+
quilt?: ManifestQuilt;
|
|
91
|
+
}
|
|
92
|
+
/** What a caller hands in for one file. */
|
|
93
|
+
export interface ManifestItemInput {
|
|
94
|
+
id: string;
|
|
95
|
+
name: string;
|
|
96
|
+
path: string;
|
|
97
|
+
size: number;
|
|
98
|
+
dek: string;
|
|
99
|
+
contentHash?: string | undefined;
|
|
100
|
+
createdAt?: string | undefined;
|
|
101
|
+
updatedAt?: string | undefined;
|
|
102
|
+
parts: readonly ManifestPartInput[];
|
|
103
|
+
quilt?: ManifestQuilt | undefined;
|
|
104
|
+
}
|
|
105
|
+
/** Where the bytes this document points at actually live. */
|
|
106
|
+
export interface StorageDescription {
|
|
107
|
+
network: string;
|
|
108
|
+
/** ⭐ WHICH chain issued the blob ids — the field that stops a reader guessing between two. */
|
|
109
|
+
chain: string;
|
|
110
|
+
/** Read endpoints this build was using. HINTS, read after a reader's own defaults. */
|
|
111
|
+
aggregators: readonly string[];
|
|
112
|
+
chain_rpc: string;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* What the document says about ITSELF, sealed with it.
|
|
116
|
+
*
|
|
117
|
+
* ⚠ `totals` IS NOT AN INTEGRITY CHECK and a reader must not treat a disagreement as tampering.
|
|
118
|
+
* The document is one authenticated envelope. It is for a RE-IMPLEMENTATION, which drops records
|
|
119
|
+
* it does not recognise and otherwise has no way to notice it read 400 of 412 files.
|
|
120
|
+
*/
|
|
121
|
+
export interface RecoveryDocMeta {
|
|
122
|
+
product: string;
|
|
123
|
+
product_url: string;
|
|
124
|
+
app_version: string;
|
|
125
|
+
tool: string;
|
|
126
|
+
tool_url: string;
|
|
127
|
+
spec_url: string;
|
|
128
|
+
storage: StorageDescription;
|
|
129
|
+
totals: {
|
|
130
|
+
items: number;
|
|
131
|
+
bytes: number;
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
/** The document itself. */
|
|
135
|
+
export interface RecoveryListDoc {
|
|
136
|
+
v: number;
|
|
137
|
+
/** Monotonic per account. Orders the chain — timestamps do not (device clocks lie). */
|
|
138
|
+
seq: number;
|
|
139
|
+
/** Blob id of the list this supersedes; explicitly `null` at the head of the chain. */
|
|
140
|
+
prev_manifest_blob_id: string | null;
|
|
141
|
+
generated_at: string;
|
|
142
|
+
account_id: string;
|
|
143
|
+
meta?: RecoveryDocMeta;
|
|
144
|
+
items: ManifestItem[];
|
|
145
|
+
}
|
|
146
|
+
export interface BuildRecoveryListDocInput {
|
|
147
|
+
seq: number;
|
|
148
|
+
prevBlobId: string | null;
|
|
149
|
+
generatedAt: string;
|
|
150
|
+
accountId: string;
|
|
151
|
+
meta?: RecoveryDocMeta | undefined;
|
|
152
|
+
items: readonly ManifestItemInput[];
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Thrown when the input cannot produce a document a recovery tool could use.
|
|
156
|
+
*
|
|
157
|
+
* ⛔ IT IS AN `NmtsError`, NOT A BARE `Error`. Anything that reaches the top of this program as a
|
|
158
|
+
* bare error prints its message with no next step and exits with the generic code — and the
|
|
159
|
+
* generic code is the one an agent retries. A discrepancy here is never worth retrying: the
|
|
160
|
+
* server has to change, or the account does. Exit 4 is "the command exists and could not do
|
|
161
|
+
* it", which is exactly what happened.
|
|
162
|
+
*/
|
|
163
|
+
export declare class RecoveryListProblem extends NmtsError {
|
|
164
|
+
constructor(message: string);
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* The lowest `v` a document holding these items may honestly declare.
|
|
168
|
+
*
|
|
169
|
+
* ⛔ A WRITER STAMPS THIS, NOT THE NEWEST NUMBER IT KNOWS. People already hold copies of the
|
|
170
|
+
* standalone recovery program, and a build only knows the forms that existed when it was made.
|
|
171
|
+
* Every version number in this format is a CEILING in every published build: a document
|
|
172
|
+
* declaring a number higher than a build knows is REFUSED, unread. So stamping `4` for no
|
|
173
|
+
* reason other than the calendar would be a wall in front of a reader that would have
|
|
174
|
+
* understood every byte of it.
|
|
175
|
+
*/
|
|
176
|
+
export declare function minimumVersion(items: readonly ManifestItem[]): number;
|
|
177
|
+
/**
|
|
178
|
+
* Assemble the document.
|
|
179
|
+
*
|
|
180
|
+
* Rejects rather than emits a list that would mislead somebody in a recovery: a file with no parts
|
|
181
|
+
* has nothing to fetch, a part list that does not add up to the file's size is missing or repeating
|
|
182
|
+
* bytes, a part that says it is somewhere other than where it sits contradicts itself, and a
|
|
183
|
+
* `seq` below 1 is not a version at all. The hostile-input version of the middle two is caught one
|
|
184
|
+
* layer up, where the size being compared against comes from a source the server cannot write —
|
|
185
|
+
* but the cost of shipping any of them is that somebody believes they are covered when they are
|
|
186
|
+
* not, so they are checked here too rather than assumed.
|
|
187
|
+
*/
|
|
188
|
+
export declare function buildRecoveryListDoc(input: BuildRecoveryListDocInput): RecoveryListDoc;
|
|
189
|
+
/** What this tool writes into a document's `meta`, minus the totals only the builder knows. */
|
|
190
|
+
export type RecoveryDocMetaDraft = Omit<RecoveryDocMeta, "totals">;
|
|
191
|
+
/** Finish a draft once the document's contents are known. */
|
|
192
|
+
export declare function withTotals(draft: RecoveryDocMetaDraft, totals: {
|
|
193
|
+
items: number;
|
|
194
|
+
bytes: number;
|
|
195
|
+
}): RecoveryDocMeta;
|
|
196
|
+
/** The `meta` block for this build, minus the totals. */
|
|
197
|
+
export declare function recoveryDocMeta(about: ArtifactAbout, storage: StorageDescription): RecoveryDocMetaDraft;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The standalone program, by the name it is published under.
|
|
3
|
+
*
|
|
4
|
+
* ⛔ ONE SPELLING FOR THE WHOLE PACKAGE. It is the executable's file name, the prefix of every
|
|
5
|
+
* asset in the table below, and the word the sealed file list writes into its own header so
|
|
6
|
+
* that whoever finds that file years later knows what reads it. Two copies of this string is
|
|
7
|
+
* how a rename lands in one of those places and not the others.
|
|
8
|
+
*/
|
|
9
|
+
export declare const RECOVERY_TOOL = "nmts-recovery";
|
|
10
|
+
/**
|
|
11
|
+
* Where that program's source and its releases are.
|
|
12
|
+
*
|
|
13
|
+
* ⚠ Printed as text into a file that has no links, and used to build the download addresses. It
|
|
14
|
+
* is the same one either way, so it is written down once.
|
|
15
|
+
*/
|
|
16
|
+
export declare const RECOVERY_TOOL_URL = "https://github.com/needmoretruth/nmts-recovery";
|
|
17
|
+
/** One executable the release publishes. */
|
|
18
|
+
export interface PublishedExecutable {
|
|
19
|
+
/** The `process.platform` it was built for. */
|
|
20
|
+
platform: string;
|
|
21
|
+
/** The `process.arch` it was built for. */
|
|
22
|
+
arch: string;
|
|
23
|
+
/** The file name the release attaches it under. */
|
|
24
|
+
asset: string;
|
|
25
|
+
/** How a person says that platform out loud, for the refusal a machine without one gets. */
|
|
26
|
+
label: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Every executable the release workflow attaches, in the order it builds them.
|
|
30
|
+
*
|
|
31
|
+
* ⚠ macOS carries the marketing word in brackets because "aarch64" and "x86_64" are not what
|
|
32
|
+
* anybody's machine calls itself on that platform — the person reading a refusal has to be able
|
|
33
|
+
* to tell whether their own machine is in this list.
|
|
34
|
+
*/
|
|
35
|
+
export declare const PUBLISHED: readonly PublishedExecutable[];
|
|
36
|
+
/** The checksum file the release attaches beside the executables. */
|
|
37
|
+
export declare const SUMS_FILE = "SHA256SUMS";
|
|
38
|
+
/** The one this machine can run, or null when the release publishes nothing for it. */
|
|
39
|
+
export declare function executableFor(platform: string, arch: string): PublishedExecutable | null;
|
|
40
|
+
/** The published platforms, for telling somebody their own is not among them. */
|
|
41
|
+
export declare function publishedLabels(): readonly string[];
|
|
42
|
+
/**
|
|
43
|
+
* The exact commands that build this program from source.
|
|
44
|
+
*
|
|
45
|
+
* ⛔ EXACT, NOT "BUILD IT YOURSELF". Somebody reading this is on an unusual machine and has
|
|
46
|
+
* already lost access to their files; "see the repository" is a research task, and three lines
|
|
47
|
+
* they can paste is not.
|
|
48
|
+
*/
|
|
49
|
+
export declare function buildFromSource(sourceUrl: string): readonly string[];
|
|
50
|
+
/**
|
|
51
|
+
* What checking the bytes against SHA256SUMS shows, and what it does not.
|
|
52
|
+
*
|
|
53
|
+
* ⛔ BOTH SENTENCES, ALWAYS TOGETHER, AND WORDED ONCE. They are printed by the command and
|
|
54
|
+
* repeated in the help text, and if they were written twice one copy would eventually start
|
|
55
|
+
* promising more than the other. There is no "verified", no "trusted" and no "safe" in either
|
|
56
|
+
* of them on purpose: a checksum published by the same release as the file it describes proves
|
|
57
|
+
* the two agree, and nothing whatsoever about who produced them.
|
|
58
|
+
*/
|
|
59
|
+
export declare const CHECK_PROVES: string;
|
|
60
|
+
/** The other half. Never printed without the one above it. */
|
|
61
|
+
export declare const CHECK_DOES_NOT_PROVE: string;
|
|
62
|
+
/** `SHA256SUMS` for whatever the source repository's newest release is. */
|
|
63
|
+
export declare function sumsUrl(sourceUrl: string): string;
|
|
64
|
+
/** One asset of one NAMED release. Never "latest": the tag is resolved once and then reused. */
|
|
65
|
+
export declare function assetUrl(sourceUrl: string, tag: string, asset: string): string;
|
|
66
|
+
/**
|
|
67
|
+
* The release tag a URL names, or null when it does not name one.
|
|
68
|
+
*
|
|
69
|
+
* Split into segments rather than matched with a regex because the file name can contain the
|
|
70
|
+
* characters a regex cares about (`nmts-recovery-windows-x86_64.exe`), and an escaping mistake
|
|
71
|
+
* there would be a silently looser check.
|
|
72
|
+
*/
|
|
73
|
+
export declare function tagFromUrl(url: string, filename: string): string | null;
|
|
74
|
+
/**
|
|
75
|
+
* The tag the redirect chain resolved to, or null.
|
|
76
|
+
*
|
|
77
|
+
* ⛔ THE WHOLE CHAIN, NOT THE LAST HOP. `…/releases/latest/download/SHA256SUMS` redirects to the
|
|
78
|
+
* tagged address and that one redirects again to wherever the bytes are actually served from,
|
|
79
|
+
* which carries no tag at all. Reading only where the chain ended would find nothing.
|
|
80
|
+
*/
|
|
81
|
+
export declare function tagFromChain(chain: readonly string[], filename: string): string | null;
|
|
82
|
+
/** What a lookup in `SHA256SUMS` found, or why it found nothing usable. */
|
|
83
|
+
export type SumsLookup = {
|
|
84
|
+
found: true;
|
|
85
|
+
hash: string;
|
|
86
|
+
} | {
|
|
87
|
+
found: false;
|
|
88
|
+
why: "missing" | "repeated" | "malformed";
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* The hash `SHA256SUMS` gives for one asset.
|
|
92
|
+
*
|
|
93
|
+
* ⛔ TWO LINES FOR ONE NAME IS A REFUSAL, NOT A FIRST-WINS. A file listed twice with different
|
|
94
|
+
* hashes has no answer, and picking one would mean this command decides which of two claims to
|
|
95
|
+
* act on. It has no basis for that, so it says so instead.
|
|
96
|
+
*/
|
|
97
|
+
export declare function hashFromSums(sums: string, asset: string): SumsLookup;
|
|
98
|
+
/**
|
|
99
|
+
* Break one long sentence into lines a terminal will not fold in the middle of a word.
|
|
100
|
+
*
|
|
101
|
+
* ⚠ The two sentences above are written once, as prose, so the help text and the command cannot
|
|
102
|
+
* drift apart. Prose has to be wrapped somewhere, and doing it here keeps the wording out of it.
|
|
103
|
+
*/
|
|
104
|
+
export declare function wrapText(text: string, width?: number): string[];
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** The highest sequence this machine has offered for an account, or 0 when it has offered none. */
|
|
2
|
+
export declare function lastOfferedSeq(accountId: string): number;
|
|
3
|
+
/**
|
|
4
|
+
* Write down a sequence this machine OFFERED — whether or not the server took it.
|
|
5
|
+
*
|
|
6
|
+
* ⛔ OFFERED, NOT ACCEPTED, AND THAT IS THE POINT. Recording only the accepted ones would make a
|
|
7
|
+
* refused number the number the next run offers again, and every run after it: the same
|
|
8
|
+
* refusal for ever. Recording the attempt is what makes a second run get past it.
|
|
9
|
+
*/
|
|
10
|
+
export declare function rememberOfferedSeq(accountId: string, seq: number): void;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/** `file_parts.storage_kind` for a quilt patch. 0 is a dedicated blob. */
|
|
2
|
+
export declare const STORAGE_QUILT = 1;
|
|
3
|
+
/** One stored piece of a file, as the server describes it. */
|
|
4
|
+
export interface SourcePart {
|
|
5
|
+
part_index: number;
|
|
6
|
+
storage_kind: number;
|
|
7
|
+
/** Storage-network code. Absent means Walrus — a fact, not a fallback. */
|
|
8
|
+
network?: number;
|
|
9
|
+
blob_id: string;
|
|
10
|
+
patch_id?: string;
|
|
11
|
+
/** What the part OCCUPIES: the sealed stream, header and tags included. */
|
|
12
|
+
sealed_len: number;
|
|
13
|
+
sui_object_id?: string;
|
|
14
|
+
/**
|
|
15
|
+
* What this part's stored header DECLARES — the plaintext length behind `sealed_len`.
|
|
16
|
+
*
|
|
17
|
+
* ⚠ DERIVED HERE, NOT SENT: the server is told what a part occupies and nothing about the file
|
|
18
|
+
* behind it. ⛔ NOT the file's own length either — a padded part declares more than the file
|
|
19
|
+
* holds, and the real per-part lengths come from `keepLengths`.
|
|
20
|
+
*/
|
|
21
|
+
streamPlaintextLen: number;
|
|
22
|
+
}
|
|
23
|
+
/** One stored file in the dump. It says what is stored, never how it is arranged. */
|
|
24
|
+
export interface SourceItem {
|
|
25
|
+
id: string;
|
|
26
|
+
size: number;
|
|
27
|
+
/** Wrapped file key. The one thing here that cannot be recreated if it is lost. */
|
|
28
|
+
dekWrapped?: string;
|
|
29
|
+
/** Sealed whole-file content hash, when the file was committed with one. */
|
|
30
|
+
contentHashCt?: string;
|
|
31
|
+
createdAt: string;
|
|
32
|
+
updatedAt: string;
|
|
33
|
+
parts: SourcePart[];
|
|
34
|
+
}
|
|
35
|
+
export interface SourceWalkOptions {
|
|
36
|
+
server: string;
|
|
37
|
+
apiKey: string;
|
|
38
|
+
/** base64url of the 32-byte proof. Sent on every page. */
|
|
39
|
+
accountProof: string;
|
|
40
|
+
/** Ticks while paging, so a very large account does not look frozen. */
|
|
41
|
+
onProgress?: ((loaded: number) => void) | undefined;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Read every page of the dump, oldest cursor first.
|
|
45
|
+
*
|
|
46
|
+
* ⛔ A REPEATED CURSOR IS A REFUSAL, NOT A LOOP. The cursor is server-issued and strictly
|
|
47
|
+
* increasing, but a bug — or a hand-edited answer — that repeated one would spin here forever
|
|
48
|
+
* with nothing to show for it.
|
|
49
|
+
*/
|
|
50
|
+
export declare function readAllRecoverySource(options: SourceWalkOptions): Promise<SourceItem[]>;
|
package/dist/redact.d.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One thing that is taken out, and how it is recognised.
|
|
3
|
+
*
|
|
4
|
+
* ⛔ DATA, NOT A CHAIN OF `if`s. The rules are read by a test one at a time, and a rule that
|
|
5
|
+
* exists only inside a function body is a rule nobody can enumerate.
|
|
6
|
+
*/
|
|
7
|
+
export interface RedactionRule {
|
|
8
|
+
/** What replaces the match. Written in the report, so it says what stood there. */
|
|
9
|
+
readonly label: string;
|
|
10
|
+
/** ⚠ Global. `redact` relies on `replaceAll` semantics and resets nothing. */
|
|
11
|
+
readonly test: RegExp;
|
|
12
|
+
/**
|
|
13
|
+
* A second opinion on a match, for shapes a regular expression cannot decide alone.
|
|
14
|
+
*
|
|
15
|
+
* The NMTS key is the one that needs it: recognising it is "exactly thirty-three symbols
|
|
16
|
+
* once the separators are gone", which is a count and not a pattern.
|
|
17
|
+
*/
|
|
18
|
+
readonly only?: (match: string) => boolean;
|
|
19
|
+
/**
|
|
20
|
+
* What the match becomes, when part of it has to stay.
|
|
21
|
+
*
|
|
22
|
+
* `token=…` is the case: the name is what makes the label readable, so the name stays and only
|
|
23
|
+
* the value goes.
|
|
24
|
+
*/
|
|
25
|
+
readonly into?: (match: string, groups: readonly (string | undefined)[]) => string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The rules, in the order they run.
|
|
29
|
+
*
|
|
30
|
+
* ⛔ ORDER IS PART OF EACH RULE. The narrow shapes go first so that the value gets the label that
|
|
31
|
+
* names it — an API key becomes `[api-key]` and not the `[secret]` that the catch-all run of
|
|
32
|
+
* key material would have given it. The catch-all rules go last, over a text where everything
|
|
33
|
+
* recognisable has already become a label, and a label is too short for them to match.
|
|
34
|
+
*/
|
|
35
|
+
export declare const RULES: readonly RedactionRule[];
|
|
36
|
+
/**
|
|
37
|
+
* The values this process is actually holding, whatever they look like.
|
|
38
|
+
*
|
|
39
|
+
* ⛔ READ AT REDACTION TIME, NOT AT IMPORT TIME. A command that sets a variable for a child, a
|
|
40
|
+
* test that swaps one, and a shell that exported one after this module loaded would all defeat
|
|
41
|
+
* a snapshot taken once.
|
|
42
|
+
*
|
|
43
|
+
* ⛔ EVERY `NMTS_*` VARIABLE, NOT THE THREE THAT ARE OBVIOUSLY SECRET. The cost of labelling a
|
|
44
|
+
* server address is one word in a report; the cost of a variable added next year that nobody
|
|
45
|
+
* thought to add here is the value itself.
|
|
46
|
+
*
|
|
47
|
+
* ⚠ LONGEST FIRST. Two variables holding one value inside the other would otherwise leave the
|
|
48
|
+
* longer one half-labelled, which is a value partly in the clear.
|
|
49
|
+
*/
|
|
50
|
+
export declare function environmentRules(): RedactionRule[];
|
|
51
|
+
/**
|
|
52
|
+
* Take the secrets out of a text.
|
|
53
|
+
*
|
|
54
|
+
* ⛔ THE ENVIRONMENT GOES FIRST. A value this process is holding is known to be a secret, and a
|
|
55
|
+
* label naming the variable it came from (`[env:NMTS_API_KEY]`) says more to whoever reads the
|
|
56
|
+
* report than the shape-based label would.
|
|
57
|
+
*/
|
|
58
|
+
export declare function redact(text: string): string;
|
|
59
|
+
/** The label a value named with `--omit` becomes. */
|
|
60
|
+
export declare const OMITTED = "[omitted]";
|
|
61
|
+
/** Values shorter than this are refused: two characters match half a text. */
|
|
62
|
+
export declare const SHORTEST_OMIT = 3;
|
|
63
|
+
/**
|
|
64
|
+
* Replace values the caller named, on top of everything `redact` finds.
|
|
65
|
+
*
|
|
66
|
+
* ⛔ LITERAL, NOT A PATTERN. What is passed is a file name or a folder somebody typed, and
|
|
67
|
+
* treating it as a regular expression would turn a dot into "any character" and a bracket into
|
|
68
|
+
* a syntax error in somebody's report.
|
|
69
|
+
*/
|
|
70
|
+
export declare function omitLiterals(text: string, values: readonly string[]): string;
|
package/dist/redact.js
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
// Taking out of a text the things that must never leave this machine.
|
|
2
|
+
//
|
|
3
|
+
// ⛔ IT RUNS BEFORE ANYTHING IS WRITTEN OR SENT, NOT AFTER. The run log is redacted on the way to
|
|
4
|
+
// the disk and a support message is redacted on the way to the wire, so a file somebody copies
|
|
5
|
+
// off this machine and a request somebody intercepts both carry labels rather than values. A
|
|
6
|
+
// filter applied at the last moment would still leave the real thing lying in a file.
|
|
7
|
+
//
|
|
8
|
+
// ⛔ IT ERRS TOWARD REPLACING. A label where an ordinary word stood costs the reader one word and
|
|
9
|
+
// is visible in the preview; a missed NMTS key costs the whole account and is visible to
|
|
10
|
+
// nobody. Every rule below is written on that side of the line, and the tests pin both
|
|
11
|
+
// directions — one text each rule must replace, one text it must leave alone.
|
|
12
|
+
//
|
|
13
|
+
// ⛔ THE MATCH IS REPLACED BY A LABEL, NOT BY STARS. `[account-code]` tells the person reading the
|
|
14
|
+
// report what stood there, which is often the useful half of the sentence ("I pasted
|
|
15
|
+
// [account-code] and it said ..."), and a row of asterisks does not.
|
|
16
|
+
//
|
|
17
|
+
// ⚠ WHAT IT CANNOT DO. It knows the SHAPES this product uses and the values this process can see.
|
|
18
|
+
// A secret belonging to something else, written in prose ("my password is hunter2"), is not a
|
|
19
|
+
// shape and is not in this process's environment, and nothing here will find it. That is why the
|
|
20
|
+
// text a person reads before sending asks them to keep personal details out: the machine covers
|
|
21
|
+
// what a machine can recognise, and the sentence covers the rest.
|
|
22
|
+
/** An NMTS key is 32 data symbols and one check symbol. `crypto/src/codes.rs` is the origin. */
|
|
23
|
+
const ACCOUNT_CODE_SYMBOLS = 33;
|
|
24
|
+
/** Crockford base32, the data half: `0-9 A-H J-K M-N P-T V-Z`, either case. No `I L O U`. */
|
|
25
|
+
const DATA_CLASS = "0-9A-HJKMNP-TV-Za-hjkmnp-tv-z";
|
|
26
|
+
/** The check symbol may also be one of the five Crockford extras. */
|
|
27
|
+
const CHECK_CLASS = `${DATA_CLASS}*~$=Uu`;
|
|
28
|
+
const DATA_ONLY = new RegExp(`^[${DATA_CLASS}]+$`, "u");
|
|
29
|
+
const CHECK_ONLY = new RegExp(`^[${CHECK_CLASS}]$`, "u");
|
|
30
|
+
/**
|
|
31
|
+
* Is this run of symbols an NMTS key?
|
|
32
|
+
*
|
|
33
|
+
* ⛔ THE GROUPS MUST BE THREE SYMBOLS OR MORE. Without that, a hyphenated phrase of single letters
|
|
34
|
+
* adding up to thirty-three would be replaced, and error sentences are exactly where hyphens
|
|
35
|
+
* live. The display form the product prints is groups of four, so nothing real is excluded.
|
|
36
|
+
*/
|
|
37
|
+
function looksLikeAnAccountCode(candidate) {
|
|
38
|
+
const groups = candidate.split("-");
|
|
39
|
+
if (groups.some((group) => group.length < 3))
|
|
40
|
+
return false;
|
|
41
|
+
const symbols = groups.join("");
|
|
42
|
+
if (symbols.length !== ACCOUNT_CODE_SYMBOLS)
|
|
43
|
+
return false;
|
|
44
|
+
return DATA_ONLY.test(symbols.slice(0, -1)) && CHECK_ONLY.test(symbols.slice(-1));
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The rules, in the order they run.
|
|
48
|
+
*
|
|
49
|
+
* ⛔ ORDER IS PART OF EACH RULE. The narrow shapes go first so that the value gets the label that
|
|
50
|
+
* names it — an API key becomes `[api-key]` and not the `[secret]` that the catch-all run of
|
|
51
|
+
* key material would have given it. The catch-all rules go last, over a text where everything
|
|
52
|
+
* recognisable has already become a label, and a label is too short for them to match.
|
|
53
|
+
*/
|
|
54
|
+
export const RULES = [
|
|
55
|
+
{
|
|
56
|
+
// A whole PEM block, header to footer. Its body is standard base64, which the run rule below
|
|
57
|
+
// deliberately does not cover, so this has to take the block rather than the first line.
|
|
58
|
+
label: "[secret]",
|
|
59
|
+
test: /-----BEGIN[^\n-]*-----[\s\S]*?-----END[^\n-]*-----/g,
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
// A header with no footer: a key that was pasted half-way, which is still most of a key.
|
|
63
|
+
label: "[secret]",
|
|
64
|
+
test: /-----BEGIN[^\n]*/g,
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
// Sui's own spelling of a private key. It is bech32, so the tail is lower-case alphanumerics.
|
|
68
|
+
label: "[secret]",
|
|
69
|
+
test: /\bsuiprivkey1[0-9a-z]+/gi,
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
// `api-key.ts` has the exact shape: the prefix, a 12-symbol handle, `_`, and 43 more.
|
|
73
|
+
label: "[api-key]",
|
|
74
|
+
test: /nmts_ak1_[A-Za-z0-9_-]+/g,
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
// Hyphen-separated or run together — the two spellings a person copies. Whitespace grouping
|
|
78
|
+
// is the rule below, kept separate because a maximal run over spaces would swallow the
|
|
79
|
+
// sentence the code sits in and then fail to recognise it.
|
|
80
|
+
//
|
|
81
|
+
// ⚠ The five extended check symbols are allowed only at the END. Letting `=` stand inside the
|
|
82
|
+
// run glued `--message=` to the code that followed it and the pair then matched nothing.
|
|
83
|
+
label: "[account-code]",
|
|
84
|
+
test: new RegExp(`(?<![0-9A-Za-z])[${DATA_CLASS}]{3,}(?:-[${DATA_CLASS}]{3,})*[*~$=Uu]?(?![0-9A-Za-z])`, "gu"),
|
|
85
|
+
only: looksLikeAnAccountCode,
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
// The display form with spaces where the dashes are: seven groups of four, then five.
|
|
89
|
+
label: "[account-code]",
|
|
90
|
+
test: new RegExp(`(?<![0-9A-Za-z])[${DATA_CLASS}]{4}(?: [${DATA_CLASS}]{4}){6} [${DATA_CLASS}]{4}[${CHECK_CLASS}](?![0-9A-Za-z])`, "gu"),
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
// ⛔ THE NAME STAYS AND THE VALUE GOES. `Authorization: [secret]` is still a sentence somebody
|
|
94
|
+
// can act on; `[secret]` alone is not. The prefix is widened to anything ending in `token`
|
|
95
|
+
// or `key` so that `api_key=` and `access_token=` are covered by one rule rather than three.
|
|
96
|
+
label: "[secret]",
|
|
97
|
+
// ⚠ `(?!\[)` KEEPS A LABEL A RULE ABOVE ALREADY PUT THERE. Without it `key=[api-key]` became
|
|
98
|
+
// `key=[secret]`, which is not wrong but is less useful: the narrower label is the one that
|
|
99
|
+
// says what stood there.
|
|
100
|
+
test: /\b(authorization\s*:\s*bearer|[A-Za-z0-9_]*(?:token|key)\s*=)\s*("?)(?!\[)[^\s"'&]+\2/gi,
|
|
101
|
+
into: (_match, groups) => {
|
|
102
|
+
const prefix = groups[0] ?? "";
|
|
103
|
+
return prefix.endsWith("=") ? `${prefix}[secret]` : `${prefix} [secret]`;
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
// Key material, session tokens, ciphertext: 43 symbols is the length of base64url over 32
|
|
108
|
+
// bytes, which is what every secret this product handles decodes to.
|
|
109
|
+
//
|
|
110
|
+
// ⛔ BOTH CASES REQUIRED, AND THAT IS WHAT KEEPS FILE NAMES. `screenshot-2026-09-04-at-...`
|
|
111
|
+
// is base64url's alphabet too, and long names built out of hyphenated lower-case words are
|
|
112
|
+
// ordinary. Random 43-symbol material without a single capital happens about once in ten
|
|
113
|
+
// billion times; a lower-case file name happens every day.
|
|
114
|
+
label: "[secret]",
|
|
115
|
+
test: /(?<![A-Za-z0-9_-])(?=[A-Za-z0-9_-]*[A-Z])(?=[A-Za-z0-9_-]*[a-z])[A-Za-z0-9_-]{43,}(?![A-Za-z0-9_-])/g,
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
// Hex of the same weight — a hash, a chain digest, a raw key written out.
|
|
119
|
+
label: "[secret]",
|
|
120
|
+
test: /(?<![A-Za-z0-9])[0-9a-f]{43,}(?![A-Za-z0-9])/gi,
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
// ⛔ A SEED PHRASE, WHICH THIS TOOL NEVER HOLDS AND A PERSON MIGHT STILL PASTE. The wallet
|
|
124
|
+
// here is derived from the NMTS key and there is no mnemonic anywhere in it — but
|
|
125
|
+
// somebody reporting a wallet problem has another wallet open, and twelve words is what
|
|
126
|
+
// that one shows them.
|
|
127
|
+
//
|
|
128
|
+
// ⛔ THE COUNT IS EXACT AND THE RUN IS MAXIMAL. Only a whole run of exactly twelve or
|
|
129
|
+
// twenty-four short lower-case words is replaced, so a sentence that happens to contain
|
|
130
|
+
// twelve of them among longer words, capitals, digits or punctuation is left alone.
|
|
131
|
+
label: "[words]",
|
|
132
|
+
test: /\b[a-z]{3,8}(?:\s+[a-z]{3,8})*\b/g,
|
|
133
|
+
only: (match) => {
|
|
134
|
+
const count = match.split(/\s+/u).length;
|
|
135
|
+
return count === 12 || count === 24;
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
];
|
|
139
|
+
/** Below this length a variable's value is too short to be a credential and too likely to be a word. */
|
|
140
|
+
const SHORTEST_ENV_VALUE = 8;
|
|
141
|
+
function escapeForRegExp(value) {
|
|
142
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* The values this process is actually holding, whatever they look like.
|
|
146
|
+
*
|
|
147
|
+
* ⛔ READ AT REDACTION TIME, NOT AT IMPORT TIME. A command that sets a variable for a child, a
|
|
148
|
+
* test that swaps one, and a shell that exported one after this module loaded would all defeat
|
|
149
|
+
* a snapshot taken once.
|
|
150
|
+
*
|
|
151
|
+
* ⛔ EVERY `NMTS_*` VARIABLE, NOT THE THREE THAT ARE OBVIOUSLY SECRET. The cost of labelling a
|
|
152
|
+
* server address is one word in a report; the cost of a variable added next year that nobody
|
|
153
|
+
* thought to add here is the value itself.
|
|
154
|
+
*
|
|
155
|
+
* ⚠ LONGEST FIRST. Two variables holding one value inside the other would otherwise leave the
|
|
156
|
+
* longer one half-labelled, which is a value partly in the clear.
|
|
157
|
+
*/
|
|
158
|
+
export function environmentRules() {
|
|
159
|
+
const found = [];
|
|
160
|
+
for (const [name, value] of Object.entries(process.env)) {
|
|
161
|
+
if (!name.startsWith("NMTS_"))
|
|
162
|
+
continue;
|
|
163
|
+
if (value === undefined || value.length < SHORTEST_ENV_VALUE)
|
|
164
|
+
continue;
|
|
165
|
+
found.push({ name, value });
|
|
166
|
+
}
|
|
167
|
+
found.sort((a, b) => b.value.length - a.value.length);
|
|
168
|
+
return found.map((v) => ({ label: `[env:${v.name}]`, test: new RegExp(escapeForRegExp(v.value), "g") }));
|
|
169
|
+
}
|
|
170
|
+
function apply(text, rule) {
|
|
171
|
+
return text.replace(rule.test, (match, ...rest) => {
|
|
172
|
+
if (rule.only !== undefined && !rule.only(match))
|
|
173
|
+
return match;
|
|
174
|
+
const groups = rest.filter((v) => typeof v === "string" || v === undefined);
|
|
175
|
+
return rule.into === undefined ? rule.label : rule.into(match, groups);
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Take the secrets out of a text.
|
|
180
|
+
*
|
|
181
|
+
* ⛔ THE ENVIRONMENT GOES FIRST. A value this process is holding is known to be a secret, and a
|
|
182
|
+
* label naming the variable it came from (`[env:NMTS_API_KEY]`) says more to whoever reads the
|
|
183
|
+
* report than the shape-based label would.
|
|
184
|
+
*/
|
|
185
|
+
export function redact(text) {
|
|
186
|
+
let out = text;
|
|
187
|
+
for (const rule of environmentRules())
|
|
188
|
+
out = apply(out, rule);
|
|
189
|
+
for (const rule of RULES)
|
|
190
|
+
out = apply(out, rule);
|
|
191
|
+
return out;
|
|
192
|
+
}
|
|
193
|
+
/** The label a value named with `--omit` becomes. */
|
|
194
|
+
export const OMITTED = "[omitted]";
|
|
195
|
+
/** Values shorter than this are refused: two characters match half a text. */
|
|
196
|
+
export const SHORTEST_OMIT = 3;
|
|
197
|
+
/**
|
|
198
|
+
* Replace values the caller named, on top of everything `redact` finds.
|
|
199
|
+
*
|
|
200
|
+
* ⛔ LITERAL, NOT A PATTERN. What is passed is a file name or a folder somebody typed, and
|
|
201
|
+
* treating it as a regular expression would turn a dot into "any character" and a bracket into
|
|
202
|
+
* a syntax error in somebody's report.
|
|
203
|
+
*/
|
|
204
|
+
export function omitLiterals(text, values) {
|
|
205
|
+
let out = text;
|
|
206
|
+
for (const value of values) {
|
|
207
|
+
if (value.length < SHORTEST_OMIT)
|
|
208
|
+
continue;
|
|
209
|
+
out = out.replace(new RegExp(escapeForRegExp(value), "g"), OMITTED);
|
|
210
|
+
}
|
|
211
|
+
return out;
|
|
212
|
+
}
|