Skip to content
Sistemo
GitHub Docs Install

Architecture

┌──────────────────────────────────────────────────┐
│  sistemo binary (CLI + daemon)                   │
│                                                  │
│  CLI ──HTTP──▶ Daemon (:7777) ◀── Dashboard     │
│                  │                               │
│         ┌───────┼───────┐                        │
│         ▼       ▼       ▼                        │
│     Mach 1  Mach 2  Mach 3                       │
│      ┌─────┐ ┌─────┐ ┌─────┐                   │
│      │netns│ │netns│ │netns│                    │
│      │ TAP │ │ TAP │ │ TAP │                    │
│      │ FC  │ │ FC  │ │ FC  │                    │
│      └──┬──┘ └──┬──┘ └──┬──┘                   │
│         └───────┼───────┘                        │
│          sistemo0 bridge (10.200.0.1/16)         │
│       Machines: 10.200.0.2, .3, .4, ...          │
│                                                  │
│  State: SQLite (~/.sistemo/sistemo.db)           │
│  Machines: ~/.sistemo/vms/{id}/                 │
└──────────────────────────────────────────────────┘

Design principles

Principle Implementation
Single binary CLI + daemon in one ~15 MB file, zero dependencies beyond Linux + KVM
Single machine Everything runs on one host, no clustering
SQLite State in ~/.sistemo/sistemo.db — machines, volumes, networks, admin credentials, audit history. No Postgres or Redis
Firecracker One process per machine — fast boot, hardware-level isolation via KVM
Dashboard Built-in Svelte web UI at /dashboard/ — manage machines, images, volumes, networks, and terminal from the browser
Terminal xterm.js in the browser, WebSocket to daemon, SSH into the machine

Data directory

All state lives under ~/.sistemo/ (override with --data-dir):

~/.sistemo/
├── sistemo.db          # SQLite database (machines, volumes, networks, IPs, port rules, admin credentials, audit history)
├── config.yml          # Config file (optional)
├── bin/firecracker     # Firecracker binary
├── kernel/vmlinux      # Guest kernel
├── ssh/sistemo_key     # ED25519 key pair for machine SSH
├── vms/{id}/           # Per-machine directory (rootfs, logs, specs)
├── images/             # Cached rootfs downloads
└── volumes/            # Persistent volumes

Networking

A shared Linux bridge (sistemo0, 10.200.0.1/16) connects all machines. Each machine gets a unique IP and runs inside its own network namespace:

  • A veth pair connecting the namespace to the sistemo0 bridge
  • A namespace bridge wiring the veth to a TAP device
  • A TAP device for Firecracker's virtual NIC
  • A unique IP (10.200.0.2, .3, .4, ...) allocated from SQLite
  • SMTP blocked — ports 25, 465, 587 are dropped per-namespace

Machines on the default bridge can communicate with each other. Named networks provide isolation — each named network gets its own bridge (br-<name>), subnet, and NAT rules. An nftables SISTEMO-ISOLATION chain blocks cross-bridge traffic. See Networking and Port expose for details.

Machine lifecycle

  1. Deploy: CLI inserts a record into SQLite, sends create request to daemon
  2. Create: Daemon copies rootfs, injects SSH key + /init, creates network namespace, launches Firecracker
  3. Running: Firecracker process runs inside the namespace, SSH is available for terminal/exec
  4. Stop: Firecracker process is terminated (SIGTERM then SIGKILL), namespace is cleaned up, rootfs is preserved
  5. Start: New namespace + Firecracker process using the existing rootfs
  6. Delete: Process killed, namespace cleaned up, machine directory removed

Auto-recovery

The daemon runs a reconciler every 30 seconds that:

  • Detects dead Firecracker processes (via kill -0)
  • Cleans up orphaned network namespaces and port expose rules
  • Removes stale machine entries from the in-memory registry

On daemon restart, it rehydrates from disk — scanning ~/.sistemo/vms/ for machines that still have running Firecracker processes. Port expose rules are restored from SQLite.

Security

Warning

The daemon runs as root (required for network namespaces and KVM). The API listens on localhost:7777.

  • Dashboard authentication — The web dashboard uses JWT-based sessions. On first visit, you create an admin account (username + password). Credentials are stored (bcrypt-hashed) in SQLite
  • Localhost bypass (CLI) — API requests from 127.0.0.1 or ::1 skip authentication, so CLI commands work without credentials on the local machine
  • Remote access — Set the HOST_API_KEY environment variable (env-var only, not in config.yml) if you expose the daemon beyond localhost. Use a reverse proxy with TLS for production
  • SSH keys — ed25519, auto-generated at ~/.sistemo/ssh/sistemo_key
  • Machine isolation — Firecracker's KVM + separate network namespaces per machine