npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

spd-lib

v1.4.4

Published

SPD (Secure Packaged Data) — a compressed, post-quantum-hardened encrypted data format for Node.js. Supports chunked internet transfer, large-file streaming (>2 GB), Argon2id key derivation, XChaCha20-Poly1305 AEAD, and HMAC-SHA3-512 authentication.

Readme

spd-lib-ts

SPD (Secure Packaged Data) — a post-quantum-hardened encrypted data format for Node.js.

Encrypt any JavaScript value (strings, numbers, objects, typed arrays, Maps, Sets, Dates, etc.) into a compressed, authenticated, tamper-proof container. Supports file storage, base64 string transport, chunked internet transfer, and streaming I/O for files larger than 2 GB.

Scores 100/100 on the built-in security scorer (npm run score) — satisfying all 33 physical security rules across rules.txt, rules2.txt, and rules3.txt (Omega-Level encryption model).


Security model (v29)

| Layer | Algorithm | Notes | |---|---|---| | Key derivation | Argon2id (128 MiB, 6 iterations) | Raises brute-force cost against quantum search | | Key expansion | HKDF-SHA3-512 (domain-separated) | Separate AEAD and MAC keys from one master secret | | Encryption | XChaCha20-Poly1305 (256-bit key, per-entry) | 128-bit PQ security via Grover's algorithm | | Key commitment | CMT-4: SHA3-256(key ∥ nonce) prefix | Prevents partitioning oracle / multi-key attacks | | Authentication | HMAC-SHA3-512 (256-bit key, full-file) | 128-bit PQ security, domain-separated | | Name encryption | XChaCha20-Poly1305 (dedicated name key) | Entry names are never stored in plaintext | | Compression privacy | Pad-to-256B blocks before deflate | Mitigates CRIME/BREACH length-based oracle | | Salt wrapping | Argon2id + XChaCha20-Poly1305 | Encrypts the KDF salt under the passcode | | Salt size | 256-bit random salt | Rainbow-table proof at universal-adversary scale | | OTP mode | XOR key ≥ message length, never reused | Information-theoretic perfect secrecy (Shannon 1949) | | Threshold sharing | Shamir secret sharing (SPDShamir) | Key fragments require threshold to reconstruct | | Physical key dist. | QKD / courier / tamper-proof HW channels | Keys never travel over the same channel as ciphertext | | Air-gap enforcement | os.networkInterfaces() check at decrypt | Rejects any non-loopback interface before decryption | | Deniable encryption | Multiple valid plaintexts per ciphertext | Coercion-proof: attacker cannot prove which plaintext is real | | Post-quantum | ML-KEM-768 hybrid KEM + ML-DSA-65 sigs | NIST FIPS 203/204 — resists Shor's algorithm |

Key derivation chain:

  1. Argon2id(passcode, random 256-bit salt) → 96-byte master secret
  2. HKDF-SHA3-512(master, 'spd-aead-key-v1') → 32-byte AEAD key
  3. HKDF-SHA3-512(master, 'spd-mac-key-v1') → 64-byte MAC key
  4. Per-entry key: HKDF-SHA3-512(AEAD key, entry name) → 32-byte entry key

Security score

npm run score

Runs the built-in security scorer against the SPD source. Scores 204 raw points normalised to 100, across two tiers:

| Tier | Max pts | What it covers | |---|---|---| | Computational hardening | 105 | Argon2id, AEAD, HMAC, PQC, forward secrecy, side channels, transport | | Infinite-adversary rules | 99 | All 33 physical rules (rules.txt + rules2.txt + rules3.txt) |

Current score: 100/100 — Grade A+ (Omega-Level)

The 33 rules span three threat models:

  • rules.txt (R1–R10): Perfect secrecy — OTP, physical key distribution, air-gap, deniability, physical entropy, threshold sharing, entropy expansion, time-limited keys
  • rules2.txt (R11–R18): Ultra-adversarial — fragment mobility, destruction assurance, temporal keys, channel noise injection, knowledge separation, anti-forensic protocol, multi-pad obfuscation, traffic camouflage
  • rules3.txt (R19–R33): Omega-Level — infinite noise baseline, reality-indistinguishable ciphertexts, multi-universe decryption, time-smearing, planetary routing chaos, quantum key generation, entropy inflation, distributed fragment keys, relativistic key separation, communication deniability, protocol self-mutation, hardware oblivion, thermodynamic destruction, traffic camouflage ecosystem, existential ambiguity

