The Agentic SRE for Zerops Cloud deployments.
Nomad watches a live Zerops deployment, detects anomalies in forwarded syslog, diagnoses failures with Claude, and applies real fixes through the Zerops REST API.
How it works · Quickstart · API · Development · Architecture · Demo script
Nomad is an agentic SRE for a Zerops project. It watches the services in the project. It receives their syslog. It detects bursts of errors. It uses Claude to compare the failure with recent log lines. This comparison finds the root cause.
Nomad then applies a fix. The fix is a restart or rollback through the Zerops REST API, or a heal over the private network for the bundled demo service. Nomad stores every incident in a searchable, filterable timeline. Nomad uses Zerops primitives throughout: syslog log-forwarding for ingestion, the REST API for remediation, and zerops.yaml for topology. Zerops is the platform that Nomad monitors, not just its host.
- Observe. The
workerruns a UDP syslog listener. It accepts RFC 5424 and tolerates RFC 3164. Any service in the project forwards its logs to this listener. Thetargetdemo service forwards logs over the private network. - Detect. Every 15 seconds the anomaly scanner looks for bursts of
errororcriticallines. A burst is five or more lines for one service within 60 seconds. A burst opens an incident. - Diagnose. Claude receives the affected service, the symptom, and the last ~50 log lines for that service. It returns a root cause, a suggested fix, and a confidence value. Without
ANTHROPIC_API_KEY, Nomad uses a heuristic fallback. - Fix. Nomad records the suggested fix. Apply it from the dashboard, or Nomad can apply the fix itself when the failure mode is safe and well understood.
- Log. The incident and its fix stay in the database. You can view them in the dashboard's incident timeline.
All run in one Zerops project. frontend, api, worker, and target are Node.js 22 runtimes defined in zerops.yaml. db is a managed Postgres 18.
| Service | Runtime | Port | Role |
|---|---|---|---|
frontend |
Next.js (nodejs@22) | 3000 | Landing page + dashboard. Data tables with facets, cursor pagination, and a row-detail drawer for diagnosis and "Apply fix" |
api |
Fastify (nodejs@22) | 3000 | REST backend: incidents, logs, chat, demo break/heal, fix application |
worker |
Node (nodejs@22) | 3001, UDP 5140 | Syslog listener, anomaly detection, Claude diagnosis, remediation |
target |
Node (nodejs@22) | 4000 | Small demo app to break and heal; emits RFC 5424 syslog to worker:5140 |
db |
Postgres 18 (managed) | N/A | incidents, logs, fixes tables via Drizzle ORM |
The schema and indexes are in db/src/schema.ts. Migrations run automatically on deploy. They are gated by zsc execOnce and are idempotent.
- Break the target:
POST /api/demo/break→ target starts emitting error lines over UDP toworker:5140. - The worker parses each line and stores it in the
logstable. - The anomaly scan trips the burst threshold and opens an incident (
status: open→diagnosing). - Claude returns a root cause and a suggested fix (
status: fix_suggested), stored as a row infixes. - Apply the fix:
POST /api/fixes/:incidentId/apply→ the target is healed over the private network, the fix is markedapplied, and the incident moves tofixedwith a resolved timestamp.
For the demo service, this needs no credentials. For real services, the fix is a restart or rollback through the Zerops REST API. This requires ZEROPS_API_TOKEN.
git clone https://github.com/devroy10/nomad.git && cd nomad
pnpm install
pnpm -r run buildRun the whole stack locally:
pnpm dev:api # API on :3000
pnpm dev:worker # worker on :3001, syslog on :5140
pnpm dev:target # target on :4000
pnpm dev:frontend # Next.js on :3000 (dashboard)api and worker need DATABASE_URL pointing at a Postgres that you can reach. Apply the schema with pnpm db:migrate. The frontend proxies /api/* to the API service.
Environment variables for the deployed services:
| Variable | Used by | Purpose |
|---|---|---|
DATABASE_URL |
api, worker | Postgres connection. Auto-injected by Zerops via ${db_connectionString} |
ANTHROPIC_API_KEY |
api, worker | Claude diagnosis and chat. Without it, Nomad uses a heuristic fallback |
ZEROPS_API_TOKEN |
api | Bearer token for the Zerops REST API. It enables restart of a service stack and rollback of an app version |
CLAUDE_MODEL |
api, worker | Claude model override (default claude-3-5-sonnet-latest) |
zerops.yaml at the repo root defines the api, worker, target, and frontend setups. The managed db is provisioned in the Zerops dashboard. Pushing to the repo triggers a webhook build for each service. zsc execOnce runs the DB migration on first start.
To watch a service outside this project, point its Advanced Observability → Log Forwarding at the worker's public syslog endpoint. Cross-project forwarding requires Direct Port Access on worker UDP 5140 in the GUI.
All routes live on the api service.
| Method | Path | Purpose |
|---|---|---|
| GET | / |
Health probe |
| GET | /api/incidents |
Incident data-table backend: filters, facets, cursor pagination, chart series |
| GET | /api/logs |
Same, for stored syslog entries |
| POST | /api/chat |
Ask about open incidents ({ "question": "..." }) |
| POST | /api/demo/break |
Break the target demo service |
| POST | /api/demo/heal |
Heal the target demo service |
| GET | /api/fixes/:incidentId |
Suggested fixes for an incident |
| POST | /api/fixes/:incidentId/apply |
Execute the incident's suggested fix |
target exposes GET /, POST /break, POST /heal, and POST /simulate-error. worker exposes GET / (health) and listens on UDP 5140.
- Syslog ingestion: parses RFC 5424 and tolerates RFC 3164, including structured data. Nomad drops malformed lines.
- Anomaly detection: bursts of
errororcriticallines (5 or more for one service in 60 seconds) open an incident. A 5-minute dedup window prevents storming. - Claude diagnosis: Claude compares the burst with recent logs for the affected service. It returns
rootCause,fix,confidence, andreasoningas strict JSON. - Remediation: Nomad heals the demo service over the private network. For real services, it restarts the service or rolls back the app version through the Zerops REST API when a token is configured.
- Dashboard: filterable incident and log tables with facets, date-range charts, a row-detail drawer, and a chat panel for incident history.
- Idempotent migrations:
zsc execOnce ${appVersionId}runs the Drizzle migration on container start. It runs exactly once per version.
- Restart or rollback of real services requires
ZEROPS_API_TOKENand a service or version id. The demo'sheal-targetfix works without any credentials. - The
targetservice is a demo fixture, not a supported integration. It exists so you can demonstrate the whole loop. - Cross-project log forwarding needs Direct Port Access on the worker's UDP 5140, configured in the Zerops GUI.
- Without
ANTHROPIC_API_KEY, diagnosis and chat fall back to heuristics. Nomad returns explicit "not configured" messages.
pnpm -r run typecheck # type-check all workspaces
pnpm -r run build # compile all workspaces to dist/
pnpm db:generate # regenerate Drizzle migrations
pnpm db:migrate # apply migrations
pnpm db:push # push schema without migration filesMonorepo layout (pnpm workspaces):
api/: Fastify server, data-table query backend, Claude chat, Zerops REST clientworker/: syslog listener, anomaly scanner, diagnosis pipeline, remediationtarget/: breakable demo service emitting syslogfrontend/: Next.js landing page and dashboarddb/: Drizzle schema, client, migration runnershared/: types shared across workspacespackages/registry/: the data-table system used by the dashboard (facet filters, cursor pagination, charts)docs/: technical spec, demo script, and challenge notes
MIT
