Soar 0.10
0.10.3
Glob Support in Binary Maps
The source field in binary mappings now supports glob patterns, making it easier to select binaries from packages that contain multiple files:
[packages]
my-tools = {
url = "https://example.com/tools.tar.gz",
binaries = [
{ source = "bin/tool-*" }
]
}
When a glob matches multiple files, each matched file is symlinked using its original filename. The link_as field is now optional — if omitted, the symlink uses the source filename. When a glob matches a single file, link_as is used if specified.
Placeholder Support
URL and path fields now support {arch}, {os}, and {version} placeholders that are automatically substituted with system metadata:
[packages]
tool = {
url = "https://example.com/tool-{version}-{arch}-{os}.tar.gz",
version = "1.2.0"
}
Removal of update Field
The update field has been removed from packages.toml. GitHub and GitLab sources are now detected automatically from the github/gitlab fields, making the separate update configuration unnecessary.
Fallback Token Environment Variables
GitHub and GitLab token detection now supports fallback environment variables:
- GitHub:
GITHUB_TOKENorGH_TOKEN - GitLab:
GITLAB_TOKENorGL_TOKEN
This improves compatibility with the GitHub CLI (gh) and other tools that use GH_TOKEN.
Bug Fixes
- Fixed HTTP status code handling in download fallback logic
- Install script now automatically uses system-wide mode (
--system) when run as root
0.10.2
Changes
- System install path changed from
/usr/lib/soarto/opt/soarfor compatibility with atomic/immutable distros
0.10.1
Bug Fixes
- Fixed sudo privilege escalation failing to find soar binary when installed in user directories not in sudo’s
secure_path
0.10.0
Soar 0.10 brings GitHub/GitLab release support, build-from-source, install hooks, Landlock sandboxing, and system-wide installs. This release addresses some of the most requested features and lays the groundwork for more flexible package management.
Breaking Changes
Before upgrading, note the following:
Database schema change: The with_pkg_id field has been removed. After updating, run soar sync to migrate your database.
URL package versioning: The versioning scheme for URL-based packages has changed. If you have URL packages in your packages.toml, run soar apply to update their tracking information. Note that if you didn’t have pkg_id explicitly set, the package may be duplicated since the ID change is treated as a different package. You can remove the old entry manually if this happens.
System-Wide Installs
Until now, Soar only supported per-user installations. This worked well for personal setups, but made it difficult to deploy packages across multi-user systems or manage packages via configuration management tools.
Soar 0.10 adds the -S (or --system) flag for system-wide package management:
soar -S install neovim
soar -S apply
soar -S update
Soar will automatically use sudo or doas for privilege escalation. System packages are installed to /opt/soar, with configuration stored at /etc/soar/config.toml.
GitHub/GitLab as Package Sources
Previously, if a project didn’t have a package in the registry, you had to either wait for someone to add it or manually manage the download URL and version tracking yourself.
Now you can point Soar directly at any GitHub or GitLab repository:
[packages]
neovim = { github = "neovim/neovim", asset_pattern = "*linux-x86_64.appimage" }
Soar will fetch the latest release and select the asset matching your pattern. Version tracking and updates work automatically.
For rate-limited or private repositories, set GITHUB_TOKEN or GITLAB_TOKEN in your environment.
Tag Filtering
Not every release is suitable for production. You can filter releases by tag pattern:
nvim-stable = {
github = "neovim/neovim",
asset_pattern = "*linux-x86_64.appimage",
tag_pattern = "v*.*.*"
}
This ensures you only receive tagged stable releases, ignoring prereleases or nightlies.
Custom Version Detection
For cases where standard release detection doesn’t work (nightly builds, non-standard versioning), you can provide a custom version_command:
nvim-nightly = {
github = "neovim/neovim",
asset_pattern = "*linux-x86_64.appimage",
version_command = "curl -s https://api.gh.pkgforge.dev/repos/neovim/neovim/releases/tags/nightly | jq -r '.published_at, (.assets[] | select(.name | endswith(\"linux-x86_64.appimage\")) | .browser_download_url, .size)'"
}
The command should output up to three newline-separated values: version identifier, download URL, and file size.
Hooks
Some packages need post-install setup: setting permissions, running initialization scripts, or registering with system services. Previously, this had to be done manually after each install or update.
Hooks let you automate these steps:
[packages]
myapp = {
url = "https://example.com/app.tar.gz",
hooks = {
post_extract = "chmod +x bin/*",
post_install = "./setup.sh",
pre_remove = "./cleanup.sh"
}
}
Four hook points are available: post_download, post_extract, post_install, and pre_remove.
Hooks receive several environment variables:
INSTALL_DIR: the package installation directoryBIN_DIR: Soar’s bin directoryPKG_NAME,PKG_ID,PKG_VERSION: package metadata
Build From Source
Not everything ships prebuilt binaries. Some projects only distribute source tarballs, or you may need custom compile flags for your environment.
Soar can now build packages from source:
[packages]
lua = {
url = "https://www.lua.org/ftp/lua-5.4.7.tar.gz",
extract_root = "lua-5.4.7",
build = {
commands = [
"make linux -j$NPROC",
"make install INSTALL_TOP=$INSTALL_DIR"
],
dependencies = ["gcc", "make", "libreadline-dev"]
},
binaries = [
{ source = "bin/lua", link_as = "lua" },
{ source = "bin/luac", link_as = "luac" }
]
}
The $NPROC environment variable is set to your CPU core count for parallel compilation. The dependencies field documents build requirements; Soar doesn’t install these automatically.
Landlock Sandboxing
Running arbitrary build scripts and hooks from the internet is inherently risky. A malicious or buggy script could read sensitive files or modify system state.
Soar 0.10 integrates Linux’s Landlock LSM to sandbox hook and build command execution:
[packages]
untrusted-pkg = {
url = "https://example.com/pkg.tar.gz",
sandbox = {
require = true,
fs_read = ["/opt/libs"],
fs_write = ["/var/tmp"]
},
hooks = {
post_extract = "make build"
}
}
By default, sandboxed commands can:
- Read:
/usr,/lib,/bin,/etc/ssl/certs,/proc,/sys, and other standard system paths - Write: the package’s install directory and
/tmp
Additional paths can be granted via fs_read and fs_write.
Setting require = true makes Soar fail if Landlock is unavailable (older kernels or disabled). With require = false, Soar warns and proceeds unsandboxed.
Binary Symlink Configuration
Soar’s automatic binary discovery works well for simple packages, but sometimes you need more control: renaming binaries, creating multiple symlinks to the same executable, or selecting specific binaries from a package.
The entrypoint field handles simple renaming:
app = {
url = "https://example.com/app.tar.gz",
entrypoint = "bin/actual-binary-name"
}
For multiple symlinks, use binaries:
neovim = {
github = "neovim/neovim",
asset_pattern = "*.appimage",
binaries = [
{ source = "neovim", link_as = "nvim" },
{ source = "neovim", link_as = "vi" }
]
}
Note: Downloaded assets are renamed to match the package name, hence the source being neovim rather than the original filename.
GHCR Support
GitHub Container Registry images can now be used as package sources:
[packages]
tool = { url = "ghcr.io/owner/repo:v1.0.0", entrypoint = "app" }
Or install directly from the command line:
soar install ghcr.io/pkgforge/tool:latest
Full Example
Here’s a packages.toml demonstrating the new features together:
[packages]
# Registry package
curl = "*"
# GitHub release with asset pattern
ripgrep = { github = "BurntSushi/ripgrep", asset_pattern = "*x86_64*linux-musl*.gz" }
# Full build configuration
my-tool = {
url = "https://example.com/tool-2.0.0.tar.gz",
version = "2.0.0",
extract_root = "tool-2.0.0",
build = {
commands = ["make -j$NPROC", "make install PREFIX=$INSTALL_DIR"],
dependencies = ["gcc", "make"]
},
hooks = { post_install = "$INSTALL_DIR/bin/tool --setup" },
sandbox = { require = false, fs_read = ["/usr/include"] },
binaries = [
{ source = "bin/tool", link_as = "tool" },
{ source = "bin/toolctl", link_as = "toolctl" }
]
}
What Else?
soar updatenow properly tracks and updates URL-based and GitHub/GitLab packages when managed declaratively viapackages.toml. Packages installed directly withsoar installdon’t support updates for these sources, as there’s not enough context to determine how to fetch newer versions