nix-ota/Cargo.toml
0m.ax 42b2ce4d1d Initial nix-ota implementation
Self-hostable OTA update system for NixOS fleets: a control server,
device agent, publisher CLI, and NixOS modules that ship prebuilt
system closures from a binary cache to devices that don't have the
flake.

- crates/common: signed manifest types (ed25519), store-path validator
- crates/server: axum + sqlite + HTMX dashboard, channel/device API
- crates/agent: poll, verify signature + revision, nix copy, switch,
  health check, magic-rollback on failure
- crates/publisher: keygen + sign + publish CLI for operators/CI
- nix/modules: NixOS modules for server and agent
- nix/tests/ota.nix: end-to-end VM test exercising publish A -> B ->
  broken C -> rollback to B (passes)

The control server never holds the signing key; manifests are signed
offline and verified against a pinned public key on each device.
2026-05-25 14:58:42 +02:00

33 lines
1.1 KiB
TOML

[workspace]
resolver = "2"
members = ["crates/common", "crates/server", "crates/agent", "crates/publisher"]
[workspace.package]
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
repository = "https://linus.dyrehytten.dk/max/nix-ota"
[workspace.dependencies]
anyhow = "1"
thiserror = "1"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tokio = { version = "1", features = ["full"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
clap = { version = "4", features = ["derive", "env"] }
reqwest = { version = "0.12", features = ["json", "rustls-tls"], default-features = false }
ed25519-dalek = { version = "2", features = ["rand_core", "pkcs8", "pem"] }
rand = "0.8"
base64 = "0.22"
hex = "0.4"
time = { version = "0.3", features = ["serde", "formatting", "parsing", "macros"] }
axum = "0.7"
tower = "0.5"
tower-http = { version = "0.6", features = ["trace", "fs"] }
sqlx = { version = "0.8", features = ["runtime-tokio-rustls", "sqlite", "time", "migrate"] }
askama = { version = "0.12", features = ["with-axum"] }
askama_axum = "0.4"
ulid = { version = "1", features = ["serde"] }
sha2 = "0.10"