Skip to main content

unpacksort

unpacksort safely recovers files from an mbox or a directory, recursively opens supported archives and attached messages, removes byte-identical duplicates, and publishes a deterministic type-grouped result with complete provenance.

It supports Python 3.12+, ZIP/ZIP64, TAR (plain, gzip, bzip2, xz, and zstandard), 7z, parser-validated PDFs, and common ZIP application packages. RAR is detected and retained as unprocessed; source links and archive links are never followed.

Status

Not yet published. semantic-release has staged the first release and the protected release is waiting on PyPI trusted-publisher setup. No PyPI, Homebrew, or WinGet release is claimed until those channels exist — the install commands below describe the intended routes, not ones you can run today.

Overview

unpacksort recovers files from a mailbox or a directory tree: it opens nested archives and attached messages, removes byte-identical duplicates, and publishes a deterministic, type-grouped result with a manifest recording where every file came from.

Deterministic means the same input produces the same output, every time — which is what makes a recovery run something you can check rather than something you have to trust.

Install

pipx install unpacksort

Future releases also provide brew install fileworks/tap/unpacksort, a Windows x64 portable ZIP, and winget install fileworks.unpacksort. Initial executable artifacts are unsigned and can trigger an operating-system trust prompt. Verify the published SHA-256 checksum, while remembering that a checksum detects damage but does not independently prove who published a file.

Quick start

unpacksort ~/Mail/archive.mbox ~/Recovered
unpacksort ~/Downloads ~/Recovered --flatten
unpacksort ~/Mail/archive.mbox ~/Recovered-PDFs --pdf-only

Hierarchy mode is the default. It preserves source, message, and archive ancestry beneath fixed type groups. Flatten mode publishes directly beneath each group; distinct collisions are named name.ext, name_1.ext, and so on. Byte-identical occurrences reference one canonical file and do not consume suffixes.

A successful run writes manifest.jsonl and report.txt. Exit 1 means the result is trustworthy but partial—for example because an encrypted, corrupt, unsafe, limit-blocked, or unsupported item was retained or reported. Re-run the same command to resume compatible committed work.

See the operating manual, release and channel setup, and security policy.

Safety model

Inputs are treated as personal but potentially malformed. Extraction is bounded, staged privately, content-addressed, and published atomically. Archive paths never directly control public paths. The initial release does not isolate parsers in a process or VM and is not a malware scanner.

Development

uv sync --locked --all-groups
uv run ruff format --check .
uv run ruff check .
uv run mypy
uv run pytest

Use an ignored CLAUDE.local.md at the repository root for per-clone paths, commands, or private preferences. Never store credentials or other secrets there.

Licensed under the MIT License.

Usage

unpacksort SOURCE DESTINATION [options]
Option Effect
--flatten One directory per type instead of mirroring the source layout
--pdf-only Extract and validate PDFs, ignore everything else
--log-file PATH Write bounded rotating progress and diagnostics (default: beside destination)
--verbose Include debug diagnostics in the logfile

unpacksort --help is authoritative.

Configuration

There is no configuration file. Behaviour and all seven safety limits are set by explicit command-line flags; run unpacksort --help for their names, defaults, and minimum values. Raising a limit is an operator decision and expands the resource budget for untrusted input, so scheduled jobs should pin reviewed values rather than accept input-controlled arguments.

Troubleshooting

A RAR archive was not extracted. RAR is detected and retained unprocessed; unpacksort does not bundle a RAR implementation.

The run stopped at a safety limit. The manifest names which limit and which container tripped it. That is the intended behaviour for a suspicious archive.

A PDF was rejected. PDFs are parser-validated; a file that cannot be parsed is retained as-is rather than published as a valid document.

The output differs between two runs of the same input. It should not. That is a bug worth reporting, with the two manifests.

Security

Report vulnerabilities privately as described in SECURITY.md. Parsers run in-process: unpacksort bounds extraction and never follows links, but it is not a malware scanner or a sandbox. Process hostile data inside an additional operating-system sandbox.

License

MIT — see LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

unpacksort-1.1.0.tar.gz (179.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

unpacksort-1.1.0-py3-none-any.whl (42.0 kB view details)

Uploaded Python 3

File details

Details for the file unpacksort-1.1.0.tar.gz.

File metadata

  • Download URL: unpacksort-1.1.0.tar.gz
  • Upload date:
  • Size: 179.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for unpacksort-1.1.0.tar.gz
Algorithm Hash digest
SHA256 d9f721056fb853d5dd351a8cf7ffdb837368a87159ee490fc2cee5b9dcf1e6e5
MD5 c0f7ef47b6595b4ced898c217b96e026
BLAKE2b-256 4c929783021eb514461d542cca6a11a3877be470cc3d284a074abe1c58f91102

See more details on using hashes here.

Provenance

The following attestation bundles were made for unpacksort-1.1.0.tar.gz:

Publisher: release.yml on fileworks/unpacksort

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file unpacksort-1.1.0-py3-none-any.whl.

File metadata

  • Download URL: unpacksort-1.1.0-py3-none-any.whl
  • Upload date:
  • Size: 42.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for unpacksort-1.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 54c831b303a01488d4d996d75a52d5f61d3104ff5714620134e5ea827b6b6fdd
MD5 35d654837f916c0660b54672b71a6efd
BLAKE2b-256 3bcc515c52e160ccd4860bee40eb8ee3ae98c51e43bc90c42bccc90ccb525390

See more details on using hashes here.

Provenance

The following attestation bundles were made for unpacksort-1.1.0-py3-none-any.whl:

Publisher: release.yml on fileworks/unpacksort

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page