- Shell 91.5%
- Awk 6.7%
- Makefile 1.8%
| .forgejo/workflows | ||
| bin | ||
| lib/brut-pack | ||
| libexec | ||
| test | ||
| vendor | ||
| .gitignore | ||
| .program.env.sh | ||
| CHANGELOG.md | ||
| LICENSE.md | ||
| Makefile | ||
| README.md | ||
brut-pack
brut-pack bundles your source-based Unix program into a single-file, plain-text portable executable for deployment or distribution.
Jump to: Installation | Usage | Contributing | License
Overview
Source-based programs written in shell, awk, and sed can be organized into multiple files using the conventional Unix filesystem layout:
bin/— your program’s main executableslib/— shared library files and scriptslibexec/— subcommands and helper executablesshare/— documentation, templates, and other plain-text assets
When a program conforms to these conventions, it can be trivially packaged and installed by any Unix package manager.
That’s great when you intend to use a program system-wide. Sometimes, though, you want to operate outside the jurisdiction of a package manager. For example, you might want to “vendor” a specific version of a program directly into a source code repository so it can be used to bootstrap another process. Or perhaps you’d just like to be able to copy a program around from one host to another as a single file.
These are the cases brut-pack is designed for. Run brut-pack encode from your program’s root directory to produce a bundle on standard output:
$ brut-pack encode >myprog
$ chmod +x myprog
$ ./myprog
The bundle is a plain-text POSIX shell script that embeds your program’s files as inline data, similar in format to a shar archive. The first time it’s run, the bundle unpacks its files into a private, content-addressed directory in temporary storage. Then it immediately execs the corresponding executable in bin/. Subsequent runs reuse the previously extracted files as a cache with very little performance overhead. Because this cache lives on volatile temporary storage, old programs are garbage-collected naturally by the operating system at boot.
Portability
brut-pack is written in POSIX shell and awk, targeting the POSIX.1-2024 standard. Bundles produced by brut-pack have no additional dependencies; brut-pack itself needs only a working sha256sum command, present on nearly all contemporary Unix systems.
We test brut-pack using Brat with continuous integration on the following platforms:
| sh | awk | |
|---|---|---|
| Alpine Linux | busybox ash | busybox awk |
| Debian Linux | dash | mawk |
| Fedora Linux | Bash | gawk |
| FreeBSD | FreeBSD ash | nawk |
| macOS | Bash (3.2) | nawk |
Installation
brut-pack runs entirely from source and has no build step or dependencies to install.
Installing brut-pack Globally
Download and extract the latest release archive and symlink bin/brut-pack into your PATH. For example, to install brut-pack in /usr/local:
# curl -sL https://codeberg.org/sstephenson/brut-pack/archive/latest.tar.gz | tar -C /usr/local -xzf -
# ln -s /usr/local/brut-pack/bin/brut-pack /usr/local/bin/brut-pack
Or if you prefer a per-user installation (assuming $HOME/.local/bin is in your PATH):
$ curl -sL https://codeberg.org/sstephenson/brut-pack/archive/latest.tar.gz | tar -C ~/.local -xzf -
$ ln -s ~/.local/brut-pack/bin/brut-pack ~/.local/bin/brut-pack
Vendoring brut-pack in Your Project
Download the latest brut-packed bundle from the package registry and commit it directly to your repository:
$ mkdir -p vendor
$ curl -sL https://codeberg.org/api/packages/sstephenson/generic/brut-pack/latest/brut-pack >vendor/brut-pack
$ chmod +x vendor/brut-pack
$ vendor/brut-pack encode >dist/myprog
Usage
Run brut-pack encode from your program’s root directory to produce a bundle on standard output.
Note: Your program must have at least one executable file in bin/.
Note: At this time, only plain-text files are supported in brut-pack bundles.
Saving Your Bundle
Every bundle produced by brut-pack uses its own filename to determine which bundled executable to run.
Redirect the output of brut-pack encode to a file with the same name as an executable in bin/. Then use chmod +x to set the bundle’s execute bit. (If your program has more than one executable in bin/, you can copy or link the bundle once for every corresponding file.)
For example, if your program has an executable bin/myprog, you should name your bundle myprog:
$ brut-pack encode >myprog
$ chmod +x myprog
$ ./myprog
Listing Files to be Bundled
Run brut-pack list to show the program files that will be included in the bundle: executables, libraries, regular files, and symlinks in bin/, lib/, libexec/, and share/, plus any files in the root directory with “license” in the name. Any files whose names begin with a dot are excluded automatically from the bundle.
Bundling Symbolic Links
Your bundle can include symbolic links as long as the paths they point to are relative, not absolute, and do not traverse outside the bundle’s root directory. This ensures that your bundle is relocatable to arbitrary locations on the filesystem.
For example, you can have a symbolic link libexec/myprog-x that points to the relative path ../bin/myprog. You cannot have symbolic links that point to /dev/zero or ../../../../../etc/passwd.
Specifying Program Metadata
If a .program.env.sh file is present in the root directory, brut-pack sources it to embed a metadata comment at the top of the bundle. The file should be a shell script that sets one or more of the following variables:
| Variable | Description |
|---|---|
PROGRAM_NAME |
Program name |
PROGRAM_VERSION |
Version string |
PROGRAM_DESCRIPTION |
One-line description |
PROGRAM_AUTHOR |
Author name and email |
PROGRAM_WEB_URL |
Project URL (preferred over PROGRAM_GIT_URL) |
PROGRAM_GIT_URL |
Git repository URL |
Additionally, if your program directory is a Git repository, brut-pack will append the short hash of the latest commit from git describe in parentheses following the version string.
Verifying Bundle Integrity
Run brut-pack verify <FILE> to verify that a bundle’s embedded hash matches the hash of the files it contains.
To print the hash embedded in a bundle, use brut-pack hash <FILE>.
Adjusting Runtime Behavior
You can set the following environment variables when running a bundle to override the default extraction and execution behavior.
| Variable | Description | Defaults to… |
|---|---|---|
BRUT_PACK_NAME |
Name of the executable in bin/ to run |
The basename of the bundle file |
BRUT_PACK_PREFIX |
Root directory for the extraction cache | $XDG_RUNTIME_DIR, $TMPDIR, or /tmp |
Contributing
brut-pack is hosted on Codeberg: https://codeberg.org/sstephenson/brut-pack
We welcome issues and tested pull requests from human contributors. However, before submitting a large pull request, or one that changes behavior that is not a bug, we ask that you please open an issue first so we can discuss whether it is a good fit for the project.
About the Test Suite
brut-pack’s tests live in the test/ directory; the test/*.brat files together comprise its test suite. The tests are written using Brat, a parallel TAP testing harness. Run them with:
$ vendor/brat -j 8 test/*.brat
Reporting Issues
brut-pack is portable software and compatibility is a moving target. When reporting issues, please be sure to include information about your operating system, including its release version, and the versions and lineage of the sh and awk commands.
Code Conventions
When contributing changes to brut-pack, please respect the conventions of existing code in lib/brut-pack/ and libexec/.
Shell should be written with set -eu and careful consideration of what is specified by POSIX. See the Shell Command Language specification for more details.
Similarly, awk code should be written in the subset specified by POSIX.
License
brut-pack is free software, distributable under the terms of the MIT + Trans Rights License. See LICENSE.md for details.
© 2026 Sam Stephenson. Handwritten in Mexico City.