#Cron-driven scans

The daemon ships an in-process scheduler that wakes every 60 seconds, evaluates every [[schedule]] entry in nyx-agent.toml, and triggers a scan through the same path the SPA's "Scan now" button uses. There is no external cron process to wire up. The scheduler runs inside nyx-agent serve, so the daemon must be running for entries to fire.

#Config

[[schedule]]
cron = "0 3 * * 1"          # Monday at 03:00 local time
repo = "nyx-agent"            # optional; omit to scan every enabled repo
label = "weekly-monday-3am" # surfaced in tracing + the UI

The cron expression is the canonical 5-field Unix form (minute hour day-of-month month day-of-week). Day-of-week uses the standard 0=Sunday, 1=Monday, ..., 7=Sunday convention; the scheduler translates internally to the underlying cron crate's ordinals so operator-facing config matches what crontab(5) documents.

Common patterns:

Expression Fires
0 3 * * 1 Every Monday at 03:00
0 * * * * Every hour on the hour
*/15 * * * * Every 15 minutes
0 3 1 * * Midnight UTC on the 1st of each month (use the local clock)

The scheduler debounces within a minute, so a 60-second wake that lands twice in the same minute fires the entry exactly once. The trigger is fire-and-forget: a saturated dispatcher returns HTTP-429 backpressure to the API and the scheduler logs a warn! and skips that fire (the next valid minute will retry).

#Keeping the daemon up

The scheduler relies on the daemon being alive. Two host-supervisor recipes ship under packaging/.

#systemd (Linux)

sudo install -m 0644 packaging/nyx-agent.service /etc/systemd/system/
sudo install -m 0644 packaging/nyx-agent.timer /etc/systemd/system/
sudo install -m 0644 packaging/nyx-agent-scan.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now nyx-agent.service
# Optional: a host-managed timer that also kicks a one-shot scan.
sudo systemctl enable --now nyx-agent.timer

nyx-agent.service runs nyx-agent serve --headless. The nyx-agent.timer + nyx-agent-scan.service pair is optional: pick either the in-process [[schedule]] entries OR the systemd timer, not both, to avoid double-firing.

#launchd (macOS)

The shipped plist is a per-user LaunchAgent, not a system LaunchDaemon. Install it under your own ~/Library/LaunchAgents/ so the daemon runs as your user account, not as root:

install -m 0644 packaging/com.nyx.agent.plist \
  "$HOME/Library/LaunchAgents/com.nyx.agent.plist"
launchctl bootstrap gui/$(id -u) "$HOME/Library/LaunchAgents/com.nyx.agent.plist"

The plist runs nyx-agent serve --headless with KeepAlive=true, so the daemon stays up across login sessions. Periodic kicks come from the in-process scheduler reading [[schedule]] entries out of nyx-agent.toml; there is no separate launchd calendar trigger to configure.

Do not install this file under /Library/LaunchDaemons/. That path runs the daemon as root, which contradicts the systemd recipe's DynamicUser=yes hardening and broadens the blast radius of every OS_COMMAND / PATH_TRAVERSAL / SSRF surface the agent exposes to its own configured repositories.

#Verifying

journalctl -u nyx-agent.service -f
# or, on macOS:
log stream --predicate 'subsystem == "com.nyx.agent"'

Look for a scheduler: trigger ok log line at the configured time.