axon-pkg
axon-pkg
Section titled βaxon-pkgβπ¦ axpm β The Axon Package Manager
Git-based dependency resolution with Minimum Version Selection (MVS). S-expression manifest format, content-addressed lock files, local caching.
π§ Under Development β axpm install is the canonical dependency-installation entrypoint. Remaining work focuses on registry publishing, signing, and ergonomic polish.
Quickstart
Section titled βQuickstartβ# 1. Create a new projectaxpm init my-project
# 2. Add a dependencyaxpm add https://git.catalystgroup.tech/labs/axon/axon-std.git
# 3. Install (resolve + lock + fetch) β the canonical entrypointaxpm install
# Or in CI for strict reproducibility:axpm install --frozen # alias for --locked --offlineCommands
Section titled βCommandsβFull per-command reference (synopsis, options, exit codes, examples, env vars) lives in docs/cli.md.
| Command | Description |
|---|---|
axpm init [name] | Create a new project with a .axon-pkg manifest |
axpm add <git-url> [version] | Add a dependency to the manifest |
axpm install [--flags] | Resolve + lock + fetch dependencies (canonical entrypoint) |
axpm fetch | Download dependencies to ~/.axon/cache/ (legacy alias) |
axpm resolve | Display the resolved dependency tree without fetching |
axpm build | Compile the project with axonc |
axpm test [dir] | Run the test suite |
axpm cache-info | Show cached packages in ~/.axon/cache/ |
axpm search <query> | Search the package registry index (with multi-registry fallback) |
axpm publish | Generate a JSON registry entry for the current package |
axpm registry <list|add|remove|use|test|path> | Manage the global registry list (mirrors, fallbacks). See docs/registry.md |
axpm login <url> [--token T] [--scheme S] | Store an auth token for a private registry (mode 0600). See docs/auth.md |
axpm logout <url> | Remove a stored registry credential |
axpm outdated [--json] [--strict] | Check for newer package versions |
axpm upgrade [pkg...] [--flags] | Bump dependencies to newer versions |
axpm vendor [dir] [--verify] | Copy all locked deps into a local vendor/ directory for offline builds |
axpm verify [--strict] [--deps-dir D] | CI gate: lockfile consistency + content-hash verification |
axpm audit [--json] [--strict-deps] [--db PATH] | Cross-reference .axon-lock against an advisory DB; exit 2 on P0/P1 |
axpm keygen [--name <id>] | Generate an Ed25519 signing keypair at $HOME/.axon/keys/<id>.key (mode 0600) |
axpm sign <pkg> | Sign a .axpkg with your Ed25519 key, writing a .sig sidecar |
axpm verify-pkg <pkg> | Verify a detached Ed25519 signature against a package. See docs/signing.md |
axpm policy print | Pretty-print the parsed (policy ...) block from axon.manifest. See docs/policy.md |
axpm policy check | Enforce the declared policy against .axon-lock. Exits 1 on violations. |
axpm clean-cache | Remove all cached packages |
axpm --help | Show CLI help |
axpm --version | Show version |
axpm init
Section titled βaxpm initβaxpm init my-projectaxpm init # uses current directory nameaxpm init --name mylib --version 0.1.0 --license MIT # non-interactiveaxpm init --force # overwrite an existing manifestCreates a .axon-pkg manifest, src/ and tests/ directories, and a src/main.axs starter file. Fails if .axon-pkg already exists, unless --force is given.
| Flag | Effect |
|---|---|
--name <name> | Package name (default: current directory name). Overrides a positional [name]. |
--version <ver> | Package version (default: 0.1.0). |
--license <id> | Record a (license "<id>") field in the manifest, e.g. --license MIT. |
--force | Overwrite an existing .axon-pkg instead of erroring. |
Passing any of --name/--version/--license runs init non-interactively (no prompts).
Note: the manifest filename is
.axon-pkgfor now. The canonical-name decision is tracked in #139; the(license ...)field is written into the manifest but is not yet part of the formal schema for the same reason.
axpm add
Section titled βaxpm addβaxpm add https://git.catalystgroup.tech/labs/axon/axon-std.gitaxpm add https://git.example.com/repo.git ">=0.2.0"Adds a dependency entry to .axon-pkg. Accepts an optional semver version constraint (e.g., ">=0.2.0").
axpm install
Section titled βaxpm installβThe canonical entrypoint for dependency installation. Performs three steps in one command:
- Resolve dependencies from
.axon-pkg - Lock exact versions into
.axon-lock(SHA-pinned) - Fetch source code to
~/.axon/cache/and symlink to.axon/deps/
| Flag | Effect |
|---|---|
| (none) | Resolve + lock + fetch (default) |
--locked | Fail (exit 3) if .axon-lock is missing or out-of-sync with manifest |
--offline | Never touch the network; refuse to install if a dep is missing from cache |
--no-fetch | Resolve + lock only; do not clone or pull |
--frozen | Alias for --locked --offline (recommended for CI) |
Exit codes: 0 success, 1 generic failure, 2 network error, 3 lockfile mismatch.
axpm resolve
Section titled βaxpm resolveβaxpm resolveDisplays the resolved dependency tree using MVS (Minimum Version Selection) without touching the network or writing lock files. Use this to preview what axpm install would resolve before committing to fetching.
Example output:
axon-std: resolved to 0.2.1 (git rev abc123) (transitive deps listed indented)axpm verify
Section titled βaxpm verifyβaxpm verify # consistency + content-hash checkaxpm verify --strict # also fail on missing hashes / unfetched depsaxpm verify --consistency-only # skip hash verificationA CI-friendly strict gate that fails with a stable exit code when
.axon-lock is missing, out of sync with .axon-pkg, or when a fetched
dependency no longer matches the content hash recorded in the lockfile.
| Exit | Meaning |
|---|---|
0 | Consistent + hashes verified |
3 | Lockfile inconsistent with manifest (version/URL/name drift) |
4 | Content-hash mismatch (supply-chain warning) |
5 | No .axon-lock present |
axpm install --locked / --frozen run the same consistency check (plus
hash verification) before fetching, so CI pipelines can use either
axpm install --frozen (install + gate) or axpm verify (gate only).
axpm build
Section titled βaxpm buildβaxpm buildCompiles the projectβs entry point (specified in .axon-pkg) using axonc, with .axon/deps/ on the library search path. Produces a binary in build/. Requires a valid .axon-pkg manifest and an entry point file.
axpm test
Section titled βaxpm testβaxpm test # runs tests/ directoryaxpm test custom-dir # runs custom-dir/ directoryDiscovers all test_*.axs and *_test.axs files in the test directory, compiles each with axonc, runs them, and compares output against .expected files if present. Requires a valid .axon-pkg manifest.
Workspaces (monorepos)
Section titled βWorkspaces (monorepos)βA workspace groups several member packages under one root so a single
axpm install / build / test operates over all of them, sharing
dependencies through the global cache. The root is described by an
axon.manifest file holding a (workspace ...) form:
;; axon.manifest β at the monorepo root(workspace (members ("packages/*" "tools/**")) (deps (dep "shared-util" (path "../shared-util")) (dep "logging" (git "https://example.com/logging.git") (version ">=1.0.0"))))(members ...)β a list of glob patterns. Supported wildcards:*(any run of characters within one path segment),**(zero or more whole path segments), and?(one character). Matched directories that contain a.axon-pkgbecome members; matched directories without one are skipped. Members are discovered in deterministic lexical order.(deps ...)β workspace-level shared dependencies, each a normal(dep "name" ...)with apathorgit/versionsource.
A member is an ordinary package (its own .axon-pkg). It refers to a shared
dependency by alias β the alias is the shared depβs name β using the
(workspace) marker instead of an inline source:
;; packages/app/.axon-pkg(package (name "app") (version "0.1.0") (entry "src/main.axs") (dependencies (dep "shared-util" (workspace)))) ; resolved from the root's shared depsaxpm install # at the workspace root: discover members, write the lock, # fetch shared deps once (deduplicated), resolve each memberaxpm build # build every member; fails if any member failsaxpm test # run every member's tests; fails if any member failsShared dependencies are deduplicated through the global cache: a git
shared dep is cloned once into ~/.axon/cache/ and every member links to it;
a path shared dep is symlinked to the one real directory. No N copies.
Install writes a deterministic axon.workspace.lock recording member
paths (sorted) and shared resolved dependencies (sorted by name). It contains
no timestamps, host-absolute paths, or discovery-order artifacts, so repeated
runs produce byte-identical output.
Error handling. Discovery rejects: malformed globs; a matched member whose
.axon-pkg is unreadable/malformed; a member that is itself a workspace
(recursive cycle); and members escaping the root after canonicalization
(symlinks are never followed). Resolving a member (workspace) alias that has
no matching root declaration is a hard error. See
docs/workspace.md for the full reference.
axpm cache-info
Section titled βaxpm cache-infoβaxpm cache-infoShows all packages currently cached in ~/.axon/cache/. Lists package name, cached path, and whether each is up-to-date with the lockfile.
axpm search
Section titled βaxpm searchβaxpm search <query>Searches the registry index for packages whose name or description matches <query>. Uses the registry at https://labs.axon-lang.dev/registry/index.json by default.
The full registry list lives in $HOME/.axon/registries.json and is managed by axpm registry. When more than one registry is configured, axpm search tries each in order β the first one to return a valid index body wins. To see which registry served your request, watch the βserved by registry:β log line.
Two override mechanisms:
# One-shot CI override β bypasses the list entirelyAXON_REGISTRY_URL="https://ci-pin.example.com/registry/index.json" axpm search <query>
# Local file-backed index (works without network)AXON_REGISTRY_URL="file:///path/to/index.json" axpm search <query>file:// URLs are supported for local registry indexes.
axpm registry
Section titled βaxpm registryβaxpm registry listaxpm registry add https://axpm-cdn.example.com/registry/index.jsonaxpm registry use https://axpm-cdn.example.com/registry/index.jsonaxpm registry remove https://labs.axon-lang.dev/registry/index.jsonaxpm registry testaxpm registry pathManage a prioritized list of registry URLs (primary + fallbacks). The list lives in $HOME/.axon/registries.json and is consulted by axpm search and any future registry-bound command.
| Subcommand | Effect |
|---|---|
list | Show all configured registries in priority order |
add <url> | Append <url> to the fallback list (rejects duplicates + non-http URLs) |
remove <url> | Remove <url> from the list (restores DEFAULT if it would be empty) |
use <url> | Make <url> the new primary; demote the current primary to the end |
test [url...] | Reachability check (5s timeout per URL); exits 1 if any URL is unreachable |
path | Print the absolute path of the config file |
The full command reference and design rationale live in docs/registry.md.
axpm login / axpm logout
Section titled βaxpm login / axpm logoutβ# Store a token for a private registry (Bearer scheme by default)axpm login https://reg.example --token abc123
# Read the token from stdin (keeps it out of your shell history)echo "$MY_TOKEN" | axpm login https://reg.example
# Read the token from the environmentAXPM_TOKEN=abc123 axpm login https://reg.example
# GitHub/GitLab-style PATs use the "token" schemeaxpm login https://git.example.com --scheme token --token glpat-xxxx
# List registries with stored credentials (never prints the token)axpm login --list
# Remove a credentialaxpm logout https://reg.exampleStore per-registry authentication tokens so axpm search (and future
registry-bound commands) can pull from private / internal registries.
When a request goes to a registry youβve logged into, axpm attaches an
Authorization: <scheme> <token> header automatically. A 401/403
from a registry prints a hint telling you to run axpm login.
| Flag / arg | Effect |
|---|---|
<registry-url> | Registry to authenticate to (matched by scheme+host+port; path ignored) |
--token T | Token value. Precedence: --token β $AXPM_TOKEN β stdin |
--scheme S | Auth scheme for the header (default Bearer; use token for PATs) |
--list | List registries with stored credentials (no secrets printed) |
Credentials are stored in $HOME/.axon/credentials.json, created mode
0600 (owner read/write only) inside the 0700 $HOME/.axon/
directory β the same secure-file posture as the Ed25519 signing keys.
The token is matched to a registry by scheme + host + port (the URL
path is ignored), so a token stored for
https://reg.example/registry/index.json also authenticates
https://reg.example/packages/foo.tar. See docs/auth.md
for the full model.
axpm publish
Section titled βaxpm publishβaxpm publishReads the local .axon-pkg manifest and git remote to generate a JSON registry entry suitable for submission to a registry index. The output includes package name, version, git URL, description, and the current git tag SHA.
{ "name": "my-package", "version": "0.1.0", "git": "https://git.example.com/my-package.git", "description": "...", "sha": "abc123def456"}Requires the project to be a git repository with an origin remote. Recommend running git tag v<version> before publishing.
axpm outdated
Section titled βaxpm outdatedβaxpm outdatedaxpm outdated --jsonaxpm outdated --strictReads the .axon-lock file and checks each locked package against a registry (or mock registry for testing) to determine if newer versions are available.
| Flag | Effect |
|---|---|
--json | Output results as a JSON array |
--strict | Exit non-zero if any package has a newer version |
Example output:
NAME CURRENT LATEST STATUS ---- ------- ----- ------ libfoo 1.0.0 2.0.0 major libbar 0.1.0 0.3.0 minor libbaz 3.0.0 3.1.0 patchJSON output (--json):
[ {"name":"libfoo","current":"1.0.0","latest":"2.0.0","outdated":true}, {"name":"libbar","current":"0.1.0","latest":"0.3.0","outdated":true}, {"name":"libbaz","current":"3.0.0","latest":"3.1.0","outdated":true}]Status values:
up-to-date: Current version matches latestmajor: New major version available (e.g., 1.x β 2.x)minor: New minor version available (e.g., 1.2.x β 1.3.x)patch: New patch version available (e.g., 1.2.0 β 1.2.1)unknown: Package not found in registry
Mock registry: Set AXPM_MOCK_REGISTRY environment variable to a JSON file path for testing without a real registry.
axpm upgrade
Section titled βaxpm upgradeβaxpm upgrade # dry-run (show what would upgrade)axpm upgrade --all # actually apply upgradesaxpm upgrade libfoo # upgrade specific packageaxpm upgrade libfoo --to=1.2.3 # pin to specific versionaxpm upgrade --interactive # prompt for each packageReads the .axon-pkg manifest and .axon-lock lockfile, then upgrades packages to the latest versions satisfying their version constraints.
| Flag | Effect |
|---|---|
| (none) | Dry-run: show what would be upgraded |
--all | Actually apply upgrades to lockfile |
--interactive | Prompt for each package before upgrading |
--to=<version> | Pin upgraded package to specific version |
--help | Show usage |
Example output:
NAME CURRENT UPGRADE TO CONSTRAINT ---- ------- ---------- --------- libfoo 1.0.0 1.2.0 ^1.0.0 libbar 0.1.0 0.1.0 ~0.1.0 libbaz 3.0.0 3.1.0 >=3.0.0
Dry run β no changes made. Use --all to apply.Constraint behavior:
^1.0.0β upgrades to latest1.x.y(wonβt accept2.0.0)~0.1.0β upgrades to latest0.1.x(wonβt accept0.2.0)>=3.0.0β upgrades to latest available
Mock registry: Uses AXPM_MOCK_REGISTRY environment variable (same as axpm outdated).
axpm vendor
Section titled βaxpm vendorβaxpm vendor # copy deps to ./vendor/axpm vendor ./my-vendor # copy deps to custom directoryaxpm vendor --verify # verify vendor/ matches lockfile hashesaxpm vendor --verify ./my-vendorBundles all locked dependencies into a local directory for offline builds
and air-gapped environments (CI runners without network, corporate VPNs,
IoT, build farms). Each locked dep is copied from ~/.axon/cache/ into
the vendor directory, and a VENDOR.txt manifest records the package
names, versions, and commit SHAs for traceability.
| Flag | Effect |
|---|---|
| (none) | Copy all locked deps to vendor/ (or specified dir) |
--verify | Verify existing vendor/ matches lockfile hashes (no copy) |
--help | Show usage |
Air-gapped workflow:
# On a machine with network:axpm install # populate ~/.axon/cache/ + write .axon-lockaxpm vendor ./vendor # bundle deps into ./vendor/git add vendor/ # commit the bundled deps
# On the air-gapped machine:axpm install --offline --vendor-dir=./vendorExample output:
axpm: vendoring 3 package(s) to vendor/ [1/3] libfoo (1.2.3) [2/3] libbar (0.1.0) [3/3] libbaz (3.1.0) axpm: vendored 3 package(s) to vendor/Verify example:
$ axpm vendor --verify axpm: verifying vendor directory: vendor axpm: all 3 package(s) verified βExit codes
Section titled βExit codesβ| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Generic failure (manifest missing, unknown flag, conflict) |
| 2 | Network error (fetch failed, or offline cache miss) |
| 3 | Lockfile mismatch under --locked / --frozen |
Key Features
Section titled βKey Featuresβ| Feature | Description | Status |
|---|---|---|
axpm install | Canonical resolve + lock + fetch entrypoint | β |
axpm init | Create new project with manifest | β |
axpm fetch | Download dependencies via Git (legacy alias) | β |
axpm resolve | Display dependency resolution tree without fetching | β |
axpm build | Compile project with dependencies | β |
axpm test | Run test suite | β |
axpm add <url> | Add dependency to manifest | β |
| Lock files | Deterministic, content-hashed | β |
| MVS resolver | Minimum Version Selection | β |
| Local cache | ~/.axon/cache/ | β |
--locked / --offline / --frozen flags | CI reproducibility | β |
axpm search | Registry index search | β |
axpm publish | JSON registry entry generation | β |
axpm cache-info | Show cached packages | β |
axpm clean-cache | Remove cached packages | β |
axpm upgrade | Bump dependencies to newer versions | β |
axpm vendor | Bundle deps into vendor/ for offline builds | β |
| Clipboard integration | Cross-platform text clipboard (macOS/Linux/Windows) | β |
| Code signing (Ed25519) | Supply-chain verification | π |
Manifest Format
Section titled βManifest Formatβ;; .axon-pkg β Project manifest(package (name "my-project") (version "0.1.0") (description "An Axon project") (entry "src/main.axs")
(dependencies (dep "axon-std" (git "https://git.example.com/axon/axon-std.git") (version ">=0.1.0")))
(dev-dependencies (dep "axon-test" (path "../axon-test"))))Version Constraints
Section titled βVersion Constraintsβaxon-pkg supports the following version operators in the version field of .axon-pkg manifest dependencies:
| Operator | Example | Meaning |
|---|---|---|
* | * | Any version |
= | =1.2.3 | Exact version |
!= | !=1.2.3 | Exclude version |
> | >1.2.3 | Greater than |
< | <2.0.0 | Less than |
>= | >=1.2.3 | Greater or equal |
<= | <=2.0.0 | Less or equal |
~ | ~1.2.3 | Same minor: >=1.2.3, <1.3.0 |
~> | ~>1.2.3 | Cargo compat: same as ~ |
^ | ^1.2.3 | Same major: >=1.2.3, <2.0.0 |
|| | 1.2.3 || 2.0.0 | Union of constraints |
Operator Details
Section titled βOperator Detailsβ^(caret): Compatible changes.^1.2.3matches>=1.2.3, <2.0.0. For^0.x.y(major=0), it matches>=0.x.y, <0.(y+1).0. For^0.0.x, it matches exactly0.0.x.~(tilde): Patch-level changes.~1.2.3matches>=1.2.3, <1.3.0.~>(Cargo compat): Same semantics as~.||(union):1.0.0 || >=2.0.0 <3.0.0matches if EITHER constraint is satisfied.
Prerelease Versions
Section titled βPrerelease VersionsβPrerelease versions (e.g., 1.0.0-alpha, 1.0.0-rc.1) follow SemVer 2.0 ordering:
1.0.0-alpha<1.0.0<1.0.1- A prerelease version satisfies a constraint like
>=1.0.0only if the base version satisfies it, BUT a stable release does NOT satisfy a prerelease constraint (e.g.,1.0.0does NOT satisfy>=1.0.0-alpha).
Lock File Format
Section titled βLock File Formatβ;; .axon-pkg.lock β Auto-generated, do not edit(lock (version 1) (packages (pkg "axon-std" (version "0.2.1") (git "https://git.example.com/axon/axon-std.git") (rev "abc123def456") (hash "sha256:..."))))Architecture
Section titled βArchitectureβaxon-pkg/βββ src/β βββ main.c # CLI entry point and argument parser (all subcommands)β βββ manifest.c # S-expression manifest parserβ βββ manifest.h # Manifest data structuresβ βββ resolver.c # MVS dependency resolverβ βββ resolver.h # Resolver APIβ βββ lockfile.c # Lock file generation/readingβ βββ lockfile.h # Lock file APIβ βββ semver.c # Semver constraint matchingβ βββ semver.h # Semver APIβ βββ sha256.c # SHA-256 content hashingβ βββ sha256.h # SHA-256 APIβ βββ util.c # String helpers, path manipulationβ βββ util.hβ βββ test_runner.c # Lightweight test harness used by C unit testsβ βββ test_runner.hβ βββ clipboard.axs # Axon clipboard API surface (axon-pkg #14)βββ tests/β βββ test_manifest.cβ βββ test_path_deps.cβ βββ test_vendor.cβ βββ test_semver.cβ βββ test_semver_edge.cβ βββ test_lockfile.cβ βββ test_resolver.cβ βββ test_sha256.cβ βββ test_install.cβ βββ test_lockfile_consistency.c # axon-pkg #69 (WIP β allow_failure in CI)β βββ test_add.cβ βββ test_fetcher.c # Git fetcher integration testsβ βββ bench_resolver.cβ βββ test_cli.sh # CLI smoke testsβ βββ test_clipboard.axs # Axon clipboard smoke tests (#14)β βββ test_clipboard.expected # Expected output (headless)βββ scripts/β βββ run_tests.sh # End-to-end integration testsβββ tools/ # Mutation testing, mock registries, clipboardβ βββ clipboard.py # Clipboard reference implementation (#14)β βββ test_clipboard.py # Clipboard Python tests (#14)βββ Makefile # Build systemβββ .gitlab-ci.yml # CI pipelineβββ README.md # This fileNote: Git-based dependency fetching is currently inlined in src/main.c
(cmd_fetch, cmd_install, etc.) rather than extracted into a dedicated
fetch.c/fetch.h pair. Extracting it is tracked but not yet done.
Building
Section titled βBuildingβ# Build axpmmake
# Run testsmake test
# Install to ~/.local/binmake install
# Clean build artifactsmake cleanBenchmarking
Section titled βBenchmarkingβ# Human-readable benchmark outputmake bench
# JSON output for CI regression gatesmake bench-jsonThe resolver benchmarks measure MVS resolution time across synthetic dependency graphs of varying sizes (10, 50, 100, 200, 500 packages) and graph types (flat, conflict, mixed). See docs/BENCHMARKING.md for the full output format specification and guidance on setting regression thresholds.
Dependencies
Section titled βDependenciesβ- C compiler (gcc or clang)
- Git (for dependency fetching)
- POSIX systems (macOS, Linux)
Development
Section titled βDevelopmentβRunning tests
Section titled βRunning testsβ# Unit testsmake test
# Integration tests (requires axpm binary in ./build/axpm)make test-integrationCode style
Section titled βCode styleβ- C99 standard
- 4-space indentation
snake_casefor functions and variablesUPPER_SNAKE_CASEfor constants/macros- Comprehensive error handling with descriptive messages
Part of the Axon Ecosystem
Section titled βPart of the Axon Ecosystemβ| Package | Description | Status |
|---|---|---|
| axon-lang | Compiler & runtime | β Active |
| axon-std | Standard library | β Active |
| axon-pkg | Package manager | π§ WIP |
| axon-debug | Debug tools | π Planned |
| axon-examples | Example programs | β Active |
| axon-web | Project website | β Active |
License
Section titled βLicenseβMIT