A hierarchical DAG engine for composable NixOS configuration management.
Tomato models system configurations as directed acyclic graphs organized in floors (levels). Each leaf node holds a NixOS configuration fragment. Gateway nodes point to subgraphs on the floor below. Walking the graph top-down in topological order composes a valid configuration.nix — which can be deployed to a NixOS machine via SSH with a single click.
mix setup
mix phx.serverOpen localhost:4000. A demo graph loads automatically with Networking, Firewall, System, and a Services gateway containing PostgreSQL and Nginx.
Floor 0 (root)
Input → Networking → System → Services (gateway) → Output
Firewall ↗
Floor 1 (inside Services)
Input → PostgreSQL → Output
Nginx ↗
- Leaf nodes hold Nix config fragments (e.g.
services.nginx.enable = true;) - Gateway nodes contain a subgraph on the floor below — composing complex configs from smaller pieces
- OODN node (Out-Of-DAG Node) holds global variables (
${hostname},${timezone}, etc.) referenced by any leaf on any floor via${key}placeholders - Edges define dependency order — the walker traverses nodes in topological order
- Generate — walks the graph, interpolates OODN variables, wraps fragments in a NixOS module skeleton → writes
.nixfile topriv/generated/ - Reconfigure — uploads
configuration.nixto the target NixOS machine via SSH/SFTP, runsnixos-rebuild switch
Real services start, stop, and reconfigure on a real NixOS machine. Change ${nginx_port} from 80 to 8080 in the OODN node → both the firewall rules and Nginx config update in one rebuild.
Click + Add Node to pick from predefined NixOS templates:
| Category | Templates |
|---|---|
| Stacks | Prometheus Stack (5 nodes), Grafana + Prometheus, Web Server Stack |
| System | System Base, Networking, Firewall, Admin User, Console |
| Web | Nginx, Nginx Reverse Proxy, Caddy |
| Database | PostgreSQL, MySQL, Redis |
| Services | OpenSSH, Docker, Tailscale, Fail2ban, Cron Jobs |
| Monitoring | Prometheus, Grafana |
| Packages | Dev Tools |
Stack templates create a gateway node with pre-wired child nodes — e.g. Prometheus Stack creates Prometheus Base + Node Exporter + Scrape configs + Alert Rules, all connected and ready to deploy.
NixOS merges list and attribute set options automatically — scrapeConfigs from multiple nodes get concatenated into one prometheus.yml.
The OODN node is a singleton on the canvas holding global key-value pairs:
hostname = tomato-node
timezone = Europe/Rome
locale = it_IT.UTF-8
keymap = it
nginx_port = 80
pg_port = 5432
Leaf nodes reference these with ${key} syntax. The walker interpolates them at generation time. Change a value once, every referencing node updates.
| Action | Effect |
|---|---|
| Click | Select node |
| Drag | Move node |
| Double-click gateway | Enter subgraph |
| Double-click leaf | Edit content |
| Cmd+click leaf | Edit content |
| Long-press / right-click | Context menu |
| Scroll / two-finger | Pan canvas |
| Pinch / Ctrl+scroll | Zoom |
Context menu actions: Connect from/to, Duplicate, Rename, Disconnect all, Delete, Reverse edge, Fit to view, Reset zoom.
To deploy generated configs to a NixOS machine, set your target via environment variables or config/deploy.secret.exs:
export TOMATO_DEPLOY_HOST=your-nixos-host
export TOMATO_DEPLOY_PORT=22
export TOMATO_DEPLOY_USER=root
export TOMATO_DEPLOY_PASSWORD=your-passwordOr copy the example file:
cp config/deploy.secret.exs.example config/deploy.secret.exsSee config/deploy.secret.exs.example for the format. This file is gitignored.
lib/tomato/
node.ex # Node struct — :input, :output, :leaf, :gateway
edge.ex # Directed edge between nodes on same floor
subgraph.ex # Self-contained DAG on a floor
graph.ex # Top-level container with subgraphs + OODN registry
oodn.ex # Out-of-DAG key-value pair
store.ex # GenServer — in-memory state, JSON persistence, PubSub
constraint.ex # DAG validation — cycles, structure, edges
walker.ex # Topological traversal + OODN interpolation → .nix output
deploy.ex # SSH/SFTP upload + nixos-rebuild switch
template_library.ex # Predefined NixOS config templates (leaf + gateway stacks)
demo.ex # Seeds demo graph on first run
lib/tomato_web/
live/graph_live.ex # Main LiveView — SVG canvas, sidebar, modals
assets/js/
hooks/graph_canvas.js # Drag-and-drop, zoom/pan, long-press context menu, Bezier edges
Each graph is a single JSON file in priv/graphs/. The Graph Manager (click filename in sidebar) lets you create, load, save-as, and delete graphs. The JSON file is the source of truth — loaded into memory on startup, flushed on every mutation with 200ms debounce.
Enforced on every mutation: no cycles (Kahn's algorithm), single :input/:output per subgraph, edges same-floor only, gateway-subgraph integrity.
mix deps.get # install dependencies
mix compile # compile
mix phx.server # start dev server at localhost:4000
iex -S mix phx.server # start with interactive shell
mix test # run tests
mix format # format code- Elixir 1.15+
- Erlang/OTP 26+
Apache License 2.0 — Copyright 2026 Alessio Battistutta. See LICENSE.
