@needmoretruth/nmts-cli 0.17.2 → 0.34.2
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 +125 -517
- package/CHANGELOG.md +104 -0
- package/README.ko.md +377 -469
- package/README.md +394 -474
- 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
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { type LockedCode } from "./code-vault.ts";
|
|
2
|
+
/** The environment variable an agent sets instead of running `login`. */
|
|
3
|
+
export declare const CODE_ENV_VAR = "NMTS_ACCOUNT_CODE";
|
|
4
|
+
/** The API key, same rules: read fresh, never an argument, never printed. */
|
|
5
|
+
export declare const API_KEY_ENV_VAR = "NMTS_API_KEY";
|
|
6
|
+
/**
|
|
7
|
+
* Opens a passphrase-locked stored code without a terminal.
|
|
8
|
+
*
|
|
9
|
+
* ⚠ A passphrase in an environment variable protects the FILE, not this run: anything that can
|
|
10
|
+
* read this variable can read the code the moment the tool decodes it. What it still buys is
|
|
11
|
+
* real — a copied home directory, a backup, an image layer and a stolen disk all yield nothing
|
|
12
|
+
* — and it is the only shape that works where no person is present to type.
|
|
13
|
+
*/
|
|
14
|
+
export declare const PASSPHRASE_ENV_VAR = "NMTS_PASSPHRASE";
|
|
15
|
+
/**
|
|
16
|
+
* Names a FILE holding the NMTS key, rather than holding it directly.
|
|
17
|
+
*
|
|
18
|
+
* ⛔ THE ONLY SAFE WAY TO GIVE A CONTAINER A SECRET. See `readSecretFile`.
|
|
19
|
+
*/
|
|
20
|
+
export declare const CODE_FILE_ENV_VAR = "NMTS_ACCOUNT_CODE_FILE";
|
|
21
|
+
/** Names a file holding the API key. Same reason. */
|
|
22
|
+
export declare const API_KEY_FILE_ENV_VAR = "NMTS_API_KEY_FILE";
|
|
23
|
+
/** Where a credential came from. Reported, never guessed. */
|
|
24
|
+
export type CredentialSource = "env" | "secret-file" | "file" | "file-locked";
|
|
25
|
+
/**
|
|
26
|
+
* An NMTS key that has been FOUND but not necessarily opened.
|
|
27
|
+
*
|
|
28
|
+
* ⛔ THE TWO CASES ARE SEPARATE TYPES so that no caller can read `.code` off a locked one. A
|
|
29
|
+
* single shape with a nullable field would compile everywhere and be wrong in exactly one
|
|
30
|
+
* place — the command that forgot to unlock and treated "no code" as "not signed in".
|
|
31
|
+
*/
|
|
32
|
+
export type ResolvedCode = {
|
|
33
|
+
readonly source: "env" | "secret-file" | "file";
|
|
34
|
+
readonly code: string;
|
|
35
|
+
} | {
|
|
36
|
+
readonly source: "file-locked";
|
|
37
|
+
readonly locked: LockedCode;
|
|
38
|
+
};
|
|
39
|
+
/** Directory holding everything this tool keeps. 0700 where the platform honours it. */
|
|
40
|
+
export declare function configDir(): string;
|
|
41
|
+
export declare function credentialsPath(): string;
|
|
42
|
+
/**
|
|
43
|
+
* What is kept on disk. `apiKey` is absent until API keys exist and one has been supplied.
|
|
44
|
+
*
|
|
45
|
+
* ⛔ EXACTLY ONE OF `accountCode` AND `lockedCode`, and `isCredentials` enforces it. A file with
|
|
46
|
+
* both would leave a reader to choose, and the wrong choice is the plain copy of a code
|
|
47
|
+
* somebody asked to have locked.
|
|
48
|
+
*/
|
|
49
|
+
export interface Credentials {
|
|
50
|
+
/**
|
|
51
|
+
* The NMTS key in the clear.
|
|
52
|
+
*
|
|
53
|
+
* ⛔ ONLY WHEN THE PERSON CHOSE IT (`login --plain`, behind the `unsafe-code-storage` consent).
|
|
54
|
+
* The default writes `lockedCode` instead.
|
|
55
|
+
*/
|
|
56
|
+
accountCode?: string;
|
|
57
|
+
/** The NMTS key sealed under a passphrase. The default form. */
|
|
58
|
+
lockedCode?: LockedCode;
|
|
59
|
+
/** Server credential that waives the human check. Optional: not every account has one. */
|
|
60
|
+
apiKey?: string;
|
|
61
|
+
/** Base URL of the NMTS server this code belongs to. */
|
|
62
|
+
server: string;
|
|
63
|
+
/**
|
|
64
|
+
* Which storage network that server uses.
|
|
65
|
+
*
|
|
66
|
+
* ⛔ Stored rather than inferred each run: the network decides WHERE the files are, and a later
|
|
67
|
+
* run that guessed differently from the run that uploaded would look in the wrong place and
|
|
68
|
+
* report an empty account.
|
|
69
|
+
*/
|
|
70
|
+
network?: string;
|
|
71
|
+
}
|
|
72
|
+
/** True on platforms where Node applies a POSIX file mode. */
|
|
73
|
+
export declare function modesAreEnforced(): boolean;
|
|
74
|
+
/**
|
|
75
|
+
* Can this machine actually keep a file private, where the NMTS key would go?
|
|
76
|
+
*
|
|
77
|
+
* ⛔ IT MEASURES RATHER THAN ASSUMES. "Not Windows" is not the same question: a container with a
|
|
78
|
+
* bind mount from a Windows host, a network drive, an exFAT stick and several FUSE filesystems
|
|
79
|
+
* all accept `chmod` and then ignore it. The mode comes back as whatever the filesystem felt
|
|
80
|
+
* like, and the tool would have written the NMTS key into a file anybody can read while
|
|
81
|
+
* believing it had locked it.
|
|
82
|
+
*
|
|
83
|
+
* So: write a file, ask for 0600, read the mode back, and delete it. The probe is empty, its name
|
|
84
|
+
* is not the credentials name, and it is removed whatever happens.
|
|
85
|
+
*
|
|
86
|
+
* Returns false on Windows without probing — the platform has no POSIX mode to check, and
|
|
87
|
+
* answering "yes" from a successful no-op would be the worst of the three possible answers.
|
|
88
|
+
*/
|
|
89
|
+
export declare function codeStorageIsPrivate(): boolean;
|
|
90
|
+
/**
|
|
91
|
+
* Write credentials, replacing any existing file, without ever leaving a readable window.
|
|
92
|
+
*
|
|
93
|
+
* The write goes to a fresh file created with `wx` (fails if the name exists, so nothing already
|
|
94
|
+
* on disk is opened) and is then renamed over the target. A rename within the same directory is
|
|
95
|
+
* atomic, so a reader either sees the old file or the new one and never a half-written one.
|
|
96
|
+
*/
|
|
97
|
+
export declare function writeCredentials(creds: Credentials): void;
|
|
98
|
+
/** Raised when the file exists but this machine is not keeping it private. */
|
|
99
|
+
export declare class CredentialsTooOpenError extends Error {
|
|
100
|
+
readonly path: string;
|
|
101
|
+
readonly mode: number;
|
|
102
|
+
constructor(path: string, mode: number);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Read credentials from disk, refusing a file other users can read.
|
|
106
|
+
*
|
|
107
|
+
* ⛔ The refusal is the point. A credentials file that went world-readable — copied with `cp -r`,
|
|
108
|
+
* restored from an archive, written by an older version — is a leak that nothing else in the
|
|
109
|
+
* system would ever mention. Reading it anyway and carrying on is how that stays quiet.
|
|
110
|
+
*/
|
|
111
|
+
export declare function readCredentialsFile(): Credentials | null;
|
|
112
|
+
/**
|
|
113
|
+
* The NMTS key this run should use, and where it came from.
|
|
114
|
+
*
|
|
115
|
+
* The environment variable wins over the file so an agent can be handed a code for one run without
|
|
116
|
+
* writing anything to disk — which is the safer shape when the machine is shared or ephemeral.
|
|
117
|
+
*/
|
|
118
|
+
export declare function resolveAccountCode(): ResolvedCode | null;
|
|
119
|
+
/**
|
|
120
|
+
* Read a secret out of the file an environment variable NAMES.
|
|
121
|
+
*
|
|
122
|
+
* ⛔ THIS IS HOW A SECRET GETS INTO A CONTAINER. An environment variable holding the value itself
|
|
123
|
+
* is readable by anybody who can inspect the container — `docker inspect` prints the whole
|
|
124
|
+
* environment, and so does the API behind it. A variable holding a PATH gives that reader a
|
|
125
|
+
* filename and nothing else, while the value rides in on a `--secret` mount, a tmpfs, or a
|
|
126
|
+
* bind-mounted file whose permissions the host controls. It is the convention the official
|
|
127
|
+
* database images use, for the same reason.
|
|
128
|
+
*
|
|
129
|
+
* Trailing whitespace and a trailing newline are removed: writing a secret to a file with `echo`
|
|
130
|
+
* appends one, and refusing a code because of it would be a puzzle with no clue.
|
|
131
|
+
*/
|
|
132
|
+
export declare function readSecretFile(variable: string): string | null;
|
|
133
|
+
/**
|
|
134
|
+
* The API key this run should use, and where it came from.
|
|
135
|
+
*
|
|
136
|
+
* ⚠ SEPARATE FROM THE NMTS KEY ON PURPOSE, and the two can come from different places. The
|
|
137
|
+
* code is what opens the files; the key is only what makes the server answer without a human
|
|
138
|
+
* check. Somebody may keep the code in the environment for one run while the key stays on the
|
|
139
|
+
* machine, or the other way round, and neither combination is unusual enough to refuse.
|
|
140
|
+
*/
|
|
141
|
+
export declare function resolveApiKey(): {
|
|
142
|
+
key: string;
|
|
143
|
+
source: CredentialSource;
|
|
144
|
+
} | null;
|
|
145
|
+
/** Scratch location used only by tests that need a directory outside the real home. */
|
|
146
|
+
export declare function testConfigDir(name: string): string;
|
package/dist/credentials.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Where an
|
|
1
|
+
// Where an NMTS key lives on this machine, and every rule about how it gets there.
|
|
2
2
|
//
|
|
3
3
|
// ⛔ NEVER FROM THE COMMAND LINE. On Linux any process can read /proc/<pid>/cmdline, so a secret
|
|
4
4
|
// passed as an argument is readable by anything running as the same user for as long as the
|
|
@@ -34,7 +34,7 @@ export const API_KEY_ENV_VAR = "NMTS_API_KEY";
|
|
|
34
34
|
*/
|
|
35
35
|
export const PASSPHRASE_ENV_VAR = "NMTS_PASSPHRASE";
|
|
36
36
|
/**
|
|
37
|
-
* Names a FILE holding the
|
|
37
|
+
* Names a FILE holding the NMTS key, rather than holding it directly.
|
|
38
38
|
*
|
|
39
39
|
* ⛔ THE ONLY SAFE WAY TO GIVE A CONTAINER A SECRET. See `readSecretFile`.
|
|
40
40
|
*/
|
|
@@ -54,12 +54,12 @@ export function modesAreEnforced() {
|
|
|
54
54
|
return process.platform !== "win32";
|
|
55
55
|
}
|
|
56
56
|
/**
|
|
57
|
-
* Can this machine actually keep a file private, where the
|
|
57
|
+
* Can this machine actually keep a file private, where the NMTS key would go?
|
|
58
58
|
*
|
|
59
59
|
* ⛔ IT MEASURES RATHER THAN ASSUMES. "Not Windows" is not the same question: a container with a
|
|
60
60
|
* bind mount from a Windows host, a network drive, an exFAT stick and several FUSE filesystems
|
|
61
61
|
* all accept `chmod` and then ignore it. The mode comes back as whatever the filesystem felt
|
|
62
|
-
* like, and the tool would have written the
|
|
62
|
+
* like, and the tool would have written the NMTS key into a file anybody can read while
|
|
63
63
|
* believing it had locked it.
|
|
64
64
|
*
|
|
65
65
|
* So: write a file, ask for 0600, read the mode back, and delete it. The probe is empty, its name
|
|
@@ -108,7 +108,7 @@ export function writeCredentials(creds) {
|
|
|
108
108
|
const target = credentialsPath();
|
|
109
109
|
const scratch = join(dir, `.credentials.${process.pid}.${Date.now()}.tmp`);
|
|
110
110
|
// ⛔ THE SCRATCH FILE NEVER OUTLIVES A FAILURE. It holds exactly what the target would — the
|
|
111
|
-
//
|
|
111
|
+
// NMTS key, in the clear when that is the shape being written — and `logout` removes
|
|
112
112
|
// only `credentials.json`, so one left behind by a full disk or a failed rename would sit
|
|
113
113
|
// there unreferenced and unnoticed. The rename is the last thing that happens; anything that
|
|
114
114
|
// stops before it leaves nothing.
|
|
@@ -173,7 +173,7 @@ export function readCredentialsFile() {
|
|
|
173
173
|
throw error;
|
|
174
174
|
}
|
|
175
175
|
// ⛔ THE PARSER'S OWN MESSAGE NEVER ESCAPES. V8 quotes about thirty characters of the input in
|
|
176
|
-
// `Unexpected token …`, and the input here is the file the
|
|
176
|
+
// `Unexpected token …`, and the input here is the file the NMTS key is in — an
|
|
177
177
|
// adversarial review printed nine symbols of a real code to stderr from a single lost quote.
|
|
178
178
|
// `errors.ts` prints an unknown error's message verbatim, so the only place to stop it is
|
|
179
179
|
// here, before it becomes an error at all.
|
|
@@ -192,7 +192,7 @@ export function readCredentialsFile() {
|
|
|
192
192
|
* The one refusal for a file this version cannot use, in the one wording.
|
|
193
193
|
*
|
|
194
194
|
* ⛔ IT NAMES THE PATH AND NOTHING ELSE. Not the contents, not the parser's complaint, not which
|
|
195
|
-
* field was wrong — the file is the
|
|
195
|
+
* field was wrong — the file is the NMTS key's file, and every one of those quotes it.
|
|
196
196
|
*
|
|
197
197
|
* ⚠ AND IT IS AN `NmtsError` WITH A WAY OUT. It used to be a bare `Error`, which meant the
|
|
198
198
|
* generic exit code and no next step: `whoami`, `ls`, `get` and `put` all died at 1 saying
|
|
@@ -203,7 +203,7 @@ function unusable(path, what) {
|
|
|
203
203
|
return new NmtsError(`${path} ${what}.`, {
|
|
204
204
|
exitCode: 3,
|
|
205
205
|
nextStep: `Nothing was read from it. \`nmts logout\` removes it, and \`nmts login\` writes a fresh ` +
|
|
206
|
-
`one. If that file is the only copy of the
|
|
206
|
+
`one. If that file is the only copy of the NMTS key, copy that key out of it by hand ` +
|
|
207
207
|
`first — this tool will not open it.`,
|
|
208
208
|
});
|
|
209
209
|
}
|
|
@@ -231,7 +231,7 @@ function isCredentials(value) {
|
|
|
231
231
|
return true;
|
|
232
232
|
}
|
|
233
233
|
/**
|
|
234
|
-
* The
|
|
234
|
+
* The NMTS key this run should use, and where it came from.
|
|
235
235
|
*
|
|
236
236
|
* The environment variable wins over the file so an agent can be handed a code for one run without
|
|
237
237
|
* writing anything to disk — which is the safer shape when the machine is shared or ephemeral.
|
|
@@ -288,7 +288,7 @@ export function readSecretFile(variable) {
|
|
|
288
288
|
/**
|
|
289
289
|
* The API key this run should use, and where it came from.
|
|
290
290
|
*
|
|
291
|
-
* ⚠ SEPARATE FROM THE
|
|
291
|
+
* ⚠ SEPARATE FROM THE NMTS KEY ON PURPOSE, and the two can come from different places. The
|
|
292
292
|
* code is what opens the files; the key is only what makes the server answer without a human
|
|
293
293
|
* check. Somebody may keep the code in the environment for one run while the key stays on the
|
|
294
294
|
* machine, or the other way round, and neither combination is unusual enough to refuse.
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One part's OPENING session, held open by the engine.
|
|
3
|
+
*
|
|
4
|
+
* `push()` the sealed bytes in any slice sizes, in order; each call returns the plaintext of
|
|
5
|
+
* whatever chunks that completed, which is usually nothing and then one whole chunk. `finish()`
|
|
6
|
+
* after the last byte checks every end-of-stream invariant the format has — that all chunks were
|
|
7
|
+
* consumed, that the final one was flagged final, that the total matches the length in the header,
|
|
8
|
+
* that nothing trails.
|
|
9
|
+
*
|
|
10
|
+
* ⛔ `finish()` IS NOT OPTIONAL AND IS NOT A FORMALITY. Tampering with a chunk is caught by
|
|
11
|
+
* `push()`, but a stream cut short is only caught here: every chunk that arrived authenticates
|
|
12
|
+
* perfectly and the file is simply missing its end. A caller that skipped this would accept a
|
|
13
|
+
* truncated part as a whole one.
|
|
14
|
+
*
|
|
15
|
+
* ⛔ `free()` IS NOT HOUSEKEEPING. The engine holds the file key for as long as the session is
|
|
16
|
+
* open, so every path out — including a failed read — has to reach it.
|
|
17
|
+
*/
|
|
18
|
+
export interface StreamOpener {
|
|
19
|
+
/** Feed sealed bytes. Returns the plaintext of any chunks that completed, possibly empty. */
|
|
20
|
+
push(data: Uint8Array): Uint8Array;
|
|
21
|
+
/** Check the stream ended as the format requires. Throws when it did not. */
|
|
22
|
+
finish(): void;
|
|
23
|
+
/** Release the engine-side memory, and the key with it. */
|
|
24
|
+
free(): void;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* One part's sealing session, held open by the engine.
|
|
28
|
+
*
|
|
29
|
+
* `header()`, then every `push()` output in order, then `finish()`, concatenated, IS the part's
|
|
30
|
+
* complete NCF-3 stream. Nothing is buffered on this side.
|
|
31
|
+
*
|
|
32
|
+
* ⛔ `free()` IS NOT HOUSEKEEPING. The engine holds the file key for as long as the session is
|
|
33
|
+
* open, so a caller that abandons one leaves key material live in the engine's memory for the
|
|
34
|
+
* rest of the process. Every path out of a sealing loop — including a failed read — has to
|
|
35
|
+
* reach it.
|
|
36
|
+
*/
|
|
37
|
+
export interface StreamSealer {
|
|
38
|
+
/** The stream header. Goes out first, before any chunk. */
|
|
39
|
+
header(): Uint8Array;
|
|
40
|
+
/** Feed plaintext. Returns whatever complete chunks that made, which may be nothing. */
|
|
41
|
+
push(data: Uint8Array): Uint8Array;
|
|
42
|
+
/** The buffered remainder plus the end mark. Refuses if fewer bytes were pushed than declared. */
|
|
43
|
+
finish(): Uint8Array;
|
|
44
|
+
/** Release the engine-side memory, and the key with it. */
|
|
45
|
+
free(): void;
|
|
46
|
+
}
|
|
47
|
+
/** The slice of the engine this tool uses. Every name here is checked at load time. */
|
|
48
|
+
export interface CryptoGlue {
|
|
49
|
+
/** A brand-new NMTS key, display form. ⛔ The only copy there will be — `registration.ts`. */
|
|
50
|
+
account_code_generate(): string;
|
|
51
|
+
/** Text form to the 20 raw bytes. Throws when the check symbol does not match. */
|
|
52
|
+
account_code_parse(input: string): Uint8Array;
|
|
53
|
+
/** The grouped, human-readable spelling of a code. */
|
|
54
|
+
account_code_display(codeBytes: Uint8Array): string;
|
|
55
|
+
/** The full derivation output. Offsets are named in DERIVED below. */
|
|
56
|
+
kdf_derive(codeBytes: Uint8Array): Uint8Array;
|
|
57
|
+
/** Display form — the grouped PUBLIC CODE a person reads, copies and types. */
|
|
58
|
+
share_address_display(address: Uint8Array): string;
|
|
59
|
+
/**
|
|
60
|
+
* The typed form back to the 16 raw bytes. Throws when the check symbol does not match.
|
|
61
|
+
*
|
|
62
|
+
* ⛔ A TYPO FAILS HERE, NOT AS A SERVER LOOKUP. Sending a mistyped address to the recipient
|
|
63
|
+
* lookup would ask the server a question about somebody who may exist, and the answer — or
|
|
64
|
+
* the refusal — is not ours to collect.
|
|
65
|
+
*/
|
|
66
|
+
share_address_parse(input: string): Uint8Array;
|
|
67
|
+
/**
|
|
68
|
+
* This account's published sharing identity: the 4,989-byte bundle other people encrypt to.
|
|
69
|
+
*
|
|
70
|
+
* ⛔ DETERMINISTIC, AND THAT IS LOAD-BEARING (NCF-3 §5.1). The self-signature uses the
|
|
71
|
+
* deterministic signing variant, so the same NMTS key produces the same bytes on every
|
|
72
|
+
* machine. A hedged signature would make each device publish a different identity, and the
|
|
73
|
+
* server takes the first one forever.
|
|
74
|
+
*/
|
|
75
|
+
share_public_key(shareKemSeed: Uint8Array, shareAuthSecret: Uint8Array, shareSigSeed: Uint8Array): Uint8Array;
|
|
76
|
+
/** The address an identity bundle fingerprints to. Used to check what a server handed back. */
|
|
77
|
+
share_address_of(recipientPublic: Uint8Array): Uint8Array;
|
|
78
|
+
/** Which account an envelope CLAIMS to be from. A claim until the unwrap succeeds. */
|
|
79
|
+
share_claimed_sender(envelope: Uint8Array): Uint8Array;
|
|
80
|
+
/**
|
|
81
|
+
* Wrap a file's key to one recipient.
|
|
82
|
+
*
|
|
83
|
+
* ⛔ THE ADDRESS IS AN ARGUMENT BECAUSE THE IDENTITY IS NOT TRUSTED. The server hands back a
|
|
84
|
+
* bundle; this checks it fingerprints to the address that was asked for, that it
|
|
85
|
+
* self-signs, and that its halves decode, BEFORE encrypting anything to it (NCF-3 §5.2a).
|
|
86
|
+
* There is deliberately no form of this that takes the bundle alone.
|
|
87
|
+
*
|
|
88
|
+
* ⛔ THE LAST THREE ARGUMENTS MUST BE THE BYTES ACTUALLY SENT. They are hashed, with length
|
|
89
|
+
* prefixes, into the wrapping key — so a name sealed after this call, or an id spelled
|
|
90
|
+
* differently, produces an envelope the recipient cannot open.
|
|
91
|
+
*/
|
|
92
|
+
share_wrap_dek(senderAuthSecret: Uint8Array, senderSigSeed: Uint8Array, recipientPublic: Uint8Array, recipientAddress: Uint8Array, dek: Uint8Array, itemId: string, nameShareCt: Uint8Array, contentHashShareCt: Uint8Array): Uint8Array;
|
|
93
|
+
/**
|
|
94
|
+
* Open an envelope somebody sent to this account.
|
|
95
|
+
*
|
|
96
|
+
* ⛔ OPENING IT IS THE AUTHENTICATION. There is no separate verify step: the sender's secret is
|
|
97
|
+
* inside the key agreement, so a bundle that is not the real sender's cannot produce a key
|
|
98
|
+
* that opens this. That is why the claimed sender may only be printed AFTER this succeeds.
|
|
99
|
+
*/
|
|
100
|
+
share_unwrap_dek(shareKemSeed: Uint8Array, shareAuthSecret: Uint8Array, shareSigSeed: Uint8Array, senderPublic: Uint8Array, envelope: Uint8Array, itemId: string, nameShareCt: Uint8Array, contentHashShareCt: Uint8Array): Uint8Array;
|
|
101
|
+
/**
|
|
102
|
+
* Open one NCF-3 envelope: key, the associated data it was sealed with, the envelope bytes.
|
|
103
|
+
*
|
|
104
|
+
* ⛔ The associated data is not decoration. An envelope sealed for one purpose cannot be opened
|
|
105
|
+
* as another, so passing the wrong string here does not silently produce wrong plaintext —
|
|
106
|
+
* it throws. That is why the strings live in one table (`AAD`) rather than at call sites.
|
|
107
|
+
*/
|
|
108
|
+
envelope_open(key: Uint8Array, aad: Uint8Array, envelope: Uint8Array): Uint8Array;
|
|
109
|
+
/**
|
|
110
|
+
* Decrypt one whole NCF-3 stream — header, every chunk, and the end-of-stream check.
|
|
111
|
+
*
|
|
112
|
+
* ⚠ IT HOLDS THE WHOLE PART OPEN IN MEMORY as well as sealed, which is why the download path
|
|
113
|
+
* stopped using it: a file is delivered as it is decrypted now, through `StreamDecryptor`
|
|
114
|
+
* below. This stays because the conformance tests seal a part and open it again in one line,
|
|
115
|
+
* and reading that test is how a person checks the streaming path against the simple one.
|
|
116
|
+
*/
|
|
117
|
+
stream_decrypt_all(dek: Uint8Array, stream: Uint8Array): Uint8Array;
|
|
118
|
+
/**
|
|
119
|
+
* A fresh random 32-byte file key.
|
|
120
|
+
*
|
|
121
|
+
* ⛔ THE RANDOMNESS IS THE ENGINE'S, NOT THIS FILE'S. Node has its own generator, and reaching
|
|
122
|
+
* for it here would put a second source of file keys in the world -- one this package's
|
|
123
|
+
* conformance vectors say nothing about. One generator, inside the engine, for both the
|
|
124
|
+
* browser and this tool.
|
|
125
|
+
*/
|
|
126
|
+
generate_dek(): Uint8Array;
|
|
127
|
+
/**
|
|
128
|
+
* Seal one NCF-3 envelope: key, the associated data to bind it to, the plaintext.
|
|
129
|
+
*
|
|
130
|
+
* The inverse of `envelope_open`, and the same warning applies to the associated data -- an
|
|
131
|
+
* envelope sealed under the wrong string is not openable as the thing it was meant to be.
|
|
132
|
+
*/
|
|
133
|
+
envelope_seal(key: Uint8Array, aad: Uint8Array, plaintext: Uint8Array): Uint8Array;
|
|
134
|
+
/**
|
|
135
|
+
* Encrypt one whole part into a complete NCF-3 stream -- header, chunks and the end mark.
|
|
136
|
+
*
|
|
137
|
+
* ⚠ IT HOLDS THE WHOLE PART IN MEMORY, open and sealed, exactly as `stream_decrypt_all` does.
|
|
138
|
+
* That is what bounds the size this tool will upload in one piece; `put` refuses a larger file
|
|
139
|
+
* rather than discovering the limit as an out-of-memory crash halfway through.
|
|
140
|
+
*
|
|
141
|
+
* ⛔ A FRESH CALL PER PART. The nonce prefix is allocated inside, and the format requires it to
|
|
142
|
+
* differ per stream. Re-sealing different bytes under a header that was already used is the
|
|
143
|
+
* one catastrophic mistake available here, so there is no entry point that accepts a header.
|
|
144
|
+
*/
|
|
145
|
+
stream_encrypt_all(dek: Uint8Array, plaintext: Uint8Array): Uint8Array;
|
|
146
|
+
/**
|
|
147
|
+
* Seal ONE part of a file, feeding the plaintext in as it is read.
|
|
148
|
+
*
|
|
149
|
+
* ⛔ THIS IS WHAT LETS A FILE BE LARGER THAN MEMORY. `stream_encrypt_all` above needs the whole
|
|
150
|
+
* part open and sealed at once; this holds one chunk at a time, so the caller can read a
|
|
151
|
+
* file in slices and never have more than a chunk of it live.
|
|
152
|
+
*
|
|
153
|
+
* ⛔ A FRESH SESSION PER PART, ALWAYS. The nonce prefix is allocated inside the engine and the
|
|
154
|
+
* format requires it to differ per stream. There is deliberately no entry point here that
|
|
155
|
+
* accepts a header: re-sealing DIFFERENT bytes under a header that was already used repeats
|
|
156
|
+
* a (key, nonce) pair, which is the one mistake in this format that loses the file's secrecy
|
|
157
|
+
* outright rather than making it unreadable.
|
|
158
|
+
*
|
|
159
|
+
* `partIndex` and `partTotal` say where this stream sits in the file. They go into the header
|
|
160
|
+
* and therefore into every chunk's associated data, so a part later served in another part's
|
|
161
|
+
* position fails authentication instead of decrypting into the wrong place. A whole file in one
|
|
162
|
+
* stream is part 0 of 1.
|
|
163
|
+
*/
|
|
164
|
+
StreamEncryptor: new (dek: Uint8Array, plaintextLen: number, partIndex: number, partTotal: number) => StreamSealer;
|
|
165
|
+
/**
|
|
166
|
+
* OPEN one part of a file, handing back its plaintext as the bytes arrive.
|
|
167
|
+
*
|
|
168
|
+
* ⛔ THIS IS WHAT LETS A FILE COME BACK ON A MACHINE SMALLER THAN THE FILE. The mirror of
|
|
169
|
+
* `StreamEncryptor`, and the download path's only decryptor: `stream_decrypt_all` needs the
|
|
170
|
+
* whole part open at once, so the size a file could be uploaded at and the size it could be
|
|
171
|
+
* read back at had drifted apart.
|
|
172
|
+
*
|
|
173
|
+
* `header` is the first 72 bytes of the stored stream and is parsed and checked here — a wrong
|
|
174
|
+
* magic, an unknown version or an impossible chunk size fails at construction, before any
|
|
175
|
+
* ciphertext is fed in.
|
|
176
|
+
*/
|
|
177
|
+
StreamDecryptor: new (dek: Uint8Array, header: Uint8Array) => StreamOpener;
|
|
178
|
+
/**
|
|
179
|
+
* The Ed25519 seed of wallet `index`, from the 32-byte wallet root (NCF-3 §1).
|
|
180
|
+
*
|
|
181
|
+
* ⛔ EVERY WALLET COMES FROM HERE, INCLUDING WALLET 0. There is no separate rule for the first
|
|
182
|
+
* one and no index this refuses. A tool that derived wallet 0 some other way would produce a
|
|
183
|
+
* different address from the browser and the recovery tool for the same NMTS key — which
|
|
184
|
+
* would look like the account's money had vanished.
|
|
185
|
+
*/
|
|
186
|
+
wallet_seed_for(walletRoot: Uint8Array, index: number): Uint8Array;
|
|
187
|
+
}
|
|
188
|
+
export declare function isCryptoGlue(value: unknown): value is CryptoGlue;
|
|
189
|
+
/** Which of the required functions this object does not have. Empty means it is the engine. */
|
|
190
|
+
export declare function missingExports(value: unknown): (keyof CryptoGlue)[];
|
|
191
|
+
/**
|
|
192
|
+
* Where the engine is.
|
|
193
|
+
*
|
|
194
|
+
* Two layouts are real: inside the published package the vendored engine sits beside the code, and
|
|
195
|
+
* inside this repository it lives in web/vendor where the browser build also reads it. Looking in
|
|
196
|
+
* both is what lets the same source run from a checkout and from an install.
|
|
197
|
+
*/
|
package/dist/crypto.d.ts
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { type CryptoGlue } from "./crypto-surface.ts";
|
|
2
|
+
export type { CryptoGlue, StreamOpener, StreamSealer } from "./crypto-surface.ts";
|
|
3
|
+
/**
|
|
4
|
+
* The associated-data strings of NCF-3, for the envelopes this tool opens.
|
|
5
|
+
*
|
|
6
|
+
* ⛔ FROZEN (NCF-3 §2.2). They are the separator between one purpose and another: the file list
|
|
7
|
+
* cannot be opened as a delegation, and neither can be opened as a file. Copied here rather
|
|
8
|
+
* than imported because this package does not import the browser tree — the conformance
|
|
9
|
+
* vectors are what arbitrate, and they are in the crypto repository.
|
|
10
|
+
*/
|
|
11
|
+
export declare const AAD: {
|
|
12
|
+
readonly fileList: "nmts/v3/file-list";
|
|
13
|
+
/** Wraps a file's own key under the account's data key (NCF-3 §3). */
|
|
14
|
+
readonly dekWrap: "nmts/v3/dek-wrap";
|
|
15
|
+
/**
|
|
16
|
+
* Wraps the SHA-256 of a file's whole plaintext (NCF-3 §2.2).
|
|
17
|
+
*
|
|
18
|
+
* Sealed rather than stored bare because a plaintext content hash identifies the FILE: it is
|
|
19
|
+
* matchable against public hash sets, and it is equal across two accounts holding the same file.
|
|
20
|
+
*/
|
|
21
|
+
readonly contentHash: "nmts/v3/content-hash";
|
|
22
|
+
/**
|
|
23
|
+
* Seals a shared file's name and size FOR THE RECIPIENT, under the file's own key (NCF-3 §5.4).
|
|
24
|
+
*
|
|
25
|
+
* ⚠ A different separator from the account's own file list on purpose: the recipient holds the
|
|
26
|
+
* file key and nothing else, so the name has to travel under that key rather than under an
|
|
27
|
+
* account data key they do not have.
|
|
28
|
+
*/
|
|
29
|
+
readonly shareName: "nmts/v3/share-name";
|
|
30
|
+
/** Seals the shared file's plaintext digest for the recipient, under the same file key. */
|
|
31
|
+
readonly shareContentHash: "nmts/v3/share-content-hash";
|
|
32
|
+
/** Seals the RECOVERY LIST — where every file's bytes are (NRM §1). ⛔ The old spelling of the
|
|
33
|
+
* artefact's name is frozen INTO the separator: every list ever sealed is bound to these bytes. */
|
|
34
|
+
readonly recoveryMap: "nmts/v3/recovery-map";
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Byte ranges inside `kdf_derive`'s output.
|
|
38
|
+
*
|
|
39
|
+
* ⛔ These are a CONTRACT WITH A FROZEN FORMAT (NCF-3 §1), not a convenience. They are written
|
|
40
|
+
* here as ranges rather than magic numbers at call sites so a reader can check them against the
|
|
41
|
+
* format document in one place — and so a future version bump changes one table.
|
|
42
|
+
*/
|
|
43
|
+
export declare const DERIVED: {
|
|
44
|
+
readonly accountId: readonly [0, 16];
|
|
45
|
+
readonly authSecret: readonly [16, 48];
|
|
46
|
+
readonly dataKey: readonly [48, 80];
|
|
47
|
+
readonly fileListKey: readonly [80, 112];
|
|
48
|
+
/**
|
|
49
|
+
* The three secrets behind this account's sharing identity (NCF-3 §5.1).
|
|
50
|
+
*
|
|
51
|
+
* ⚠ `shareSigSeed` SITS AT THE TAIL, not beside the other two, and that is not tidiness lost —
|
|
52
|
+
* it was appended on 2026-08-02 because filing it in the obvious place would have shifted
|
|
53
|
+
* `walletRoot` and `shareAddress`, which are frozen. `web/src/lib/crypto/kdf-offsets.ts`
|
|
54
|
+
* carries the same table for the browser.
|
|
55
|
+
*/
|
|
56
|
+
readonly shareKemSeed: readonly [112, 144];
|
|
57
|
+
readonly shareAuthSecret: readonly [144, 176];
|
|
58
|
+
/**
|
|
59
|
+
* The root every one of this account's wallets is derived from.
|
|
60
|
+
*
|
|
61
|
+
* ⚠ IT IS AN HKDF PRK, NOT KEY MATERIAL TO EXTRACT AGAIN. `wallet_seed_for` expands from it
|
|
62
|
+
* directly; a re-implementation that ran a second Extract over these bytes would produce
|
|
63
|
+
* plausible-looking seeds for a different set of addresses.
|
|
64
|
+
*/
|
|
65
|
+
readonly walletRoot: readonly [176, 208];
|
|
66
|
+
readonly shareAddress: readonly [208, 224];
|
|
67
|
+
readonly shareSigSeed: readonly [224, 256];
|
|
68
|
+
/**
|
|
69
|
+
* The root every one of this account's AI-account NMTS KEYS is expanded from (NCF-3 §1.5).
|
|
70
|
+
*
|
|
71
|
+
* ⚠ Appended at the TAIL on 2026-09-06, for the reason `shareSigSeed` was: filing it anywhere
|
|
72
|
+
* else would have shifted frozen offsets. Like `walletRoot` it is an HKDF PRK, expanded from
|
|
73
|
+
* directly — and what it expands to is 20 bytes that ARE an NMTS key, not a seed.
|
|
74
|
+
*/
|
|
75
|
+
readonly aiAccountRoot: readonly [256, 288];
|
|
76
|
+
};
|
|
77
|
+
export declare function engineDir(): string;
|
|
78
|
+
/** Load the engine once per process. */
|
|
79
|
+
export declare function loadCrypto(): Promise<CryptoGlue>;
|
|
80
|
+
/** For tests that need a fresh load. */
|
|
81
|
+
export declare function forgetCrypto(): void;
|
package/dist/crypto.js
CHANGED
|
@@ -35,6 +35,11 @@ import { isCryptoGlue, missingExports } from "./crypto-surface.js";
|
|
|
35
35
|
*/
|
|
36
36
|
export const AAD = {
|
|
37
37
|
fileList: "nmts/v3/file-list",
|
|
38
|
+
// ⚠ THE CHUNK LABEL IS NOT SPELLED HERE. One CHUNK of the chunked file list is sealed under
|
|
39
|
+
// `nmts/v3/file-list-chunk` (NCF-3 §6.3.2), and that string is declared in the shared copy of
|
|
40
|
+
// the chunk codec as `AAD_FILE_LIST_CHUNK` and used from there. A second spelling in this
|
|
41
|
+
// table is exactly how the two halves of the product would come to disagree about a separator
|
|
42
|
+
// whose whole job is that a chunk can never be presented as an index.
|
|
38
43
|
/** Wraps a file's own key under the account's data key (NCF-3 §3). */
|
|
39
44
|
dekWrap: "nmts/v3/dek-wrap",
|
|
40
45
|
/**
|
|
@@ -90,6 +95,14 @@ export const DERIVED = {
|
|
|
90
95
|
walletRoot: [176, 208],
|
|
91
96
|
shareAddress: [208, 224],
|
|
92
97
|
shareSigSeed: [224, 256],
|
|
98
|
+
/**
|
|
99
|
+
* The root every one of this account's AI-account NMTS KEYS is expanded from (NCF-3 §1.5).
|
|
100
|
+
*
|
|
101
|
+
* ⚠ Appended at the TAIL on 2026-09-06, for the reason `shareSigSeed` was: filing it anywhere
|
|
102
|
+
* else would have shifted frozen offsets. Like `walletRoot` it is an HKDF PRK, expanded from
|
|
103
|
+
* directly — and what it expands to is 20 bytes that ARE an NMTS key, not a seed.
|
|
104
|
+
*/
|
|
105
|
+
aiAccountRoot: [256, 288],
|
|
93
106
|
};
|
|
94
107
|
export function engineDir() {
|
|
95
108
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { AccountSettings } from "./shared/lib/drive/manifest-settings.ts";
|
|
2
|
+
export declare const DEPOSIT_CREDITS_MAX = 64;
|
|
3
|
+
export declare const DEPOSIT_CREDITS_DEFAULT = 64;
|
|
4
|
+
/**
|
|
5
|
+
* What `--deposit <n>` asked for, or undefined when it was not given.
|
|
6
|
+
*
|
|
7
|
+
* ⛔ WHOLE CREDITS ONLY. A deposit is a number of credits the ledger moves, and there is no such
|
|
8
|
+
* thing as half of one; `--deposit 1.5` is refused rather than rounded, because rounding picks
|
|
9
|
+
* an amount for somebody who was already being specific.
|
|
10
|
+
*/
|
|
11
|
+
export declare function parseDeposit(asked: string | number | undefined): number | undefined;
|
|
12
|
+
/** The account's own default: what the sealed list holds, or `DEPOSIT_CREDITS_DEFAULT`. */
|
|
13
|
+
export declare function depositDefaultOf(settings: AccountSettings | undefined): number;
|
|
14
|
+
/** ⛔ The wallet buys its own storage, so there is no treasury deposit to set aside against it. */
|
|
15
|
+
export declare function refuseDepositWithWallet(asked: string | number | undefined): void;
|
|
16
|
+
/**
|
|
17
|
+
* What the price says about the deposit, under the line that names the credits.
|
|
18
|
+
*
|
|
19
|
+
* ⚠ ONE SENTENCE FOR 0 AND NONE OF THE USUAL HEDGING. It is the one answer whose cost lands later
|
|
20
|
+
* and somewhere else — on a release, out of the balance — so it is said where the choice shows.
|
|
21
|
+
*
|
|
22
|
+
* `files` is how many files this run puts the deposit on: absent for one file, a count for a
|
|
23
|
+
* directory, where the number set aside is per file and the total is what the balance must cover.
|
|
24
|
+
*/
|
|
25
|
+
export declare function depositLines(credits: number, files?: number): string[];
|
package/dist/deposit.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// How many credits an upload sets aside on the file it uploads — the rule, in one place.
|
|
2
|
+
//
|
|
3
|
+
// ⛔ IT IS A CHOICE, AND THE TOOL ALWAYS SENDS IT. A credit-paid upload holds a deposit against
|
|
4
|
+
// the chain fee of any later operation on that file (releasing its storage early, or reshaping
|
|
5
|
+
// it): the fee is converted to credits and taken from that deposit, at least one credit, and
|
|
6
|
+
// whatever is left comes back when the storage period ends. The number is chosen per upload,
|
|
7
|
+
// from 0 to `DEPOSIT_CREDITS_MAX`, and an account's own default lives in the sealed file list.
|
|
8
|
+
//
|
|
9
|
+
// ⚠ ZERO IS A REAL ANSWER, NOT A MISTAKE. A file with no deposit still releases — it just pays
|
|
10
|
+
// twice the same fee out of the balance at that moment, and is refused when the balance cannot
|
|
11
|
+
// cover that. Withholding the option would be safety that shackles; the cost is said instead,
|
|
12
|
+
// at the moment it is chosen and again in the price the upload prints.
|
|
13
|
+
//
|
|
14
|
+
// ⛔ THE REFUSAL HAPPENS BEFORE ANYTHING IS SENT. A number outside the range is a command line to
|
|
15
|
+
// fix, not a request to make: rounding it into range would spend money on an amount nobody
|
|
16
|
+
// typed, and asking the server would cost a round trip to learn about a typo.
|
|
17
|
+
import { NmtsError } from "./errors.js";
|
|
18
|
+
import { BINARY_NAME } from "./product.js";
|
|
19
|
+
import { DEPOSIT_DEFAULT_CREDITS, DEPOSIT_MAX_CREDITS, } from "./shared/lib/drive/manifest-settings.js";
|
|
20
|
+
// The shared module (generated from the browser's source) names the two numbers the other way
|
|
21
|
+
// round; the CLI's own vocabulary is kept so the commands and their tests read as written.
|
|
22
|
+
export const DEPOSIT_CREDITS_MAX = DEPOSIT_MAX_CREDITS;
|
|
23
|
+
export const DEPOSIT_CREDITS_DEFAULT = DEPOSIT_DEFAULT_CREDITS;
|
|
24
|
+
/**
|
|
25
|
+
* What `--deposit <n>` asked for, or undefined when it was not given.
|
|
26
|
+
*
|
|
27
|
+
* ⛔ WHOLE CREDITS ONLY. A deposit is a number of credits the ledger moves, and there is no such
|
|
28
|
+
* thing as half of one; `--deposit 1.5` is refused rather than rounded, because rounding picks
|
|
29
|
+
* an amount for somebody who was already being specific.
|
|
30
|
+
*/
|
|
31
|
+
export function parseDeposit(asked) {
|
|
32
|
+
if (asked === undefined || asked === "")
|
|
33
|
+
return undefined;
|
|
34
|
+
const credits = typeof asked === "number" ? asked : Number(asked.trim());
|
|
35
|
+
if (!Number.isInteger(credits) || credits < 0 || credits > DEPOSIT_CREDITS_MAX) {
|
|
36
|
+
throw new NmtsError(`--deposit takes a whole number of credits from 0 to ${DEPOSIT_CREDITS_MAX}, not "${String(asked)}".`, {
|
|
37
|
+
exitCode: 2,
|
|
38
|
+
nextStep: `Nothing was sent and nothing was charged. Leave --deposit off to use this account's ` +
|
|
39
|
+
`default (\`${BINARY_NAME} deposit\` prints it).`,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
return credits;
|
|
43
|
+
}
|
|
44
|
+
/** The account's own default: what the sealed list holds, or `DEPOSIT_CREDITS_DEFAULT`. */
|
|
45
|
+
export function depositDefaultOf(settings) {
|
|
46
|
+
const held = settings?.depositDefault;
|
|
47
|
+
return typeof held === "number" && Number.isInteger(held) && held >= 0 && held <= DEPOSIT_CREDITS_MAX
|
|
48
|
+
? held
|
|
49
|
+
: DEPOSIT_CREDITS_DEFAULT;
|
|
50
|
+
}
|
|
51
|
+
/** ⛔ The wallet buys its own storage, so there is no treasury deposit to set aside against it. */
|
|
52
|
+
export function refuseDepositWithWallet(asked) {
|
|
53
|
+
if (asked === undefined || asked === "")
|
|
54
|
+
return;
|
|
55
|
+
throw new NmtsError("--deposit only applies when credits pay: the wallet buys its own storage.", {
|
|
56
|
+
exitCode: 2,
|
|
57
|
+
nextStep: `Nothing was sent. Leave --pay wallet off to pay with credits and set a deposit aside.`,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* What the price says about the deposit, under the line that names the credits.
|
|
62
|
+
*
|
|
63
|
+
* ⚠ ONE SENTENCE FOR 0 AND NONE OF THE USUAL HEDGING. It is the one answer whose cost lands later
|
|
64
|
+
* and somewhere else — on a release, out of the balance — so it is said where the choice shows.
|
|
65
|
+
*
|
|
66
|
+
* `files` is how many files this run puts the deposit on: absent for one file, a count for a
|
|
67
|
+
* directory, where the number set aside is per file and the total is what the balance must cover.
|
|
68
|
+
*/
|
|
69
|
+
export function depositLines(credits, files) {
|
|
70
|
+
if (credits === 0) {
|
|
71
|
+
return [` no deposit — a release costs twice the fee from your balance`];
|
|
72
|
+
}
|
|
73
|
+
const each = `${credits} credit${credits === 1 ? "" : "s"}`;
|
|
74
|
+
const set = files === undefined || files === 1
|
|
75
|
+
? ` ${each} set aside as a deposit`
|
|
76
|
+
: ` ${each} set aside as a deposit on each of them (${credits * files} in all)`;
|
|
77
|
+
return [`${set}, back when the storage period ends`];
|
|
78
|
+
}
|