Port management + security for self-hosted infrastructure.
Know every port. Claim every port. Score your attack surface.
$ portkeep scan
localhost — 12 ports (6 loopback · 1 lan · 2 tailscale · 3 wildcard)
PORT PROTO ADDRESS SCOPE PID PROCESS
22 tcp 0.0.0.0 ⛔wildcard 1517 sshd
53 tcp 0.0.0.0 ⛔wildcard 1489 pihole-FTL
443 tcp 100.91.13.85 🔴tailscale 1503 nginx
3000 tcp :: ⛔wildcard 1498 node
18789 tcp 127.0.0.1 🟢loopback 1497 openclaw
9090 tcp 127.0.0.1 🟢loopback 1490 prometheus
...
⚠ 3 unclaimed ports
⚠ 3 wildcard binds
Note: Process names and PIDs are resolved on Linux by walking
/procinode tables. Run PortKeep as root for full visibility; non-root users see blank process names for processes they don't own.
$ portkeep audit
╔══════════════════════════════════════════════════╗
║ PortKeep — Security Audit · localhost ║
║ Exposure Score: 38/100 ████████░░░░░░░░░░░░ MODERATE ║
╚══════════════════════════════════════════════════╝
THREAT INTEL synced 2h ago
✓ no C2 port or KEV CVE matches
CRITICAL FINDINGS
⛔ port 22/tcp (sshd) — wildcard bind, SSH exposed
⛔ port 53/tcp (pihole-FTL) — wildcard bind, privileged port exposed
🔴 port 3000/tcp (node) — wildcard bind, unclaimed
...
- Discovers every listening port across multiple nodes (local + SSH remote)
- Claims ports into a registry — no more "I think 3002 was free"
- Detects drift — declared vs actual, rogue ports, bind scope mismatches
- Audits security — exposure score (0–100), risk flags, firewall cross-reference
- Syncs threat intel from 9 sources and surfaces C2 port matches + CISA-KEV CVE hits
- Alerts — Telegram, webhook, or script when something changes
- Tracks history — git-log style timeline of every port change
- Daemon mode — systemd user service for continuous monitoring
portkeep scan # discover every port on this host
portkeep audit # security score + risk flags
portkeep sync # pull threat intel from 9 sources
portkeep claim 3000 "api" # register a port as expected
portkeep drift # declared vs actual — exits 1 on drift
portkeep claim next # find the next available port in a rangePortKeep syncs 9 sources on demand via portkeep sync.
No authentication required (syncs out of the box):
| Source | What it provides |
|---|---|
| CISA-KEV | Known Exploited Vulnerabilities catalog (CVE + product names) |
| EPSS | Top-1000 CVEs by exploit probability (FIRST.org) |
| Feodo | Active C2 botnet IPs and destination ports (abuse.ch) |
| Emerging Threats | Compromised host IP list (Proofpoint/ET) |
| Blocklist.de | Attacking IP list |
| DShield/SANS | Top attacking /24 netblocks (SANS Internet Storm Center) |
Free Auth-Key required — obtain at https://auth.abuse.ch:
| Source | What it provides |
|---|---|
| ThreatFox | IOC database with C2 IPs and ports (abuse.ch) |
| URLhaus | Malicious URL host IPs (abuse.ch) |
| MalwareBazaar | Malware hash metadata cached for process correlation (planned v0.2) |
# Set key once; the other 6 sources always sync without it
export ABUSE_CH_AUTH_KEY=your-key-here
portkeep syncWhat the audit uses from threat intel:
- C2 port matches: if your machine exposes a port that Feodo/ThreatFox track as an active C2 port, it gets flagged as CRITICAL (+30 score)
- CISA-KEV hits: if a port's process name matches a product in the KEV catalog (e.g.,
nginx,openssh), the count of active CVEs is surfaced as a finding
| Command | Description |
|---|---|
scan |
Discover all listening ports (local or SSH remote) |
sync |
Pull threat intel from all 9 sources |
audit |
Full security audit — score, risk flags, threat intel, firewall |
claim |
Register a port as expected/owned |
claim next |
Find the next available port in a range |
unclaim |
Remove a port registration |
drift |
Compare declared vs actual — exits 1 on drift |
list |
List all registered port claims |
history |
Change timeline — git-log style |
node |
Manage remote nodes for multi-host scanning |
alert |
Configure alert rules and notification destinations |
daemon |
Run as a background service (systemd support) |
config |
Configuration management |
version |
Print version info |
All commands support --json for scripting and --quiet for cron.
From binary (Linux/macOS):
# Download from https://github.com/lowwattlabs/portkeep/releases/latest
curl -sSL https://github.com/lowwattlabs/portkeep/releases/download/v0.1.0/portkeep_0.1.0_linux_amd64.tar.gz | tar xz
sudo mv portkeep /usr/local/bin/From source (requires Go 1.22+):
git clone https://github.com/lowwattlabs/portkeep.git
cd portkeep && go build -o portkeep .
sudo mv portkeep /usr/local/bin/Homebrew tap (macOS / Linux):
brew tap lowwattlabs/tap
brew install portkeep# Add a remote node (key-only SSH auth)
portkeep node add myserver --host 10.0.0.50 --ssh-key ~/.ssh/id_ed25519
# Scan a remote node
portkeep scan --node myserver# Install as systemd user service
portkeep daemon install
systemctl --user enable --now portkeep
# Check status
portkeep daemon status# Telegram alert for rogue ports
portkeep alert add --trigger rogue --destination telegram \
--config '{"chat_id":"YOUR_CHAT_ID","bot_token":"YOUR_TOKEN"}'
# Webhook alert for bind changes
portkeep alert add --trigger bind-change --destination webhook \
--config '{"url":"https://hooks.example.com/portkeep"}'- Single Go binary — no runtime dependencies, no Docker, no cloud
- SQLite storage (pure Go, no CGO via
modernc.org/sqlite) — WAL mode - SSH remote scanning — key-only auth, no agent required on remote nodes
- Firewall cross-reference — auto-detects UFW, iptables, nftables, firewalld
- Exposure scoring — 0–100 based on bind scope, claims, and threat intel
ClawSecure scans OpenClaw skill code in the ClawHub registry for malicious patterns. SecureClaw audits OpenClaw configuration and hardens the OpenClaw gateway (port exposure, permissions, sandbox settings). Neither tool inventories the full host network layer across all non-OpenClaw services.
On a typical homelab node, dozens of processes expose ports: databases, dashboards, metrics collectors, game servers, n8n, Home Assistant. None of those go through the OpenClaw agent, so ClawSecure/SecureClaw have no visibility into them. PortKeep sees every listening socket on the machine, claims them into a registry, detects when one appears unexpectedly, and cross-references every port against live threat intel.
PortKeep and ClawSecure/SecureClaw solve different problems. You'd run both: ClawSecure to harden the agent, PortKeep to harden the host.
Cloud-hosted attack surface management platforms typically cost $100+/month per host and require an internet-connected agent phoning home. PortKeep is free, runs locally, and stores all data in a SQLite file on your own machine. It is not a replacement for a full external ASM scan — it is a local registry and continuous monitor for hosts you own and can SSH into.
v0.1.0 — Core features implemented: scan, claim, drift, audit, sync (6-of-9 sources without Auth-Key), alert, daemon, history, multi-node SSH. Tested on Linux amd64.
PortKeep is also available as an OpenClaw tool plugin. Install it and any OpenClaw agent can scan, audit, drift-check, and claim ports — including remote nodes — without knowing the CLI.
openclaw plugins install portkeepSix tools: portkeep_scan, portkeep_audit, portkeep_drift, portkeep_claim, portkeep_list, portkeep_sync.
Download the latest binary from GitHub Releases for your platform:
# Linux amd64
curl -sL https://github.com/lowwattlabs/portkeep/releases/latest/download/portkeep_linux_amd64.tar.gz | tar xz
sudo mv portkeep /usr/local/bin/
# macOS (Apple Silicon)
curl -sL https://github.com/lowwattlabs/portkeep/releases/latest/download/portkeep_darwin_arm64.tar.gz | tar xz
sudo mv portkeep /usr/local/bin/
# Verify
portkeep version- Process-hash correlation against MalwareBazaar (v0.2)
- CVE lookup command (
portkeep cve) cross-referencing running services with KEV/EPSS - Service fingerprinting (nmap -sV integration)
- Compliance templates (CIS, CISA SMB baseline)
- Prometheus/Grafana export
- Interactive terminal UI (Bubble Tea, v0.2)
- SPEC.md — Full feature specification
- CLI.md — CLI design and usage examples
- BUILD.md — Architecture and build phases
MIT
Jason Chandler · Low Watt Labs
☕ Found PortKeep useful? Buy me a coffee