A modern, atomic package manager for macOS ARM64 with rollback capabilities and hermetic builds.
This project is in the early stages of development. It is not yet recommended for production use. The API, package format, and internal architecture are still subject to change. There is no public package repository yet.
- ✅ Working:
install
,uninstall
,rollback
,history
,list
,vulndb update
,check-health
- 🚧 In Progress:
draft
andbuild
(functional but incomplete) ⚠️ Untested:update
,upgrade
,info
,search
,reposync
,cleanup
,audit
,self-update
- 🔄 Atomic Updates - All package operations are atomic with instant rollback
- 📦 Content-Addressed Storage - Deduplication via BLAKE3 hashing
- 🏗️ Hermetic Builds - Reproducible builds in isolated environments
- 🔐 Security First - Minisign signatures, SBOM generation, CVE scanning
- 🚀 Fast & Parallel - Concurrent downloads and installations
- 🎯 Single Prefix - Clean design with everything in
/opt/pm/live/
- 🐍 Python-Style Versions - Familiar version constraints (
>=1.2.0,<2.0.0
) - 📝 YAML Recipes - Declarative, staged build definitions
- macOS with Apple Silicon
- Rust 1.86.0 or later
- SQLite 3.x
- sudo access for
/opt/pm
directory
# Clone the repository
git clone https://github.com/yourusername/sps2.git
cd sps2
# Build the project
cargo build --release
# Run setup script (requires sudo)
sudo ./setup.sh
# Add to PATH in your shell config
echo 'export PATH="/opt/pm/live/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc
# Verify installation
sps2 --version
Some working .sp packages in the test_build/ dir. Layout is messy, will eventually clean that up.
# Install from repository (when available)
sps2 install jq
# Install specific version
sps2 install "jq==1.7"
# Install with version constraints
sps2 install "curl>=8.0.0,<9.0.0"
# Install from local .sp file
sps2 install ./package-1.0.0-1.arm64.sp
# Build and install
sps2 build my-package.yml
# Build without installing
sps2 build my-package.yml -o ./packages/
Use the draft
command to automatically generate recipes:
# From a Git repository
sps2 draft -g "https://github.com/BurntSushi/ripgrep"
# From a source archive URL
sps2 draft -u "https://example.com/package-1.0.tar.gz"
# From a local directory
sps2 draft -p ./my-project
# From a local archive
sps2 draft -a ./my-archive.tar.gz
# Specify output file
sps2 draft -g "https://github.com/helix-editor/helix" -o helix.yml
Example recipe for ripgrep (generated by draft
):
metadata:
name: ripgrep
version: "14.1.1"
description: "Line-oriented search tool that recursively searches for regex patterns"
license: "MIT"
homepage: "https://github.com/BurntSushi/ripgrep"
environment:
network: true # Cargo needs network for dependencies
source:
git:
url: "https://github.com/BurntSushi/ripgrep"
ref: "14.1.1"
build:
system: cargo
args: ["--release"]
Build with various options:
# Basic build
sps2 build ripgrep.yml
# Build with custom output directory
sps2 build ripgrep.yml -o ./packages/
# Build with maximum compression
sps2 build ripgrep.yml --max
# Build with custom job count
sps2 build ripgrep.yml -j 8
# List installed packages
sps2 list
# Example output:
# ┌─────────┬─────────┬───────────┬─────────────┐
# │ Package ┆ Version ┆ Status ┆ Description │
# ╞═════════╪═════════╪═══════════╪═════════════╡
# │ bat ┆ 0.25.0 ┆ Installed ┆ - │
# ├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┤
# │ helix ┆ 25.1.1 ┆ Installed ┆ - │
# ├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌┤
# │ ripgrep ┆ 14.1.1 ┆ Installed ┆ - │
# └─────────┴─────────┴───────────┴─────────────┘
# Show package info
sps2 info jq
# Search for packages
sps2 search rust
# Update packages (respects version constraints)
sps2 update
# Upgrade to latest versions
sps2 upgrade curl
# Uninstall packages
sps2 uninstall jq
# View state history
sps2 history
# Example output:
# ┌────────────────────────┬─────────┬───────────┬──────────────────┬──────────┐
# │ State ID ┆ Current ┆ Operation ┆ Created ┆ Packages │
# ╞════════════════════════╪═════════╪═══════════╪══════════════════╪══════════╡
# │ 48b6f85f-78bb-4dc5-... ┆ * ┆ install ┆ 2025-06-13 16:12 ┆ 4 │
# ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤
# │ a7be49ca-a976-4f75-... ┆ ┆ install ┆ 2025-06-13 16:11 ┆ 3 │
# └────────────────────────┴─────────┴───────────┴──────────────────┴──────────┘
# Rollback to previous state
sps2 rollback
# Rollback to specific state
sps2 rollback 48b6f85f-78bb-4dc5-9487-a8a60e97423b
# Clean up orphaned packages and old states
sps2 cleanup
# Check system health
sps2 check-health
# Update vulnerability database
sps2 vulndb update
# Show vulnerability database statistics
sps2 vulndb stats
# Audit installed packages for vulnerabilities
sps2 audit
# Audit specific package
sps2 audit --package curl
# Fail on critical vulnerabilities
sps2 audit --fail-on-critical
# Sync repository index
sps2 reposync
# Update sps2 itself
sps2 self-update
sps2 uses an innovative atomic update system:
- Content-Addressed Store - All package files are stored by their BLAKE3 hash
- State Directories - Each system state is a complete filesystem tree
- Atomic Swaps - Updates use APFS clones and atomic renames
- Instant Rollback - Previous states are preserved and can be restored instantly
/opt/pm/
├── live/ # Current active state (add /opt/pm/live/bin to PATH)
├── store/ # Content-addressed package storage
├── states/ # Historical states for rollback
└── state.sqlite # Package database
sps2 uses YAML format for package recipes with declarative, staged build definitions. See Build Script Documentation
metadata:
name: helix
version: "25.1.1"
description: "A post-modern modal text editor"
license: "MIT"
homepage: "https://github.com/helix-editor/helix"
environment:
network: true # Cargo needs network access
source:
git:
url: "https://github.com/helix-editor/helix"
ref: "25.1.1"
build:
system: cargo
args: ["--release"]
metadata:
name: pkgconf
version: "2.4.3"
description: "A system for managing library compile/link flags"
license: "ISC"
homepage: "https://github.com/pkgconf/pkgconf"
source:
git:
url: "https://github.com/pkgconf/pkgconf"
ref: "pkgconf-2.4.3"
build:
system: meson
args: ["--buildtype=release"]
# sps2 build recipe for curl
#
# This recipe builds curl from source archive.
# It enables support for OpenSSL, zlib, and nghttp2 (for HTTP/2).
metadata:
name: curl
version: "8.14.1"
description: "Command-line tool for transferring data with URLs"
license: "MIT"
homepage: "https://curl.se"
dependencies:
runtime:
- openssl
- zlib
- nghttp2
- brotli
- libssh2
- libidn2
- libpsl
environment:
defaults: true # Optimized flags for macOS ARM64
source:
fetch:
url: "https://github.com/curl/curl/releases/download/curl-8_14_1/curl-8.14.1.tar.bz2"
checksum:
sha256: "2893f7b7614192c2a4d8289f3d0009798a7c5f5d895011b5e4c0cf910c4a8b1e"
build:
system: cmake
args:
- "-DCMAKE_BUILD_TYPE=Release"
- "-GNinja"
- "-DBUILD_SHARED_LIBS=ON"
- "-DBUILD_STATIC_LIBS=OFF"
- "-DCURL_USE_OPENSSL=ON"
- "-DCURL_ZLIB=ON"
- "-DUSE_NGHTTP2=ON"
- "-DENABLE_IPV6=ON"
- "-DCURL_USE_LIBSSH2=ON"
- "-DUSE_LIBIDN2=ON"
- "-DCURL_BROTLI=ON"
- "-DCURL_USE_LIBPSL=ON"
- "-DBUILD_TESTING=OFF<
8890
span class="pl-pds">"
- "-DENABLE_CURL_MANUAL=OFF"
post:
patch_rpaths:
style: homebrew # curl needs absolute paths
install:
auto: true
We welcome contributions! Please see CONTRIBUTING.md for guidelines.
- Architecture Overview - Detailed system design
- Build Script Documentation - YAML recipe format reference
- Contributing Guide - How to contribute
- Code of Conduct - Community guidelines
- Packages are signed with Minisign // TODO
- All downloads are verified against BLAKE3 hashes BLAKE3
- SBOM (Software Bill of Materials) generated for every package
- Offline CVE scanning via integrated vulnerability database
BSD 3-Clause License. See LICENSE.md for details.
sps2 stands on the shoulders of giants:
- moss & boulder AerynOS - atomic updates, content-addressed storage, stateful package management
- Homebrew - Inspiration for macOS-native packaging
- Ansible - Inspiration for YAML format and shell/command distinction
- Starlark - Previously used for build recipes
Building the future of package management on macOS, one atomic update at a time.