Velnor Documentation

Observability

Observability

Velnor exposes local, read-only operator views. They answer different questions and have different retention. A healthy local view does not prove that GitHub accepted a result.

Navigation: ← Operator guide · Index · Troubleshooting

First checks

velnorctl status
velnorctl status --json
velnorctl get hosts
velnorctl get instances
velnorctl get slots
velnorctl get runners
velnorctl get jobs
velnorctl get runs
velnorctl get queue
velnorctl events
velnorctl logs JOB_UID_OR_RUN_ID
velnorctl telemetry
velnorctl top host
velnorctl top instances
velnorctl top slots
velnorctl top jobs
velnorctl top storage
velnorctl doctor --url https://github.com/ORG --name NAME --slots N
velnorctl preflight --config-dir DIR --work-dir DIR
velnorctl capabilities export
velnorctl capabilities check JOB_MESSAGE.json
velnorctl run list
velnorctl run view RUN_ID
velnorctl run watch RUN_ID
velnorctl run logs RUN_ID

These are read-only views. velnorctl top TARGET requires one of host, instances, slots, jobs, or storage; it queries that resource collection and is not a metrics backend. velnorctl wait takes a snapshot, watches for changes, and resnapshots when its bounded cursor expires.

RUN_ID is a numeric workflow-run ID. JOB_UID_OR_RUN_ID is either that bare numeric run ID or the exact job UID; neither subject uses a run/ or job/ prefix.

The control socket is normally /run/velnor/<instance>/control.sock. The admin socket is separate, but mutation routes are currently unsupported; /v1/info reports mutations:false. Reconciliation, storage mutation, diagnostics-bundle, and run mutation commands therefore cannot be used as operator actions today.

doctor checks registered runners against GitHub. preflight checks local configuration and execution prerequisites. Capability export/check inspects the strict capability manifest and a job message. Run list/view/watch/logs are read-only; run cancel, rerun, download, dispatch, and open are unavailable. Run watch currently treats its run identifier as a cursor and does not filter events by run identity.

Health

The guardian writes an atomic health.json snapshot. An optional health.sock provides a one-request fast path; if it is absent or unusable, read the file. A missing or invalid file means health is unavailable, not healthy.

For the packaged single-instance service, the exact paths are /var/lib/velnor/health.json and /var/lib/velnor/health.sock. For a templated instance named NAME, they are /var/lib/velnor-NAME/health.json and /var/lib/velnor-NAME/health.sock. These paths come from systemd's StateDirectory; custom --config-dir or storage settings can change the runtime directory, so use the instance's configured state directory when those settings are in use.

Health includes control and journal state, GitHub connectivity, routing and runner-group proof, desired/actual/ready capacity, executor/backend readiness, queue and outbox age, lifecycle state, and alert codes.

Interpret states carefully:

  • ready: control, journal, routing, registration, executor, and capacity checks permit work.
  • degraded: Velnor is alive but a dependency, routing proof, registration, or capacity condition is impaired.
  • not_ready: Velnor must not accept work, for example when control/journal is unavailable or no slot/permit is ready.

controller-metrics.json is a current local snapshot: process counts, reconcile timing, journal/WAL bytes, and CPU totals. Phase CPU buckets are currently zero; do not treat them as measured per-phase metrics. There is no Prometheus endpoint, dashboard, SSE endpoint, or HTTP health endpoint.

Jobs, events, logs, and telemetry

Resource queries cover hosts, instances, slots, runners, jobs, runs, queue, events, reservations, and leases. Use the repository and workflow/job identity shown in a job or run record to correlate with GitHub. When local records are ambiguous, correlate timestamps with the GitHub workflow run and job ID; Velnor does not provide a separate dashboard correlation view.

velnorctl events reads a bounded event stream. Watches retain at most 4,096 items. Event cursors are opaque and expire; resnapshot before continuing.

velnorctl logs JOB_UID_OR_RUN_ID reads bounded local log records (up to 16,384 records or 16 MiB). Log cursors expire with retention. --step and --failed are accepted by the CLI but currently do not filter results. Runner forensic files are lifecycle.log, broker.log, registry.log, and daemon.log; each has an active file and one rotated .1 generation. These writers are best effort.

GitHub receives separate log forms: live masked lines over the Results Service WebSocket, uploaded step/job blobs, and timeline feed updates. Local logs can show that publishing was attempted; GitHub is the authority for what it actually received and rendered.

velnorctl telemetry returns versioned records with opaque cursors. A cursor is a continuation position, not a timestamp or record ID to interpret. When a page has more records, next_cursor tells the next request where to continue; dropped_before reports the oldest available position when retention has already discarded earlier records. Example response shape:

{
  "records": [{"cursor": "opaque", "kind": "..."}],
  "next_cursor": "opaque",
  "dropped_before": "opaque"
}

The default bounds are 4,096 records and an 8 MiB file. Rotation drops old generations; telemetry is not an archive. If a cursor expires, start with a fresh snapshot/page. The optional trace file is <config>/logs/trace.jsonl, rotated at 32 MiB with one .1. OTLP (OpenTelemetry Protocol) export is optional: set VELNOR_OTLP_ENDPOINT to send traces to an OTLP collector; export failure does not stop the runner.

Durability and visibility limits

The SQLite control journal durably stores events and recovery intent. Normal query, log, and storage projections are not fully rehydrated after service recreation, so a reopened service can have durable events while resource or log views are empty. Treat an empty projection after restart as a visibility limitation, not proof that no work occurred.

Completion recovery uses local outbox state plus remote acknowledgement. An outbox entry is retained until GitHub acknowledges completion. Inspect local outbox age and completion-related events when execution succeeded locally but GitHub remains pending.

There is no currently available diagnostics bundle command. Fixture reports and host-doctor scripts are maintainer/evidence tooling, use synthetic inputs in some cases, and are not live-service observability.

Correlating a GitHub job

  1. Copy the GitHub repository, workflow run, job, and queued/start timestamps.
  2. Query velnorctl get runs, get jobs, and events.
  3. Confirm runner, slot, labels, backend, admission, and completion state.
  4. Inspect velnorctl logs JOB_UID_OR_RUN_ID and velnorctl telemetry for the same subject and time window.
  5. Compare GitHub's rendered logs/timeline/result with Velnor's local terminal and outbox evidence.

The broker delivers a job reference; the run service supplies the full job. Therefore a broker-delivery event without an acquired job is not execution, and local execution completion without remote acknowledgement is not GitHub completion.