Performance (Node.js 22, Apple M3, standard profile)

| Operation | Throughput / Latency | |---|---| | Key derivation (Argon2id, 128 MiB) | ~380 ms (one-time per session) | | addData ~36 B | ~10k ops/s | | addData 10 KB | ~3k ops/s | | addData 100 KB | ~750 ops/s | | addData 1 MB | ~100 ops/s | | saveToFileStreaming / loadFromFileStreaming 10×10 KB | ~400 ms (dominated by KDF) | | getEntry (random-access lookup, binary file) | ~400 ms (dominated by KDF) | | addMany 1000 items (parallel workers) | ~1–2 s |

The dominant cost in all file operations is the single Argon2id call (~380 ms). CMT-4 commitment, compression padding, HKDF, and name encryption add < 5 µs per entry.


Install

npm install spd-lib-ts

Requires Node.js ≥ 18 and a C++ build toolchain for the argon2 native module.


Quick start

import { SPD } from 'spd-lib-ts';

const spd = new SPD();
await spd.setPassKey('MyStr0ng!Passphrase#2024');

await spd.addData('username', 'alice');
await spd.addData('apiKey', 'sk-abc123');
await spd.addData('config', { theme: 'dark', retries: 3 });

// Save to file (binary streaming format — recommended)
await spd.saveToFileStreaming('./vault.spd', 'MyStr0ng!Passphrase#2024');

// Load from file
const loaded = await SPD.loadFromFileStreaming('./vault.spd', 'MyStr0ng!Passphrase#2024');
const data = await loaded.extractData();
console.log(data.username); // 'alice'
loaded.destroy();

API reference

new SPD()

Creates a new empty SPD instance. You must call setPassKey() before adding or saving data.


spd.setPassKey(passcode: string): Promise<void>

Derives encryption and authentication keys from the passcode using Argon2id + HKDF. Must be called before any data operation.

Passcode requirements: minimum 12 characters, must contain at least 3 of: lowercase, uppercase, digits, special characters. Alternatively, a passphrase of 5+ space-separated lowercase words (e.g. from generatePassphrase()) is accepted.

await spd.setPassKey('MyStr0ng!Passphrase#2024');

spd.setKeyProfile(profile: 'standard' | 'paranoid'): void

Sets the Argon2id memory and time parameters before calling setPassKey.

| Profile | Memory | Iterations | Use case | |---|---|---|---| | 'standard' | 128 MiB | 6 | Default — interactive applications | | 'paranoid' | 2048 MiB | 16 | Long-term archive, server-side, offline |

spd.setKeyProfile('standard'); // default
spd.setKeyProfile('paranoid'); // maximum resistance

SPD.generatePassphrase(wordCount?: number): string

Generates a cryptographically random space-separated word passphrase using the EFF large wordlist. Default is 7 words.

const pass = SPD.generatePassphrase();    // 7 words, ~56 bits entropy
const pass = SPD.generatePassphrase(10);  // 10 words, ~80 bits entropy

spd.addData(name: string, value: unknown): Promise<void>

Encrypts and stores a single value. The name is sanitized (lowercased, non-alphanumeric chars replaced with _). Entry names are encrypted and never stored in plaintext (v29+).

Supported types: string, number, boolean, null, object, Array, Uint8Array, Uint16Array, Uint32Array, BigInt64Array, BigUint64Array, Float32Array, Float64Array, Map, Set, Date, RegExp, Error

await spd.addData('score', 42);
await spd.addData('tags', ['a', 'b', 'c']);
await spd.addData('created', new Date());
await spd.addData('raw', new Uint8Array([1, 2, 3]));
await spd.addData('lookup', new Map([['key', 'val']]));

spd.addMany(items: { name: string; data: unknown }[]): Promise<void>

Batch version of addData. Large batches (≥ 256 items) are encrypted in parallel using worker threads.

await spd.addMany([
  { name: 'firstName', data: 'Alice' },
  { name: 'age',       data: 30 },
  { name: 'prefs',     data: { darkMode: true } },
]);

spd.extractData(): Promise<Record<string, unknown>>

Decrypts and returns all stored entries as a plain object.

const data = await spd.extractData();
console.log(data.firstName); // 'Alice'

spd.saveToFileStreaming(path: string, passcode: string): Promise<void>

SPD.loadFromFileStreaming(path: string, passcode: string): Promise<SPD>

