lpm: The Lateralus Package Manager
Every modern language needs a package manager. lpm is ours — designed for content-addressed storage, reproducible builds, and minimal ceremony. Here's how it works and why we made certain choices.
◉ Design Goals
We wanted:
- Reproducibility: Same inputs → same outputs, always
- Speed: Fast installs, minimal network traffic
- Security: Verify integrity without trusting servers
- Simplicity: Few commands, obvious behavior
- Offline support: Work without network when possible
◉ Basic Usage
# Create a new project
lpm init my-project
cd my-project
# Add dependencies
lpm add json
lpm add http --dev # Dev-only dependency
lpm add crypto@2.1.0 # Specific version
# Build and run
lpm build
lpm run
# Run tests
lpm test
# Publish to registry
lpm publish
◉ Project Structure
After lpm init:
my-project/
├── lpm.toml # Project manifest
├── lpm.lock # Locked dependencies
├── src/
│ └── main.ltl # Entry point
├── tests/
│ └── test_main.ltl # Tests
└── .lpm/
└── cache/ # Downloaded packages
lpm.toml
[package]
name = "my-project"
version = "0.1.0"
authors = ["bad-antics "]
license = "MIT"
description = "A cool project"
[dependencies]
json = "^1.0"
http = "^2.3"
[dev-dependencies]
testing = "^1.0"
[build]
target = "python" # or "c", "bytecode"
optimize = true
[scripts]
start = "lpm run --release"
check = "lpm lint && lpm test"
◉ Content-Addressed Storage
This is the key innovation. Every package version is identified by its content hash, not just name+version:
# lpm.lock
[dependencies]
json = { version = "1.2.3", hash = "sha256:a1b2c3d4..." }
http = { version = "2.3.4", hash = "sha256:e5f6g7h8..." }
Benefits:
- Tamper detection: If content changes, hash changes
- Deduplication: Same content = same hash = stored once
- Reproducibility: Lock file pins exact content, not just version
- Offline verification: Verify integrity without hitting the registry
The registry can't silently update a "1.2.3" release. The hash would change, lpm would reject it.
◉ Resolution Algorithm
Dependency resolution uses a SAT solver (similar to Cargo and Poetry):
- Collect all constraints from direct and transitive dependencies
- Feed constraints to solver
- Find solution that satisfies all constraints, or error with explanation
- Write solution to lpm.lock
When conflicts occur, lpm explains why:
$ lpm add old-lib
error: Cannot resolve dependencies
Package A requires json >= 2.0
Package B requires json < 2.0
These constraints cannot be satisfied simultaneously.
Consider:
- Update A to a version compatible with newer json
- Use B@1.3.0 which supports json 2.x
◉ Workspaces
For multi-package projects:
# Root lpm.toml
[workspace]
members = [
"packages/core",
"packages/cli",
"packages/web",
]
# Shared dependencies resolved once
[workspace.dependencies]
json = "^1.0"
All workspace members share a single lpm.lock. No version drift between packages.
◉ Build Targets
lpm handles compilation for all Lateralus targets:
# Build for Python (default)
lpm build
# Build for C
lpm build --target c
# Build for bytecode
lpm build --target bytecode
# Cross-compile for freestanding
lpm build --target c --freestanding
Each target can have specific configuration:
[build.targets.c]
cc = "gcc"
cflags = ["-O2", "-Wall"]
ldflags = ["-lm"]
[build.targets.python]
min_version = "3.10"
◉ Scripts
Custom scripts in lpm.toml:
[scripts]
start = "lpm run --release"
dev = "lpm run --watch"
lint = "lateralus lint src/"
fmt = "lateralus fmt src/"
check = "lpm lint && lpm test && lpm build"
deploy = "./scripts/deploy.sh"
Run with lpm run-script start or shorthand lpm start.
◉ Publishing
Publishing to the registry:
# Login (once)
lpm login
# Publish
lpm publish
# Publish with specific tag
lpm publish --tag beta
Before publishing, lpm:
- Runs tests
- Checks for uncommitted changes
- Validates manifest
- Computes content hash
- Uploads to registry
Published packages are immutable. You cannot overwrite a version — only yank it.
◉ Private Registries
For corporate use:
# ~/.config/lpm/config.toml
[registries.internal]
url = "https://lpm.internal.company.com"
token = "${LPM_INTERNAL_TOKEN}"
# In project lpm.toml
[dependencies]
internal-lib = { version = "1.0", registry = "internal" }
◉ Offline Mode
Once dependencies are cached, no network needed:
# Force offline
lpm build --offline
# Vendor dependencies into project
lpm vendor
# Creates vendor/ directory with all sources
# Build from vendored sources
lpm build --vendored
◉ Security
Security features:
- Hash verification: All downloads verified against lock file
- Audit command:
lpm auditchecks for known vulnerabilities - Supply chain: Optional GPG signatures for packages
- SBOM generation:
lpm sbomgenerates software bill of materials
Source at bad-antics/lpm. Registry API docs at lpm.lateralus.dev/docs.