@needmoretruth/nmts-cli 0.17.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 +591 -0
- package/LICENSE +202 -0
- package/LICENSING.md +49 -0
- package/README.ko.md +641 -0
- package/README.md +657 -0
- package/dist/account-proof.js +78 -0
- package/dist/account.js +75 -0
- package/dist/api-key.js +310 -0
- package/dist/api.js +271 -0
- package/dist/args.js +137 -0
- package/dist/artifact-about.js +70 -0
- package/dist/autonomy.js +98 -0
- package/dist/code-access.js +76 -0
- package/dist/code-vault.js +233 -0
- package/dist/collision.js +133 -0
- package/dist/commands/balance.js +93 -0
- package/dist/commands/consent.js +77 -0
- package/dist/commands/create.js +356 -0
- package/dist/commands/env.js +136 -0
- package/dist/commands/expiring.js +167 -0
- package/dist/commands/extend.js +236 -0
- package/dist/commands/get.js +130 -0
- package/dist/commands/kit.js +136 -0
- package/dist/commands/listfile.js +105 -0
- package/dist/commands/login.js +335 -0
- package/dist/commands/logout.js +33 -0
- package/dist/commands/ls.js +202 -0
- package/dist/commands/marks.js +176 -0
- package/dist/commands/mcp.js +139 -0
- package/dist/commands/mode.js +54 -0
- package/dist/commands/on-collision.js +45 -0
- package/dist/commands/organise.js +296 -0
- package/dist/commands/public-code.js +89 -0
- package/dist/commands/pull.js +185 -0
- package/dist/commands/push.js +278 -0
- package/dist/commands/put.js +282 -0
- package/dist/commands/rebuild.js +160 -0
- package/dist/commands/receive.js +125 -0
- package/dist/commands/recovery-list.js +104 -0
- package/dist/commands/recovery.js +291 -0
- package/dist/commands/s3.js +241 -0
- package/dist/commands/settings.js +28 -0
- package/dist/commands/share.js +276 -0
- package/dist/commands/sweep.js +240 -0
- package/dist/commands/trash.js +288 -0
- package/dist/commands/trial.js +264 -0
- package/dist/commands/update.js +169 -0
- package/dist/commands/usage.js +84 -0
- package/dist/commands/verify.js +331 -0
- package/dist/commands/wallet.js +122 -0
- package/dist/commands/whoami.js +33 -0
- package/dist/consent.js +200 -0
- package/dist/credentials.js +311 -0
- package/dist/crypto-surface.js +54 -0
- package/dist/crypto.js +137 -0
- package/dist/download-part.js +144 -0
- package/dist/download-sink.js +213 -0
- package/dist/download.js +163 -0
- package/dist/drive-paths.js +170 -0
- package/dist/environment.js +190 -0
- package/dist/errors.js +71 -0
- package/dist/exit.js +107 -0
- package/dist/expiry.js +115 -0
- package/dist/extend-chain.js +137 -0
- package/dist/extend-plan.js +149 -0
- package/dist/extend-sign.js +130 -0
- package/dist/guards.js +15 -0
- package/dist/help.js +192 -0
- package/dist/human-check.js +54 -0
- package/dist/item-trash.js +25 -0
- package/dist/kit-file.js +133 -0
- package/dist/list-file.js +71 -0
- package/dist/list-view-find.js +71 -0
- package/dist/list-view-order.js +96 -0
- package/dist/main.js +394 -0
- package/dist/manifest-create.js +81 -0
- package/dist/manifest-write.js +244 -0
- package/dist/manifest.js +213 -0
- package/dist/mark-render.js +45 -0
- package/dist/mcp-args.js +154 -0
- package/dist/mcp-tools/context.js +50 -0
- package/dist/mcp-tools/files.js +133 -0
- package/dist/mcp-tools/organise.js +128 -0
- package/dist/mcp-tools/reads.js +89 -0
- package/dist/mcp-tools/share.js +61 -0
- package/dist/mcp.js +142 -0
- package/dist/net-retry.js +84 -0
- package/dist/network.js +43 -0
- package/dist/notice.js +45 -0
- package/dist/product.js +38 -0
- package/dist/progress.js +110 -0
- package/dist/prompt.js +145 -0
- package/dist/rebuild.js +255 -0
- package/dist/recovery-assemble.js +148 -0
- package/dist/recovery-build.js +260 -0
- package/dist/recovery-map-file.js +114 -0
- package/dist/recovery-map.js +191 -0
- package/dist/recovery-release.js +229 -0
- package/dist/recovery-seq.js +63 -0
- package/dist/recovery-source.js +143 -0
- package/dist/registration.js +83 -0
- package/dist/s3/listing.js +134 -0
- package/dist/s3/multipart.js +104 -0
- package/dist/s3/response-sink.js +41 -0
- package/dist/s3/same-file.js +117 -0
- package/dist/s3/server.js +252 -0
- package/dist/s3/sigv4.js +168 -0
- package/dist/s3/staging.js +87 -0
- package/dist/s3/xml.js +80 -0
- package/dist/safe-path.js +89 -0
- package/dist/seal.js +206 -0
- package/dist/secret-reader.js +112 -0
- package/dist/server.js +33 -0
- package/dist/session.js +49 -0
- package/dist/setup-questions.js +31 -0
- package/dist/share.js +195 -0
- package/dist/shared/lib/crypto/size-padding.js +126 -0
- package/dist/shared/lib/drive/manifest-codec.js +287 -0
- package/dist/shared/lib/drive/manifest-index.js +235 -0
- package/dist/shared/lib/drive/manifest-ops.js +267 -0
- package/dist/shared/lib/drive/manifest-settings.js +65 -0
- package/dist/shared/lib/drive/name-conflict.js +121 -0
- package/dist/shared/lib/drive/unique-name.js +48 -0
- package/dist/shared/lib/extend/epochs.js +256 -0
- package/dist/shared/lib/net/retry-budget.js +95 -0
- package/dist/shared/lib/share/shared-file-info.js +65 -0
- package/dist/shared/lib/storage-network.js +64 -0
- package/dist/shared/lib/upload/part-plan.js +28 -0
- package/dist/stdout.js +109 -0
- package/dist/trash-sweep.js +123 -0
- package/dist/units.js +19 -0
- package/dist/update-check.js +164 -0
- package/dist/update-source.js +143 -0
- package/dist/upload-api.js +98 -0
- package/dist/upload-file.js +242 -0
- package/dist/upload-price.js +117 -0
- package/dist/upload-steps.js +111 -0
- package/dist/upload-store.js +252 -0
- package/dist/upload-wire.js +25 -0
- package/dist/upload.js +294 -0
- package/dist/usage-report.js +54 -0
- package/dist/wallet-chain.js +76 -0
- package/dist/wallet.js +209 -0
- package/dist/walrus-write.js +188 -0
- package/dist/walrus.js +156 -0
- package/package.json +56 -0
- package/vendor/nmts-crypto/nmts_crypto_wasm.d.ts +469 -0
- package/vendor/nmts-crypto/nmts_crypto_wasm.js +1300 -0
- package/vendor/nmts-crypto/nmts_crypto_wasm_bg.wasm +0 -0
- package/vendor/nmts-crypto/nmts_crypto_wasm_bg.wasm.d.ts +55 -0
- package/vendor/nmts-crypto/package.json +17 -0
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@needmoretruth/nmts-cli",
|
|
3
|
+
"version": "0.17.2",
|
|
4
|
+
"description": "Command-line access to NMTS end-to-end encrypted storage, for people and for their agents.",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://github.com/needmoretruth/nmts-cli",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/needmoretruth/nmts-cli.git"
|
|
13
|
+
},
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/needmoretruth/nmts-cli/issues"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"nmts",
|
|
19
|
+
"walrus",
|
|
20
|
+
"sui",
|
|
21
|
+
"encryption",
|
|
22
|
+
"e2ee",
|
|
23
|
+
"storage",
|
|
24
|
+
"cli",
|
|
25
|
+
"mcp",
|
|
26
|
+
"agent"
|
|
27
|
+
],
|
|
28
|
+
"type": "module",
|
|
29
|
+
"bin": {
|
|
30
|
+
"nmts": "./dist/main.js"
|
|
31
|
+
},
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=22"
|
|
34
|
+
},
|
|
35
|
+
"files": [
|
|
36
|
+
"dist",
|
|
37
|
+
"vendor",
|
|
38
|
+
"README.md",
|
|
39
|
+
"AGENTS.md",
|
|
40
|
+
"LICENSE",
|
|
41
|
+
"LICENSING.md"
|
|
42
|
+
],
|
|
43
|
+
"scripts": {
|
|
44
|
+
"compile": "tsc -p tsconfig.json",
|
|
45
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
46
|
+
"test": "node --test \"test/**/*.test.ts\""
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@types/node": "^24.3.0",
|
|
50
|
+
"typescript": "^5.9.3"
|
|
51
|
+
},
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"@mysten/sui": "^2.20.1",
|
|
54
|
+
"@mysten/walrus": "^1.2.3"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
/* tslint:disable */
|
|
2
|
+
/* eslint-disable */
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Incremental SHA-256 over a file's PLAINTEXT, for the content-hash envelope
|
|
6
|
+
* (`docs/CRYPTO-FORMAT-NCF3.md` §3).
|
|
7
|
+
*
|
|
8
|
+
* WHY STREAMING: the one-shot `sha256` needs the whole file in memory at once. Uploads run
|
|
9
|
+
* to many gigabytes and parts may be encrypted concurrently, so the hash is accumulated by
|
|
10
|
+
* a separate sequential pass that never holds more than one slice. `update()` in order,
|
|
11
|
+
* then `finalize()` exactly once. Call `free()` afterwards to release the wasm-side memory.
|
|
12
|
+
*
|
|
13
|
+
* Not key material: the digest is of plaintext the caller already holds. It is the SEALED
|
|
14
|
+
* form (see `wrap::seal_content_hash`) that ever reaches the server.
|
|
15
|
+
*/
|
|
16
|
+
export class Sha256Hasher {
|
|
17
|
+
free(): void;
|
|
18
|
+
[Symbol.dispose](): void;
|
|
19
|
+
/**
|
|
20
|
+
* The 32-byte digest. Consumes the state — calling twice errors rather than returning
|
|
21
|
+
* the digest of a silently restarted hasher.
|
|
22
|
+
*/
|
|
23
|
+
finalize(): Uint8Array;
|
|
24
|
+
/**
|
|
25
|
+
* A fresh hasher with empty state.
|
|
26
|
+
*/
|
|
27
|
+
constructor();
|
|
28
|
+
/**
|
|
29
|
+
* Absorb the next plaintext slice, in order. Any slice size.
|
|
30
|
+
*/
|
|
31
|
+
update(data: Uint8Array): void;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Streaming sequential NCF-3 decryptor with full anti-truncation/reorder verification
|
|
36
|
+
* (the download path). Construct with the file DEK and the 72-byte stream header,
|
|
37
|
+
* `push()` ciphertext in arbitrary slice sizes — each call returns decrypted plaintext
|
|
38
|
+
* for any chunks that completed — then call `finish()` after the last byte: it enforces
|
|
39
|
+
* every end-of-stream invariant (all chunks consumed, final chunk flagged, decoded total
|
|
40
|
+
* equals `plaintext_len`, no trailing bytes). Call `free()` afterwards.
|
|
41
|
+
*/
|
|
42
|
+
export class StreamDecryptor {
|
|
43
|
+
free(): void;
|
|
44
|
+
[Symbol.dispose](): void;
|
|
45
|
+
/**
|
|
46
|
+
* Verifies the stream ended cleanly (end-of-stream invariants above). Errors on an
|
|
47
|
+
* incomplete or oversized stream; idempotent after a successful call (mirrors Rust).
|
|
48
|
+
*/
|
|
49
|
+
finish(): void;
|
|
50
|
+
/**
|
|
51
|
+
* `dek` must be 32 bytes; `header` the 72-byte NCF-3 stream header (parsed and
|
|
52
|
+
* validated here — bad magic/version/chunk size error immediately).
|
|
53
|
+
*/
|
|
54
|
+
constructor(dek: Uint8Array, header: Uint8Array);
|
|
55
|
+
/**
|
|
56
|
+
* Feeds ciphertext in. Returns decrypted plaintext for any chunks that completed
|
|
57
|
+
* (possibly empty). Tampering, reordering, and truncation surface as errors here.
|
|
58
|
+
*/
|
|
59
|
+
push(data: Uint8Array): Uint8Array;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Streaming NCF-3 encryptor (misuse-resistant by construction): the random
|
|
64
|
+
* `nonce_prefix` is generated INSIDE Rust and chunk indexes are managed internally, so a
|
|
65
|
+
* JS-side bug can never cause nonce reuse or out-of-order sealing.
|
|
66
|
+
*
|
|
67
|
+
* Protocol (CRYPTO-FORMAT §3): construct with the file DEK and the total plaintext
|
|
68
|
+
* length, emit `header()` (32 bytes) first, `push()` plaintext in arbitrary slice sizes —
|
|
69
|
+
* each call returns any now-complete sealed NON-final chunks (possibly empty) — then call
|
|
70
|
+
* `finish()` exactly once for the remaining sealed bytes including the final chunk.
|
|
71
|
+
* `header || push outputs || finish output`, concatenated in order, is the complete
|
|
72
|
+
* stream. Call `free()` afterwards to release the wasm-side memory.
|
|
73
|
+
*/
|
|
74
|
+
export class StreamEncryptor {
|
|
75
|
+
free(): void;
|
|
76
|
+
[Symbol.dispose](): void;
|
|
77
|
+
/**
|
|
78
|
+
* Total chunk count for this stream: `max(1, ceil(plaintext_len / chunk_size))`.
|
|
79
|
+
*/
|
|
80
|
+
chunk_count(): number;
|
|
81
|
+
/**
|
|
82
|
+
* Flushes any buffered chunks plus the final chunk (sealed with `is_final = 0x01`).
|
|
83
|
+
* Errors if fewer than `plaintext_len` bytes were pushed. A second call returns an
|
|
84
|
+
* empty array (idempotent — mirrors the Rust engine's `finish`).
|
|
85
|
+
*/
|
|
86
|
+
finish(): Uint8Array;
|
|
87
|
+
/**
|
|
88
|
+
* The 32 plaintext header bytes (magic/version/log2/plaintext_len/nonce_prefix).
|
|
89
|
+
* Emit these first, before any chunk output. On a resume handle these are the SAME
|
|
90
|
+
* bytes passed to `resumeFromHeader`.
|
|
91
|
+
*/
|
|
92
|
+
header(): Uint8Array;
|
|
93
|
+
/**
|
|
94
|
+
* `dek` must be 32 bytes; `plaintext_len` a non-negative integer ≤ 2^53. Uses the
|
|
95
|
+
* production constructor only: fresh random nonce prefix, chunk_size_log2 = 22
|
|
96
|
+
* (4 MiB) — callers can never supply nonces or chunk sizes.
|
|
97
|
+
*
|
|
98
|
+
* `part_index` and `part_total` say WHERE this blob sits in the file (NCF-3 §4.1, defect
|
|
99
|
+
* A4). They go into the header and therefore into every chunk's AAD, so a part that is
|
|
100
|
+
* later served in another part's position fails authentication instead of decrypting into
|
|
101
|
+
* the wrong place. A whole file in one blob is part 0 of 1.
|
|
102
|
+
*
|
|
103
|
+
* The pair is validated HERE rather than left to Rust's debug assertion: an impossible
|
|
104
|
+
* placement panics inside the engine, and a panic across the wasm boundary aborts the
|
|
105
|
+
* worker rather than raising something the caller can handle.
|
|
106
|
+
*/
|
|
107
|
+
constructor(dek: Uint8Array, plaintext_len: number, part_index: number, part_total: number);
|
|
108
|
+
/**
|
|
109
|
+
* Feeds plaintext in. Returns the sealed bytes (`ciphertext||tag` each) of any chunks
|
|
110
|
+
* that became complete — possibly empty. Errors if more than `plaintext_len` bytes
|
|
111
|
+
* are pushed in total.
|
|
112
|
+
*/
|
|
113
|
+
push(data: Uint8Array): Uint8Array;
|
|
114
|
+
/**
|
|
115
|
+
* ☠️ RESUME RE-DERIVATION ONLY. Reconstructs an encryptor from a PERSISTED 72-byte NCF-3
|
|
116
|
+
* header so a cross-reload resume can re-derive a registered part's EXACT ciphertext.
|
|
117
|
+
* Validates the header like the parser (magic/version/chunk_size_log2/plaintext_len) and
|
|
118
|
+
* re-emits those same 32 header bytes; seeds the nonce prefix + chunk sizing from it and
|
|
119
|
+
* starts at chunk index 0.
|
|
120
|
+
*
|
|
121
|
+
* The caller MUST NOT transmit any output unless its Walrus blobId bit-matches the
|
|
122
|
+
* originally registered blobId — reusing `(DEK, nonce_prefix)` for DIFFERENT plaintext is
|
|
123
|
+
* catastrophic. This is NEVER a fresh-upload path (use the constructor, random nonce).
|
|
124
|
+
* See `ResumeEncryptor`'s safety note for the full rationale.
|
|
125
|
+
*/
|
|
126
|
+
static resumeFromHeader(dek: Uint8Array, header: Uint8Array): StreamEncryptor;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* The display string for a set of 20 raw account-code bytes.
|
|
131
|
+
*/
|
|
132
|
+
export function account_code_display(code_bytes: Uint8Array): string;
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Generates a fresh 160-bit account code and returns its display string
|
|
136
|
+
* (`XXXX-XXXX-…-XXXXC`). The bytes never leave the worker except as this one-time string.
|
|
137
|
+
*/
|
|
138
|
+
export function account_code_generate(): string;
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Parses+validates a user-entered account code (any spacing/case), returning the 20 raw
|
|
142
|
+
* bytes. Errors if the check symbol fails.
|
|
143
|
+
*/
|
|
144
|
+
export function account_code_parse(input: string): Uint8Array;
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Unpadded base64url of arbitrary bytes. The textual `accountId` is
|
|
148
|
+
* `b64_encode(account_id_16_bytes)`.
|
|
149
|
+
*/
|
|
150
|
+
export function b64_encode(data: Uint8Array): string;
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Derives the 32-byte wrapping key for a passphrase-protected "remember this device" record.
|
|
154
|
+
*
|
|
155
|
+
* `salt` is 16 bytes the CALLER generated fresh for that record and stores beside the
|
|
156
|
+
* ciphertext — a human-chosen passphrase must never share a salt with another user's (see
|
|
157
|
+
* `kdf::derive_device_wrap_key`). Errors on a short passphrase or a wrong-length salt rather
|
|
158
|
+
* than deriving something weak.
|
|
159
|
+
*
|
|
160
|
+
* The returned key is ordinary bytes: the browser imports it into WebCrypto to seal the record
|
|
161
|
+
* and drops it. It is NOT an account key and reaches nothing an account key reaches.
|
|
162
|
+
*/
|
|
163
|
+
export function device_wrap_key(passphrase: Uint8Array, salt: Uint8Array): Uint8Array;
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Decrypts and authenticates an envelope (`nonce||ct||tag`) under `key`, checking `aad`.
|
|
167
|
+
*/
|
|
168
|
+
export function envelope_open(key: Uint8Array, aad: Uint8Array, envelope: Uint8Array): Uint8Array;
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Encrypts `plaintext` under `key` with a FRESH random 24-byte nonce (production path).
|
|
172
|
+
*/
|
|
173
|
+
export function envelope_seal(key: Uint8Array, aad: Uint8Array, plaintext: Uint8Array): Uint8Array;
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* A fresh random 32-byte file DEK (WebCrypto-backed).
|
|
177
|
+
*/
|
|
178
|
+
export function generate_dek(): Uint8Array;
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Chunk count derived from a validated header: `max(1, ceil(plaintext_len/chunk_size))`.
|
|
182
|
+
* Errors on a malformed header or a count beyond 2^53.
|
|
183
|
+
*/
|
|
184
|
+
export function header_chunk_count(header: Uint8Array): number;
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Chunk size in bytes from a validated header (`1 << chunk_size_log2`; 4 MiB in v1).
|
|
188
|
+
* Errors on a malformed header. Always a power of two, hence exact as a JS number.
|
|
189
|
+
*/
|
|
190
|
+
export function header_chunk_size(header: Uint8Array): number;
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Where this part sits in its file, and how many parts the file has (NCF-3 §4.1).
|
|
194
|
+
* Exposed so the download path can show and check placement without decrypting anything.
|
|
195
|
+
*/
|
|
196
|
+
export function header_part_index(header: Uint8Array): number;
|
|
197
|
+
|
|
198
|
+
export function header_part_total(header: Uint8Array): number;
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* `plaintext_len` from a validated 32-byte header (u64 LE at offset 8). Errors on a
|
|
202
|
+
* malformed header, or on a declared length beyond 2^53 (not a JS-safe integer).
|
|
203
|
+
*/
|
|
204
|
+
export function header_plaintext_len(header: Uint8Array): number;
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Derives the account keys from the 20 raw account-code bytes (NCF-3 §1).
|
|
208
|
+
*
|
|
209
|
+
* Returns one concatenated buffer (`KDF_DERIVE_LEN` = 256 bytes) the caller slices:
|
|
210
|
+
* ```text
|
|
211
|
+
* 0.. 16 account_id public — the server's lookup key
|
|
212
|
+
* 16.. 48 auth_secret secret — sent to the server over TLS at login
|
|
213
|
+
* 48.. 80 data_key secret — wraps every file DEK; NEVER leaves the worker
|
|
214
|
+
* 80..112 file_list_key secret — opens the sealed drive index, and nothing else
|
|
215
|
+
* 112..144 share_kem_seed secret — X-Wing seed for private sharing (NCF-3 §5.1)
|
|
216
|
+
* 144..176 share_auth_secret secret — proves this account SENT a share (NCF-3 §5.5)
|
|
217
|
+
* 176..208 wallet_root secret — parent of EVERY wallet, including the first
|
|
218
|
+
* 208..224 share_address public — the address a user hands out to be shared with
|
|
219
|
+
* 224..256 share_sig_seed secret — ML-DSA-44 seed; its key IS the identity root (§5.2a)
|
|
220
|
+
* ```
|
|
221
|
+
* Every secret region above must be retained inside the crypto worker and never cross the
|
|
222
|
+
* postMessage boundary.
|
|
223
|
+
*
|
|
224
|
+
* ⚠ **This layout only ever grows at the TAIL.** `share_sig_seed` was appended in 2026-08-02
|
|
225
|
+
* rather than filed beside the other two share secrets, where it would read better, because
|
|
226
|
+
* inserting it there would shift `wallet_root` and `share_address` and every constant on the
|
|
227
|
+
* JS side would be silently wrong about which 32 bytes it was holding. Readability loses to
|
|
228
|
+
* that, every time.
|
|
229
|
+
*
|
|
230
|
+
* `share_address` is included even though it is not an HKDF output: since NCF-3 it is the
|
|
231
|
+
* FINGERPRINT of the share identity's root (§5.2), and computing it here means the browser never
|
|
232
|
+
* has to decide for itself which key an address belongs to.
|
|
233
|
+
*/
|
|
234
|
+
export function kdf_derive(code_bytes: Uint8Array): Uint8Array;
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* The name this account's recovery manifest is stored under inside a quilt (NCF-3 §2.5).
|
|
238
|
+
*
|
|
239
|
+
* Public, and deliberately not secret-looking: it is a v4-shaped UUID exactly like the random
|
|
240
|
+
* per-item identifiers beside it in the same quilt. What it buys is that a recovery holding only
|
|
241
|
+
* an account code can compute the one name to ask a public aggregator for — no NMTS server, no
|
|
242
|
+
* saved file, no prior knowledge of the account's data.
|
|
243
|
+
*
|
|
244
|
+
* Takes `dataKey` rather than the account code because that key already lives in the worker; the
|
|
245
|
+
* code does not, and moving it here to hash it would put it somewhere it has no reason to be.
|
|
246
|
+
*/
|
|
247
|
+
export function recovery_patch_name(data_key: Uint8Array): string;
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* SHA-256 using the same `sha2` implementation as the Rust engine.
|
|
251
|
+
*/
|
|
252
|
+
export function sha256(data: Uint8Array): Uint8Array;
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* The display form of a share address (`kdf_derive` bytes 208..224):
|
|
256
|
+
* `XXXXXXXXX-XXXXXXXXX-XXXXXXXXC` — Crockford Base32 with a trailing check symbol.
|
|
257
|
+
*/
|
|
258
|
+
export function share_address_display(share_address: Uint8Array): string;
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* The 16-byte share ADDRESS a published identity fingerprints to (NCF-3 §5.2).
|
|
262
|
+
*
|
|
263
|
+
* ⚠ **It parses the whole bundle first**, which means an identity whose self-signature does not
|
|
264
|
+
* verify, whose version is unknown, or whose X25519 halves are degenerate has no address at all
|
|
265
|
+
* as far as this function is concerned — it throws instead of returning one. Returning an address
|
|
266
|
+
* for a bundle nothing vouches for would be handing the caller a value that looks checkable and
|
|
267
|
+
* is not.
|
|
268
|
+
*
|
|
269
|
+
* Exposed so the browser can show a sender WHY a share was refused: the address it looked up and
|
|
270
|
+
* the address the returned identity actually belongs to are different values, and saying so is
|
|
271
|
+
* more useful than "failed".
|
|
272
|
+
*/
|
|
273
|
+
export function share_address_of(recipient_public: Uint8Array): Uint8Array;
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Parses a user-entered share address (any spacing/case) back to its 16 bytes, verifying the
|
|
277
|
+
* check symbol. A typo fails HERE, in the browser, before any lookup reaches the server.
|
|
278
|
+
*/
|
|
279
|
+
export function share_address_parse(input: string): Uint8Array;
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* The sender address an envelope CLAIMS, so the caller knows whose identity to fetch.
|
|
283
|
+
*
|
|
284
|
+
* ⚠ A claim, not a fact, until `share_unwrap_dek` succeeds — the address is bound into the
|
|
285
|
+
* wrapping key, so an envelope that opens is one whose claim was true.
|
|
286
|
+
*/
|
|
287
|
+
export function share_claimed_sender(envelope: Uint8Array): Uint8Array;
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* The 4989-byte PUBLIC share identity, built from the three secrets at `kdf_derive` bytes
|
|
291
|
+
* 112..176 and 224..256 (KEM seed, auth secret, signing seed).
|
|
292
|
+
*
|
|
293
|
+
* Layout: `version(1) || derivation_index(4) || pk_sig(1312) || key_epoch(4) || pk_kem(1216) ||
|
|
294
|
+
* pk_auth(32) || self_sig(2420)`. This is the only part of the identity the server holds, the
|
|
295
|
+
* address is the fingerprint of its ROOT, and the self-signature is what makes every key after
|
|
296
|
+
* the root attributable to that address (NCF-3 §5.2a).
|
|
297
|
+
*
|
|
298
|
+
* ⚠ The bytes are the same on every device the account code is entered on — deterministic
|
|
299
|
+
* signing, never hedged — which is what lets the server hold one bundle per account
|
|
300
|
+
* first-writer-wins without rejecting the account's own second device.
|
|
301
|
+
*/
|
|
302
|
+
export function share_public_key(share_kem_seed: Uint8Array, share_auth_secret: Uint8Array, share_sig_seed: Uint8Array): Uint8Array;
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Unwraps a share envelope addressed to us, returning the 32-byte file DEK.
|
|
306
|
+
*
|
|
307
|
+
* An envelope meant for somebody else fails exactly like a tampered one — the recipient's key is
|
|
308
|
+
* bound into the wrapping key, so there is nothing to tell the two cases apart. Since NCF-3 §5.3
|
|
309
|
+
* the same is true of an envelope stored beside a substituted name, digest or item id: the row is
|
|
310
|
+
* bound in too, and a rewritten row is indistinguishable from a forged envelope.
|
|
311
|
+
*/
|
|
312
|
+
export function share_unwrap_dek(share_kem_seed: Uint8Array, share_auth_secret: Uint8Array, share_sig_seed: Uint8Array, sender_public: Uint8Array, envelope: Uint8Array, item_id: string, name_share_ct: Uint8Array, content_hash_share_ct: Uint8Array): Uint8Array;
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Wraps a file DEK for ONE recipient, given the public key the server returned AND the address
|
|
316
|
+
* the sender was actually given.
|
|
317
|
+
*
|
|
318
|
+
* ⚠ **Both arguments are required on purpose.** The key is checked against the address before
|
|
319
|
+
* anything is encrypted to it, so a server that substitutes its own key is refused here rather
|
|
320
|
+
* than silently handed a readable DEK (NCF-3 §5.2, defect A1). There is deliberately no
|
|
321
|
+
* two-argument form.
|
|
322
|
+
*
|
|
323
|
+
* ⚠ **The last three arguments are the row this envelope will be stored in** — the item id and
|
|
324
|
+
* the already-sealed name and content digest — and they are required for the same kind of reason
|
|
325
|
+
* (NCF-3 §5.3, defect A6). They are hashed into the wrapping key, so an envelope kept next to
|
|
326
|
+
* different columns stops opening. **This therefore has to be called AFTER `share_seal_name` and
|
|
327
|
+
* the re-sealed digest, not before.**
|
|
328
|
+
*
|
|
329
|
+
* Returns the 1240-byte share envelope (`sender_address(16) || kem_ciphertext(1120) || sealed
|
|
330
|
+
* DEK(104)`). A fresh encapsulation is drawn per call, so wrapping the same DEK for the same
|
|
331
|
+
* recipient twice produces unrelated bytes — the server cannot tell two shares went to the same
|
|
332
|
+
* person.
|
|
333
|
+
*/
|
|
334
|
+
export function share_wrap_dek(sender_auth_secret: Uint8Array, sender_sig_seed: Uint8Array, recipient_public: Uint8Array, recipient_address: Uint8Array, dek: Uint8Array, item_id: string, name_share_ct: Uint8Array, content_hash_share_ct: Uint8Array): Uint8Array;
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Decrypts a whole NCF-3 stream under the file DEK, verifying framing/anti-tamper.
|
|
338
|
+
*/
|
|
339
|
+
export function stream_decrypt_all(dek: Uint8Array, stream: Uint8Array): Uint8Array;
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Decrypts ONE chunk for random access (ranged reads), independent of any stream state.
|
|
343
|
+
*
|
|
344
|
+
* `header` is the 72-byte stream header; `chunk_index` the zero-based chunk index (non-negative
|
|
345
|
+
* integer); `ciphertext` must be EXACTLY that chunk's bytes — chunk `i` starts at stream
|
|
346
|
+
* offset `72 + i·(chunk_size+16)` and is `chunk_plaintext_len(i) + 16` bytes. `is_final`
|
|
347
|
+
* is derived from the header INSIDE Rust, so callers cannot mis-authenticate finality;
|
|
348
|
+
* a wrongly sized or out-of-range slice is rejected.
|
|
349
|
+
*
|
|
350
|
+
* `expected_part_index` / `expected_part_total` are WHERE THE CALLER BELIEVES IT IS READING
|
|
351
|
+
* (§4.1, defect A4) and are required, not optional. A ranged reader receives the header and the
|
|
352
|
+
* chunk in the SAME response, so the AAD it authenticates against is self-consistent whichever
|
|
353
|
+
* part the server actually served — and every part of a file is sealed under one DEK, so the
|
|
354
|
+
* wrong part opens cleanly with a valid tag. A whole file in one blob is `0, 1`. Take these from
|
|
355
|
+
* the position the byte range was computed FROM; reading them back out of `header` compares a
|
|
356
|
+
* value with itself and restores the hole.
|
|
357
|
+
*/
|
|
358
|
+
export function stream_decrypt_chunk(dek: Uint8Array, header: Uint8Array, expected_part_index: number, expected_part_total: number, chunk_index: number, ciphertext: Uint8Array): Uint8Array;
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Encrypts `plaintext` into a whole NCF-3 stream under `dek` (production: random nonce prefix).
|
|
362
|
+
*/
|
|
363
|
+
export function stream_encrypt_all(dek: Uint8Array, plaintext: Uint8Array): Uint8Array;
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Checks that a multi-part file's headers are the complete set, in order, of one file
|
|
367
|
+
* (NCF-3 §4.1, defect A4). `headers` is the parts' headers CONCATENATED in the order they
|
|
368
|
+
* will be decrypted — `part_total × 72` bytes.
|
|
369
|
+
*
|
|
370
|
+
* ⚠ Order is the whole point: the check is "the i-th header says part i", not "every index
|
|
371
|
+
* appears once". Sorting the parts by their own claimed index before calling this would make
|
|
372
|
+
* it pass on any permutation, which is exactly the attack it exists to catch. Pass them in
|
|
373
|
+
* the order the download will actually consume, straight from the server's list.
|
|
374
|
+
*/
|
|
375
|
+
export function verify_part_set(headers: Uint8Array): void;
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* `SHA-256(normalize(input))` — the voucher redemption hash for arbitrary user input.
|
|
379
|
+
*/
|
|
380
|
+
export function voucher_hash_from_input(input: string): Uint8Array;
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Derives the Ed25519 seed for wallet number `index` from the 32-byte `wallet_root`.
|
|
384
|
+
*
|
|
385
|
+
* EVERY wallet comes from here, including wallet 0. NCF-2 gave the first wallet its own
|
|
386
|
+
* derivation off the account PRK because it already existed on chain and could not move; NCF-3
|
|
387
|
+
* deletes that exception, so there is one rule and no index this function refuses.
|
|
388
|
+
*/
|
|
389
|
+
export function wallet_seed_for(wallet_root: Uint8Array, index: number): Uint8Array;
|
|
390
|
+
|
|
391
|
+
export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
|
|
392
|
+
|
|
393
|
+
export interface InitOutput {
|
|
394
|
+
readonly memory: WebAssembly.Memory;
|
|
395
|
+
readonly __wbg_sha256hasher_free: (a: number, b: number) => void;
|
|
396
|
+
readonly __wbg_streamdecryptor_free: (a: number, b: number) => void;
|
|
397
|
+
readonly __wbg_streamencryptor_free: (a: number, b: number) => void;
|
|
398
|
+
readonly account_code_display: (a: number, b: number) => [number, number, number, number];
|
|
399
|
+
readonly account_code_generate: () => [number, number];
|
|
400
|
+
readonly account_code_parse: (a: number, b: number) => [number, number, number, number];
|
|
401
|
+
readonly b64_encode: (a: number, b: number) => [number, number];
|
|
402
|
+
readonly device_wrap_key: (a: number, b: number, c: number, d: number) => [number, number, number, number];
|
|
403
|
+
readonly envelope_open: (a: number, b: number, c: number, d: number, e: number, f: number) => [number, number, number, number];
|
|
404
|
+
readonly envelope_seal: (a: number, b: number, c: number, d: number, e: number, f: number) => [number, number, number, number];
|
|
405
|
+
readonly generate_dek: () => [number, number];
|
|
406
|
+
readonly header_chunk_count: (a: number, b: number) => [number, number, number];
|
|
407
|
+
readonly header_chunk_size: (a: number, b: number) => [number, number, number];
|
|
408
|
+
readonly header_part_index: (a: number, b: number) => [number, number, number];
|
|
409
|
+
readonly header_part_total: (a: number, b: number) => [number, number, number];
|
|
410
|
+
readonly header_plaintext_len: (a: number, b: number) => [number, number, number];
|
|
411
|
+
readonly kdf_derive: (a: number, b: number) => [number, number, number, number];
|
|
412
|
+
readonly recovery_patch_name: (a: number, b: number) => [number, number, number, number];
|
|
413
|
+
readonly sha256: (a: number, b: number) => [number, number];
|
|
414
|
+
readonly sha256hasher_finalize: (a: number) => [number, number, number, number];
|
|
415
|
+
readonly sha256hasher_new: () => number;
|
|
416
|
+
readonly sha256hasher_update: (a: number, b: number, c: number) => [number, number];
|
|
417
|
+
readonly share_address_display: (a: number, b: number) => [number, number, number, number];
|
|
418
|
+
readonly share_address_of: (a: number, b: number) => [number, number, number, number];
|
|
419
|
+
readonly share_address_parse: (a: number, b: number) => [number, number, number, number];
|
|
420
|
+
readonly share_claimed_sender: (a: number, b: number) => [number, number, number, number];
|
|
421
|
+
readonly share_public_key: (a: number, b: number, c: number, d: number, e: number, f: number) => [number, number, number, number];
|
|
422
|
+
readonly share_unwrap_dek: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number, k: number, l: number, m: number, n: number, o: number, p: number) => [number, number, number, number];
|
|
423
|
+
readonly share_wrap_dek: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number, k: number, l: number, m: number, n: number, o: number, p: number) => [number, number, number, number];
|
|
424
|
+
readonly stream_decrypt_all: (a: number, b: number, c: number, d: number) => [number, number, number, number];
|
|
425
|
+
readonly stream_decrypt_chunk: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number) => [number, number, number, number];
|
|
426
|
+
readonly stream_encrypt_all: (a: number, b: number, c: number, d: number) => [number, number, number, number];
|
|
427
|
+
readonly streamdecryptor_finish: (a: number) => [number, number];
|
|
428
|
+
readonly streamdecryptor_new: (a: number, b: number, c: number, d: number) => [number, number, number];
|
|
429
|
+
readonly streamdecryptor_push: (a: number, b: number, c: number) => [number, number, number, number];
|
|
430
|
+
readonly streamencryptor_chunk_count: (a: number) => number;
|
|
431
|
+
readonly streamencryptor_finish: (a: number) => [number, number, number, number];
|
|
432
|
+
readonly streamencryptor_header: (a: number) => [number, number];
|
|
433
|
+
readonly streamencryptor_new: (a: number, b: number, c: number, d: number, e: number) => [number, number, number];
|
|
434
|
+
readonly streamencryptor_push: (a: number, b: number, c: number) => [number, number, number, number];
|
|
435
|
+
readonly streamencryptor_resumeFromHeader: (a: number, b: number, c: number, d: number) => [number, number, number];
|
|
436
|
+
readonly verify_part_set: (a: number, b: number) => [number, number];
|
|
437
|
+
readonly voucher_hash_from_input: (a: number, b: number) => [number, number];
|
|
438
|
+
readonly wallet_seed_for: (a: number, b: number, c: number) => [number, number, number, number];
|
|
439
|
+
readonly __wbindgen_exn_store: (a: number) => void;
|
|
440
|
+
readonly __externref_table_alloc: () => number;
|
|
441
|
+
readonly __wbindgen_externrefs: WebAssembly.Table;
|
|
442
|
+
readonly __wbindgen_malloc: (a: number, b: number) => number;
|
|
443
|
+
readonly __externref_table_dealloc: (a: number) => void;
|
|
444
|
+
readonly __wbindgen_free: (a: number, b: number, c: number) => void;
|
|
445
|
+
readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
|
|
446
|
+
readonly __wbindgen_start: () => void;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
export type SyncInitInput = BufferSource | WebAssembly.Module;
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Instantiates the given `module`, which can either be bytes or
|
|
453
|
+
* a precompiled `WebAssembly.Module`.
|
|
454
|
+
*
|
|
455
|
+
* @param {{ module: SyncInitInput }} module - Passing `SyncInitInput` directly is deprecated.
|
|
456
|
+
*
|
|
457
|
+
* @returns {InitOutput}
|
|
458
|
+
*/
|
|
459
|
+
export function initSync(module: { module: SyncInitInput } | SyncInitInput): InitOutput;
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* If `module_or_path` is {RequestInfo} or {URL}, makes a request and
|
|
463
|
+
* for everything else, calls `WebAssembly.instantiate` directly.
|
|
464
|
+
*
|
|
465
|
+
* @param {{ module_or_path: InitInput | Promise<InitInput> }} module_or_path - Passing `InitInput` directly is deprecated.
|
|
466
|
+
*
|
|
467
|
+
* @returns {Promise<InitOutput>}
|
|
468
|
+
*/
|
|
469
|
+
export default function __wbg_init (module_or_path?: { module_or_path: InitInput | Promise<InitInput> } | InitInput | Promise<InitInput>): Promise<InitOutput>;
|