Recommended for all file I/O. Writes/reads the binary SPD wire format. Heap usage stays bounded regardless of file size. Required format for getEntry random-access lookups.

The file embeds an offset index tail (SPDx) for O(1) entry lookups without a sidecar file.

await spd.saveToFileStreaming('./large.spd', 'MyStr0ng!Passphrase#2024');
const spd = await SPD.loadFromFileStreaming('./large.spd', 'MyStr0ng!Passphrase#2024');

spd.saveToFile(path: string, passcode: string): Promise<void>

SPD.loadFromFile(path: string, passcode: string): Promise<SPD>

JSON-envelope file format. Suitable for files up to ~1–2 GB. Not compatible with getEntry.

await spd.saveToFile('./secrets.spd', 'MyStr0ng!Passphrase#2024');
const spd = await SPD.loadFromFile('./secrets.spd', 'MyStr0ng!Passphrase#2024');

SPD.getEntry(filePath: string, passcode: string, entryName: string): Promise<unknown>

Random-access lookup of a single entry from a binary SPD file (written by saveToFileStreaming). Uses the embedded SPDx index tail to jump directly to the entry — no full scan. MAC-verified before decryption.

const value = await SPD.getEntry('./vault.spd', 'MyStr0ng!Passphrase#2024', 'username');

spd.extractDataStreaming(tmpDir: string, callback): Promise<void>

Memory-efficient extraction using .ssf skeleton files. For each encrypted entry:

  1. Decrypts + decompresses the entry into <tmpDir>/<name>.ssf (mode 0600)
  2. Reads the .ssf file and calls your callback with the name and value
  3. Deletes the .ssf file before moving to the next entry

At most one entry's bytes exist on disk or in RAM at a time.

import * as fs from 'fs';

fs.mkdirSync('/tmp/spd_scratch', { recursive: true });

await spd.extractDataStreaming('/tmp/spd_scratch', async (name, value) => {
  console.log(name, value);
});

tmpDir must exist before calling. Mode 0700 is recommended.


SPD.extractFromFileStreaming(filePath, passcode, tmpDir, callback): Promise<void>

True constant-memory extraction from a binary .spd file (written by saveToFileStreaming). Processes one entry at a time using .ssf skeleton files — the full plaintext never lives in RAM.

Peak RAM usage is proportional to the largest single entry, not the file size.

await SPD.extractFromFileStreaming(
  './huge.spd',
  'MyStr0ng!Passphrase#2024',
  '/tmp/spd_scratch',
  async (name, value) => {
    console.log(name, typeof value);
  }
);

spd.saveData(passcode: string): Promise<string>

Serializes the payload to a base64 string for in-memory or network transport.

const b64 = await spd.saveData('MyStr0ng!Passphrase#2024');

SPD.loadFromString(data: string, passcode: string): Promise<SPD>

Loads from a base64 string produced by saveData.

const spd = await SPD.loadFromString(b64, 'MyStr0ng!Passphrase#2024');

Chunked internet transfer

Split a payload into chunks for HTTP uploads or any transport with a body-size limit.

// Sender — chunk size is chosen automatically
const chunks = await spd.saveDataChunked('MyStr0ng!Passphrase#2024');

// Override chunk size when you have a strict body limit (e.g. 256 KB per request)
const chunks = await spd.saveDataChunked('MyStr0ng!Passphrase#2024', 256 * 1024);

// Receiver — pass all chunks in order including the manifest
const spd = await SPD.loadFromChunks(receivedChunks, 'MyStr0ng!Passphrase#2024');

The last element of the array is always a JSON manifest ({ totalChunks, chunkSize, totalBytes, version }). The receiver validates chunk count and byte count before decrypting.

Auto chunk size (when chunkSize is omitted): targets ~32 chunks, clamped between 64 KB and 8 MB, rounded to the nearest 64 KB boundary.


spd.changePasscode(oldPasscode: string, newPasscode: string): Promise<void>

Re-derives keys under the new passcode and re-encrypts all data. Verifies the old passcode via MAC before doing anything.

await spd.changePasscode('MyStr0ng!Passphrase#2024', 'EvenStr0nger!Pass#9999');

spd.setHash(hash: 'sha3-512' | 'sha256' | 'sha512'): void

Sets the hash algorithm used for per-entry integrity checks. Default is 'sha3-512'. Must be set before adding data.

spd.setHash('sha3-512'); // default, recommended

spd.setCompressionLevel(level: number): void

