Archive formats used by package managers, with edge cases and oddities.
4
3 commits
updated Dec 1, 2025
Archive formats used by package managers, with edge cases and oddities.
A .gem file is a tar archive containing nested compressed archives:
package.gem (tar)
├── checksums.yaml.gz
├── metadata.gz
├── data.tar.gz
├── checksums.yaml.gz.sig (if signed)
├── metadata.gz.sig (if signed)
└── data.tar.gz.sig (if signed)
metadata.gz contains the gemspec as YAML. data.tar.gz contains the actual files (lib/, bin/, etc.). checksums.yaml.gz contains SHA256 and SHA512 hashes of the other archives.
Edge cases:
Gem::Package::TarReaderReferences:
npm packages are gzipped tarballs. The tarball contains a single package/ directory with the package contents.
package-1.0.0.tgz
└── package/
├── package.json
├── index.js
└── ...
Edge cases:
tar npm package (used by npm itself) has a noPax option, but using it truncates long pathsintegrity field in package-lock.json is a Subresource Integrity stringnpm pack always names the internal directory package/ regardless of the package name@scope/name) become scope-name-version.tgz filenamesReferences:
A wheel is a ZIP archive with a specific filename format and internal structure. The .whl extension is just a renamed .zip.
Filename format: {distribution}-{version}(-{build})?-{python}-{abi}-{platform}.whl
Example: numpy-1.24.0-cp311-cp311-macosx_10_9_x86_64.whl
package-1.0.0-py3-none-any.whl (ZIP)
├── package/
│ ├── __init__.py
│ └── ...
├── package-1.0.0.dist-info/
│ ├── METADATA
│ ├── WHEEL
│ ├── RECORD
│ └── RECORD.jws (optional signature)
└── package-1.0.0.data/ (optional)
├── scripts/
├── headers/
└── ...
Edge cases:
.data/ directory holds files that go outside site-packages (scripts, headers, etc.).pyc files since they need to work across Python versionssetup.py or setup.cfgReferences:
Source distributions are gzipped tarballs with a PKG-INFO metadata file.
package-1.0.0.tar.gz
└── package-1.0.0/
├── pyproject.toml
├── PKG-INFO
├── setup.py (legacy)
└── src/
Edge cases:
pyproject.toml; legacy ones used setup.pytarfile module with 'r:gz'References:
Conda packages can contain any language runtime or library, not just Python. Two formats exist: modern .conda and legacy .tar.bz2.
.conda format (conda 4.7+):
package-1.0-py39_0.conda (ZIP, uncompressed outer container)
├── info-*.tar.zst (metadata, compressed separately)
└── pkg-*.tar.zst (package contents, compressed separately)
.tar.bz2 format (legacy):
package-1.0-py39_0.tar.bz2
├── info/
│ ├── index.json (name, version, dependencies)
│ ├── paths.json (file list with hashes)
│ ├── files (simple file list)
│ ├── LICENSE.txt
│ └── recipe/ (build recipe)
├── bin/
└── lib/
Edge cases:
.conda container is an uncompressed ZIP; inner tarballs use zstdinfo/has_prefix lists files with hardcoded build paths that need rewriting at install timeinfo/paths.json replaced separate files, has_prefix, no_link files (2016+)/ delimiter even on Windowspy39_0) encodes Python version and build numberReferences:
A JAR is a ZIP archive with a manifest file. WAR and EAR files use the same format with different conventions.
package.jar (ZIP)
├── META-INF/
│ ├── MANIFEST.MF
│ ├── *.SF (signature files)
│ └── *.DSA/.RSA/.EC (signature block files)
└── com/
└── example/
└── MyClass.class
JAR variants (same format, different contents):
artifact.jar - binary JAR with compiled .class filesartifact-sources.jar - source JAR with .java filesartifact-javadoc.jar - documentation JAR with generated HTMLMaven uses classifier in coordinates (groupId:artifactId:version:classifier) to distinguish them. All three use identical ZIP/MANIFEST.MF structure.
Edge cases:
getManifest() may return null if it isn't first/References:
A NuGet package uses the Open Packaging Conventions (OPC) format, which is also used by Office documents (.docx, .xlsx). It's a ZIP archive with specific metadata files.
Package.1.0.0.nupkg (ZIP/OPC)
├── [Content_Types].xml
├── _rels/
│ └── .rels
├── package/
│ └── services/
│ └── metadata/
│ └── core-properties/
│ └── *.psmdcp
├── Package.nuspec
└── lib/
└── net6.0/
└── Package.dll
Edge cases:
_rels/ directories, .rels files within them, and [Content_Types].xml.rels files define relationships between "parts" (files) using URIs[Content_Types].xml maps file extensions to MIME types.nuspec manifest is XML, not JSONReferences:
A .crate file is a gzipped tarball containing the package source.
package-1.0.0.crate (tar.gz)
└── package-1.0.0/
├── Cargo.toml
├── Cargo.toml.orig
├── Cargo.lock
├── .cargo_vcs_info.json
└── src/
Edge cases:
Cargo.toml.orig--exclude-lockfile is used; cargo install --locked uses it.cargo_vcs_info.json contains the git SHA and whether the worktree was dirty:
{
"git": { "sha1": "aac20b6e...", "dirty": false },
"path_in_vcs": ""
}
path_in_vcs is set for packages in subdirectories of a repositoryReferences:
Go modules are ZIP archives with strict path requirements.
example.com/[email protected]
└── example.com/[email protected]/
├── go.mod
└── ...
Edge cases:
<module>@<version>/go.mod must be exactly go.mod, not Go.mod or GO.MODCON, NUL, etc.) or end with tilde+digits (EXAMPL~1)README.md and readme.md, it cannot be a Go module! prefix in the download cache (e.g., GitHub.com becomes !github.com)References:
Composer packages are distributed as ZIP archives downloaded directly from source (usually GitHub/GitLab archives) or from Packagist.
Edge cases:
composer archive creates a ZIP of your projectvendor/ directory and vendor/autoload.php are generated locally, not distributedReferences:
Hex packages are uncompressed tar archives containing metadata and a nested compressed tarball.
package-1.0.0.tar
├── VERSION (ASCII: "3")
├── CHECKSUM (SHA256 hex uppercase)
├── metadata.config (Erlang term format)
└── contents.tar.gz (actual package files)
Edge cases:
file:consult/1 format, not JSON or YAML; must be parsed with Erlang/Elixir term parserReferences:
Hackage packages are standard gzipped tarballs produced by cabal sdist.
package-1.0.0.tar.gz
└── package-1.0.0/
├── package.cabal
├── LICENSE
├── Setup.hs (optional)
└── src/
Edge cases:
package-version exactlycabal sdist fails without it.cabal file are included; add READMEs to extra-source-files.cabal file contains all metadata (no separate metadata file)cabal sdist does validation; manually created tarballs may be rejectedReferences:
OPAM doesn't define its own archive format; it downloads source archives from URLs specified in opam files.
opam repository structure:
packages/
└── package-name/
└── package-name.version/
├── opam (package definition)
├── files/ (patches, additional files)
└── url (legacy, now in opam file)
Edge cases:
opam file is a custom DSL, not YAML/JSONurl section: url { src: "..." checksum: "sha256=..." }files/ directory contents are overlaid on the source after extractionReferences:
A .rock file is a ZIP archive containing a rockspec and source/binary files.
myrock-1.0-1.src.rock (ZIP)
├── myrock-1.0-1.rockspec
└── (source files or directories)
myrock-1.0-1.linux-x86_64.rock (ZIP)
├── myrock-1.0-1.rockspec
├── lua/
│ └── myrock.lua
└── lib/
└── myrock.so
Filename format: {name}-{version}-{revision}.{platform}.rock
Edge cases:
.src.rock; binary rocks use platform like .linux-x86_64.rock.all.rock (no native code, works everywhere).rockspec file is valid Lua code (not YAML/JSON), though without access to Lua functions1.0-1 means version 1.0, rockspec revision 1)zip to be installed for creating rocksReferences:
CPAN distributions are gzipped tarballs with specific metadata files.
Distribution-Name-1.0.tar.gz
└── Distribution-Name-1.0/
├── META.json (v2+ metadata)
├── META.yml (v1.4 metadata, legacy)
├── MANIFEST (file listing)
├── Makefile.PL (or Build.PL)
└── lib/
Edge cases:
- (Distribution-Name), module names use :: (Distribution::Name)References:
Pub packages are distributed via pub.dev; the package format is a standard directory structure uploaded as a tarball.
package/
├── pubspec.yaml
├── lib/
│ └── package.dart
├── bin/ (optional)
└── test/ (optional)
Edge cases:
name and version fieldsdart pub publish performs secret detection and refuses to publish potential credential leaksReferences:
Conan uses internal .tgz archives for storage and transfer, but these are not meant for direct manipulation.
Recipe files (conan_export.tgz):
├── conanfile.py
├── conandata.yml (optional)
└── conanmanifest.txt
Binary package (conan_package.tgz):
├── conaninfo.txt
├── conanmanifest.txt
└── (compiled files)
Edge cases:
name/version@user/channel plus a package ID hashReferences:
A .deb file is an ar archive containing exactly three members in order:
package_1.0-1_amd64.deb (ar archive)
├── debian-binary (text: "2.0\n")
├── control.tar.xz (or .gz, .zst)
└── data.tar.xz (or .gz, .zst, .bz2, .lzma)
control.tar contains:
control (required) - package metadatamd5sums - checksums of installed filesconffiles - list of configuration filespreinst, postinst, prerm, postrm - maintainer scriptstriggers, shlibs, symbols - optional metadataEdge cases:
!<arch> and uses the common ar format (no long filename extensions)References:
An RPM file has four sections: lead, signature, header, and payload.
package-1.0-1.x86_64.rpm
├── Lead (96 bytes magic + info)
├── Signature (digital signatures)
├── Header (all metadata)
└── Payload (compressed cpio archive)
The payload is a gzip-compressed cpio archive (SVR4/newc format with CRC).
Edge cases:
07070X magic) for larger filesReferences:
An Alpine package is a concatenation of three gzipped tar segments:
package-1.0-r0.apk (concatenated tar.gz segments)
├── signature.tar.gz (optional, contains .SIGN.*)
├── control.tar.gz (contains .PKGINFO, etc.)
└── data.tar.gz (package contents)
Edge cases:
. for historical reasons (.PKGINFO, .pre-install, etc.).SIGN.<type>.<keyname> (type is DSA or RSA)DESCRIPTION and APKINDEX filesP: for package name, V: for version, etc.)Q1 prefix (not hex)References:
A FreeBSD package is a tar archive (compressed with xz by default) with JSON manifests.
package-1.0.pkg (tar.xz)
├── +COMPACT_MANIFEST
├── +MANIFEST
└── usr/local/...
Edge cases:
+ and must come firstReferences:
A snap is a SquashFS filesystem image.
package_1.0_amd64.snap (SquashFS image)
├── meta/
│ └── snap.yaml
├── bin/
├── lib/
└── usr/
Edge cases:
snap.yaml in meta/ contains package metadataunsquashfs, not standard archive toolsReferences:
Flatpak uses OSTree, a git-like content-addressed storage system for binaries.
Edge cases:
<app|runtime>/<id>/<arch>/<version> naming (e.g., app/org.gnome.gedit/x86_64/stable)References:
Bottles are gzipped tarballs with formula-specific naming.
Filename format: <formula>-<version>.<os_version>.bottle[.<rebuild>].tar.gz
Examples:
wget-1.21.3.arm64_monterey.bottle.tar.gzopenssl-3.0.7.ventura.bottle.1.tar.gz (rebuild 1)Edge cases:
brew bottle sometimes generates two hyphens which must be fixed<formula>/<version>/.brew/<formula>.rb544b4d73...--wget-1.21.3.monterey.bottle.tar.gzReferences:
Pacman packages are zstd-compressed tarballs with metadata files.
package-1.0-1-x86_64.pkg.tar.zst
├── .PKGINFO (package metadata)
├── .BUILDINFO (build reproducibility info)
├── .MTREE (file hashes and permissions)
├── .INSTALL (optional install scripts)
├── .Changelog (optional)
└── usr/
└── bin/
Edge cases:
. and must come first-1 in 1.0-1 is the pkgrel (packaging revision), separate from upstream versionReferences:
Gentoo has two binary package formats: legacy XPAK (.tbz2) and modern GPKG (.gpkg.tar).
XPAK format (.tbz2):
package-1.0.tbz2
└── [bzip2 tarball] + [XPAK metadata trailer]
GPKG format (.gpkg.tar) (default since Portage 3.0.36):
package-1.0.gpkg.tar
├── gpkg-1 (empty, format marker)
├── Manifest (checksums)
├── package-1.0/
│ ├── metadata.tar{.xz} (ebuild, environment)
│ └── image.tar{.xz} (installed files)
└── *.sig (optional OpenPGP signatures)
Edge cases:
qtbz2, qxpak)BINPKG_FORMAT=gpkg or BINPKG_FORMAT=xpak in make.conf to choose formatReferences:
XBPS packages are compressed archives with plist metadata.
package-1.0_1.x86_64.xbps
├── props.plist (package properties)
├── files.plist (file list with hashes)
├── INSTALL (optional install script)
├── REMOVE (optional remove script)
└── (package files)
Edge cases:
/var/db/xbps/pkgdb-0.38.plist.xbps.sig files contain RSA signaturesReferences:
NAR is a custom archive format designed for deterministic, reproducible builds. It is not based on tar or zip.
Edge cases:
nix-archive-1 header, then recursive file/directory/symlink serializationnix-store --dump and nix-store --restore to create/extractReferences:
OCI images are layered tarballs with JSON manifests, used by Docker, Podman, and other container runtimes.
image/
├── index.json (points to manifests)
├── oci-layout ({"imageLayoutVersion": "1.0.0"})
├── blobs/
│ └── sha256/
│ ├── abc123... (manifest.json)
│ ├── def456... (config.json)
│ ├── 789abc... (layer.tar.gz)
│ └── ...
Manifest points to config and layers:
{
"schemaVersion": 2,
"config": { "digest": "sha256:...", "mediaType": "...+json" },
"layers": [
{ "digest": "sha256:...", "mediaType": "...tar+gzip" }
]
}
Edge cases:
.wh.<filename>) mark deleted files from previous layers.wh..wh..opq) hide all files in a directory from lower layersapplication/vnd.oci.image.layer.v1.tar, .tar+gzip, .tar+zstdReferences:
Helm charts are gzipped tarballs with a specific directory structure for Kubernetes deployments.
mychart-1.0.0.tgz
└── mychart/
├── Chart.yaml (chart metadata, required)
├── values.yaml (default configuration)
├── values.schema.json (optional JSON Schema)
├── charts/ (dependency charts)
├── crds/ (Custom Resource Definitions)
└── templates/
├── deployment.yaml
├── service.yaml
└── _helpers.tpl
Edge cases:
--set or -f values.yaml_*.tpl are template helpers, not rendered directly.yaml extension; .tpl for helpers that produce no outputReferences:
Terraform providers are ZIP archives containing a single Go binary and metadata.
terraform-provider-aws_4.0.0_linux_amd64.zip
└── terraform-provider-aws_v4.0.0_x5
Registry release artifacts:
├── terraform-provider-NAME_VERSION_OS_ARCH.zip (multiple)
├── terraform-provider-NAME_VERSION_manifest.json
├── terraform-provider-NAME_VERSION_SHA256SUMS
└── terraform-provider-NAME_VERSION_SHA256SUMS.sig
Edge cases:
terraform-provider-{NAME}_v{VERSION} (note the v prefix in binary, not in zip)terraform-provider-{NAME}_{VERSION}_{OS}_{ARCH}.zip{"version": 1, "metadata": {"protocol_versions": ["6.0"]}}HOSTNAME/NAMESPACE/TYPE/VERSION/TARGET/References:
Nested archives: gem (tar containing gzipped tars), deb (ar containing tars), Hex (tar containing tar.gz)
Concatenated archives: Alpine apk (multiple tar.gz segments concatenated)
Trailer metadata: Gentoo XPAK appends metadata after the tarball, not inside it
Not actually archives: Snap (SquashFS image), Flatpak (OSTree repo), Nix NAR (custom format)
ZIP-based but not plain ZIP: NuGet (OPC with relationship metadata), wheel (with required RECORD file), LuaRocks .rock
Order matters: deb members, FreeBSD +MANIFEST files, JAR MANIFEST.MF position, Pacman metadata files
Case sensitivity: Go modules reject case-only filename differences; npm/pip don't care
Hash algorithm transitions: npm SHA1 to SHA512, gem MD5 to SHA256/512
Filename encoding: tar variants handle UTF-8 differently; pax standardizes it
Compression format changes over time: Pacman (gz -> xz -> zstd), deb (gz -> xz -> zstd options)
Config files that are code: LuaRocks .rockspec (Lua), Conan conanfile.py (Python), Helm templates (Go templates)
Erlang-specific formats: Hex metadata.config uses Erlang term format, not JSON/YAML
Layered formats: OCI images layer tarballs with whiteout files for deletions
Determinism requirements: Nix NAR strips timestamps/ownership, Go modules forbid case collisions
Most package managers build on tar or zip, but the variants matter.
The tar format has several incompatible variants with different limitations:
v7 (Original)
UStar (POSIX.1-1988)
GNU tar
PAX (POSIX.1-2001)
npm uses pax headers for long filenames. Most modern package managers default to or support pax.
References:
Archive formats used by package managers, with edge cases and oddities.
4
3 commits
updated Dec 1, 2025
Archive formats used by package managers, with edge cases and oddities.
A .gem file is a tar archive containing nested compressed archives:
package.gem (tar)
├── checksums.yaml.gz
├── metadata.gz
├── data.tar.gz
├── checksums.yaml.gz.sig (if signed)
├── metadata.gz.sig (if signed)
└── data.tar.gz.sig (if signed)
metadata.gz contains the gemspec as YAML. data.tar.gz contains the actual files (lib/, bin/, etc.). checksums.yaml.gz contains SHA256 and SHA512 hashes of the other archives.
Edge cases:
Gem::Package::TarReaderReferences:
npm packages are gzipped tarballs. The tarball contains a single package/ directory with the package contents.
package-1.0.0.tgz
└── package/
├── package.json
├── index.js
└── ...
Edge cases:
tar npm package (used by npm itself) has a noPax option, but using it truncates long pathsintegrity field in package-lock.json is a Subresource Integrity stringnpm pack always names the internal directory package/ regardless of the package name@scope/name) become scope-name-version.tgz filenamesReferences:
A wheel is a ZIP archive with a specific filename format and internal structure. The .whl extension is just a renamed .zip.
Filename format: {distribution}-{version}(-{build})?-{python}-{abi}-{platform}.whl
Example: numpy-1.24.0-cp311-cp311-macosx_10_9_x86_64.whl
package-1.0.0-py3-none-any.whl (ZIP)
├── package/
│ ├── __init__.py
│ └── ...
├── package-1.0.0.dist-info/
│ ├── METADATA
│ ├── WHEEL
│ ├── RECORD
│ └── RECORD.jws (optional signature)
└── package-1.0.0.data/ (optional)
├── scripts/
├── headers/
└── ...
Edge cases:
.data/ directory holds files that go outside site-packages (scripts, headers, etc.).pyc files since they need to work across Python versionssetup.py or setup.cfgReferences:
Source distributions are gzipped tarballs with a PKG-INFO metadata file.
package-1.0.0.tar.gz
└── package-1.0.0/
├── pyproject.toml
├── PKG-INFO
├── setup.py (legacy)
└── src/
Edge cases:
pyproject.toml; legacy ones used setup.pytarfile module with 'r:gz'References:
Conda packages can contain any language runtime or library, not just Python. Two formats exist: modern .conda and legacy .tar.bz2.
.conda format (conda 4.7+):
package-1.0-py39_0.conda (ZIP, uncompressed outer container)
├── info-*.tar.zst (metadata, compressed separately)
└── pkg-*.tar.zst (package contents, compressed separately)
.tar.bz2 format (legacy):
package-1.0-py39_0.tar.bz2
├── info/
│ ├── index.json (name, version, dependencies)
│ ├── paths.json (file list with hashes)
│ ├── files (simple file list)
│ ├── LICENSE.txt
│ └── recipe/ (build recipe)
├── bin/
└── lib/
Edge cases:
.conda container is an uncompressed ZIP; inner tarballs use zstdinfo/has_prefix lists files with hardcoded build paths that need rewriting at install timeinfo/paths.json replaced separate files, has_prefix, no_link files (2016+)/ delimiter even on Windowspy39_0) encodes Python version and build numberReferences:
A JAR is a ZIP archive with a manifest file. WAR and EAR files use the same format with different conventions.
package.jar (ZIP)
├── META-INF/
│ ├── MANIFEST.MF
│ ├── *.SF (signature files)
│ └── *.DSA/.RSA/.EC (signature block files)
└── com/
└── example/
└── MyClass.class
JAR variants (same format, different contents):
artifact.jar - binary JAR with compiled .class filesartifact-sources.jar - source JAR with .java filesartifact-javadoc.jar - documentation JAR with generated HTMLMaven uses classifier in coordinates (groupId:artifactId:version:classifier) to distinguish them. All three use identical ZIP/MANIFEST.MF structure.
Edge cases:
getManifest() may return null if it isn't first/References:
A NuGet package uses the Open Packaging Conventions (OPC) format, which is also used by Office documents (.docx, .xlsx). It's a ZIP archive with specific metadata files.
Package.1.0.0.nupkg (ZIP/OPC)
├── [Content_Types].xml
├── _rels/
│ └── .rels
├── package/
│ └── services/
│ └── metadata/
│ └── core-properties/
│ └── *.psmdcp
├── Package.nuspec
└── lib/
└── net6.0/
└── Package.dll
Edge cases:
_rels/ directories, .rels files within them, and [Content_Types].xml.rels files define relationships between "parts" (files) using URIs[Content_Types].xml maps file extensions to MIME types.nuspec manifest is XML, not JSONReferences:
A .crate file is a gzipped tarball containing the package source.
package-1.0.0.crate (tar.gz)
└── package-1.0.0/
├── Cargo.toml
├── Cargo.toml.orig
├── Cargo.lock
├── .cargo_vcs_info.json
└── src/
Edge cases:
Cargo.toml.orig--exclude-lockfile is used; cargo install --locked uses it.cargo_vcs_info.json contains the git SHA and whether the worktree was dirty:
{
"git": { "sha1": "aac20b6e...", "dirty": false },
"path_in_vcs": ""
}
path_in_vcs is set for packages in subdirectories of a repositoryReferences:
Go modules are ZIP archives with strict path requirements.
example.com/[email protected]
└── example.com/[email protected]/
├── go.mod
└── ...
Edge cases:
<module>@<version>/go.mod must be exactly go.mod, not Go.mod or GO.MODCON, NUL, etc.) or end with tilde+digits (EXAMPL~1)README.md and readme.md, it cannot be a Go module! prefix in the download cache (e.g., GitHub.com becomes !github.com)References:
Composer packages are distributed as ZIP archives downloaded directly from source (usually GitHub/GitLab archives) or from Packagist.
Edge cases:
composer archive creates a ZIP of your projectvendor/ directory and vendor/autoload.php are generated locally, not distributedReferences:
Hex packages are uncompressed tar archives containing metadata and a nested compressed tarball.
package-1.0.0.tar
├── VERSION (ASCII: "3")
├── CHECKSUM (SHA256 hex uppercase)
├── metadata.config (Erlang term format)
└── contents.tar.gz (actual package files)
Edge cases:
file:consult/1 format, not JSON or YAML; must be parsed with Erlang/Elixir term parserReferences:
Hackage packages are standard gzipped tarballs produced by cabal sdist.
package-1.0.0.tar.gz
└── package-1.0.0/
├── package.cabal
├── LICENSE
├── Setup.hs (optional)
└── src/
Edge cases:
package-version exactlycabal sdist fails without it.cabal file are included; add READMEs to extra-source-files.cabal file contains all metadata (no separate metadata file)cabal sdist does validation; manually created tarballs may be rejectedReferences:
OPAM doesn't define its own archive format; it downloads source archives from URLs specified in opam files.
opam repository structure:
packages/
└── package-name/
└── package-name.version/
├── opam (package definition)
├── files/ (patches, additional files)
└── url (legacy, now in opam file)
Edge cases:
opam file is a custom DSL, not YAML/JSONurl section: url { src: "..." checksum: "sha256=..." }files/ directory contents are overlaid on the source after extractionReferences:
A .rock file is a ZIP archive containing a rockspec and source/binary files.
myrock-1.0-1.src.rock (ZIP)
├── myrock-1.0-1.rockspec
└── (source files or directories)
myrock-1.0-1.linux-x86_64.rock (ZIP)
├── myrock-1.0-1.rockspec
├── lua/
│ └── myrock.lua
└── lib/
└── myrock.so
Filename format: {name}-{version}-{revision}.{platform}.rock
Edge cases:
.src.rock; binary rocks use platform like .linux-x86_64.rock.all.rock (no native code, works everywhere).rockspec file is valid Lua code (not YAML/JSON), though without access to Lua functions1.0-1 means version 1.0, rockspec revision 1)zip to be installed for creating rocksReferences:
CPAN distributions are gzipped tarballs with specific metadata files.
Distribution-Name-1.0.tar.gz
└── Distribution-Name-1.0/
├── META.json (v2+ metadata)
├── META.yml (v1.4 metadata, legacy)
├── MANIFEST (file listing)
├── Makefile.PL (or Build.PL)
└── lib/
Edge cases:
- (Distribution-Name), module names use :: (Distribution::Name)References:
Pub packages are distributed via pub.dev; the package format is a standard directory structure uploaded as a tarball.
package/
├── pubspec.yaml
├── lib/
│ └── package.dart
├── bin/ (optional)
└── test/ (optional)
Edge cases:
name and version fieldsdart pub publish performs secret detection and refuses to publish potential credential leaksReferences:
Conan uses internal .tgz archives for storage and transfer, but these are not meant for direct manipulation.
Recipe files (conan_export.tgz):
├── conanfile.py
├── conandata.yml (optional)
└── conanmanifest.txt
Binary package (conan_package.tgz):
├── conaninfo.txt
├── conanmanifest.txt
└── (compiled files)
Edge cases:
name/version@user/channel plus a package ID hashReferences:
A .deb file is an ar archive containing exactly three members in order:
package_1.0-1_amd64.deb (ar archive)
├── debian-binary (text: "2.0\n")
├── control.tar.xz (or .gz, .zst)
└── data.tar.xz (or .gz, .zst, .bz2, .lzma)
control.tar contains:
control (required) - package metadatamd5sums - checksums of installed filesconffiles - list of configuration filespreinst, postinst, prerm, postrm - maintainer scriptstriggers, shlibs, symbols - optional metadataEdge cases:
!<arch> and uses the common ar format (no long filename extensions)References:
An RPM file has four sections: lead, signature, header, and payload.
package-1.0-1.x86_64.rpm
├── Lead (96 bytes magic + info)
├── Signature (digital signatures)
├── Header (all metadata)
└── Payload (compressed cpio archive)
The payload is a gzip-compressed cpio archive (SVR4/newc format with CRC).
Edge cases:
07070X magic) for larger filesReferences:
An Alpine package is a concatenation of three gzipped tar segments:
package-1.0-r0.apk (concatenated tar.gz segments)
├── signature.tar.gz (optional, contains .SIGN.*)
├── control.tar.gz (contains .PKGINFO, etc.)
└── data.tar.gz (package contents)
Edge cases:
. for historical reasons (.PKGINFO, .pre-install, etc.).SIGN.<type>.<keyname> (type is DSA or RSA)DESCRIPTION and APKINDEX filesP: for package name, V: for version, etc.)Q1 prefix (not hex)References:
A FreeBSD package is a tar archive (compressed with xz by default) with JSON manifests.
package-1.0.pkg (tar.xz)
├── +COMPACT_MANIFEST
├── +MANIFEST
└── usr/local/...
Edge cases:
+ and must come firstReferences:
A snap is a SquashFS filesystem image.
package_1.0_amd64.snap (SquashFS image)
├── meta/
│ └── snap.yaml
├── bin/
├── lib/
└── usr/
Edge cases:
snap.yaml in meta/ contains package metadataunsquashfs, not standard archive toolsReferences:
Flatpak uses OSTree, a git-like content-addressed storage system for binaries.
Edge cases:
<app|runtime>/<id>/<arch>/<version> naming (e.g., app/org.gnome.gedit/x86_64/stable)References:
Bottles are gzipped tarballs with formula-specific naming.
Filename format: <formula>-<version>.<os_version>.bottle[.<rebuild>].tar.gz
Examples:
wget-1.21.3.arm64_monterey.bottle.tar.gzopenssl-3.0.7.ventura.bottle.1.tar.gz (rebuild 1)Edge cases:
brew bottle sometimes generates two hyphens which must be fixed<formula>/<version>/.brew/<formula>.rb544b4d73...--wget-1.21.3.monterey.bottle.tar.gzReferences:
Pacman packages are zstd-compressed tarballs with metadata files.
package-1.0-1-x86_64.pkg.tar.zst
├── .PKGINFO (package metadata)
├── .BUILDINFO (build reproducibility info)
├── .MTREE (file hashes and permissions)
├── .INSTALL (optional install scripts)
├── .Changelog (optional)
└── usr/
└── bin/
Edge cases:
. and must come first-1 in 1.0-1 is the pkgrel (packaging revision), separate from upstream versionReferences:
Gentoo has two binary package formats: legacy XPAK (.tbz2) and modern GPKG (.gpkg.tar).
XPAK format (.tbz2):
package-1.0.tbz2
└── [bzip2 tarball] + [XPAK metadata trailer]
GPKG format (.gpkg.tar) (default since Portage 3.0.36):
package-1.0.gpkg.tar
├── gpkg-1 (empty, format marker)
├── Manifest (checksums)
├── package-1.0/
│ ├── metadata.tar{.xz} (ebuild, environment)
│ └── image.tar{.xz} (installed files)
└── *.sig (optional OpenPGP signatures)
Edge cases:
qtbz2, qxpak)BINPKG_FORMAT=gpkg or BINPKG_FORMAT=xpak in make.conf to choose formatReferences:
XBPS packages are compressed archives with plist metadata.
package-1.0_1.x86_64.xbps
├── props.plist (package properties)
├── files.plist (file list with hashes)
├── INSTALL (optional install script)
├── REMOVE (optional remove script)
└── (package files)
Edge cases:
/var/db/xbps/pkgdb-0.38.plist.xbps.sig files contain RSA signaturesReferences:
NAR is a custom archive format designed for deterministic, reproducible builds. It is not based on tar or zip.
Edge cases:
nix-archive-1 header, then recursive file/directory/symlink serializationnix-store --dump and nix-store --restore to create/extractReferences:
OCI images are layered tarballs with JSON manifests, used by Docker, Podman, and other container runtimes.
image/
├── index.json (points to manifests)
├── oci-layout ({"imageLayoutVersion": "1.0.0"})
├── blobs/
│ └── sha256/
│ ├── abc123... (manifest.json)
│ ├── def456... (config.json)
│ ├── 789abc... (layer.tar.gz)
│ └── ...
Manifest points to config and layers:
{
"schemaVersion": 2,
"config": { "digest": "sha256:...", "mediaType": "...+json" },
"layers": [
{ "digest": "sha256:...", "mediaType": "...tar+gzip" }
]
}
Edge cases:
.wh.<filename>) mark deleted files from previous layers.wh..wh..opq) hide all files in a directory from lower layersapplication/vnd.oci.image.layer.v1.tar, .tar+gzip, .tar+zstdReferences:
Helm charts are gzipped tarballs with a specific directory structure for Kubernetes deployments.
mychart-1.0.0.tgz
└── mychart/
├── Chart.yaml (chart metadata, required)
├── values.yaml (default configuration)
├── values.schema.json (optional JSON Schema)
├── charts/ (dependency charts)
├── crds/ (Custom Resource Definitions)
└── templates/
├── deployment.yaml
├── service.yaml
└── _helpers.tpl
Edge cases:
--set or -f values.yaml_*.tpl are template helpers, not rendered directly.yaml extension; .tpl for helpers that produce no outputReferences:
Terraform providers are ZIP archives containing a single Go binary and metadata.
terraform-provider-aws_4.0.0_linux_amd64.zip
└── terraform-provider-aws_v4.0.0_x5
Registry release artifacts:
├── terraform-provider-NAME_VERSION_OS_ARCH.zip (multiple)
├── terraform-provider-NAME_VERSION_manifest.json
├── terraform-provider-NAME_VERSION_SHA256SUMS
└── terraform-provider-NAME_VERSION_SHA256SUMS.sig
Edge cases:
terraform-provider-{NAME}_v{VERSION} (note the v prefix in binary, not in zip)terraform-provider-{NAME}_{VERSION}_{OS}_{ARCH}.zip{"version": 1, "metadata": {"protocol_versions": ["6.0"]}}HOSTNAME/NAMESPACE/TYPE/VERSION/TARGET/References:
Nested archives: gem (tar containing gzipped tars), deb (ar containing tars), Hex (tar containing tar.gz)
Concatenated archives: Alpine apk (multiple tar.gz segments concatenated)
Trailer metadata: Gentoo XPAK appends metadata after the tarball, not inside it
Not actually archives: Snap (SquashFS image), Flatpak (OSTree repo), Nix NAR (custom format)
ZIP-based but not plain ZIP: NuGet (OPC with relationship metadata), wheel (with required RECORD file), LuaRocks .rock
Order matters: deb members, FreeBSD +MANIFEST files, JAR MANIFEST.MF position, Pacman metadata files
Case sensitivity: Go modules reject case-only filename differences; npm/pip don't care
Hash algorithm transitions: npm SHA1 to SHA512, gem MD5 to SHA256/512
Filename encoding: tar variants handle UTF-8 differently; pax standardizes it
Compression format changes over time: Pacman (gz -> xz -> zstd), deb (gz -> xz -> zstd options)
Config files that are code: LuaRocks .rockspec (Lua), Conan conanfile.py (Python), Helm templates (Go templates)
Erlang-specific formats: Hex metadata.config uses Erlang term format, not JSON/YAML
Layered formats: OCI images layer tarballs with whiteout files for deletions
Determinism requirements: Nix NAR strips timestamps/ownership, Go modules forbid case collisions
Most package managers build on tar or zip, but the variants matter.
The tar format has several incompatible variants with different limitations:
v7 (Original)
UStar (POSIX.1-1988)
GNU tar
PAX (POSIX.1-2001)
npm uses pax headers for long filenames. Most modern package managers default to or support pax.
References: