Jallarhorn

Jallarhorn documentation

Install Jallarhorn, configure sensors, wire up alerting, federate across sites, and integrate with your own tooling. Everything is on this page; use the left sidebar to jump.

Downloads

Pick the installer for your platform. Each download is signed (Cosign keyless via GitHub OIDC) and ships with an SPDX SBOM. The installer will fetch and configure required components on first run.

PlatformFormatNotes
Windows latest .exe installer Windows 10/11, Server 2019+. amd64/arm64. Installs as a Windows service. Uninstaller included.
Linux latest .deb / .rpm / tarball Any glibc-2.31+ distro. amd64 / arm64 / armv7. systemd units included. One-liner: curl -fsSL https://jallarhorn.com/install.sh | sh.
macOS latest tarball macOS 12+. Apple Silicon and Intel. launchd plist. brew install jakeharlan73/tap/jallarhorn for the Homebrew-managed path.
Docker ghcr.io/jakeharlan73/jallarhorn-control:latest Multi-architecture image. Tags pin to releases; :latest tracks main.
Kubernetes Helm chart at helm/jallarhorn/ Deployments for control + sensor, DaemonSet for per-node sensors, StatefulSet for the event-queue component, ServiceAccount + Ingress.

Verifying a release

Every release artifact has a detached .sig from Cosign keyless signing.

cosign verify-blob \
  --certificate-identity-regexp 'https://github.com/Jakeharlan73/jallarhorn' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com \
  --signature jallarhorn-control_linux_amd64.tar.gz.sig \
  jallarhorn-control_linux_amd64.tar.gz

Install & configure

Three executables — jallarhorn-control (REST + dashboard + alert engine, port 28090), jallarhorn-sensor (polling agent), and jallarhorn-farm (multi-site federation, port 28092). The installer fetches the required runtime components on first run.

Quickstart (Docker)

docker run -d \
  --name jallarhorn-control \
  -p 28090:28090 \
  -v jallarhorn-data:/var/lib/jallarhorn \
  -e DATABASE_URL='postgres://jallarhorn:jallarhorn@db:5432/jallarhorn?sslmode=disable' \
  -e JWT_SECRET="$(openssl rand -hex 32)" \
  -e CONTROL_SECRET="$(openssl rand -hex 32)" \
  -e JALLARHORN_ENCRYPTION_KEY="$(LC_ALL=C tr -dc 'A-Za-z0-9' </dev/urandom | head -c 32)" \
  ghcr.io/jakeharlan73/jallarhorn-control:latest

The repo ships a production-shaped docker-compose.yml wiring every required component. docker compose up -d brings up the full stack.

Native install

Download from the Downloads table above. The installers drop the executable into /opt/jallarhorn (Linux), /usr/local/jallarhorn (macOS), or %PROGRAMFILES%\Jallarhorn (Windows); register the service; and write a default jallarhorn.env next to it.

First-run bootstrap

The first user to register is automatically promoted to admin and the bootstrap completes. After that, registration is invite-only (JALLARHORN_INVITE_ONLY=true by default). Generate further invites from Settings → Users → Invite; tokens are single-use and expire in 7 days. Set JALLARHORN_INVITE_ONLY=false to disable the gate for open self-hosted installs.

Environment variables

