Quartz Command fleet-management agent for SONiC switches — the SONiC
counterpart to the QuartzFire firewall agent. It enrolls a switch into
Quartz Command and maintains a
persistent mTLS control channel so the console can monitor and manage the
switch. Ships as a single static .deb that installs on both community SONiC
and Enterprise SONiC.
One-liner, on the switch (amd64):
curl -fsSL https://raw.githubusercontent.com/quartzsystems/quartz-sonic/main/scripts/install.sh | sudo sh
The same command updates an installed agent: the script always fetches
the latest release, dpkg -i upgrades in place, and the package restarts
quartz-sonic.service after the upgrade — enrollment state and identity in
/var/lib/quartz-sonic/ are untouched, so the switch reconnects as itself.
(To skip the script, the package itself is at
https://github.com/quartzsystems/quartz-sonic/releases/latest/download/quartz-sonic_amd64.deb.)
Generate an enrollment token in the Quartz Command console, then on the switch:
sudo quartz-sonic enroll '<TOKEN>'
The token pins the controller's issuing device CA by SHA-256 fingerprint, so
no public PKI trust is involved. Enrollment generates the device's Ed25519
identity on first run (/var/lib/quartz-sonic/, root-only), proves key
possession, and receives the mTLS client certificate the daemon then uses.
The device ID is derived from the public key
(QS-XXXX-XXXX-XXXX-XXXX) and shown by:
quartz-sonic status
An adopted device cannot re-enroll until it is revoked in the console.
sudo quartz-sonic unenroll
deletes the certificates and enrollment state and restarts the daemon, which
then idles unenrolled. This is local only — the fleet protocol has no
unenroll RPC — so also revoke/remove the device in the Quartz Command
console; until then it stays listed (offline) and its issued certificate
remains valid. The device keypair is kept, so a later enroll reconnects
under the same device ID; add --wipe-identity to delete the keypair too and
get a brand-new device ID on the next enrollment.
curl -fsSL https://raw.githubusercontent.com/quartzsystems/quartz-sonic/main/scripts/uninstall.sh | sudo sh
stops and disables the service, purges the package, and removes all local
state (/var/lib/quartz-sonic, /etc/quartz-sonic, /run/quartz-sonic).
Append -s -- --keep-state to keep /var/lib/quartz-sonic so a later
reinstall reconnects as the same enrolled switch. Uninstalling does not
notify the controller either — unenroll first or revoke the device in the
console.
quartz-sonic.service (installed enabled, Restart=always) connects to the
assigned gateway with mTLS and holds the ControlStream open:
- answers the console's proxied
/api/…calls — currentlyGET /api/system/info(SONiC version, platform, HWSKU, serial),GET /api/system/health,POST /api/system/reboot, and the Configure → Switching pages'GET /api/switching/ports,GET /api/switching/port-channels, andGET /api/switching/vlans— backed by CONFIG_DB/STATE_DB/COUNTERS_DB and the platform CLIs; - pushes
DeviceStatsevery ~30 s (CPU/mem/disk gauges plus true byte figures, uptime, best-effort public IP, and the top ~8 interfaces by traffic from COUNTERS_DB); - pushes
SecurityTelemetryevery ~60 s (all service blocks reported absent — switches have no IPS/app-control/geo/content-filter); - renews its client certificate at 2/3 of cert lifetime and reconnects with jittered exponential backoff whenever the stream drops.
Logs go to journald: journalctl -u quartz-sonic.
| path | content |
|---|---|
/usr/bin/quartz-sonic |
single binary: enroll, unenroll, status, run |
/var/lib/quartz-sonic/ |
identity, certificates, state (0700) |
/etc/quartz-sonic/ |
reserved for config (none needed yet) |
/run/quartz-sonic/status.json |
live status for quartz-sonic status |
On Linux (or WSL), with musl-tools installed:
./scripts/build-deb.sh
produces target/x86_64-unknown-linux-musl/debian/quartz-sonic_<ver>_amd64.deb
— a static musl build, so the one package runs across the Debian bases the
SONiC images use. (arm64 is a follow-up: TARGET=aarch64-unknown-linux-musl ./build-deb.sh with a musl cross toolchain.)
Development builds and the test suite run on any host (no protoc needed — protos compile with protox):
cargo test
CI (.github/workflows/build-deb.yml) tests and packages on every push and
uploads the .deb as an artifact.
The VERSION file at the repo root is the single source of truth. build.rs
bakes it into the binary and fails the build if Cargo.toml's [package]
version drifts from it (cargo-deb stamps the package from Cargo.toml, so the
two must agree). To release:
- Bump
VERSIONand theversioninCargo.tomlto match. - Tag and push:
git tag v$(cat VERSION) && git push --tags.
CI verifies the tag matches VERSION, then publishes the GitHub release with
the versioned .deb plus the stable-named quartz-sonic_amd64.deb the
install one-liner fetches.
The gRPC contract lives in proto/quartzcommand/… and is copied verbatim
from quartz-command (backend/proto/…). Do not edit it here; it is shared
fleet-wide. The qf_version fields are likewise fleet-wide: quartz-sonic
reports its own agent version in them.