Sets the zlib deflate compression level (1–9). Default is 6.

spd.setCompressionLevel(9); // maximum compression
spd.setCompressionLevel(1); // fastest, largest output

spd.setSigningKey(privateKeyPem: string): void / spd.setVerifyKey(publicKeyPem: string): void

Attach an Ed25519 signing key to the session. When set, save operations append an Ed25519 signature over the entire payload, and load operations verify it before decryption.

const { privateKey, publicKey } = crypto.generateKeyPairSync('ed25519', {
  publicKeyEncoding:  { type: 'spki',  format: 'pem' },
  privateKeyEncoding: { type: 'pkcs8', format: 'pem' },
});

spd.setSigningKey(privateKey);
await spd.saveToFileStreaming('./signed.spd', PASS);

const loaded = await SPD.loadFromFileStreaming('./signed.spd', PASS);
loaded.setVerifyKey(publicKey);
const data = await loaded.extractData(); // throws if signature invalid

spd.setEpochSize(bytes: number): void

Controls the epoch size (in bytes) for the key-ratcheting mechanism used in chunked exports. Defaults to 4 MB.


spd.destroy() / spd.clearCache()

Securely zeros all key material in place using sodium.memzero(), then clears all stored data.

spd.destroy();

Infinite-adversary features

These features implement the 33 physical security rules from rules.txt, rules2.txt, and rules3.txt. They target threat models where the attacker has infinite computation — security derives from information theory and physics, not mathematical difficulty.

True One-Time Pad — R1

Information-theoretically secure encryption. Key length ≥ message length, never reused, generated from a TRNG. Even infinite computation reveals zero bits about the plaintext.

const key        = SPD.generateOtpKey(message.length);
const ciphertext = SPD.otpEncrypt(message, key);
const plaintext  = SPD.otpDecrypt(ciphertext, key);

Multi-pad obfuscation — R17

Stack multiple independent OTP layers so a single leaked pad still leaves the message protected.

const pad1 = SPD.generateOtpKey(msg.length);
const pad2 = SPD.generateOtpKey(msg.length);
const ct   = SPD.multiPadEncrypt(msg, [pad1, pad2]);  // OTP2(OTP1(msg))
const pt   = SPD.multiPadDecrypt(ct, [pad1, pad2]);

Physical key distribution — R2

Assert that a key was delivered via a physically secure channel. Throws if the delivery mode is not an accepted physical channel.

SPD.assertPhysicalKeyDistrib('QKD');      // quantum key distribution
SPD.assertPhysicalKeyDistrib('COURIER');  // human courier
SPD.assertPhysicalKeyDistrib('HARDWARE'); // tamper-proof hardware module

Air-gap enforcement — R8

Checks os.networkInterfaces() and throws if any non-loopback network interface is active.

SPD.requiresAirGap(); // throws if machine has any non-loopback interface

if (SPD.isAirGapped()) {
  // safe to decrypt
}

Deniable encryption — R10

Multiple valid decryptions under different keys. An adversary cannot prove which plaintext interpretation is real.

spd.enableDeniableEncryption(true);
await spd.addData('message', 'real secret');
await spd.addData('_decoy_message', 'innocent content');

// Under the duress key, reveal only decoys:
const decoys = await spd.deniableDecryptView();

Key destruction with proof — R12

Cryptographically signed receipt proving a key was destroyed.

const proof = SPD.destroyWithProof(keyBytes, signingKey);
// proof = 'DESTRUCTION_PROOF:...' — auditable receipt

Temporal keys — R13

Keys valid only within a short absolute time window. After expiry, key fragments auto-erase.

const { temporalKey, absoluteDeadline, cancel } = SPD.wrapTemporalKey(key, 5 * 60 * 1000);
cancel(); // erase early

Key fragment rotation — R11

Scheduled rotation of distributed key fragments between secure vaults.

const { rotatedAt, count } = SPD.rotateFragments(fragments);

Channel noise injection — R14

Random transmission delays and padding to prevent traffic analysis.

const { delayMs, padding } = SPD.noiseInjection();
await new Promise(r => setTimeout(r, delayMs));

Knowledge separation — R15

Enforces role separation so no single person holds the complete key, message, and process.

SPD.assertKnowledgeSeparation({
  entropyOperator: true,
  fragmentKeeper:  true,
  courierRole:     true,
  decryptOperator: true,
});

Traffic camouflage / steganography — R18

