self 20f94aad8a fix(resources): reconcile bound entities after restart
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.
2026-07-13 12:58:50 +02:00
2026-07-13 12:58:50 +02:00
2026-06-08 19:54:05 +02:00
2026-03-15 22:23:54 +01:00
2026-06-12 19:30:32 +02:00
2026-05-29 14:39:18 +02:00
2026-06-01 22:51:02 +02:00
2026-06-06 15:19:06 +02:00
2026-05-24 19:53:08 +02:00
2026-03-20 15:41:40 +01:00
2026-04-09 20:01:17 +02:00
2026-05-24 16:25:49 +02:00
2026-06-28 16:34:05 +02:00
2026-04-09 20:01:17 +02:00
2026-07-03 19:14:48 +02:00
2026-04-09 20:01:17 +02:00

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
S
Description
Legion Kommando & Kontrolle TS/Svelte/Electron App with CLI
Readme 108 MiB
Languages
TypeScript 92.9%
Svelte 5.9%
JavaScript 0.4%
CSS 0.3%
Shell 0.2%
Other 0.3%