VariablePurpose
DATABASE_URLDatabase connection string. The installer prepares the database for you.
JWT_SECRETHMAC secret for session JWTs. 32+ hex bytes.
CONTROL_SECRETShared secret between control and sensor over /api/*.
JALLARHORN_ENCRYPTION_KEY32-byte symmetric key for at-rest credential encryption (SSH passwords, SNMP communities, etc.). Rotation procedure in the security page.
HTTP_LISTENControl HTTP listen, default 127.0.0.1:28090. Bind to 0.0.0.0 to expose.
JALLARHORN_LICENSE_URLLicense server. Defaults to https://license.jallarhorn.com.
JALLARHORN_LICENSE_MODEfail-open (default) keeps collecting if the license server is unreachable; fail-closed halts after the grace period.
JALLARHORN_INVITE_ONLYtrue (default) requires an invite token after the bootstrap admin; false opens registration.
JALLARHORN_SMTP_HOST / _PORT / _FROMSMTP relay for outbound alert email. STARTTLS expected.

Sensor catalog

Jallarhorn ships 17 native sensor plugins covering the protocols a typical per-sensor tool covers, plus five generic frameworks (below) that close the long tail. Every sensor has a 30-second default poll, a per-sensor override, a warn threshold, an error threshold, and structured channel output for graphing.

Network probes

PluginChannels
ICMP / PingRTT, jitter, packet loss
TCP portreachability, banner read, latency
DNSA / AAAA / MX / CNAME / TXT / DNSSEC
SSL certexpiry days, chain validation, hostname mismatch
HTTPstatus, content match, latency, redirect handling

Device polling

PluginChannels
SNMP (v1/v2c/v3)get, walk, counter wrap, delta rates
SSHdisk, load, memory, uptime, CPU, custom script
WinRMCPU, memory, disk, service, process, custom WQL / PowerShell

System & services

PluginChannels
Processmatch by name + arg pattern
Diskmountpoint fan-out, % full, inode pressure
PostgreSQLreplication lag, connections, custom SQL
RedisINFO parse, key count, master/replica
HTTP-JSONJSONPath extraction (see custom sensors)
Prom-scrapePrometheus exposition (see custom sensors)
Dockerwraps any image as a one-shot probe (see custom sensors)

What's intentionally not native

Out of scope on purpose: full packet capture (use ntopng or zeek), NetFlow collection (use the dedicated nfdump pipeline, then ingest via webhook-push), Active Directory introspection (PowerShell remoting via WinRM custom_script handles this). The custom-sensor frameworks cover roughly 70% of the long tail without writing bespoke code. Need a fully-custom plugin? Contact support@jallarhorn.com — bespoke plugin integration is available for Business and Enterprise tiers.

Custom sensors

Five generic frameworks for anything not natively covered. All five share the same output schema, so a custom sensor's data graphs, alerts, and reports identically to a native one.

The channel format

Every custom sensor emits JSON shaped like this:

{
  "channels": [
    { "name": "cpu",    "value": 42.7, "unit": "%" },
    { "name": "memory", "value": 1.8,  "unit": "GiB" }
  ]
}

Each channel becomes a separate metric track on the device, with its own warn/error threshold and its own graph line.

exec-script

Run any local command — shell script, executable, language runtime — and parse its stdout as the channel JSON above. Per-sensor timeout, working directory, env vars, and stdin. Bounded stdout/stderr buffers. SIGKILL via process group on timeout.

http-json

GET (or POST) any HTTP endpoint, parse the response as JSON, and extract channels via JSONPath. Supports Bearer tokens, custom headers, mTLS client certs, and JSON-body POSTs. Handles missing keys and type coercion (string → float).

webhook-push

Inverts the polling model: the integration POSTs metrics to /api/v2/sensors/:id/push on its own schedule, authenticated by a SHA-256-hashed per-sensor ingest token. Useful when the source is push-only (FaaS, CI runners, scheduled jobs).

prom-scrape

Polls a Prometheus exposition endpoint (/metrics) and turns selected metrics into Jallarhorn channels. Label filtering and metric rename rules. Pair this with any existing Prometheus exporter (node_exporter, mysqld_exporter, blackbox_exporter, …) to cover the long tail.

docker

Runs docker run --rm <image> <args> on a schedule and parses stdout as channel JSON. Defaults to --network none; opt-in for network access. SIGKILL via process group on timeout. Bounded stdout/stderr. Useful for vendor-supplied CLIs, Playwright scrapers, OPC UA / Modbus / SIP / DICOM / HL7 probes via public images.

Choosing the right framework

Source shapeFramework
Local CLI or scriptexec-script
HTTP JSON endpointhttp-json
Source pushes on its own schedulewebhook-push
Prometheus expositionprom-scrape
Vendor binary / container imagedocker
A vendor-specific binary protocolcontact support — bespoke plugin integration available

Alert rules

An alert rule has a sensor, a comparison operator (>, <, ==, etc.), and two thresholds: warn and error. The alert engine runs every 30 seconds; when a sample crosses a threshold, an alert opens with that severity. Alerts auto-resolve when the underlying value crosses back. Duplicate suppression keeps a flapping sensor from generating storms.

Operators can acknowledge an alert (silences further notifications without closing it) or resolve it (closes immediately, even if the underlying value is still over threshold). Auto-resolve fires when the sensor returns to OK for the configured cooldown period.

Dependency suppression

Mark a sensor as depending on another (e.g. an HTTP check on a web app depends on the ICMP check of its host). When the parent goes red, child alerts are recorded with status: suppressed — visible in the audit log but no notifications are sent. When the parent recovers, child alerts that are still real fire normally.

Maintenance windows

Schedule planned downtime per device or per sensor. While the window is active, alerts are auto-suppressed; they still appear in the audit log labeled status: maintenance. Recurring windows are supported via cron-style expressions.

Notification channels

ChannelTransport / Notes
EmailStandard SMTP with STARTTLS. Auth via JALLARHORN_SMTP_* env vars or sidecar relay.
SlackIncoming webhook. Severity-coloured blocks with sensor, device, current value, and deep-link to the alert.
Microsoft TeamsAdaptive Card 1.4 via incoming webhook. Same payload shape as Slack.
DiscordIncoming webhook. Useful for community-run installs.
PagerDutyEvents API v2. Maps Jallarhorn severities to PagerDuty's warning / error / critical.
OpsgenieAlert API. Auto-resolve when the Jallarhorn alert clears (closes the Opsgenie incident).
Web Push (VAPID)Browser + PWA push notifications with min-severity, quiet-hours, and timezone awareness.
Generic webhookOutbound JSON POST to any URL. Configurable headers, retry-with-backoff, HMAC-SHA-256 request signing.

Every channel has a Send test button in the dashboard. The test payload identifies itself as a test so it can't be mistaken for a real incident.

Escalation chains

Define a sequence of channels with delays — e.g. "Slack at T+0, page on-call at T+5 min if not acknowledged, page secondary at T+15 min." Escalations stop when the alert is acked or resolved.

Federation with jallarhorn-farm

Run multiple Jallarhorn control instances (one per site, one per tenant, one per regulatory boundary) and view them through a single aggregated UI. Each control keeps its own database; the farm executable is a stateless aggregator that fans queries out, merges results, and serves a unified dashboard at port 28092.

Seeding the farm

Start jallarhorn-farm with --seeds host1:28090,host2:28090,host3:28090. The farm GETs /api/v2/farm/announce on each seed, learns its peers, and continues discovering via SWIM-style gossip on UDP. New control instances joining the farm auto-announce.

Aggregated endpoints

EndpointBehaviour
GET /api/v2/farm/statsSum of stats across instances
GET /api/v2/farm/devicesUnion of devices with origin annotation
GET /api/v2/farm/alertsUnion of open alerts
GET /api/v2/farm/sensorsUnion of sensors

Partial failures (one control unreachable) return the merged result with X-Partial-Results: true and the failing instance(s) listed in the response body. Per-instance timeout 5s.

Write-proxy via ?instance=

Writes (create device, ack alert, edit sensor) need to land on the specific control that owns the record. Add ?instance=<id> to the request — the farm proxies it. Without that parameter, writes return 400 rather than silently writing to a random instance.

REST API

Jallarhorn-control exposes a REST API at /api/v2/* (JWT-authenticated, for humans and integrations) and a simpler shared-secret surface at /api/* (used by the sensor binary to publish metrics). All responses are JSON. Per-IP rate limit on /api/v2/auth/login: 10/min then exponential backoff.

Authentication

# Login
curl -sS https://app.example.com/api/v2/auth/login \
  -H 'Content-Type: application/json' \
  -d '{"username":"alice","password":"..."}'
# → { "token": "eyJ...", "user": { "id": "...", "role": "admin" } }

# Subsequent calls
curl -sS https://app.example.com/api/v2/devices \
  -H 'Authorization: Bearer eyJ...'

JWTs land in an httpOnly Strict cookie when issued via the web flow. Programmatic clients should use the Authorization: Bearer header.

Resources

PathOperations
/api/v2/usersList, create, update, delete (admin only)
/api/v2/devicesList, create, get, update, delete; tagged + grouped
/api/v2/sensorsList, create, get, update, delete; tied to a device
/api/v2/metrics/:sensorIDTime-series query — ?from=…&to=…&channel=…&agg=avg|max|min|count
/api/v2/alertsList, ack, resolve; filter by severity / device / sensor
/api/v2/alert-rulesCRUD on threshold rules + dependency suppression
/api/v2/discovery/scanKick off a CIDR scan; poll /jobs/:id and adopt with /adopt
/api/v2/reports/slaSLA PDF download; /sla/preview returns JSON
/api/v2/notificationsConfigure channels, send tests, view delivery log
/api/v2/webhooksOutbound webhook subscriptions with HMAC signing
/api/v2/maintenanceSchedule + manage windows
/api/v2/statusComponent health rollup; GET /api/v2/health for liveness
/api/v2/wsWebSocket subscription for real-time metric + alert pushes
/api/v2/auditAudit log query

WebSocket

Connect with the same Bearer token (sent as a ?token= query param). Server pushes { "type": "metric", … } or { "type": "alert", … } messages per tenant; the dashboard uses this for live tiles.

Node / TypeScript SDK

npm install @jallarhorn/client

import { Jallarhorn } from "@jallarhorn/client";
const j = new Jallarhorn({
  baseURL: "https://app.example.com",
  token: process.env.JALLARHORN_TOKEN!,
});

const devices = await j.devices.list();
await j.alerts.ack(alertId);

Python SDK

pip install jallarhorn

from jallarhorn import Jallarhorn
j = Jallarhorn(base_url="https://app.example.com",
               token=os.environ["JALLARHORN_TOKEN"])

for d in j.devices.list():
    print(d.name, d.status)

j.alerts.ack(alert_id)

Both SDKs share the same method names + paginated-iterator shape, so a script translated between languages reads near-identically. A /api/v3/* migration — if it ever happens — would ship as @jallarhorn/client@2.x / jallarhorn-client>=2.0.

Mobile / PWA

The Jallarhorn dashboard is a Progressive Web App — installable to the home screen on iOS and Android, with offline-aware loading for cached views. There is no separate native app; the PWA covers the use cases that warrant one (alerts, ack, drill-down). The /monitor route renders a viewport-sized dashboard with safe-area awareness for the iPhone notch — useful as a status-board page on a wall-mounted tablet or as the on-call engineer's lock-screen shortcut.

Web Push

VAPID-authenticated push notifications. Users opt in from Settings → Notifications → Browser push. Per-user preferences: minimum severity, quiet hours, timezone. Operators can send a test push from the same screen.

Migrate from per-sensor monitoring

A staged migration where Jallarhorn and the legacy tool run side-by-side; sensors move group-by-group; the legacy install gets retired only after the new one is proven.

Phase 1 — Stand Jallarhorn up alongside

Install per Install & configure. Point Jallarhorn at the same network segments your existing tool already monitors. Don’t disable any of the legacy sensors yet.

Phase 2 — Auto-discover

Run POST /api/v2/discovery/scan against each CIDR. The discovery engine sweeps ICMP, walks SNMP sysDescr / sysName / sysOID, reverse-DNS-looks-up everything, and produces a candidate list. Adopt with one click per device — Jallarhorn then auto-creates sensors based on the device type fingerprint.

Phase 3 — Sensor mapping

Most legacy per-sensor types map 1:1 to a Jallarhorn native plugin (ping, SNMP, HTTP, SSL, SSH, WinRM, DNS, TCP, disk, process, PostgreSQL, Redis, Docker). For the long tail, a custom-sensor framework usually covers it.

Phase 4 — Alert rules

Recreate threshold rules in Jallarhorn (UI or API). Run both alert engines in parallel for a week, watching for divergence. The Jallarhorn alert log includes a source_sensor field so cross-system comparison is mechanical.

Phase 5 — Cutover

Disable the legacy sensors group-by-group as confidence builds. Keep the old install read-only for ~30 days as a rollback option, then decommission. Save the legacy Probe Devices XML export — Jallarhorn can ingest it into a tagged group called imported-legacy.

For a side-by-side feature + pricing comparison, see how Jallarhorn compares.

Docs version: 2026-05-26. Product version: see /health on your control instance. Something missing or wrong? Email support@jallarhorn.com.