The Safe, Deterministic
ZIP Engine

100 % TypeScript. Zero runtime dependencies. One API across Node ≥ 22, browsers, Deno, Bun and Workers — for modern apps and the agents that operate them. One engine, three surfaces: the library, the CLI and the MCP server.

Get Started → GitHub
CI status CodeQL security scan OpenSSF Scorecard npm version npm monthly downloads zipnative-cli npm version zipnative-mcp npm version Minified + gzipped bundle size Zero runtime dependencies TypeScript strict mode 95.2 percent statement coverage npm provenance signed MIT License
npm install zipnative
npx zipnative-cli inspect release.zip --check deterministic,safe-names
claude mcp add zipnative -- npx -y zipnative-mcp

1.1 — additive, zero breaking changes: range access over remote and huge archives, cancellation and progress, raw transplant, canonicalisation of any archive, legacy name decoding, the Zip64 streaming opt-in. The API surface, the 39-code error vocabulary and the deterministic bytes stay frozen under semver. Both satellites pin zipnative ^1.0.0.

757
Tests
95.2%
Coverage
0
Dependencies
6
Foreign interop tools
39
Frozen error codes
—
GitHub Stars

What only zipnative does

Not a faster deflate — a safer, reproducible, streaming archive engine built for scenarios the incumbents cannot do.

Random access

Read one entry of a 4 GB archive with three range reads — the central directory is parsed lazily, payloads are zero-copy views. No “extract everything first”.

Safe by default

Zip-slip, symlinks, duplicate names, decompression bombs, overlapping entries, Zip64 spoofing and EOCD ambiguity are all rejected unless you explicitly opt out. Every parser loop runs under a named, CWE-tagged bound.

Deterministic

Reproducible-build mode with a written contract: identical inputs, identical SHA-256, on every runtime — via a pure-TS deflate encoder whose constants are frozen public API.

Incremental modification

Replace, add, remove or rename entries and save without recompressing the untouched 99 % — the append-only model proven in pdfnative's PDF incremental updates.

True streaming

Create archives in fixed-size chunks with bounded memory, and read unseekable streams forward — data-descriptor entries included, thanks to a resumable pure-TS inflater.

Worker parallelism

zipnative/worker fans per-entry compression across a real pool (worker_threads and Web Workers) — byte-identical output, graceful degradation, never an infra failure.

Remote and huge archives 1.1

openZipRange() opens a 50 GB archive on S3, behind HTTP Range or in a browser File with the memory of its central directory — you inject the byte-range source, the engine never fetches, every security check still applies. Cancel anything with an AbortSignal.

Reproducible, for any archive 1.1

analyzeDeterminism() names what would drift; canonicalizeZip() rewrites a Gradle, Maven, zip or Explorer archive into a canonical form without recompression — under the same frozen byte contract. addFromReader() assembles artefacts from existing parts in O(bytes copied).

Pick your surface

One zero-dependency engine, three ways to call it — the artefacts are interchangeable, so the choice is about who is calling. The full capability × surface matrix →

zipnative v1.1.0

The library

You write application code — Node, Deno, Bun, browser or Worker. The full surface: lazy reader, secure extraction, deterministic writer, incremental modifier, forward streaming, worker parallelism, codec injection, 39 frozen error codes.

npm install zipnative
zipnative-cli v1.0.0 · binary zipnative

The CLI

You drive a shell, a CI job, a container. 15 commands over stdin/stdout with an agent contract — a --json envelope carrying err.code verbatim, --dry-run, --summary / --fields, JSON Schemas, completion — and the filesystem trust boundary the engine refuses to be. Offline, always.

npx zipnative-cli <command>
zipnative-mcp v1.0.0

The MCP server

You give an AI assistant tool access — Claude Desktop, Claude Code, Cursor, VS Code. 13 tools with strict schemas, 7 prompts, sandboxed resources, stdio or loopback Streamable HTTP, and no network access of any kind.

npx -y zipnative-mcp

No surface encrypts, by policy — confidentiality belongs to the document layer. Encrypt first, then archive →

Ninety seconds of API

Sync-first, no classes, every error carries a stable machine-readable code.

import { openZip, extractZip } from 'zipnative';

const zip = openZip(bytes);              // lazy — nothing decompressed
const manifest = zip.readEntry('manifest.json'); // one entry, CRC-verified

const files = extractZip(bytes);         // traversal/bombs rejected by default
import { createZip } from 'zipnative';