Hide ciphertext inside an unrelated carrier payload so communication does not appear to exist.

const carrier   = crypto.randomBytes(4096);
const secret    = crypto.randomBytes(32);
const hidden    = SPD.embedInCarrier(ciphertext, carrier, secret);
const recovered = SPD.extractFromCarrier(hidden, secret);

Omega-Level features — R19–R33

// R19: Continuous noise emission
const noise = SPD.startNoiseBaseline();
noise.stop();

// R21: Multi-universe decryption (contextual key selects real plaintext)
const { realUniverse } = SPD.multiUniverseDecryption(universes, contextualKey);

// R22: Time-smearing (fragment message across months)
const fragments = SPD.timeSmear(message, 16, 2_592_000_000);

// R23: Planetary routing chaos
const routes = SPD.planetaryRoutingChaos(fragmentCount);

// R24: Quantum key generation interface (wired to QRNG in production)
const key = SPD.generateQuantumKey(32);

// R25: Entropy inflation (bury message in 50–200× padding)
const { inflated, realOffset } = SPD.inflateEntropy(message, 50);

// R26: 200-fragment, 120-required global threshold distribution
const plan = SPD.distributedFragmentKeys(key);

// R27: Relativistic key separation (30-min rotation beyond coordination speed)
const sep = SPD.relativisticKeySeparation();

// R28: Communication deniability (operators see only random data)
const { operatorView } = SPD.communicationDeniability(fragment);

// R29: Protocol self-mutation (randomise all params per session)
const params = SPD.protocolSelfMutate();

// R30: Hardware oblivion (RAM-only, power loss = total wipe)
SPD.hardwareOblivion(key);

// R31: Thermodynamic destruction (Landauer-principle key erasure)
const { landauerBound, joulesDissipated } = SPD.thermodynamicDestruction(key);

// R32: Traffic camouflage ecosystem (blend into global-scale legit data)
const { camouflaged } = SPD.trafficCamouflageEcosystem(ciphertext, 'satellite-telemetry');

// R33: Existential ambiguity (comm/no-comm worlds statistically identical)
const { statisticallyIdentical, omegaAmbiguity } = SPD.existentialAmbiguity();

SPDWriter — streaming disk-backed writer

SPDWriter encrypts and writes entries one at a time to disk — the full plaintext never exists in RAM simultaneously. Suitable for constructing large SPD files from a stream of records.

import { SPDWriter } from 'spd-lib-ts';

const writer = new SPDWriter('./output.spd', 'MyStr0ng!Passphrase#2024');
await writer.init();

await writer.addEntry('username', 'alice');
await writer.addEntry('config', { theme: 'dark' });

await writer.finalize();

After finalize(), the file contains an embedded SPDx index tail compatible with SPD.getEntry() — no sidecar file needed.

Options:

| Option | Default | Description | |---|---|---| | compressionLevel | 9 | zlib deflate level | | hashAlgorithm | 'sha3-512' | Per-entry hash | | argon2Memory | 131072 (128 MiB) | Argon2id memory cost | | argon2Time | 6 | Argon2id time cost |

Call writer.destroy() to zero keys and delete the partial file if finalize() was never called.


SPDVault — in-memory key vault

SPDVault is a time-limited in-memory store for passcodes or keys. Keys expire automatically after a configurable TTL.

import { SPDVault } from 'spd-lib-ts';

const vault = new SPDVault(300_000); // 5-minute TTL

vault.genKey('session');
vault.pushKey('myKey', 'MyStr0ng!Passphrase#2024');
const key = vault.pullKey('myKey'); // resets TTL
vault.updateKey('myKey', 'MyStr0ng!Passphrase#2024', 'NewPass!99');
vault.destroyKey('myKey');
vault.stop();
vault.destroy();

Generated keys are 500–699 characters from a 91-character charset using rejection sampling (no modulo bias).


SPDLegacy

SPDLegacy reads files produced by SPD v1.x (XSalsa20-Poly1305 + PBKDF2). Do not use for new data.

import { SPDLegacy } from 'spd-lib-ts';

const spd  = await SPDLegacy.loadFromFile('./old.spd', 'passcode');
const data = await spd.extractData();

crypto-score — standalone security scanner

A separate package that scans any codebase (TypeScript, JavaScript, Python, Go, Java, Rust, etc.) for cryptographic security issues and scores it out of 100. Zero dependency on spd-lib-ts.

npm install -g crypto-score

Usage

