https://github.com/alexykn/sps2
atomic package manager for macos
https://github.com/alexykn/sps2
macos package-manager rust state-management
Last synced: 12 months ago
JSON representation
atomic package manager for macos
- Host: GitHub
- URL: https://github.com/alexykn/sps2
- Owner: alexykn
- License: bsd-3-clause
- Created: 2025-05-28T11:12:44.000Z (about 1 year ago)
- Default Branch: main
- Last Pushed: 2025-06-22T20:37:54.000Z (12 months ago)
- Last Synced: 2025-06-22T21:26:47.395Z (12 months ago)
- Topics: macos, package-manager, rust, state-management
- Language: Rust
- Homepage:
- Size: 253 MB
- Stars: 29
- Watchers: 1
- Forks: 3
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE.md
- Code of conduct: CODE_OF_CONDUCT.md
- Security: SECURITY.md
Awesome Lists containing this project
README
# sps2
A modern, atomic package manager for macOS ARM64 with rollback capabilities and hermetic builds.
## Early Development Notice
**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.
### Current Status
- ✅ **Working**: `install`, `uninstall`, `rollback`, `history`, `list`, `vulndb update`, `check-health`
- 🚧 **In Progress**: `draft` and `build` (functional but incomplete)
- ⚠️ **Untested**: `update`, `upgrade`, `info`, `search`, `reposync`, `cleanup`, `audit`, `self-update`
## Features
- 🔄 **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
## Installation
### Prerequisites
- macOS with Apple Silicon
- Rust 1.87.0 or later
- SQLite 3.x
- sudo access for `/opt/pm` directory
### Build from Source
```bash
# 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
```
## Quick Start
### Installing Packages
**Some working .sp packages in the test_build/ dir. Layout is messy, will eventually clean that up.**
```bash
# 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/
```
### Generating Build Recipes
Use the `draft` command to automatically generate recipes:
```bash
# 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
```
### Building Packages
Example recipe for ripgrep (generated by `draft`):
```yaml
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:
```bash
# 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
```
### Managing Packages
```bash
# 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
```
### State Management
```bash
# 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
```
### Security Features
```bash
# 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
```
### Repository Management
```bash
# Sync repository index
sps2 reposync
# Update sps2 itself
sps2 self-update
```
## How It Works
sps2 uses an innovative atomic update system:
1. **Content-Addressed Store** - All package files are stored by their BLAKE3 hash
2. **State Directories** - Each system state is a complete filesystem tree
3. **Atomic Swaps** - Updates use APFS clones and atomic renames
4. **Instant Rollback** - Previous states are preserved and can be restored instantly
### Directory Structure
```
/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
```
## Building Your Own Packages
sps2 uses YAML format for package recipes with declarative, staged build definitions. See [Build Script Documentation](BUILD_SCRIPT_DOCUMENTATION.md)
### Example: Rust Application (Helix Editor)
```yaml
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"]
```
### Example: Meson Project (pkgconf)
```yaml
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"]
```
### Example: Building from Source Archive
```yaml
# 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"
- "-DENABLE_CURL_MANUAL=OFF"
post:
patch_rpaths:
style: homebrew # curl needs absolute paths
install:
auto: true
```
## Developer Tools
### Store List (sls) - Debugging Content-Addressed Storage
The `sls` utility is a specialized debugging tool for exploring sps2's content-addressed storage system. It provides ls-like functionality for both the object store and package metadata.
#### Basic Usage
```bash
# List all hash prefix directories (00-ff)
sls
# List objects with a specific hash prefix
sls ff
# List a specific object by partial hash
sls ffff5aa9
# Show full hash without filename mapping
sls --hash ff
```
#### Object Store Exploration
```bash
# Long format with permissions and sizes
sls -l ff
# Example output:
# -r--r--r-- 2.11 KiB ffff5aa98bd51ec0 -> include/c++/15/fenv.h (gcc:15.1.0)
# -r--r--r-- 6.46 KiB ffff1d26e56791c5 -> share/man/man3/SSL_config.3ossl (openssl:3.5.0)
# Recursive listing of entire store
sls -R
# Find all files from a specific package
sls -R | grep "bat:0.25.0"
# Find all Python files
sls -R | grep "\.py "
# Find files by name pattern using ripgrep
sls -R | rg "libcurl.*dylib"
# Find all files from multiple packages
sls -R | rg "(gcc|clang|llvm):"
```
#### Package Directory Exploration
```bash
# List all packages with their names and versions
sls -p
# Example output:
# 0543d3aaf01da99f -> bat:0.25.0
# 098b0c7c800fd65f -> autoconf:2.72.0
# 0c0be36a37e74b78 -> cmake:4.0.3
# List specific package by hash prefix
sls -p 0543
# Long format for packages (shows metadata files)
sls -p -l
# Find packages by name pattern
sls -p | grep "python"
# Find all compiler packages using ripgrep
sls -p | rg "(gcc|clang|llvm|rust)"
```
- **Object Storage**: Files stored by hash in `/opt/pm/store/objects/[first-2-chars]/[full-64-char-hash]`
- **Package Storage**: Package Metadata stored in `/opt/pm/store/packages/[package-hash]/` with files:
- `manifest.toml` - Package metadata and file list
- `files.json` - Detailed file information
- `sbom.spdx.json` - Software Bill of Materials
- **Deduplication**: Multiple packages can reference the same file hash (content deduplication)
## Contributing
We welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
## Documentation
- [Architecture Overview](ARCHITECTURE.md) - Detailed system design
- [Build Script Documentation](BUILD_SCRIPT_DOCUMENTATION.md) - YAML recipe format reference
- [Contributing Guide](CONTRIBUTING.md) - How to contribute
- [Code of Conduct](CODE_OF_CONDUCT.md) - Community guidelines
## Security
- Packages are signed with [Minisign](https://jedisct1.github.io/minisign/) // TODO
- All downloads are verified against BLAKE3 hashes [BLAKE3](https://github.com/BLAKE3-team/BLAKE3)
- SBOM (Software Bill of Materials) generated for every package
- Offline CVE scanning via integrated vulnerability database
## License
BSD 3-Clause License. See [LICENSE.md](LICENSE.md) for details.
## Acknowledgments
sps2 stands on the shoulders of giants:
- [moss & boulder](https://github.com/AerynOS/os-tools) AerynOS - atomic updates, content-addressed storage, stateful package management
- [Homebrew](https://brew.sh/) - Inspiration for macOS-native packaging
- [Ansible](https://www.ansible.com/) - Inspiration for YAML format and shell/command distinction
- [Starlark](https://github.com/bazelbuild/starlark) - Previously used for build recipes
---
*Building the future of package management on macOS, one atomic update at a time.*