Local-first, reservation-aware cluster substrate.
AXIS reads real hardware facts from machines you already own, then tells you (and your agents) where work fits.
Install to ~/.local/bin (no sudo), then check the binary. macOS and Linux only.
curl -fsSL https://raw.githubusercontent.com/toasterbook88/axis/main/install.sh \
| AXIS_VERSION=v0.19.6 AXIS_REQUIRE_PINNED=1 \
AXIS_INSTALL_DIR="$HOME/.local/bin" bashexport PATH="$HOME/.local/bin:$PATH"
axis versionDescribe your machines. axis init is interactive, so run it on its own and answer the prompts. It writes ~/.axis/nodes.yaml.
axis initSee the cluster and ask where a job would fit. Placement is advisory: nothing runs.
axis cluster status
axis task place "run ollama inference on a 7b model"Example output (one-node cluster)
CLUSTER STATUS (1 nodes, 1 healthy)
NAME STATUS ARCH RAM TOTAL RAM FREE PRESSURE GPUS
─────────────────────────────────────────────────────────────────────────────────────
● local (local) complete amd64 16002MB 3674MB low
Snapshot: live sweep | age 0s
When no node qualifies, axis task place says why and exits with code 3:
✗ No suitable node found.
- 0 of 1 nodes qualify
- workload class: local-llm-inference
- local: need 4096MB free RAM, have 3658MB allocatable (free 3658MB, short 438MB)
AXIS collects facts over SSH (plus optional UDP beacons), builds a deterministic cluster snapshot, and makes reservation-aware placement decisions. Optional surfaces add guarded execution, resident-model lifecycle, a local HTTP API, MCP, and an A2A agent card and task plane.
flowchart BT
L5["Advisory: axis agent · MCP · A2A cards and tasks"]
L4["Execution and lifecycle: guarded exec · models"]
L3["Placement: Filter → Rank → Select"]
L2["Snapshot: ClusterSnapshot · daemon cache"]
L1["Fact plane: SSH probes · UDP beacons · mesh"]
L1 --> L2 --> L3 --> L4 --> L5
Each layer is subordinate to the one below it. Advisory surfaces never override observed state.
Truth boundary: no generated output may present itself as cluster truth unless a real snapshot or live probe backs it.
| You need | AXIS does |
|---|---|
| Honest placement | Filter → Rank → Select on a real ClusterSnapshot |
| Shared RAM accounting | Soft reservations overlaid on live and cached reads |
| Safe execution | Safety gates, then reserve → run → release |
| Local operation | API on ~/.axis/axis.sock; state under ~/.axis/ |
| Platforms | macOS and Linux, amd64 and arm64. Windows is not supported. |
| Installer | bash, curl, and sha256sum or shasum |
| Cluster | Key-based SSH from this machine to every node in nodes.yaml |
| Source builds | Go 1.26.9 or newer and make. Go 1.21+ downloads 1.26.9 itself unless GOTOOLCHAIN=local is set. |
Important
Use v0.19.6 or later. Git history was rewritten on 2026-10-12, and v0.9.0 through v0.19.5 are retracted in go.mod.
The Quick start command is the recommended install. Other options:
System-wide install to /usr/local/bin (uses sudo)
curl -fsSL https://raw.githubusercontent.com/toasterbook88/axis/main/install.sh \
| AXIS_VERSION=v0.19.6 AXIS_REQUIRE_PINNED=1 bashA system install may remove verified user-local AXIS copies so only one axis is on PATH. Set AXIS_KEEP_LEGACY=1 to keep them.
Build from source
git clone https://github.com/toasterbook88/axis.git
cd axis
make build
make install-usermake install-user copies the binary to ~/.local/bin/axis.
Install with go install
go install github.com/toasterbook88/axis/cmd/axis@latestThe binary lands in $(go env GOPATH)/bin. Builds made this way don't record a commit in axis version.
Installer options
| Variable | Default | Effect |
|---|---|---|
AXIS_VERSION |
latest |
Release tag to install |
AXIS_REQUIRE_PINNED |
0 |
1 refuses latest |
AXIS_INSTALL_DIR |
/usr/local/bin |
Target directory |
AXIS_DRY_RUN |
0 |
1 prints the plan and writes nothing |
AXIS_KEEP_LEGACY |
0 |
1 leaves other AXIS copies in place |
AXIS_FORCE_REPLACE |
0 |
1 overwrites a non-AXIS file at the target |
Preview an install without writing anything:
curl -fsSL https://raw.githubusercontent.com/toasterbook88/axis/main/install.sh \
| AXIS_DRY_RUN=1 AXIS_VERSION=v0.19.6 bashVerify a release archive yourself
The installer already checks checksums.txt. Release archives also carry GitHub build-provenance attestations. With GitHub CLI 2.49 or newer:
gh attestation verify axis_0.19.6_linux_amd64.tar.gz --repo toasterbook88/Axisaxis node facts
axis doctor
axis cluster status --live
axis placement explain "run ollama inference on a 7b model"axis cluster status reads the daemon's cached snapshot when it is under 5 minutes old and prints its source and age. --live forces a fresh sweep; --cached-only fails instead of sweeping. axis task run is the separate command that actually executes work, behind safety gates.
axis serve runs in the foreground on the Unix socket ~/.axis/axis.sock. It is the same listener as axis daemon start.
axis serveIn a second terminal. /health and the agent card need no token; localhost here is only a placeholder curl needs for --unix-socket:
curl -fsS --unix-socket "$HOME/.axis/axis.sock" \
http://localhost/health
curl -fsS --unix-socket "$HOME/.axis/axis.sock" \
http://localhost/.well-known/agent-card.jsonOther routes (/snapshot, /run, /a2a/v1/...) need Authorization: Bearer with the token in ~/.axis/token. The agent card advertises streaming: false: synchronous send and get only.
To run the daemon as a launchd or systemd user service instead:
axis daemon service install
axis daemon statusStable operator commands
| Command | Purpose |
|---|---|
axis version |
Build version, commit, Go version |
axis init |
Interactive cluster configuration (--config overrides ~/.axis/nodes.yaml) |
axis node facts |
This machine (axis facts is the same command) |
axis cluster status |
Cached snapshot under 5 minutes old; --live sweeps; --cached-only fails closed |
axis task place |
Advisory placement (--format text|json) |
axis placement explain |
Per-node ranking explanation |
axis task run |
Guarded execution with safety gates |
axis task delegate|status|approve|reject |
A2A task lifecycle |
axis model |
plan, start, await, query, stop, list, inspect, catalog, evict, resume |
axis reservations |
list, inspect, release, doctor |
axis doctor |
Config, SSH, daemon ownership, local AI backends (--strict) |
axis daemon |
status, refresh, invalidate, restart, service install|status|uninstall |
axis serve |
Local HTTP API (foreground; Unix socket by default) |
axis mesh |
Gossip mesh diagnostics: status, peers |
axis tui |
Full-screen cluster dashboard (also bare axis on a TTY) |
axis update |
Self-update from GitHub Releases |
Experimental and advisory commands
| Command | Purpose |
|---|---|
axis mcp serve |
Read-only MCP server over stdio |
axis mcp client |
MCP client (list, tools, call, …) |
axis agent |
Tool-calling agent loop, subordinate to observed facts |
axis ai / axis cortex |
Inference routing and vector-memory helpers |
axis chat and axis llm were removed. Use axis agent and axis ai route.
Full command notes: docs/current-state.md.
Warning
The daemon joins a UDP gossip mesh on port 42426 unless nodes.yaml disables it. Without discovery.secret, gossip accepts unsigned messages from any peer that can reach that port.
-
Set a shared secret on every node, or set
enabled: falseto turn discovery and the mesh off:discovery: enabled: true secret: "replace-with-a-long-random-string"
-
Allow UDP 42424 (beacons) and 42426 (gossip) only from your own network or VPN.
-
Keep the API on the Unix socket. If you pass
axis serve --addr, bind to127.0.0.1. -
A2A task commands refuse non-loopback
--addrtargets unlessAXIS_ALLOW_OFFBOX_BEARER=1is set. Configured remote-node names bypass that check and attach the local token. Until a cross-node auth model exists, use the target host's local Unix socket. See the token boundaries. -
~/.axis/tokenis the API credential. Don't commit or share it.
Report vulnerabilities privately: see SECURITY.md. Secret handling rules: docs/authority-secrets.md.
axis update --check
axis updateaxis update --all also updates other validated AXIS copies on PATH and in common install locations.
To roll back, re-run the installer with the tag you want. It replaces an existing AXIS binary in place:
curl -fsSL https://raw.githubusercontent.com/toasterbook88/axis/main/install.sh \
| AXIS_VERSION=v0.19.6 AXIS_REQUIRE_PINNED=1 \
AXIS_INSTALL_DIR="$HOME/.local/bin" bashUninstall
Stop the user service, then remove the binary. The last command also deletes your config and API token.
axis daemon service uninstall
rm "$HOME/.local/bin/axis"
rm -rf "$HOME/.axis"For a system-wide install, remove /usr/local/bin/axis with sudo instead.
| Doc | Contents |
|---|---|
docs/current-state.md |
Live product truth (trust this over older specs) |
docs/operator-quickstart.md |
Longer operator onboarding |
docs/architecture.md |
Layered architecture reference |
docs/daemon-service.md |
launchd and systemd user service |
docs/troubleshooting.md |
Common failures and fixes |
nodes.example.yaml |
Annotated cluster config |
docs/ |
Authority contracts (authority-*.md: reservation, secrets, execution, …) |
CHANGELOG.md |
Release history |
| Item | Value |
|---|---|
| Latest release | v0.19.6 (tag commit 990a836) |
| Supported security line | v0.19.x (see SECURITY.md) |
| Release builds | darwin and linux × amd64 and arm64 (reproducible GoReleaser builds) |
make build
make test| Target | What it does |
|---|---|
make build |
CGO_ENABLED=0 go build -trimpath -o axis ./cmd/axis/ |
make install-user |
Installs to ~/.local/bin/axis |
make install-system |
Installs to /usr/local/bin/axis (needs sudo) |
make test |
Hermetic go test ./... plus docs tool tests |
make test-race |
Tests with the race detector |
make lint |
gofmt, go vet, staticcheck |
make coverage |
hack/coverage-check.sh |
See CONTRIBUTING.md. main is protected: required CI checks and squash-only merges. AI agents: read AGENTS.md first.
MIT. Copyright © 2026 Smith Software Solutions.
axismcp.app · axismcp.tech · smithsolutionssc.com · @AXISBRIDGEMACOS on X