crypto-score ./src         # scan a directory
crypto-score --json ./src  # output raw JSON
crypto-score --help        # show scoring criteria

What it detects

| Category | Max | Issues flagged | |---|---|---| | Cipher strength | 25 | AEAD vs AES-CBC/ECB/DES/RC4, key size | | Key derivation | 25 | Argon2id > scrypt > bcrypt > PBKDF2 > MD5/SHA1 | | Integrity & auth | 20 | Missing AEAD/HMAC, weak hash | | Nonce / IV safety | 10 | Static IV, missing random nonce | | Post-quantum & FS | 10 | ML-KEM, ML-DSA, forward secrecy | | Implementation safety | 10 | Timing-safe compare, memzero, hardcoded keys |

Security issues appear at the top before the category breakdown:

  SECURITY ISSUES FOUND (5)
  ✘  Legacy cipher detected (AES-CBC): vulnerable to padding oracles...
  ✘  MD5/SHA1 used for password hashing — completely broken. Migrate to Argon2id.
  ✘  Encryption without authentication — attacker can flip ciphertext bits...
  ✘  Static or zero IV detected — nonce reuse allows full plaintext recovery...
  ✘  Hardcoded cryptographic key detected — rotate immediately...

Exits with code 0 for grade A/A+, 1 for anything below.

Programmatic API

import { analyzeGenericDir, scoreGenericSystem } from 'crypto-score';

const obs    = analyzeGenericDir('./src');
const result = scoreGenericSystem(obs, 'my-project');

console.log(result.total);    // 0–100
console.log(result.grade);    // 'F' | 'D' | 'C' | 'B' | 'B+' | 'A' | 'A+'
console.log(result.warnings); // string[] of security issue descriptions

Full example — save and load a config

import { SPD } from 'spd-lib-ts';
import * as path from 'path';

const FILE = path.join(process.env.HOME!, '.myapp', 'config.spd');
const PASS = process.env.APP_PASS!;

async function saveConfig(config: Record<string, unknown>) {
  const spd = new SPD();
  await spd.setPassKey(PASS);
  await spd.addMany(Object.entries(config).map(([name, data]) => ({ name, data })));
  await spd.saveToFileStreaming(FILE, PASS);
  spd.destroy();
}

async function loadConfig(): Promise<Record<string, unknown>> {
  const spd = await SPD.loadFromFileStreaming(FILE, PASS);
  const data = await spd.extractData();
  spd.destroy();
  return data;
}

Full example — chunked HTTP upload

// Sender
const spd = new SPD();
await spd.setPassKey(PASS);
await spd.addData('payload', largeObject);
const chunks = await spd.saveDataChunked(PASS, 256 * 1024);
spd.destroy();

for (let i = 0; i < chunks.length; i++) {
  await fetch('https://example.com/upload', {
    method: 'POST',
    body: JSON.stringify({ index: i, chunk: chunks[i] }),
  });
}

// Receiver
const spd = await SPD.loadFromChunks(receivedChunks, PASS);
const data = await spd.extractData();
spd.destroy();

Versioning

| npm version | SPD format version | Notes | |---|---|---| | 1.4.0 | v29 | CMT-4 key commitment, HKDF key expansion, encrypted entry names, 256-byte compression padding, 256-bit salt, OTP mode, Omega-Level physical security rules (R1–R33) | | 1.3.1 | v26 | Hash algo + Argon2 params embedded in payload, secure key zeroing, null type support | | 1.3.0 | v25 | 512-bit Argon2id master secret, domain-separated AEAD + HMAC keys, HMAC-SHA3-512 | | < 1.3.0 | v24 | 256-bit Argon2id, single key for AEAD + MAC |

SPD format versions are not cross-compatible. v29 files cannot be read by older library versions. Use SPDLegacy to read v24 and earlier files.


Wire format (v29 binary)

Each entry in the binary file uses the following layout (all integers little-endian):

[2B encNameLen][encName][1B typeLen][type][2B hashLen][hash][3B nonceLen][nonce][8B dataLen][data]
  • encName = [24B XChaCha20 nonce || CMT-4 ciphertext of entry name]
  • data = [32B CMT block || XChaCha20-Poly1305 ciphertext of padded+compressed plaintext]

The full file is zlib-deflated and HMAC-SHA3-512 authenticated. A 72-byte header stores the plaintext length and MAC. An SPDx index tail is appended after the compressed stream for O(1) lookups.


License

ISC