Keep the applied desired signature unknown when entity actors rehydrate. Verify that a bound DNS zone performs provider reconciliation on its first apply instead of assuming its binding proves convergence.
Legion Kommando & Kontrolle
Cloud provisioning for Tribes nodes, an Electron application with Svelte and TypeScript.
Legion is the desktop control application for planning, provisioning, and operating Tribes cloud nodes. It keeps infrastructure state local and encrypted, guides the operator through provider setup and first-node creation, and reconciles the desired plan with the running cloud resources.
Major features
- Local-first encrypted state for provider credentials, plans, and node inventory.
- Guided setup for bootstrapping the first Tribes server.
- Cloud provider account configuration and catalogue-backed server selection.
- Planned versus active infrastructure views, so operators can review intended changes before applying them.
- Provider-backed provisioning and reconciliation for creating and updating nodes.
- DNS and domain planning for Tribes deployments.
- Desktop GUI plus a production CLI for inspection and automation.
Project Setup
Install dependencies
$ npm install
Run the app
$ npm run dev
$ VITE_LOCALE=de-DE npm run dev # Force UI language
Optional XState tracing
For local actor debugging, you can enable a dev-only XState trace log:
$ LEGION_TRACE_XSTATE=1 npm run dev
When enabled, the main process writes a JSONL trace file under the Electron user data logs
directory and prints the full path once on startup. This is local-only and intended for
development debugging of actor events, actions, and state transitions.
Run the CLI
Production builds include a compiled Node CLI, exposed as the legion package bin when
installed as a package. In a checkout, build first and run the compiled CLI with npm:
$ npm run build
$ npm run cli:prod -- node list
During development, use the repo-local wrapper:
$ devenv shell legion-dev node list
$ devenv shell legion-dev provider configure hetzner --api-key-env HCLOUD_TOKEN
$ devenv shell legion-dev node create --name node-a --provider hetzner --region fsn1 --instance cax11
$ devenv shell legion-dev apply
Or run the TypeScript entry point via npm:
$ npm run cli -- node list
Operator config
Legion keeps encrypted desired/tracked state in legion-state.json, and keeps
non-secret operator defaults in a sibling legion-config.jsonc file. The file is
generated on startup when missing. By default it lives under the same state root
as legion-state.json ($LEGION_STATE_DIR when set, otherwise the platform user
config directory such as ~/.config/legion-kk).
legion-config.jsonc currently controls managed DNS record TTL, public
recursive resolvers used for DNS propagation/certificate checks, and the ACME
directory URL. Tests and automation can still override only the DNS TTL with
LEGION_DNS_RECORD_TTL_SECONDS.
Quality checks
$ npm run precommit
$ npm run lint
$ npm run format:check
$ npm test
$ npm run check
Credentialed integration and E2E runs are documented in TESTING.md.
Architecture notes
For the current runtime layout, daemon security posture, GUI boundary, XState actors, and recovery loops, see docs/arch.md.
Refresh the pinned Guix base channel
When ../guix-fork changes, refresh the Guix pin in ../guix-tribes first:
$ (cd ../guix-tribes && ./scripts/update-base-channels-pin)
$ npm run generate:guix-base-channel
Use ../guix-tribes/scripts/update-base-channels-pin --official when switching
back to the mirrored official Guix channel.
Refresh the default kexec installer
The default kexec installer image is built and published by the Guix mirror
builder from the kexec-installer branch in guix-tribes. After changing the
installer definition or pinned Guix channels, move that branch to the desired
signed guix-tribes commit so the mirror builder can publish
tribes-1/guix-kexec-installer-<system>-<commit>.tar.gz.
After the mirror publishes the matching .sha256, refresh Legion's generated
pin:
$ npm run update:kexec-installer-pin
Regenerate OpenAPI clients
$ npm run update:hcloud-client
$ npm run update:serverspace-client
Refresh bundled data
npm run update:provider-catalogs reads provider credentials from the environment:
# Hetzner
export HCLOUD_TOKEN=...
# or
export HETZNER_API_TOKEN=...
# OVH
export OVH_APPLICATION_KEY=...
export OVH_APPLICATION_SECRET=...
export OVH_CONSUMER_KEY=...
# optional, defaults to ovh-eu
export OVH_ENDPOINT=ovh-eu
# Scaleway
export SCW_ACCESS_KEY=...
export SCW_SECRET_KEY=...
export SCW_DEFAULT_PROJECT_ID=...
# optional, defaults to fr-par-1
export SCW_DEFAULT_ZONE=fr-par-1
$ npm run update:provider-catalogs
When you enter the devenv shell, pre-commit hooks are generated from devenv.nix and run the same npm-based lint and format checks before commit.
Build installers
# For windows
$ npm run build:win
# For macOS
$ npm run build:mac
# For Linux
$ npm run build:linux