archive 0.8.0
List, extract and create tar archives, gzip- or zstd-compressed or not (and read bzip2- and xz-compressed ones), and zips, and compress one file with gzip or zstd; refuses links, absolute names, .. and overwriting by rule, checks the whole archive before writing any of it, and writes the same bytes for the same files.
archive [--root DIR] --op list|extract|create [--into DIR] [--as tar|tgz|gz|zip] [--dry-run] [--max-bytes N] [--max-entries N] ARCHIVE [PATH...]
- output
- NDJSON stream ending in an
endrecord - schema
- archive.v1
- reversibility
irreversible-bounded- guarantees
deterministic,dry_run- concurrency
- read-only: it never writes, so any number may run at once
- confinement
beneath: with --root, paths are checked lexically against it and then opened beneath it one component at a time, following no symbolic link (path.symlink), so nothing outside the root is reachable through one; without it, a reader opens the path as given, links and all, and a writer still follows no link in the file's own name- reads the environment
- no
- reads the clock
- no
- compiler
- de4e52fd8180
- for an agent
- SKILL.md (
archive skill)
Authority
Derived by cancho authority from the source, embedded in the binary, and checked against tools.toml's ceiling in CI. Every label is bounded.
- args
- dir_read
- dir_write
- err_write
- file_read
- file_write
- fs_read("")
- heap
- io_write
What the row cannot say:
- path extent: a path from the command line is checked at run time, so the row says fs_read("")
- what a symlink reaches
- what standard output carries
- resource use beyond the tool's own caps (memory, wall time)
- per-invocation behaviour such as --dry-run, which the row cannot see
Operands
| Operand | Role | How many | Meaning |
|---|---|---|---|
ARCHIVE | path-write | 1 | the archive to list or extract, or the one to create (it must not exist) |
PATH... | path-read | 0 or more | create: the files and directories to add, in order (a directory with everything beneath it, sorted). For list and extract these are the entries to select, as list shows them (a directory selects everything beneath it), and none selects every entry |
Flags
| Flag | Kind | Role | Default | Meaning |
|---|---|---|---|---|
--root | path | root | resolve every path relative to this directory (default: the working directory) and refuse paths outside it | |
--op | choice:list/extract/create | none | list | list shows the entries, extract writes them beneath --into, create writes an archive of the PATHs |
--into | path | path-write | extract beneath this directory, which must exist (default: the root) | |
--as -a | choice:tar/tgz/gz/zip/tzst/zst | none | tgz | what create writes: a tar, a gzip-compressed tar, one file gzipped, a zip, a zstd-compressed tar, or one file zstd-compressed |
--dry-run | bool | guard | check everything and report what would be written, exit 9, write nothing | |
--max-bytes | nat | none | 67108864 | the largest archive read, expanded size included, and the largest archive written (ceiling 1073741824) |
--max-entries | nat | none | 10000 | the most entries read or written (ceiling 1000000) |
Limits
| Limit | Default | Ceiling |
|---|---|---|
max-bytes | 67,108,864 | 1,073,741,824 |
max-entries | 10,000 | 1,000,000 |
Exit codes
| Code | Name | Meaning |
|---|---|---|
| 0 | SUCCESS | what was asked was done; zero matches is success |
| 1 | GENERAL_ERROR | the operating system failed mid-operation, or the tool caught a bug in itself |
| 2 | INVALID_ARGS | the invocation is malformed or names something of the wrong kind; nothing was read |
| 3 | NOT_FOUND | a named input does not exist |
| 4 | PERMISSION_DENIED | the operating system refused, or the tool's own confinement did |
| 5 | CONFLICT | state is not what the caller said it would be |
| 8 | PRECONDITION_FAILED | the answer is no, or a limit was reached |
| 9 | DRY_RUN | a --dry-run completed; planned_actions lists what an apply would do |
Rules (44)
Match on rule, not on the message. A retry repair is an argv that can be run as it stands, and never widens what the tool may touch.
| Rule | Exit | Repair | When |
|---|---|---|---|
args.unknown-flag | 2 | sometimes | a flag that is not in the tool's table |
args.missing-value | 2 | never | a flag that takes a value was given none |
args.bad-value | 2 | never | a flag's value is not of the flag's kind |
args.unexpected-value | 2 | never | a flag that takes no value was given one |
args.duplicate-flag | 2 | never | the same flag was given twice |
args.conflict | 2 | never | two flags that exclude each other were both given |
args.missing-operand | 2 | never | an operand the tool needs was not given |
args.too-many-operands | 2 | never | more operands than the tool takes |
args.required-flag | 2 | never | a flag the tool needs was not given |
path.empty | 2 | never | an empty path |
path.dotdot | 2 | sometimes | a path with a `..` component |
path.absolute | 2 | always | an absolute path where one relative to --root is wanted |
path.outside-root | 4 | never | an absolute path that is not under --root |
path.too-long | 2 | never | a path longer than 4096 bytes |
path.symlink | 4 | never | a path that runs through a symbolic link, which the tool does not follow |
io.not-found | 3 | never | a named input does not exist |
io.not-a-directory | 3 | never | a path runs through something that is not a directory |
io.is-a-directory | 2 | never | a directory where a file is wanted |
io.permission-denied | 4 | never | the operating system refused access |
io.read-failed | 1 | never | the operating system failed a read |
io.write-failed | 1 | never | the operating system failed a write, or a write was short |
io.rename-unsupported | 8 | never | the filesystem cannot refuse an existing name atomically, so move did not rename |
conflict.exists | 5 | never | --create was given and the path exists |
conflict.locked | 5 | never | another writer holds the path's lock |
archive.not-an-archive | 8 | never | neither gzip nor a tar: no gzip magic and no tar header with a valid checksum |
archive.bad-gzip | 8 | never | a gzip header that cannot be read, a second member, or bytes after the member |
archive.crc-mismatch | 8 | never | the gzip trailer's CRC-32 or length is not the data's |
archive.bad-deflate | 8 | never | the DEFLATE stream is invalid |
archive.bad-checksum | 8 | never | a tar header's checksum is not its bytes' |
archive.bad-header | 8 | never | a tar header field that cannot be read |
archive.truncated | 8 | never | the archive ends inside a header or body, or before its end marker |
archive.unsafe-name | 4 | never | an entry name that is absolute, has `..` or holds a NUL |
archive.entry-kind | 8 | never | an entry that is not a regular file or a directory |
archive.duplicate-entry | 8 | never | two entries name the same file |
archive.directory-missing | 8 | never | the --into directory, or one above it, does not exist |
archive.special-mode | 8 | never | an entry has a setuid, setgid or sticky bit |
archive.no-match | 8 | never | a selected path matches no entry of the archive |
archive.unsupported | 8 | never | the archive uses encryption, zip64, several disks, a zip method other than stored and deflate, a randomised bzip2 block, an xz filter other than LZMA2, or a zstd frame that needs a dictionary |
archive.bad-bzip2 | 8 | never | a bzip2 stream that is not valid, or bytes after it |
archive.bad-xz | 8 | never | an xz stream that is not valid, or bytes after it |
archive.bad-zstd | 8 | never | a zstd frame that is not valid, or bytes after it |
archive.name-too-long | 8 | never | a name that does not fit a tar header |
limit.archive-too-large | 8 | never | the archive, expanded, would pass --max-bytes |
limit.too-many-entries | 8 | never | more entries than --max-entries |
Evidence
each gate is a test in cancho-tools/tests/conformance, run by CI on the commit this binary was built from:
- M1 schema conformance
- M2 determinism
- M3 rule coverage and hint soundness
- M4 fault injection
- M5 differential against GNU
- M6 authority
- M7 mutation behaviour
- M8 confinement
- M9 memory flatness
Agent in the loop: not run: no claim that this tool is more agent-friendly than the incumbents is made (cancho docs/agent-toolbox.md section 7.2).