A Realm Watch project

Heimdall

Fleet security from one small hub.

A self-hosted inventory of your Linux servers. Heimdall records what each one runs, checks it against public vulnerability and end-of-life data, and shows what needs updating, where, and how urgently.

The watchman of the gods sees everything across all nine realms, and touches nothing.

Security first

Heimdall’s database lists every weak spot in every server you manage, which makes the tool itself a target. It’s built so that a fully compromised hub gives an attacker that list and nothing else.

  • The hub is a sink, not a controller. Servers push to it. It never connects to a server, holds no SSH keys and has no way to run anything remotely, not even as an option.
  • It never serves code. The collector is installed from a signed release, never from the hub, so a compromised hub can’t distribute malware.
  • Ingest is write-only. A host’s token can append reports for that host and nothing else. The collector ignores everything the hub sends back except the status code.
  • The UI is private. The UI and ingest listen on separate ports. Only ingest faces your servers; the UI sits behind a VPN or tailnet.
  • Small and auditable. The hub is one Go binary with five direct dependencies. The collector is a shell script you can read in five minutes.
If an attacker gets… They can… They cannot…
The hub Read the inventory and findings Log into or run code on any server
One server and its token Send fake reports for that host Read anything, or touch other hosts
A user account See the orgs that account is granted See other orgs, or create users or tokens
The network path Nothing useful; it’s all TLS Read or alter reports in transit
A feed provider Return bad advisory data Learn your hostnames or addresses
How data flows through HeimdallServers run the collector and push reports one way over HTTPS to the hub's ingest port. The hub stores them in SQLite, fetches advisory feeds from an allowlist, and serves its UI only on a private network. The hub never connects to a server.web-01collector · hourlydb-01collector · hourlyair-gapped boxmanual importone-way HTTPS push✕ hub never connects out to serversHeimdall hubIngest :8443POST /ingest, write-onlyOne binary + SQLitesandboxed, non-rootWeb UItailnet / VPN onlyfetchPublic feedsOSV · CISA KEVEPSS · endoflife.dateallowlisted hostsyou, from your laptop

Features

What Heimdall does

Inventory

OS, kernel, packages (dpkg, rpm, apk, pacman), pending and security updates, reboot status, runtimes, services and Docker containers, with history so you can see what changed.

Vulnerabilities and end of life

Package versions matched against OSV.dev, CISA KEV flags, optional EPSS scores, and end-of-life dates for operating systems and runtimes. Only package and version tuples leave the hub.

Sites and Laravel

Every nginx and Apache site, the PHP-FPM version serving it, its Laravel version and every Composer package, with findings per site. It never reads .env or runs app code.

Anomaly signals

Tripwires in the data it already has. Silent hosts, replayed reports, packages changed outside a patch plan, a kernel change without a reboot, a collector that changed unexpectedly.

Logins and privileged access

Opt-in watches for sudo, su and root SSH sessions and for failed SSH logins, with every address named as a person, a deploy service, your own servers or nobody known.

Attack surface

Every public hostname followed to its DNS record, the host it reaches, the site there and that site's open findings, worst first. Cloudflare data is optional and read-only.

Patch plans

Per-host update lists sorted by KEV, severity and EPSS, commands you copy and run yourself, and plans that batch hosts and confirm the fix on the next check-in.

API and MCP

A REST API and an MCP server, so an AI assistant can ask which hosts are exposed. A threat list of attacker addresses that firewalls can pull.

Screenshots

A look inside

From the demo hub, with placeholder data.

Heimdall dashboard showing known-exploited findings, fixable vulnerabilities, end-of-life software and a table of hosts that need attention.
The dashboard puts what to patch first at the top.
Attack surface page listing public hostnames, the hosts and Laravel sites they serve, and their critical and high findings.
Each public name is followed through to the host and site it reaches.
Sites page listing each site with its host, web server, PHP-FPM and Laravel versions, end-of-life badges and finding counts.
Sites, with the PHP and Laravel versions actually serving them.

How it works

Getting it running

  1. Install the hub

    One Go binary and one SQLite file on a small VM. install-hub.sh sets it up as a sandboxed systemd service, with the UI kept on your tailnet.

  2. Add your servers

    Add a host in the UI for a one-line install command, or create an enrollment key to add many servers at once. Each server gets its own write-only token.

  3. Let the collector report

    A short POSIX shell script, installed from a signed release and run as an unprivileged user, reports hourly and soon after packages change.

Documentation

Read more

Status: Pre-1.0. Everything below is built; the last steps before 1.0 are an independent security review and a run on real fleets.