How the Noxen scan engine works
One scan per host runs six probes in parallel. Findings stream in as each probe finishes — you don't wait for the slowest. Total time: 10–60 seconds per host, depending on open-port count.
The probe pipeline
The ScanEngine actor coordinates everything. Each
probe is an async function that owns its own
cancellation, error handling, and finding emission. They run
concurrently — not sequentially — so a slow TLS handshake
doesn't block the SSH inventory.
- Port scan (Apple's
Network.framework) — top 1000 TCP ports. No externalnmapbinary, no sandbox-incompatible raw socket trickery. Open ports feed the TLS, HTTP-header, and admin-surface probes downstream. - SSH inventory — connects via
SystemSSHHostProbe(shells out to/usr/bin/sshfor reliability), reads/etc/os-release, kernel version, dpkg/rpm package list,sshd_config, and~/.ssh/authorized_keys. All read-only — Noxen never modifies remote state. - CVE match — every installed package version is cross-referenced against the loaded CVE feed using a CPE index. Matches surface as findings with the matching CVE ID, CVSS score, and distro-specific remediation.
- TLS audit — for any TLS-capable open port (443, 465, 636, 993, 995, 8443, 9443), runs a handshake with every supported cipher suite, parses the certificate chain, and checks for weak ciphers, deprecated protocols (TLSv1.0, TLSv1.1, SSLv3), HSTS presence, OCSP stapling, and near-expiry certificates.
- HTTP security headers — for every web-capable
open port, GETs
/and inspects the response headers: Content-Security-Policy, X-Frame-Options, Referrer-Policy, Permissions-Policy, Strict-Transport-Security, X-Content-Type-Options. Missing headers become medium-severity findings. - Exposed admin surfaces — fingerprints ~70
services across 10 categories: home automation
(Home Assistant, Pi-hole), media (Plex, Jellyfin, *arr suite),
infrastructure (Proxmox, TrueNAS, Synology, QNAP), network
(pfSense, OPNsense, UniFi, Mikrotik), DevOps (Gitea, Jenkins,
Grafana, Prometheus, Uptime Kuma, Netdata), containers
(Portainer, Kubernetes dashboard, Docker daemon), service mesh
(Consul, Vault, Traefik), data (Redis, Mongo, Elasticsearch,
phpMyAdmin), source-code leaks (
.git/config,.env). Why this is the #1 homelab compromise vector →
Cancellation
Click Cancel scan in the toolbar (or hit
Esc with the host selected) — every in-flight probe
observes Swift's Task cancellation and unwinds
cleanly. Partial findings already emitted are saved to the scan
record; the scan's status flips to Cancelled with a
timestamp. No side effects on the remote host.
Failed probes
Probes degrade gracefully. A failure in one probe doesn't stop
the others — instead, the failed probe emits a single
.probeError finding with the underlying error, and
the rest of the scan continues. Common failure modes:
- SSH auth failed → SSH inventory + CVE match skip
- Network probes (port scan, TLS, HTTP, admin surfaces) still run. You get exposure data without inventory data.
- Network unreachable → entire scan flagged failed
- If port 22 itself isn't reachable, none of the probes have
anything to do. The scan record's
statusis.failedand the sidebar shows a red dot. - Custom check JSON malformed → that check skipped
- Other custom checks and built-in probes continue. The malformed check is logged for debugging — see custom checks reference.
Persistence
Each scan creates one Scan record and many
Finding records, all in SwiftData. Scans are
retained indefinitely so you can diff month-over-month;
individual findings reference back to their parent scan.
CloudKit sync (when enabled) replicates host catalog and scan
history; SSH keys never sync — they live in
Keychain.
Diff-from-yesterday
The default UI view on a scanned host is "what's new", not "what's known". Each scan is compared to the previous; the detail view defaults to filtering out findings that already existed in the prior scan. Toggle Show all to see the full set. The diff is severity-aware — a finding that changed severity (e.g., a CVE that escalated from medium to high after a vector update) shows as updated, not unchanged.
What Noxen does not do
- No default-credential testing. Noxen flags an exposed admin surface; it never tries to log in. Why →
- No write access to the remote host. Every
SSH command is read-only. No
apt updateauto-runs, no config rewrites, no telemetry agent installation. - No outbound traffic about your fleet. CVE matching happens locally after the feed is downloaded. Noxen's servers only host the signed feed — Privacy & data.