← Back to Blog

lpm: The Lateralus Package Manager

March 10, 2025 toolinglpm

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:

◉ 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:

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):

  1. Collect all constraints from direct and transitive dependencies
  2. Feed constraints to solver
  3. Find solution that satisfies all constraints, or error with explanation
  4. 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:

  1. Runs tests
  2. Checks for uncommitted changes
  3. Validates manifest
  4. Computes content hash
  5. 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:

Source at bad-antics/lpm. Registry API docs at lpm.lateralus.dev/docs.