Skip to main content
O

dsh-port-map

ozanstn1-stack/dsh-port-map

Read-only Linux port, service and Caddy route map: listening sockets with their owning unit or container, a reverse service view, and reverse_proxy liveness checks. Requires iproute2 (ss); docker and a Caddyfile are optional.

Install

dsh plugin --profile web add github:ozanstn1-stack/dsh-port-map

README

dsh-port-map

Port, service and Caddy route map for DeepSeek Harness.

dsh-port-map answers the questions a busy server keeps asking: what is on 9094? which unit owns this port? is every Caddy route still pointing at something alive? what is exposed to the internet?

Every tool is read-only. Nothing is opened, closed or reconfigured.

Tools

ToolKindWhat it does
listening_portsread-onlyTCP listening sockets: port, bound address, reachability scope (public / localhost / interface), process, and the systemd unit or docker container that owns it. Filters: scope, port.
service_mapread-onlyThe reverse view: every unit/container with the ports it listens on, plus the ports nothing claims.
caddy_routesread-onlyParses the Caddyfile into site → path → reverse_proxy upstream, then checks each upstream against the listening sockets.
url_checkread-onlyHTTP status, latency and content type for an allow-listed host. A redirect is reported, not followed.

Example output

Listening TCP sockets (39):
  port  address         scope     owner
   8790  127.0.0.1       localhost dsh-web.service
  1933  127.0.0.1       localhost docker:openviking
  9090  0.0.0.0         public    map-service.service

Bound to all interfaces: 22, 443, 8642, 8799, 9090, 9091, 9094, 11434
Caddy routes (11) from /etc/caddy/Caddyfile:
  example.org /panel → 127.0.0.1:8788 · up (vps-panel.service)
  example.org /gone  → 127.0.0.1:9999 · DOWN

Install

dsh plugin --profile web add dsh-port-map
systemctl restart dsh-web   # or restart your DSH process
- id: port-map
  config:
    caddyfile: /etc/caddy/Caddyfile
    urlCheck:
      hosts:
        - 127.0.0.1
        - localhost
        - example.org
      timeoutMs: 10000
    docker: true
    safety:
      timeoutMs: 15000
      maxOutputBytes: 65536

Configuration

KeyDefaultMeaning
caddyfile/etc/caddy/CaddyfileFile parsed by caddy_routes.
urlCheck.hosts["127.0.0.1", "localhost"]Hosts url_check may request; everything else is refused.
urlCheck.timeoutMs10000Per-request timeout.
dockertrueResolve container names for published ports.
safety.timeoutMs15000Default command timeout.
safety.maxOutputBytes65536Output cap per call.

How the owner is resolved

  1. ss -tlnpH gives the socket, its bound address and the owning PID.
  2. The PID's cgroup gives the systemd unit (/proc/<pid>/cgroup).
  3. A docker-proxy socket is matched to its container through the published port (docker ps --format '{{.Names}}\t{{.Ports}}'), so the map shows docker:openviking rather than docker.service.

Nothing is guessed: an unclaimed socket is reported as unowned:<process>.

Honest limits

  • Read-only: it reports, it does not fix. No firewall changes, no restarts.
  • The Caddyfile parser understands site blocks, handle/handle_path and reverse_proxy (with or without an options block). !!js expressions, imported snippets or php_fastcgi-style directives are not modelled and are simply not reported.
  • Docker name resolution needs docker ps to work for the current user; without it the socket is still listed with its docker:<id> marker.
  • url_check never follows a redirect, so a redirect chain is reported as one hop, not resolved to the final page.

Requirements

  • DSH >= 0.2.1-rc.4 (Node ^22.19.0 || >=24.0.0).
  • ss (iproute2) for the socket tools; docker optional; the Caddyfile optional.

Development

npm run link-peers
node --test

License

MIT

Related plugins