archive

Make and open .zip, .tar(.gz) and .gz files. Extraction is safe by default: members that would land outside the destination (zip-slip), absolute paths, links and device files are refused, and archives that would unpack to more than 2 GB (or compress suspiciously well) are stopped. archive.list(path) shows what is inside without extracting.

import archive

Example

import archive
import fs

fs.write("evidence.txt", "auth.log excerpt")
say(archive.zip("evidence.zip", ["evidence.txt"]))
for entry in archive.list("evidence.zip") {
  say(entry.name, entry.size)
}
# unzip refuses entries that would land outside the target folder.
say(archive.unzip("evidence.zip", "out"))

Functions

zip(target, paths, options?)

returns number

Make a .zip file holding the files and folders in paths. Folders are added with everything inside. Replaces an existing target. Gives back the number of files added.

Options

  • base a folder. Paths in the zip are kept relative to it. Without it, names in the zip are just the file or folder names.
Visualizehow archive.zip works

archive.zip("notes.zip", ["notes.txt", "logs"])

archive.zip: inputs flowing into the function and the resultpathsnotes.txtlogsoptions?left outarchive.ziptarget"notes.zip"inside the .zipnotes.txtlogslogsoldapp.logold2025.log

Real result: worked out by running this example with TB while the site was built.

unzip(path, dest, options?)

returns list

Extract a .zip into the folder dest (made if needed) and give back the paths written. It is safe by default.

Refused by default

  • Members with absolute paths or '..' in them (zip-slip).
  • Links.
  • Archives that unpack to more than 2 GB.

Options

  • safe false only skips the dangerous members, instead of stopping.
  • overwrite false keeps files that already exist.
  • max_size the most bytes the archive may unpack to.
  • members a list of names, to extract only some.
Visualizehow archive.unzip works

archive.unzip("bundle.zip", "unpacked") gives ["/home/you/project/unpacked/notes.txt", "/home/you/project/unpacked/logs/app.log", "/hom…

archive.unzip: inputs flowing into the function and the resultpath"bundle.zip"dest"unpacked"options?left outarchive.unzipthe paths written/home/you/project/unpackednotes.txtlogsapp.logold2025.log

Real result: worked out by running this example with TB while the site was built.

tar(target, paths, options?)

returns number

Make a .tar file. Links are skipped. Gives back the number of files added.

It is compressed when the name ends in .tar.gz or .tgz, .tar.bz2 or .tar.xz.

Options

  • compress like "gz", to compress whatever the name is.
  • base works like in zip(), and so do paths.
Visualizehow archive.tar works

archive.tar("logs.tar.gz", ["logs"])

Drawing…

Real result: worked out by running this example with TB while the site was built.

untar(path, dest, options?)

returns list

Extract a .tar / .tar.gz / .tar.bz2 / .tar.xz into dest and give the paths written. Same safety and options as unzip(); links and device files are refused (or skipped with {"safe": false}).

Visualizehow archive.untar works

archive.untar("backup.tar.gz", "restored") gives ["/home/you/project/restored/logs/app.log", "/home/you/project/restored/logs/old/2025.log…

archive.untar: inputs flowing into the function and the resultpath"backup.tar.gz"dest"restored"options?left outarchive.untarthe paths written/home/you/project/restoredlogsapp.logold2025.logtodo.txt

Real result: worked out by running this example with TB while the site was built.

gzip(path, target?)

returns text

Compress one file with gzip. target defaults to path + ".gz". Gives the target path.

Visualizehow archive.gzip works

archive.gzip("big.log")

archive.gzip: inputs flowing into the function and the resultpath"big.log"target?left out: path +".gz"archive.gzipthe .gz path, and how much smallerbig.log.gz is 96 bytes (was 10000)

Real result: worked out by running this example with TB while the site was built.

gunzip(path, target?)

returns text

Uncompress a .gz file. target defaults to the name without ".gz". Stops if the result would be bigger than 2 GB. Gives the target path.

Visualizehow archive.gunzip works

archive.gunzip("day.log.gz") gives "day.log"

archive.gunzip: inputs flowing into the function and the resultpath"day.log.gz"target?left out: the namewithout .gzarchive.gunzipthe file writtenday.log

Real result: worked out by running this example with TB while the site was built.

list(path)

returns list

What is inside a .zip or .tar archive, without extracting: maps with "name", "size" (unpacked bytes), "compressed" (zip only), "is_dir" and "modified".

Visualizehow archive.list works

archive.list("site.zip") gives [{"name": "notes.txt", "size": 30, "compressed": 32, "is_dir": false, "modified": "2026-1…

archive.list: inputs flowing into the function and the resultpath"site.zip"archive.listwhat is inside, nothing extractednamesizeis_dirnotes.txt30falselogs/0truelogs/old/0truelogs/app.log24falselogs/old/202…9false

Real result: worked out by running this example with TB while the site was built.

kind(path)

returns text

"zip", "tar", "gzip" or "unknown", by looking at the file's first bytes.

Visualizehow archive.kind works

archive.kind("k.zip") gives "zip"

archive.kind: inputs flowing into the function and the resultpath"k.zip"archive.kindread from the first bytes, not the name"zip"path"k.tar"archive.kindread from the first bytes, not the name"tar"path"k.txt.gz"archive.kindread from the first bytes, not the name"gzip"path"notes.txt"archive.kindread from the first bytes, not the name"unknown"

Real result: worked out by running this example with TB while the site was built.

extract(path, dest, options?)

returns list

unzip() or untar() depending on what the file is (gunzip for a lone .gz).

Visualizehow archive.extract works

archive.extract("mixed.tgz", "out") gives ["/home/you/project/out/logs/app.log", "/home/you/project/out/logs/old/2025.log"]

archive.extract: inputs flowing into the function and the resultpath"mixed.tgz"dest"out"options?left out (same as unzip/untar)archive.extract (picks untar)the paths written/home/you/project/out/logsapp.logold2025.log

Real result: worked out by running this example with TB while the site was built.

Values

NameValueWhat it is
archive.max_size2147483648

How many unpacked bytes an extraction allows by default (2 GB).

Visualizehow archive.max_size works

archive.max_size gives 2147483648

The value archive.max_sizearchive.max_size2147483648what it isHow many unpacked bytes an extractionallows by default (2 GB).

No inputs: call it as it is.

Real value, read from TB while the site was built.