cputools logo cputools

Archive tools

Three in-process archive operations on fflate (MIT, pure-JS) — the same open-source library you could run yourself. Every op is a POST https://api.relaystation.ai/v1/archive/<op>. Everything happens in memory: cputools never writes an entry to a filesystem, so there is no path-traversal (“zip-slip”) surface — and the extraction is bomb-guarded (below).

Inputs and outputs

Same uniform shape as the PDF tools: a file input source is { "inline": "<base64>" } (≤ 4 MB) or { "inputKey": "..." } (from POST /v1/cputools/upload-url, ≤ 50 MB). zip takes a files array. unzip returns a manifest{ entries: [{ name, output }] } — where each name is a sanitized basename and each output is the uniform envelope (inline ≤ 4 MB, else a presigned outputUrl). zip/gzip return a single output envelope.

Billing

Per MB of input (compressed) bytes, minimum 1 MiB, at cputools.price.archive.<op>.per_mb_micros (launch default $0.0002 / MB). Billing is on the input you send — so unzip bills the compressed archive, not its (larger) expansion. The live 402 challenge is authoritative.

OpBilled onRate
ziptotal input MB$0.0002 / MB
unzipinput MB$0.0002 / MB
gzipinput MB$0.0002 / MB

zip

Zip one or more files into a single archive. files is an array of input sources (≥ 1, ≤ cputools.archive.max_files_per_zip, default 1000); optional filenames must match files.length. Filenames are reduced to a sanitized basename (path separators, .., drive prefixes, and null bytes stripped); collisions get a -N suffix.

POST /v1/archive/zip
{ "files": [{"inline":"<b64>"}, {"inline":"<b64>"}], "filenames": ["a.txt", "b.txt"] }

unzip

Extract a zip to a manifest of entries. Zip-bomb-guarded in two layers (see below). A nested .zip entry is returned as raw bytes — never recursively extracted (one level only).

POST /v1/archive/unzip
{ "file": {"inline":"<b64 zip>"} }

gzip

Compress or decompress a single file. mode is compress (→ application/gzip) or decompress (→ application/octet-stream). Decompress is guarded by the same uncompressed-size cap (gzip bombs too).

POST /v1/archive/gzip
{ "file": {"inline":"<b64>"}, "mode": "compress" }

The zip-bomb defense (documented limits)

A decompression bomb — a tiny archive that expands to gigabytes — is stopped by layered, operator-tunable caps, and the rejection is a feature:

  1. Pre-charge directory check. Before any charge, cputools reads the archive’s central directory and rejects — with a 422, no charge — an archive whose declared uncompressed total exceeds cputools.archive.max_uncompressed_bytes (default 128 MiB), whose entry count exceeds cputools.archive.max_entries (default 10000), or whose per-entry declared compression ratio exceeds cputools.archive.max_compression_ratio (default 1000:1).
  2. Authoritative running-total during extraction. Declared sizes can lie, so extraction streams and sums the actual decompressed bytes, aborting the instant it crosses the cap — memory never balloons (and the charge is refunded).
  3. Encrypted/password-protected zips are rejected (422 UNSUPPORTED_ENCRYPTED_ZIP) — cputools doesn’t decrypt.

Sample

curl -X POST https://api.relaystation.ai/v1/archive/unzip \
  -H 'X-Payment: <base64 EIP-3009 auth>' \
  -H 'Idempotency-Key: unzip-bundle-20260609' \
  -H 'Content-Type: application/json' \
  -d '{"file":{"inline":"<b64 zip>"}}'

Errors

  • 402 PAYMENT_REQUIRED — no valid payment.
  • 422 ARCHIVE_TOO_LARGE — declared (pre-charge) or actual (in-flight) uncompressed output over the cap.
  • 422 ARCHIVE_TOO_MANY_ENTRIES / 422 ARCHIVE_RATIO_EXCEEDED — over the entry-count or compression-ratio cap (pre-charge).
  • 422 UNSUPPORTED_ENCRYPTED_ZIP — an encrypted/password-protected entry.
  • 422 ARCHIVE_INVALID — not a valid zip/gzip (or a ZIP64 archive, which is unsupported).
  • 422 ARCHIVE_TOO_MANY_FILES / 422 FILENAME_COUNT_MISMATCH — zip input over the file cap, or a filenames length that doesn’t match files.

Next

PDF tools · CSV tools · Image tools · Utils tools · Generate tools · Pricing · API reference