const zip = createZip({ compression: { deterministic: true } });
zip.add('data.json', payload);
const bytes = zip.toBytes();             // same inputs → same SHA-256, anywhere
import { createZipModifier, openZip } from 'zipnative';

const mod = createZipModifier(openZip(bytes));
mod.replaceEntry('word/document.xml', updated);
const saved = mod.save();                // untouched entries never recompressed
import { openZipRange, canonicalizeZip } from 'zipnative';

const zip = await openZipRange({ size, read: (offset, length) => rangeFetch(url, offset, length) });
const manifest = await zip.readEntry('manifest.json'); // a tail window + the CD + this entry

const canonical = canonicalizeZip(foreignBytes);  // any archive → reproducible, no recompression

Full quickstart →

The honest comparison

fflate keeps the raw-deflate crown and we do not chase it — zipnative wins on scenarios, not drag races.

Compared 2026-09-01 against fflate 0.8.x, jszip 3.10.x, yauzl/yazl, adm-zip 0.6.x.
zipnativefflatejszipyauzl/yazladm-zip
Zero runtime dependencies✓✓✗✗✗
Random access (1 entry, no full parse)✓✗✗yauzl ✓✗
Streaming read + write✓partial✗one per lib✗
Safe-extract defaults (slip/bomb/ambiguity)✓DIYDIYDIYhistorical CVEs
Deterministic output (written contract)✓DIY✗✗✗
Modify in place, no recompression✓✗rewrite-all✗partial
Stable machine-readable error codes✓✗✗✗✗
Browser + Node + Deno + Bun + Workers✓✓✓Node-onlyNode-only
Raw deflate throughputgood (platform zlib)bestslowgoodpoor

Benchmarks

Measured scenarios, security checks always on — including the one we lose.

Create 1000 small entries (deflate) — fflate's home turf
fflate
15.2 op/s
adm-zip
14.3 op/s
zipnative
7.2 op/s
jszip
5.4 op/s
Inventory a 10 000-entry archive (no decompression)
zipnative
fastest
fflate
1.25×
jszip
3.39×
adm-zip
14.8×
Random access: read 1 entry out of 10 000
zipnative
fastest
fflate
1.04×
jszip
3.37×
adm-zip
11.8×

Windows 11, Node v22.17, tiny-entry corpus — the worst case for the random-access rows (with megabyte entries the gap becomes orders of magnitude). zipnative numbers include the always-on security checks. Full methodology: bench/RESULTS.md.

Security model

Every archive is untrusted input; the default code path is the safe one.

Extraction refuses traversal (../, absolute paths, drive letters, NTFS streams), symlink entries and bombs; the reader refuses overlapping entries, parser-differential headers and ambiguous end-of-central-directory records — over a byte-range source exactly as over bytes in memory, and a decoder you inject for legacy names cannot smuggle a path past the same gate. Encrypted entries are detected and labelled by scheme (ZipCrypto, WinZip AES, strong encryption), never decrypted in 1.x. Bounds are named, CWE-tagged, documented and caller-configurable — raising one is always an explicit decision. Every refusal throws a stable machine-readable error code.

Read the full threat model

Determinism as a contract

Identical inputs, identical SHA-256 — on any runtime.

compression: { deterministic: true } pins the pure-TS encoder: the mode for content addressing, caching, signatures and reproducible builds. The contract is written down and golden-tested; changing the emitted bytes is a semver-major event. Since 1.1 the contract covers archives you did not build: canonicalizeZip() rewrites any ZIP into the canonical form without recompression, and analyzeDeterminism() reports what would otherwise drift — the reproducible builds guide.

Read the determinism contract

Architecture

Two satellites consuming one library through npm; inside it, five strict layers, one direction of imports, zero reverse edges — and one write path with two byte-identical consumers.

Built for AI agents

Machine-readable ground truth at every level — never guessed, always verified.

Stable error codes (errors.json), a structured diagnostics channel, executable recipes, and the mechanically extracted export surface in api.json — plus llms.txt and llms-full.txt for context ingestion, with byte and approximate token budgets for every artefact in llms-index.json so an agent can decide what to fetch before spending the tokens. On the other surfaces the same discipline: zipnative schema manifest and doctor --json on the CLI, describe_engine and typed outputSchemas on the MCP server. Developed under a human-in-the-loop AI governance policy.

Agent brief →

Origin

zipnative is the sibling of pdfnative: the same doctrine — zero dependencies, closure factories, strict layers, sync-first, security by default — applied to the ZIP format. The incremental-update model, the AI governance policy and this documentation pipeline were extracted from pdfnative and hardened here.