roxify 1.6.5 → 1.6.7

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/README.md CHANGED
@@ -1,377 +1,715 @@
1
- # RoxCompressor Transform
1
+ # Roxify
2
2
 
3
- > Encode binary data into PNG images and decode them back. Fast, efficient, with optional encryption and native Rust acceleration.
3
+ > Encode binary data into PNG images and decode them back, losslessly. Roxify combines native Rust acceleration, multi-threaded Zstd compression, and AES-256-GCM encryption into a single, portable Node.js module.
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/roxify.svg)](https://www.npmjs.com/package/roxify)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
7
7
 
8
- ## Features
9
-
10
- - ⚡ **Blazing Fast**: Native Rust acceleration via N-API — **1GB/s** throughput on modern hardware
11
- - 🚀 **Optimized Compression**: Multi-threaded Zstd compression (level 19) with parallel processing
12
- - 🔒 **Secure**: AES-256-GCM encryption support with PBKDF2 key derivation
13
- - 🎨 **Multiple modes**: Compact, chunk, pixel, and screenshot modes
14
- - 📦 **CLI & API**: Use as command-line tool or JavaScript library
15
- - 🔄 **Lossless**: Perfect roundtrip encoding/decoding
16
- - 📖 **Full TSDoc**: Complete TypeScript documentation
17
- - 🦀 **Rust Powered**: Optional native module for extreme performance (falls back to pure JS)
8
+ ---
18
9
 
19
- ## Real-world benchmarks 🔧
10
+ ## Table of Contents
20
11
 
21
- **Highlights**
12
+ - [Overview](#overview)
13
+ - [Features](#features)
14
+ - [Benchmarks](#benchmarks)
15
+ - [Installation](#installation)
16
+ - [CLI Usage](#cli-usage)
17
+ - [JavaScript API](#javascript-api)
18
+ - [Encoding Modes](#encoding-modes)
19
+ - [Encryption](#encryption)
20
+ - [Performance Tuning](#performance-tuning)
21
+ - [Cross-Platform Support](#cross-platform-support)
22
+ - [Building from Source](#building-from-source)
23
+ - [Architecture](#architecture)
24
+ - [Error Handling](#error-handling)
25
+ - [Security Considerations](#security-considerations)
26
+ - [Contributing](#contributing)
27
+ - [License](#license)
22
28
 
23
- - Practical benchmarks on large codebase datasets showing significant compression and high throughput while handling many small files efficiently.
29
+ ---
24
30
 
25
- **Results**
31
+ ## Overview
26
32
 
27
- | Dataset | Files | Original | Compressed | Ratio | Time | Throughput | Notes |
28
- | -------- | ------: | -------: | ---------: | --------: | -----: | ---------: | ------------------------------------------- |
29
- | 4,000 MB | 731,340 | 3.93 GB | 111.42 MB | **2.8%** | 26.9 s | 149.4 MB/s | gzip: 2.26 GB (57.5%); 7z: 1.87 GB (47.6%) |
30
- | 1,000 MB | 141,522 | 1.03 GB | 205 MB | **19.4%** | ~6.2 s | ≈170 MB/s | shows benefits for many-small-file datasets |
33
+ Roxify is a PNG steganography toolkit. It packs arbitrary binary data -- files, directories, or raw buffers -- into standard PNG images that can be shared, uploaded, and stored anywhere images are accepted. The data is compressed with multi-threaded Zstd, optionally encrypted with AES-256-GCM, and embedded in valid PNG structures that survive re-uploads and screenshots.
31
34
 
32
- ### Methodology
35
+ The core compression and image-processing logic is written in Rust and exposed to Node.js through N-API. When the native module is unavailable, Roxify falls back to a pure TypeScript implementation transparently.
33
36
 
34
- - Compression: multithreaded Zstd (level 19) and Brotli (configurable).
35
- - Setup: parallel I/O and multithreaded compression on modern SSD-backed systems.
36
- - Measurements: wall-clock time; throughput = original size / time; comparisons against gzip and 7z with typical defaults.
37
- - Reproducibility: full benchmark details, commands and raw data are available in `docs/BENCHMARK_FINAL_REPORT.md`.
37
+ ---
38
38
 
39
- These results demonstrate Roxify's strength for packaging large codebases and many-small-file archives where speed and a good compression/throughput trade-off matter.
39
+ ## Features
40
40
 
41
- ## Documentation
41
+ - **Native Rust acceleration** via N-API with automatic fallback to pure JavaScript
42
+ - **Multi-threaded Zstd compression** (level 19) with parallel chunk processing via Rayon
43
+ - **AES-256-GCM encryption** with PBKDF2 key derivation (100,000 iterations)
44
+ - **Lossless roundtrip** -- encoded data is recovered byte-for-byte
45
+ - **Lossy-resilient mode** -- QR-code-style Reed-Solomon error correction survives JPEG, WebP, MP3, AAC, and OGG recompression
46
+ - **Audio container** -- encode data as structured multi-frequency tones (not white noise) in WAV files
47
+ - **Directory packing** -- encode entire directory trees into a single PNG
48
+ - **Screenshot reconstitution** -- recover data from photographed or screenshotted PNGs
49
+ - **CLI and programmatic API** -- use from the terminal or import as a library
50
+ - **Cross-platform** -- prebuilt binaries for Linux x64, macOS x64/ARM64, and Windows x64
51
+ - **Full TypeScript support** with exported types and TSDoc annotations
52
+ - **mimalloc allocator** for reduced memory fragmentation under heavy workloads
53
+
54
+ ---
55
+
56
+ ## Benchmarks
57
+
58
+ All measurements were taken on Linux x64 (Intel i7-6700K @ 4.0 GHz, 32 GB RAM) with Node.js v20. Every tool uses its **maximum compression** setting: zip -9, gzip -9, 7z LZMA2 -mx=9, and Roxify Zstd level 19. Roxify produces a valid PNG or WAV file rather than a raw archive.
59
+
60
+ ### Compression Ratio (Maximum Compression for All Tools)
61
+
62
+ | Dataset | Original | zip -9 | gzip -9 | 7z LZMA2 -9 | Roxify PNG | Roxify WAV |
63
+ |---|---|---|---|---|---|---|
64
+ | Text 1 MB | 1.00 MB | 219 KB (21.4%) | 219 KB (21.4%) | 187 KB (18.3%) | **188 KB (18.3%)** | **187 KB (18.3%)** |
65
+ | JSON 1 MB | 1.00 MB | 263 KB (25.7%) | 263 KB (25.7%) | 225 KB (22.0%) | **220 KB (21.5%)** | **219 KB (21.4%)** |
66
+ | Binary 1 MB | 1.00 MB | 1.00 MB (100%) | 1.00 MB (100%) | 1.00 MB (100%) | 1.00 MB (100%) | 1.00 MB (100%) |
67
+ | Mixed 5 MB | 5.00 MB | 2.45 MB (49.0%) | 2.45 MB (49.1%) | 2.33 MB (46.6%) | 2.38 MB (47.6%) | 2.38 MB (47.6%) |
68
+ | Text 10 MB | 10.00 MB | 2.13 MB (21.3%) | 2.13 MB (21.3%) | 1.71 MB (17.1%) | **1.71 MB (17.1%)** | **1.70 MB (17.0%)** |
69
+ | Mixed 10 MB | 10.00 MB | 4.90 MB (49.0%) | 4.90 MB (49.0%) | 4.65 MB (46.5%) | 4.73 MB (47.3%) | 4.73 MB (47.3%) |
70
+
71
+ > **Roxify matches 7z LZMA2 ultra-compression on text** (18.3% for both at 1 MB) and **beats LZMA2 on JSON** (21.4% vs 22.0%). On mixed data, Roxify is within 1 percentage point of LZMA2 while producing a shareable PNG/WAV instead of an archive.
72
+
73
+ ### Encode and Decode Speed (CLI)
74
+
75
+ | Dataset | Tool | Encode | Decode | Enc Throughput | Dec Throughput |
76
+ |---|---|---|---|---|---|
77
+ | Text 1 MB | zip -9 | 112 ms | 36 ms | 8.9 MB/s | 27.6 MB/s |
78
+ | | gzip -9 | 146 ms | 38 ms | 6.9 MB/s | 26.0 MB/s |
79
+ | | 7z LZMA -9 | 303 ms | 21 ms | 3.3 MB/s | 46.6 MB/s |
80
+ | | **Roxify PNG** | **859 ms** | **577 ms** | **1.2 MB/s** | **1.7 MB/s** |
81
+ | | **Roxify WAV** | **794 ms** | **480 ms** | **1.3 MB/s** | **2.1 MB/s** |
82
+ | JSON 1 MB | zip -9 | 79 ms | 20 ms | 12.7 MB/s | 50.5 MB/s |
83
+ | | 7z LZMA -9 | 197 ms | 26 ms | 5.1 MB/s | 37.9 MB/s |
84
+ | | **Roxify PNG** | **1.14 s** | **755 ms** | **0.9 MB/s** | **1.3 MB/s** |
85
+ | | **Roxify WAV** | **1.49 s** | **518 ms** | **0.7 MB/s** | **1.9 MB/s** |
86
+ | Text 10 MB | zip -9 | 1.21 s | 70 ms | 8.2 MB/s | 143.8 MB/s |
87
+ | | 7z LZMA -9 | 5.05 s | 99 ms | 2.0 MB/s | 100.8 MB/s |
88
+ | | **Roxify PNG** | **9.05 s** | **4.53 s** | **1.1 MB/s** | **2.2 MB/s** |
89
+ | | **Roxify WAV** | **9.22 s** | **2.59 s** | **1.1 MB/s** | **3.9 MB/s** |
90
+
91
+ > Roxify CLI includes Node.js startup overhead (~400 ms). In the JS API (below), the same operations are significantly faster. WAV decode is consistently faster than PNG decode due to simpler container parsing.
92
+
93
+ ### JavaScript API Throughput
94
+
95
+ Direct API calls (no CLI startup overhead):
96
+
97
+ | Size | Container | Encode | Decode | Enc Throughput | Dec Throughput | Output | Ratio | Integrity |
98
+ |---|---|---|---|---|---|---|---|---|
99
+ | 1 KB | PNG | 9 ms | 12 ms | 0.1 MB/s | 0.1 MB/s | 1.14 KB | 114.3% | ✓ |
100
+ | 10 KB | PNG | 18 ms | 34 ms | 0.5 MB/s | 0.3 MB/s | 10.32 KB | 103.2% | ✓ |
101
+ | 100 KB | PNG | 52 ms | 109 ms | 1.9 MB/s | 0.9 MB/s | 100.52 KB | 100.5% | ✓ |
102
+ | 500 KB | PNG | 339 ms | 541 ms | 1.4 MB/s | 0.9 MB/s | 502.64 KB | 100.5% | ✓ |
103
+ | 1 MB | PNG | 875 ms | 1.24 s | 1.1 MB/s | 0.8 MB/s | 1.00 MB | 100.3% | ✓ |
104
+ | 5 MB | PNG | 3.39 s | 4.12 s | 1.5 MB/s | 1.2 MB/s | 5.01 MB | 100.2% | ✓ |
105
+ | 10 MB | PNG | 6.84 s | 12.28 s | 1.5 MB/s | 0.8 MB/s | 10.01 MB | 100.1% | ✓ |
106
+ | 1 KB | WAV | 2 ms | 2 ms | 0.6 MB/s | 0.6 MB/s | 1.08 KB | 107.5% | ✓ |
107
+ | 10 KB | WAV | 4 ms | 5 ms | 2.3 MB/s | 1.8 MB/s | 10.08 KB | 100.8% | ✓ |
108
+ | 100 KB | WAV | 39 ms | 28 ms | 2.5 MB/s | 3.5 MB/s | 100.08 KB | 100.1% | ✓ |
109
+ | 500 KB | WAV | 172 ms | 190 ms | 2.8 MB/s | 2.6 MB/s | 500.09 KB | 100.0% | ✓ |
110
+ | 1 MB | WAV | 452 ms | 276 ms | 2.2 MB/s | 3.6 MB/s | 1.00 MB | 100.0% | ✓ |
111
+ | 5 MB | WAV | 2.70 s | 1.65 s | 1.8 MB/s | 3.0 MB/s | 5.00 MB | 100.0% | ✓ |
112
+ | 10 MB | WAV | 4.81 s | 2.56 s | 2.1 MB/s | 3.9 MB/s | 10.00 MB | 100.0% | ✓ |
113
+
114
+ > WAV container is **2–4× faster** than PNG for decoding at large sizes, and produces slightly smaller output thanks to simpler framing.
115
+
116
+ ### Reed-Solomon ECC Throughput
117
+
118
+ | Size | Encode | Decode | Enc Throughput | Dec Throughput | Overhead |
119
+ |---|---|---|---|---|---|
120
+ | 1 KB | 6 ms | 4 ms | 0.2 MB/s | 0.2 MB/s | 125.7% |
121
+ | 10 KB | 7 ms | 6 ms | 1.3 MB/s | 1.5 MB/s | 119.6% |
122
+ | 100 KB | 49 ms | 45 ms | 2.0 MB/s | 2.1 MB/s | 118.8% |
123
+ | 1 MB | 483 ms | 377 ms | 2.1 MB/s | 2.7 MB/s | 118.6% |
124
+
125
+ ### Lossy-Resilient Encoding
126
+
127
+ #### Robust Image (QR-code-style, block size 4×4)
128
+
129
+ | Data Size | Encode Time | Output (PNG) |
130
+ |---|---|---|
131
+ | 32 B | 32 ms | 122 KB |
132
+ | 128 B | 39 ms | 122 KB |
133
+ | 512 B | 76 ms | 316 KB |
134
+ | 1 KB | 139 ms | 508 KB |
135
+ | 2 KB | 251 ms | 986 KB |
136
+
137
+ #### Robust Audio (MFSK 8-channel, medium ECC)
138
+
139
+ | Data Size | Encode | Decode | Output (WAV) | Integrity |
140
+ |---|---|---|---|---|
141
+ | 10 B | 33 ms | 44 ms | 1.35 MB | ✓ |
142
+ | 32 B | 19 ms | 31 ms | 1.35 MB | ✓ |
143
+ | 64 B | 22 ms | 24 ms | 1.35 MB | ✓ |
144
+ | 128 B | 21 ms | 28 ms | 1.35 MB | ✓ |
145
+ | 256 B | 40 ms | 45 ms | 2.59 MB | ✓ |
146
+
147
+ ### Data Integrity Verification
148
+
149
+ All encode/decode roundtrips produce bit-exact output, verified by SHA-256:
150
+
151
+ | Test Case | PNG | WAV |
152
+ |---|---|---|
153
+ | Empty buffer (0 B) | ✓ | ✓ |
154
+ | Single byte (1 B) | ✓ | ✓ |
155
+ | All byte values (256 B) | ✓ | ✓ |
156
+ | 1 KB text | ✓ | ✓ |
157
+ | 100 KB random | ✓ | ✓ |
158
+ | 1 MB random | ✓ | ✓ |
159
+ | 5 MB random | ✓ | ✓ |
160
+
161
+ **14 / 14 integrity tests passed** across both containers.
162
+
163
+ ### Key Observations
164
+
165
+ - **Roxify matches LZMA2 ultra-compression** on text data (18.3%) and **outperforms it on JSON** (21.4% vs 22.0%), while producing a standard PNG or WAV file instead of an archive.
166
+ - **WAV container decode is 2–4× faster** than PNG decode at large sizes (3.9 MB/s vs 0.8 MB/s for 10 MB).
167
+ - **WAV encode for 1 KB data completes in 2 ms** — well under the sub-second target.
168
+ - **Lossy-resilient audio** encode/decode completes in under 50 ms for data up to 256 bytes, with full integrity.
169
+ - **100% data integrity** across all sizes and containers — every byte is recovered exactly.
170
+ - The CLI overhead (~400 ms Node.js startup) is amortized on larger inputs. For programmatic use, the JS API eliminates this entirely.
171
+ - On incompressible (random) data, all tools converge to ~100% as expected. No compression algorithm can shrink truly random data.
42
172
 
43
- - 📘 **[CLI Documentation](./docs/CLI.md)** - Complete command-line usage guide
44
- - 📗 **[JavaScript SDK](./docs/JAVASCRIPT_SDK.md)** - Programmatic API reference with examples
45
- - 📙 **[Quick Start](#quick-start)** - Get started in 2 minutes
173
+ ### Methodology
46
174
 
47
- ## Installation
175
+ Benchmarks were generated using `test/benchmark-detailed.cjs`. Datasets consist of procedurally generated text, JSON, and random binary data. Each tool was invoked with its maximum compression setting:
48
176
 
49
- ### As CLI tool (npx)
177
+ | Tool | Command / Setting |
178
+ |---|---|
179
+ | zip | `zip -r -q -9` |
180
+ | tar/gzip | `tar -cf - \| gzip -9` |
181
+ | 7z | `7z a -mx=9` (LZMA2 ultra) |
182
+ | Roxify | Zstd level 19, compact mode |
50
183
 
51
- No installation needed! Use directly with npx:
184
+ To reproduce:
52
185
 
53
186
  ```bash
54
- npx rox encode input.zip output.png
55
- npx rox decode output.png original.zip
187
+ node test/benchmark-detailed.cjs
56
188
  ```
57
189
 
58
- ### As library
59
-
60
- ```bash
61
- npm install roxify
62
- ```
190
+ ---
63
191
 
64
- ## CLI Usage
192
+ ## Installation
65
193
 
66
- ### Quick Start
194
+ ### As a CLI tool (no installation required)
67
195
 
68
196
  ```bash
69
- # Encode a file
70
- npx rox encode document.pdf document.png
197
+ npx rox encode input.zip output.png
198
+ npx rox decode output.png original.zip
199
+ ```
71
200
 
72
- # Decode it back
73
- npx rox decode document.png document.pdf
201
+ ### As a library
74
202
 
75
- # With encryption
76
- npx rox encode secret.zip secret.png -p mypassword
77
- npx rox decode secret.png secret.zip -p mypassword
203
+ ```bash
204
+ npm install roxify
78
205
  ```
79
206
 
80
- ### CLI Commands
81
-
82
- #### `encode` - Encode file to PNG
207
+ ### Global installation
83
208
 
84
209
  ```bash
85
- npx rox encode <input> [output] [options]
210
+ npm install -g roxify
211
+ rox encode input.zip output.png
86
212
  ```
87
213
 
88
- **Options:**
214
+ ---
89
215
 
90
- - `-p, --passphrase <pass>` - Encrypt with passphrase (AES-256-GCM)
91
- - `-m, --mode <mode>` - Encoding mode: `compact|chunk|pixel|screenshot` (default: `screenshot`)
92
- - `-q, --quality <0-11>` - Brotli compression quality (default: `1`)
93
- - `0` = fastest, largest
94
- - `11` = slowest, smallest
95
- - `-e, --encrypt <type>` - Encryption: `auto|aes|xor|none` (default: `aes` if passphrase)
96
- - `--no-compress` - Disable compression
97
- - `-o, --output <path>` - Output file path
216
+ ## CLI Usage
98
217
 
99
- **Examples:**
218
+ ### Encoding
100
219
 
101
220
  ```bash
102
- # Basic encoding
103
- npx rox encode data.bin output.png
104
-
105
- # Fast compression for large files
106
- npx rox encode large-video.mp4 output.png -q 0
107
-
108
- # High compression for small files
109
- npx rox encode config.json output.png -q 11
221
+ rox encode <input> [output] [options]
222
+ ```
110
223
 
111
- # With encryption
112
- npx rox encode secret.pdf secure.png -p "my secure password"
224
+ | Option | Description | Default |
225
+ |---|---|---|
226
+ | `-p, --passphrase <pass>` | Encrypt with AES-256-GCM | none |
227
+ | `-m, --mode <mode>` | Encoding mode: `screenshot`, `compact` | `screenshot` |
228
+ | `-q, --quality <0-11>` | Compression effort (0 = fastest, 11 = smallest) | `1` |
229
+ | `-e, --encrypt <type>` | Encryption method: `auto`, `aes`, `xor`, `none` | `aes` if passphrase is set |
230
+ | `--no-compress` | Disable compression entirely | false |
231
+ | `-o, --output <path>` | Explicit output file path | auto-generated |
113
232
 
114
- # Compact mode (smallest PNG)
115
- npx rox encode data.bin tiny.png -m compact
233
+ ### Decoding
116
234
 
117
- # Screenshot mode (recommended, looks like a real image)
118
- npx rox encode archive.tar.gz screenshot.png -m screenshot
235
+ ```bash
236
+ rox decode <input> [output] [options]
119
237
  ```
120
238
 
121
- #### `decode` - Decode PNG to file
239
+ | Option | Description | Default |
240
+ |---|---|---|
241
+ | `-p, --passphrase <pass>` | Decryption passphrase | none |
242
+ | `-o, --output <path>` | Output file path | auto-detected from metadata |
243
+ | `--dict <file>` | Zstd dictionary for improved decompression | none |
122
244
 
123
- ```bash
124
- npx rox decode <input> [output] [options]
125
- ```
245
+ ### Examples
126
246
 
127
- **Options:**
247
+ ```bash
248
+ # Encode a single file
249
+ rox encode document.pdf document.png
128
250
 
129
- - `-p, --passphrase <pass>` - Decryption passphrase
130
- - `-o, --output <path>` - Output file path (auto-detected from metadata if not provided)
251
+ # Encode with encryption
252
+ rox encode secret.zip secret.png -p "strong passphrase"
131
253
 
132
- **Examples:**
254
+ # Decode back to original
255
+ rox decode secret.png secret.zip -p "strong passphrase"
133
256
 
134
- ```bash
135
- # Basic decoding
136
- npx rox decode encoded.png output.bin
257
+ # Fast compression for large files
258
+ rox encode video.mp4 output.png -q 0
137
259
 
138
- # Auto-detect filename from metadata
139
- npx rox decode encoded.png
260
+ # Best compression for small files
261
+ rox encode config.json output.png -q 11 -m compact
140
262
 
141
- # With decryption
142
- npx rox decode encrypted.png output.pdf -p "my secure password"
263
+ # Encode an entire directory
264
+ rox encode ./my-project project.png
143
265
  ```
144
266
 
267
+ ---
268
+
145
269
  ## JavaScript API
146
270
 
147
- ### Basic Usage
271
+ ### Basic Encode and Decode
148
272
 
149
273
  ```typescript
150
274
  import { encodeBinaryToPng, decodePngToBinary } from 'roxify';
151
275
  import { readFileSync, writeFileSync } from 'fs';
152
276
 
153
- const input = readFileSync('input.zip');
154
- const png = await encodeBinaryToPng(input, {
155
- mode: 'screenshot',
156
- name: 'input.zip',
157
- });
158
- writeFileSync('output.png', png);
277
+ // Encode
278
+ const input = readFileSync('document.pdf');
279
+ const png = await encodeBinaryToPng(input, { name: 'document.pdf' });
280
+ writeFileSync('document.png', png);
159
281
 
160
- const encoded = readFileSync('output.png');
282
+ // Decode
283
+ const encoded = readFileSync('document.png');
161
284
  const result = await decodePngToBinary(encoded);
162
285
  writeFileSync(result.meta?.name || 'output.bin', result.buf);
163
286
  ```
164
287
 
165
- ### With Encryption
288
+ ### Encrypted Roundtrip
166
289
 
167
290
  ```typescript
168
291
  const png = await encodeBinaryToPng(input, {
169
- mode: 'screenshot',
170
- passphrase: 'my-secret-password',
292
+ passphrase: 'my-secret',
171
293
  encrypt: 'aes',
172
- name: 'secret.zip',
294
+ name: 'confidential.pdf',
173
295
  });
174
296
 
175
- const result = await decodePngToBinary(encoded, {
176
- passphrase: 'my-secret-password',
297
+ const result = await decodePngToBinary(png, {
298
+ passphrase: 'my-secret',
177
299
  });
178
300
  ```
179
301
 
180
- ### Fast Compression
302
+ ### Directory Packing
303
+
304
+ ```typescript
305
+ import { packPaths, unpackBuffer } from 'roxify';
306
+
307
+ // Pack files into a buffer
308
+ const { buf, list } = packPaths(['./src', './README.md'], process.cwd());
309
+
310
+ // Encode the packed buffer into a PNG
311
+ const png = await encodeBinaryToPng(buf, { name: 'project.tar' });
312
+
313
+ // Later: decode and unpack
314
+ const decoded = await decodePngToBinary(png);
315
+ const unpacked = unpackBuffer(decoded.buf);
316
+ for (const file of unpacked.files) {
317
+ console.log(file.name, file.buf.length);
318
+ }
319
+ ```
320
+
321
+ ### Progress Reporting
181
322
 
182
323
  ```typescript
183
324
  const png = await encodeBinaryToPng(largeBuffer, {
184
- mode: 'screenshot',
185
- brQuality: 0,
186
325
  name: 'large-file.bin',
187
- });
188
-
189
- const png = await encodeBinaryToPng(smallBuffer, {
190
- mode: 'compact',
191
- brQuality: 11,
192
- name: 'config.json',
326
+ onProgress: ({ phase, loaded, total }) => {
327
+ console.log(`${phase}: ${loaded}/${total}`);
328
+ },
193
329
  });
194
330
  ```
195
331
 
196
- ### Encoding Modes
332
+ ### EncodeOptions
197
333
 
198
- #### `screenshot` (Recommended)
334
+ ```typescript
335
+ interface EncodeOptions {
336
+ compression?: 'zstd'; // Compression algorithm
337
+ compressionLevel?: number; // Zstd compression level (0-19)
338
+ passphrase?: string; // Encryption passphrase
339
+ dict?: Buffer; // Zstd dictionary for improved ratios
340
+ name?: string; // Original filename stored in metadata
341
+ mode?: 'screenshot'; // Encoding mode
342
+ encrypt?: 'auto' | 'aes' | 'xor' | 'none';
343
+ output?: 'auto' | 'png' | 'rox'; // Output format
344
+ includeName?: boolean; // Include filename in PNG metadata
345
+ includeFileList?: boolean; // Include file manifest in PNG
346
+ fileList?: Array<string | { name: string; size: number }>;
347
+ skipOptimization?: boolean; // Skip PNG optimization pass
348
+ lossyResilient?: boolean; // Enable lossy-resilient encoding (RS ECC)
349
+ eccLevel?: EccLevel; // 'low' | 'medium' | 'quartile' | 'high'
350
+ robustBlockSize?: number; // 2–8 pixels per data block (lossy image)
351
+ container?: 'image' | 'sound'; // Output container format
352
+ onProgress?: (info: ProgressInfo) => void;
353
+ showProgress?: boolean;
354
+ verbose?: boolean;
355
+ }
356
+ ```
199
357
 
200
- Encodes data as RGB pixel values, optimized for screenshot-like appearance. Best balance of size and compatibility.
358
+ ### DecodeOptions
201
359
 
202
360
  ```typescript
203
- const png = await encodeBinaryToPng(data, { mode: 'screenshot' });
361
+ interface DecodeOptions {
362
+ passphrase?: string; // Decryption passphrase
363
+ outPath?: string; // Output directory for unpacked files
364
+ files?: string[]; // Extract only specific files from archive
365
+ onProgress?: (info: ProgressInfo) => void;
366
+ showProgress?: boolean;
367
+ verbose?: boolean;
368
+ }
204
369
  ```
205
370
 
206
- #### `compact` (Smallest)
207
-
208
- Minimal 1x1 PNG with data in custom chunk. Fastest and smallest.
371
+ ### DecodeResult
209
372
 
210
373
  ```typescript
211
- const png = await encodeBinaryToPng(data, { mode: 'compact' });
374
+ interface DecodeResult {
375
+ buf?: Buffer; // Decoded binary payload
376
+ meta?: { name?: string }; // Metadata (original filename)
377
+ files?: PackedFile[]; // Unpacked directory entries, if applicable
378
+ correctedErrors?: number; // RS errors corrected (lossy-resilient mode)
379
+ }
212
380
  ```
213
381
 
214
- #### `pixel`
382
+ ---
215
383
 
216
- Encodes data as RGB pixel values without screenshot optimization.
384
+ ## Encoding Modes
217
385
 
218
- ```typescript
219
- const png = await encodeBinaryToPng(data, { mode: 'pixel' });
220
- ```
386
+ | Mode | Description | Use Case |
387
+ |---|---|---|
388
+ | `screenshot` | Encodes data as RGB pixels in a standard PNG. The image looks like a gradient or noise pattern and survives re-uploads and social media processing. | Sharing on image-only platforms, bypassing file-type filters |
389
+ | `compact` | Minimal 1x1 PNG with data embedded in a custom ancillary chunk (`rXDT`). Produces the smallest possible output. | Programmatic use, archival, maximum compression ratio |
221
390
 
222
- #### `chunk`
391
+ ---
223
392
 
224
- Standard PNG with data in custom rXDT chunk.
393
+ ## Encryption
225
394
 
226
- ```typescript
227
- const png = await encodeBinaryToPng(data, { mode: 'chunk' });
228
- ```
395
+ Roxify supports two encryption methods:
396
+
397
+ | Method | Algorithm | Strength | Use Case |
398
+ |---|---|---|---|
399
+ | `aes` | AES-256-GCM with PBKDF2 (100,000 iterations) | Cryptographically secure, authenticated | Sensitive data, confidential documents |
400
+ | `xor` | XOR cipher with passphrase-derived key | Obfuscation only, not cryptographically secure | Casual deterrent against inspection |
401
+
402
+ When `encrypt` is set to `auto` (the default when a passphrase is provided), AES is selected.
229
403
 
230
- ## API Reference
404
+ ---
231
405
 
232
- ### `encodeBinaryToPng(input, options)`
406
+ ## Lossy-Resilient Mode
233
407
 
234
- Encodes binary data into a PNG image.
408
+ Enable `lossyResilient: true` to produce output that survives lossy compression. This uses the same error correction algorithm as QR codes (Reed-Solomon over GF(256)) combined with block-based signal encoding.
235
409
 
236
- **Parameters:**
410
+ ### How It Works
237
411
 
238
- - `input: Buffer` - The binary data to encode
239
- - `options?: EncodeOptions` - Encoding options
412
+ 1. **Reed-Solomon ECC** adds configurable redundancy (10–100%) to the data.
413
+ 2. **Interleaving** spreads data across RS blocks so burst errors don't overwhelm a single block.
414
+ 3. **Block encoding** (image: large pixel blocks; audio: multi-frequency tones) makes the signal robust against quantization.
415
+ 4. **Finder patterns** (image only) enable automatic alignment after re-encoding.
240
416
 
241
- **Returns:** `Promise<Buffer>` - The encoded PNG
417
+ ### Error Correction Levels
242
418
 
243
- **Options:**
419
+ | Level | Parity Symbols | Overhead | Correctable Errors |
420
+ |-------|---------------:|---------:|-------------------:|
421
+ | `low` | 20 / block | ~10% | ~4% |
422
+ | `medium` | 40 / block | ~19% | ~9% |
423
+ | `quartile` | 64 / block | ~33% | ~15% |
424
+ | `high` | 128 / block | ~100% | ~25% |
425
+
426
+ ### Example
244
427
 
245
428
  ```typescript
246
- interface EncodeOptions {
247
- compression?: 'br' | 'none';
429
+ // Image that survives JPEG compression
430
+ const png = await encodeBinaryToPng(data, {
431
+ lossyResilient: true,
432
+ eccLevel: 'quartile',
433
+ robustBlockSize: 4, // 4×4 pixels per data bit
434
+ });
435
+
436
+ // Audio that survives MP3 compression
437
+ const wav = await encodeBinaryToPng(data, {
438
+ container: 'sound',
439
+ lossyResilient: true,
440
+ eccLevel: 'medium',
441
+ });
248
442
 
249
- passphrase?: string;
443
+ // Decode automatically detects the format
444
+ const result = await decodePngToBinary(png);
445
+ console.log('Errors corrected:', result.correctedErrors);
446
+ ```
250
447
 
251
- name?: string;
448
+ For full documentation, see [Lossy Resilience Guide](./docs/LOSSY_RESILIENCE.md).
252
449
 
253
- mode?: 'compact' | 'chunk' | 'pixel' | 'screenshot';
450
+ ---
254
451
 
255
- encrypt?: 'auto' | 'aes' | 'xor' | 'none';
452
+ ## Audio Container
256
453
 
257
- output?: 'auto' | 'png' | 'rox';
454
+ Roxify can encode data into WAV audio files using `container: 'sound'`.
258
455
 
259
- includeName?: boolean;
456
+ ### Standard Mode (`lossyResilient: false`)
260
457
 
261
- brQuality?: number;
262
- }
458
+ Data bytes are stored directly as 8-bit PCM samples. This is the fastest and most compact option, but the output sounds like white noise and does not survive lossy audio compression.
459
+
460
+ ### Lossy-Resilient Mode (`lossyResilient: true`)
461
+
462
+ Data is encoded using **8-channel multi-frequency shift keying (MFSK)**:
463
+
464
+ - 8 carrier frequencies (600–2700 Hz) encode 1 byte per symbol.
465
+ - Each carrier is modulated with raised-cosine windowing.
466
+ - The output sounds like a series of **musical chords** — structured and pleasant, not white noise.
467
+ - Reed-Solomon ECC enables recovery after MP3/AAC/OGG transcoding.
468
+
469
+ ```typescript
470
+ const wav = await encodeBinaryToPng(data, {
471
+ container: 'sound',
472
+ lossyResilient: true,
473
+ eccLevel: 'medium',
474
+ });
263
475
  ```
264
476
 
265
- ### `decodePngToBinary(pngBuf, options)`
477
+ ---
266
478
 
267
- Decodes a PNG image back to binary data.
479
+ ## Performance Tuning
268
480
 
269
- **Parameters:**
481
+ ### Compression Level
270
482
 
271
- - `pngBuf: Buffer` - The PNG image to decode
272
- - `options?: DecodeOptions` - Decoding options
483
+ The `compressionLevel` option (CLI: `-q`) controls the trade-off between speed and output size:
273
484
 
274
- **Returns:** `Promise<DecodeResult>` - The decoded data and metadata
485
+ | Level | Speed | Ratio | Recommendation |
486
+ |---|---|---|---|
487
+ | 0 | Fastest | Largest | Files over 100 MB, real-time workflows |
488
+ | 1 | Fast | Good | Default; general-purpose use |
489
+ | 5 | Moderate | Better | Archival of medium-sized datasets |
490
+ | 11 | Slowest | Smallest | Small files under 1 MB, long-term storage |
275
491
 
276
- **Options:**
492
+ ### Native Module
493
+
494
+ The Rust native module provides 10--50x throughput improvement over the pure JavaScript fallback. It is loaded automatically when present. To verify availability:
277
495
 
278
496
  ```typescript
279
- interface DecodeOptions {
280
- passphrase?: string;
281
- }
497
+ import { native } from 'roxify';
498
+ console.log('Native module loaded:', !!native);
282
499
  ```
283
500
 
284
- **Result:**
501
+ If the native module is not found for the current platform, Roxify falls back to TypeScript transparently. No code changes are needed.
502
+
503
+ ### Zstd Dictionary
504
+
505
+ For datasets consisting of many similar small files (e.g., JSON API responses, log entries), a Zstd dictionary can improve compression ratios by 20--40%:
285
506
 
286
507
  ```typescript
287
- interface DecodeResult {
288
- buf: Buffer;
508
+ import { readFileSync } from 'fs';
289
509
 
290
- meta?: {
291
- name?: string;
292
- };
293
- }
510
+ const dict = readFileSync('my-dictionary.zdict');
511
+ const png = await encodeBinaryToPng(data, { dict });
294
512
  ```
295
513
 
296
- ## Performance Tips
514
+ ---
515
+
516
+ ## Cross-Platform Support
517
+
518
+ Roxify ships prebuilt native modules for the following targets:
519
+
520
+ | Platform | Architecture | Binary Name |
521
+ |---|---|---|
522
+ | Linux | x86_64 | `libroxify_native-x86_64-unknown-linux-gnu.node` |
523
+ | macOS | x86_64 | `libroxify_native-x86_64-apple-darwin.node` |
524
+ | macOS | ARM64 (Apple Silicon) | `libroxify_native-aarch64-apple-darwin.node` |
525
+ | Windows | x86_64 | `roxify_native-x86_64-pc-windows-msvc.node` |
297
526
 
298
- ### For Large Files (>10 MB)
527
+ The correct binary is resolved automatically at runtime. If no binary is found for the current platform, Roxify falls back silently to the pure JavaScript implementation.
528
+
529
+ ### Building Native Modules for Specific Targets
299
530
 
300
531
  ```bash
301
- # Use quality 0 for fastest encoding
302
- npx rox encode large.bin output.png -q 0
303
- ```
532
+ # Current platform
533
+ npm run build:native
304
534
 
305
- ```typescript
306
- const png = await encodeBinaryToPng(largeFile, {
307
- mode: 'screenshot',
308
- brQuality: 0,
309
- });
535
+ # Specific platform
536
+ npm run build:native:linux
537
+ npm run build:native:macos-x64
538
+ npm run build:native:macos-arm
539
+ npm run build:native:windows
540
+
541
+ # All configured targets
542
+ npm run build:native:targets
310
543
  ```
311
544
 
312
- ### For Small Files (<1 MB)
545
+ ---
546
+
547
+ ## Building from Source
548
+
549
+ ### Prerequisites
550
+
551
+ - Node.js 18 or later
552
+ - Rust 1.70 or later (install via [rustup](https://rustup.rs))
553
+
554
+ ### Commands
313
555
 
314
556
  ```bash
315
- # Use quality 11 for best compression
316
- npx rox encode small.json output.png -q 11 -m compact
557
+ # Install dependencies
558
+ npm install
559
+
560
+ # Build TypeScript only
561
+ npm run build
562
+
563
+ # Build native Rust module
564
+ npm run build:native
565
+
566
+ # Build everything (Rust + TypeScript + CLI binary)
567
+ npm run build:all
568
+
569
+ # Run the full test suite
570
+ npm test
317
571
  ```
318
572
 
319
- ```typescript
320
- const png = await encodeBinaryToPng(smallFile, {
321
- mode: 'compact',
322
- brQuality: 11,
323
- });
573
+ ### Project Structure
574
+
575
+ ```
576
+ roxify/
577
+ native/ Rust source code (N-API module and CLI binary)
578
+ src/ TypeScript source code (library and CLI entry point)
579
+ dist/ Compiled JavaScript output
580
+ test/ Test suite and benchmarks
581
+ docs/ Additional documentation
582
+ scripts/ Build, release, and CI helper scripts
583
+ ```
584
+
585
+ ---
586
+
587
+ ## Architecture
588
+
589
+ Roxify is a hybrid Rust and TypeScript module. The performance-critical paths -- compression, CRC computation, pixel scanning, encryption -- are implemented in Rust and exposed through N-API bindings. The TypeScript layer handles PNG construction, CLI argument parsing, and high-level orchestration.
590
+
591
+ ### Compression Pipeline
592
+
593
+ ```
594
+ Input --> Zstd Compress (multi-threaded, Rayon) --> AES-256-GCM Encrypt (optional) --> PNG Encode --> Output
324
595
  ```
325
596
 
326
- ### Benchmark Results
597
+ ### Lossy-Resilient Pipeline
327
598
 
328
- File: 3.8 MB binary
599
+ ```
600
+ Input --> RS ECC Encode --> Interleave --> Block Encode (MFSK audio / QR-like image) --> WAV/PNG Output
601
+ ```
602
+
603
+ ### Decompression Pipeline
604
+
605
+ ```
606
+ Input --> PNG Parse --> AES-256-GCM Decrypt (optional) --> Zstd Decompress --> Output
607
+ ```
329
608
 
330
- - **Quality 0**: ~500-800ms, output ~1.2 MB
331
- - **Quality 1** (default): ~1-2s, output ~800 KB
332
- - **Quality 5**: ~8-12s, output ~750 KB
333
- - **Quality 11**: ~20-30s, output ~720 KB
609
+ ### Lossy-Resilient Decode Pipeline
610
+
611
+ ```
612
+ Input --> Detect Format --> Demodulate/Read Blocks --> De-interleave --> RS ECC Decode --> Output
613
+ ```
614
+
615
+ ### Rust Modules
616
+
617
+ | Module | Responsibility |
618
+ |---|---|
619
+ | `core.rs` | Pixel scanning, CRC32, Adler32, delta coding, Zstd compress/decompress |
620
+ | `encoder.rs` | PNG payload encoding with marker pixels and metadata chunks |
621
+ | `packer.rs` | Directory tree serialization and streaming deserialization |
622
+ | `crypto.rs` | AES-256-GCM encryption and PBKDF2 key derivation |
623
+ | `archive.rs` | Tar-based archiving with optional Zstd compression |
624
+ | `reconstitution.rs` | Screenshot detection and automatic crop to recover encoded data |
625
+ | `audio.rs` | WAV container encoding and decoding (PCM byte packing) |
626
+ | `bwt.rs` | Parallel Burrows-Wheeler Transform |
627
+ | `rans.rs` | rANS (Asymmetric Numeral Systems) entropy coder |
628
+ | `hybrid.rs` | Block-based orchestration of BWT, context mixing, and rANS |
629
+ | `pool.rs` | Buffer pooling and zero-copy memory management |
630
+ | `image_utils.rs` | Image resizing, pixel format conversion, metadata extraction |
631
+ | `png_utils.rs` | Low-level PNG chunk read/write operations |
632
+ | `progress.rs` | Progress tracking for long-running compression/decompression |
633
+
634
+ ### TypeScript Modules
635
+
636
+ | Module | Responsibility |
637
+ |---|---|
638
+ | `ecc.ts` | Reed-Solomon GF(256) codec, block ECC, interleaving |
639
+ | `robust-audio.ts` | MFSK audio modulation/demodulation, Goertzel detection, sync preamble |
640
+ | `robust-image.ts` | QR-code-like block encoding, finder patterns, majority voting |
641
+ | `encoder.ts` | High-level encoding orchestration (standard + lossy-resilient) |
642
+ | `decoder.ts` | High-level decoding with automatic format detection |
643
+ | `audio.ts` | Standard WAV container (8-bit PCM) |
644
+ | `helpers.ts` | Delta coding, XOR cipher, palette generation |
645
+ | `zstd.ts` | Parallel Zstd compression via native module |
646
+
647
+ ---
334
648
 
335
649
  ## Error Handling
336
650
 
651
+ Roxify throws descriptive errors for common failure modes:
652
+
337
653
  ```typescript
654
+ import { decodePngToBinary } from 'roxify';
655
+
338
656
  try {
339
- const result = await decodePngToBinary(encoded, {
657
+ const result = await decodePngToBinary(pngBuffer, {
340
658
  passphrase: 'wrong-password',
341
659
  });
342
660
  } catch (err) {
343
661
  if (err.message.includes('Incorrect passphrase')) {
344
- console.error('Wrong password!');
345
- } else if (err.message.includes('Invalid ROX format')) {
346
- console.error('Not a valid RoxCompressor PNG');
347
- } else {
348
- console.error('Decode failed:', err.message);
662
+ // Wrong decryption key
663
+ } else if (err.message.includes('not a valid PNG')) {
664
+ // Input is not a valid roxified PNG
665
+ } else if (err.message.includes('corrupted')) {
666
+ // Data integrity check failed
349
667
  }
350
668
  }
351
669
  ```
352
670
 
353
- ## Security
671
+ | Error | Cause |
672
+ |---|---|
673
+ | `Incorrect passphrase` | Wrong password provided for decryption |
674
+ | `not a valid PNG` | Input buffer is not a PNG or lacks Roxify markers |
675
+ | `Passphrase required` | File is encrypted but no passphrase was supplied |
676
+ | `Image too large to decode` | PNG dimensions exceed the in-process memory limit |
354
677
 
355
- - **AES-256-GCM**: Authenticated encryption with 100,000 PBKDF2 iterations
356
- - **XOR cipher**: Simple obfuscation (not cryptographically secure)
357
- - **No encryption**: Data is compressed but not encrypted
678
+ ---
358
679
 
359
- ⚠️ **Warning**: Use strong passphrases for sensitive data. The `xor` encryption mode is not secure and should only be used for obfuscation.
680
+ ## Security Considerations
360
681
 
361
- ## License
682
+ - **AES-256-GCM** provides authenticated encryption. Tampered ciphertext is detected and rejected.
683
+ - **PBKDF2** with 100,000 iterations is used for key derivation, making brute-force attacks computationally expensive.
684
+ - **XOR encryption** is not cryptographically secure. Use it only for casual obfuscation.
685
+ - Passphrases are never stored in the output file. There is no recovery mechanism for a lost passphrase.
686
+ - The PNG output does not visually reveal whether data is encrypted. An observer cannot distinguish an encrypted Roxify PNG from an unencrypted one by inspection.
362
687
 
363
- MIT © RoxCompressor
688
+ ---
364
689
 
365
690
  ## Contributing
366
691
 
367
- Contributions welcome! Please open an issue or PR on GitHub.
692
+ Contributions are welcome. Please open an issue to discuss proposed changes before submitting a pull request.
368
693
 
369
- ## Links
694
+ 1. Fork the repository
695
+ 2. Create a feature branch (`git checkout -b feature/my-change`)
696
+ 3. Run the test suite (`npm test`)
697
+ 4. Submit a pull request
370
698
 
371
- - [GitHub Repository](https://github.com/RoxasYTB/roxify)
372
- - [npm Package](https://www.npmjs.com/package/roxify)
373
- - [Report Issues](https://github.com/RoxasYTB/roxify/issues)
699
+ ---
374
700
 
375
- ## CI / Multi-platform builds
701
+ ## License
702
+
703
+ MIT. See [LICENSE](LICENSE) for details.
376
704
 
377
- This project runs continuous integration on Linux and Windows via GitHub Actions. Native modules are built on each platform and attached to the workflow (and release) as artifacts. On releases we also publish platform artifacts to GitHub Releases. For npm publishing, set the `NPM_TOKEN` secret in your repository settings to allow automated publishes on release.
705
+ ---
706
+
707
+ ## Links
708
+
709
+ - [npm Package](https://www.npmjs.com/package/roxify)
710
+ - [GitHub Repository](https://github.com/RoxasYTB/roxify)
711
+ - [Issue Tracker](https://github.com/RoxasYTB/roxify/issues)
712
+ - [CLI Documentation](./docs/CLI.md)
713
+ - [JavaScript SDK Reference](./docs/JAVASCRIPT_SDK.md)
714
+ - [Cross-Platform Build Guide](./docs/CROSS_PLATFORM.md)
715
+ - [Lossy Resilience Guide](./docs/LOSSY_RESILIENCE.md)