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.
| Platform | Format | Notes |
|---|---|---|
| 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
| Variable | Purpose |
|---|---|
DATABASE_URL | Database connection string. The installer prepares the database for you. |
JWT_SECRET | HMAC secret for session JWTs. 32+ hex bytes. |
CONTROL_SECRET | Shared secret between control and sensor over /api/*. |
JALLARHORN_ENCRYPTION_KEY | 32-byte symmetric key for at-rest credential encryption (SSH passwords, SNMP communities, etc.). Rotation procedure in the security page. |
HTTP_LISTEN | Control HTTP listen, default 127.0.0.1:28090. Bind to 0.0.0.0 to expose. |
JALLARHORN_LICENSE_URL | License server. Defaults to https://license.jallarhorn.com. |
JALLARHORN_LICENSE_MODE | fail-open (default) keeps collecting if the license server is unreachable; fail-closed halts after the grace period. |
JALLARHORN_INVITE_ONLY | true (default) requires an invite token after the bootstrap admin; false opens registration. |
JALLARHORN_SMTP_HOST / _PORT / _FROM | SMTP 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
| Plugin | Channels |
|---|---|
| ICMP / Ping | RTT, jitter, packet loss |
| TCP port | reachability, banner read, latency |
| DNS | A / AAAA / MX / CNAME / TXT / DNSSEC |
| SSL cert | expiry days, chain validation, hostname mismatch |
| HTTP | status, content match, latency, redirect handling |
Device polling
| Plugin | Channels |
|---|---|
| SNMP (v1/v2c/v3) | get, walk, counter wrap, delta rates |
| SSH | disk, load, memory, uptime, CPU, custom script |
| WinRM | CPU, memory, disk, service, process, custom WQL / PowerShell |
System & services
| Plugin | Channels |
|---|---|
| Process | match by name + arg pattern |
| Disk | mountpoint fan-out, % full, inode pressure |
| PostgreSQL | replication lag, connections, custom SQL |
| Redis | INFO parse, key count, master/replica |
| HTTP-JSON | JSONPath extraction (see custom sensors) |
| Prom-scrape | Prometheus exposition (see custom sensors) |
| Docker | wraps 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 shape | Framework |
|---|---|
| Local CLI or script | exec-script |
| HTTP JSON endpoint | http-json |
| Source pushes on its own schedule | webhook-push |
| Prometheus exposition | prom-scrape |
| Vendor binary / container image | docker |
| A vendor-specific binary protocol | contact 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
| Channel | Transport / Notes |
|---|---|
Standard SMTP with STARTTLS. Auth via JALLARHORN_SMTP_* env vars or sidecar relay. | |
| Slack | Incoming webhook. Severity-coloured blocks with sensor, device, current value, and deep-link to the alert. |
| Microsoft Teams | Adaptive Card 1.4 via incoming webhook. Same payload shape as Slack. |
| Discord | Incoming webhook. Useful for community-run installs. |
| PagerDuty | Events API v2. Maps Jallarhorn severities to PagerDuty's warning / error / critical. |
| Opsgenie | Alert 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 webhook | Outbound 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
| Endpoint | Behaviour |
|---|---|
GET /api/v2/farm/stats | Sum of stats across instances |
GET /api/v2/farm/devices | Union of devices with origin annotation |
GET /api/v2/farm/alerts | Union of open alerts |
GET /api/v2/farm/sensors | Union 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
| Path | Operations |
|---|---|
/api/v2/users | List, create, update, delete (admin only) |
/api/v2/devices | List, create, get, update, delete; tagged + grouped |
/api/v2/sensors | List, create, get, update, delete; tied to a device |
/api/v2/metrics/:sensorID | Time-series query — ?from=…&to=…&channel=…&agg=avg|max|min|count |
/api/v2/alerts | List, ack, resolve; filter by severity / device / sensor |
/api/v2/alert-rules | CRUD on threshold rules + dependency suppression |
/api/v2/discovery/scan | Kick off a CIDR scan; poll /jobs/:id and adopt with /adopt |
/api/v2/reports/sla | SLA PDF download; /sla/preview returns JSON |
/api/v2/notifications | Configure channels, send tests, view delivery log |
/api/v2/webhooks | Outbound webhook subscriptions with HMAC signing |
/api/v2/maintenance | Schedule + manage windows |
/api/v2/status | Component health rollup; GET /api/v2/health for liveness |
/api/v2/ws | WebSocket subscription for real-time metric + alert pushes |
/api/v2/audit | Audit 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.