bytelyst-devops-tools/docs/vm-exposure-inventory.md
Hermes VM d60c81ebda
Some checks failed
pre-commit / pre-commit (push) Failing after 38s
docs: record internal port loopback hardening
2026-05-27 21:25:38 +00:00

14 KiB

ByteLyst VM Exposure Inventory

Generated: 2026-05-27 Host: srv1491630 Purpose: Phase 0 inventory for docs/vm-security-blind-spots-roadmap.md.

This inventory is a pre-change control document. It does not approve exposure by itself. Each Needs decision row requires owner approval before firewall, Compose, Caddy, or SSH changes.

Classification Key

Class Meaning Expected Controls
public-caddy Public app/API intended to be reached through Caddy Caddy TLS, hostname/path routing, app auth where needed, no direct host-port exposure
public-direct Direct host-port access intentionally public explicit approval, provider/UFW allowance, monitoring
private-admin Admin/dev/internal tool Tailscale/VPN, SSH tunnel, IP allowlist, or auth gate
loopback-only Host-local service used by Caddy or local automation bind 127.0.0.1:port; no external bind
docker-internal Container-to-container only no host port mapping
retire Unused/deprecated remove service or disable host exposure
needs-decision Existing exposure with unknown/unclear intent owner must classify before remediation

Caddy Public Routes

Hostname/path Upstream Initial class Decision needed
api.bytelyst.com/platform/* platform-service:4003 public-caddy Confirm auth posture
api.bytelyst.com/extraction/* extraction-service:4005 public-caddy Confirm auth posture
api.bytelyst.com/mcp/* mcp-server:4007 public-caddy Confirm public need
api.bytelyst.com/peakpulse/* peakpulse-backend:4010 public-caddy Confirm direct host port can close
api.bytelyst.com/chronomind/* chronomind-backend:4011 public-caddy Confirm direct host port can close
api.bytelyst.com/jarvisjr/* jarvisjr-backend:4012 public-caddy Confirm direct host port can close
api.bytelyst.com/nomgap/* nomgap-backend:4013 public-caddy Confirm direct host port can close
api.bytelyst.com/mindlyst/* mindlyst-backend:4014 public-caddy Confirm direct host port can close
api.bytelyst.com/lysnrai/* lysnrai-backend:4015 public-caddy Confirm direct host port can close
api.bytelyst.com/notelett/* notelett-backend:4016 public-caddy Confirm direct host port can close
api.bytelyst.com/flowmonk/* flowmonk-backend:4017 public-caddy Confirm direct host port can close
api.bytelyst.com/actiontrail/* actiontrail-backend:4020 public-caddy Confirm direct host port can close
api.bytelyst.com/localmemgpt/* localmemgpt-backend:4019 public-caddy Confirm direct host port can close
api.bytelyst.com/invttrdg/* invttrdg-backend:4018 public-caddy Confirm direct host port can close
api.bytelyst.com/devops/* devops-backend:4004 private-admin Should require auth/private access
gitea.bytelyst.com gitea-npm-registry:3000 public-caddy Confirm direct 3300 can close
admin.bytelyst.com admin-web:3001 private-admin Confirm route still resolves; upstream container not in current docker ps
devops.bytelyst.com devops-web:3000 private-admin Should require auth/private access
tracker.bytelyst.com tracker-web:3003 public-caddy Confirm direct host port can close
llmlab.bytelyst.com llmlab-dashboard:3075 private-admin Dashboard currently unhealthy; decide public/private/retire
ollama.bytelyst.com 172.17.0.1:11434 private-admin Model endpoint should not be unauthenticated public
trading-api.bytelyst.com trading-backend:5000 public-caddy Confirm auth posture
invttrdg.bytelyst.com invttrdg-web:3085 public-caddy Confirm direct host port can close
notes.bytelyst.com notelett-web:3045 public-caddy Confirm direct host port can close
clock.bytelyst.com chronomind-web:3030 public-caddy Confirm direct host port can close

Public Bind Inventory

These listeners were bound on 0.0.0.0 and/or [::] during review.

Port Service/container Owner / Compose source Current route Initial class Proposed action
22 sshd host systemd direct SSH public-direct Keep public only after SSH key hardening
80, 443 caddy /opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml public ingress public-caddy Keep public
3000 notelett-web /opt/bytelyst/learning_ai_notes/docker-compose.yml notes.bytelyst.com public-caddy with direct bypass Bind loopback or remove host port after Caddy smoke
3002 lysnrai-dashboard /opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml none found in Caddy needs-decision Private/admin or retire direct exposure
3003 tracker-web /opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml tracker.bytelyst.com public-caddy with direct bypass Bind loopback or remove host port after Caddy smoke
3030 chronomind-web /root/bytelyst.ai/repos/learning_ai_clock/docker-compose.yml clock.bytelyst.com public-caddy with direct bypass Bind loopback or remove host port after Caddy smoke
3035 jarvisjr-web /opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml none found in Caddy needs-decision Unhealthy; classify as private/admin or retire
3040 flowmonk-web /opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml none found in Caddy needs-decision Unhealthy; classify as private/admin or retire
3049 devops-web /opt/bytelyst/bytelyst-devops-tools/dashboard/docker-compose.yml devops.bytelyst.com private-admin with direct bypass Fix old repo path drift, then bind loopback/private
3050 mindlyst-web /opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml none found in Caddy needs-decision Unhealthy; classify as private/admin or retire
3055 nomgap-web orphan from older /opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml none found in Caddy retire Retired on 2026-05-27; current Compose says Nomgap web is deployed to Vercel
3060 actiontrail-web /opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml none found in Caddy needs-decision Unhealthy; classify as private/admin or retire
3070 localmemgpt-web /opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml none found in Caddy needs-decision Unhealthy; classify as private/admin or retire
3075 llmlab-dashboard /opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml llmlab.bytelyst.com private-admin with direct bypass Dashboard unhealthy; gate or retire
3085 invttrdg-web /opt/bytelyst/learning_ai_invt_trdg/docker-compose.yml invttrdg.bytelyst.com public-caddy with direct bypass Bind loopback or remove host port after Caddy smoke
3100 loki /opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml none found in Caddy loopback-only Bound to 127.0.0.1 on 2026-05-27
3300 gitea-npm-registry non-Compose container labels absent gitea.bytelyst.com public-caddy with direct bypass Bind loopback or private; keep Caddy route
4004 devops-backend /opt/bytelyst/learning_ai_devops_tools/dashboard/docker-compose.yml api.bytelyst.com/devops/* private-admin with direct bypass Bind loopback/private
4010 peakpulse-backend /opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml api.bytelyst.com/peakpulse/* public-caddy with direct bypass Bind loopback or remove host port after Caddy smoke
4011 chronomind-backend /root/bytelyst.ai/repos/learning_ai_clock/docker-compose.yml api.bytelyst.com/chronomind/* public-caddy with direct bypass Bind loopback or remove host port after Caddy smoke
4012 jarvisjr-backend /opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml api.bytelyst.com/jarvisjr/* public-caddy with direct bypass Bind loopback or remove host port after Caddy smoke
4013 nomgap-backend /opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml api.bytelyst.com/nomgap/* public-caddy with direct bypass Bind loopback or remove host port after Caddy smoke
4014 mindlyst-backend /opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml api.bytelyst.com/mindlyst/* public-caddy with direct bypass Bind loopback or remove host port after Caddy smoke
4015 lysnrai-backend /opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml api.bytelyst.com/lysnrai/* public-caddy with direct bypass Bind loopback or remove host port after Caddy smoke
4016 notelett-backend /opt/bytelyst/learning_ai_notes/docker-compose.yml api.bytelyst.com/notelett/* public-caddy with direct bypass Bind loopback or remove host port after Caddy smoke
4017 flowmonk-backend /opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml api.bytelyst.com/flowmonk/* public-caddy with direct bypass Bind loopback or remove host port after Caddy smoke
4019 localmemgpt-backend /opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml api.bytelyst.com/localmemgpt/* public-caddy with direct bypass Bind loopback or remove host port after Caddy smoke
4020 actiontrail-backend /opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml api.bytelyst.com/actiontrail/* public-caddy with direct bypass Bind loopback or remove host port after Caddy smoke
4025 invttrdg-backend /opt/bytelyst/learning_ai_invt_trdg/docker-compose.yml api.bytelyst.com/invttrdg/* public-caddy with direct bypass Bind loopback or remove host port after Caddy smoke
1025 mailpit SMTP /opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml none found in Caddy loopback-only Bound to 127.0.0.1 on 2026-05-27
8025 mailpit UI /opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml none found in Caddy loopback-only Bound to 127.0.0.1 on 2026-05-27
10000 azurite /opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml none found in Caddy loopback-only Bound to 127.0.0.1 on 2026-05-27
1234, 8081 cosmos-emulator /opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml none found in Caddy loopback-only Bound to 127.0.0.1 on 2026-05-27
11434 ollama host process host service ollama.bytelyst.com private-admin Bind loopback/private or auth-gate; do not leave raw public

Non-Public / Internal Listeners

Address/port Process/service Initial class Notes
127.0.0.53:53, 127.0.0.54:53 systemd-resolve host-internal Expected resolver listeners
127.0.0.1:44561 ollama host-internal Secondary loopback listener observed
100.87.53.10:9119, 100.87.53.10:9120 hermes private-admin Tailscale-only bind; keep private
100.87.53.10:51855, [fd7a:115c:a1e0::3c33:350a]:43379 tailscaled private-admin Tailscale control/data listeners
Docker-internal only platform-service, mcp-server, extraction-service, prometheus, cadvisor, node-exporter, valkey, trading-backend docker-internal/private No direct host bind seen, except Caddy may route to some by service name

Unhealthy Containers At Inventory Time

Container Port exposure Initial action
learning_ai_common_plat-llmlab-dashboard-1 0.0.0.0:3075 and Caddy llmlab.bytelyst.com Fix/gate/retire before treating public
learning_ai_common_plat-actiontrail-web-1 0.0.0.0:3060 Classify and fix/retire
learning_ai_common_plat-jarvisjr-web-1 0.0.0.0:3035 Classify and fix/retire
learning_ai_common_plat-localmemgpt-web-1 0.0.0.0:3070 Classify and fix/retire
learning_ai_common_plat-nomgap-web-1 0.0.0.0:3055 Classify and fix/retire
learning_ai_common_plat-flowmonk-web-1 0.0.0.0:3040 Classify and fix/retire
learning_ai_common_plat-mindlyst-web-1 0.0.0.0:3050 Classify and fix/retire

Drift / Follow-Up Findings

  • nomgap-web was an orphan from an older Compose revision, had no Caddy route, and was retired on 2026-05-27.
  • devops-backend runs from /opt/bytelyst/learning_ai_devops_tools/dashboard/docker-compose.yml.
  • devops-web runs from /opt/bytelyst/bytelyst-devops-tools/dashboard/docker-compose.yml, an older path. Align this before changing devops dashboard port bindings.
  • gitea-npm-registry has no Compose labels in Docker inspect output. Find its systemd/compose owner before changing 3300.
  • admin.bytelyst.com points at admin-web:3001, but no admin-web container was present in docker ps during this inventory.

Proposed First Remediation Groups

Do these in separate commits/windows with smoke checks after each group.

  1. Internal emulators and mail tools: 1025, 8025, 10000, 1234, 8081.
    • Expected class: docker-internal or private-admin.
    • Preferred fix: remove host port mappings or bind to 127.0.0.1.
  2. Observability internals: 3100 and any future Prometheus/Grafana/exporter direct binds.
    • Expected class: private-admin.
    • Preferred fix: Docker-internal or Tailscale-only.
  3. Admin/model surfaces: 11434, 3075, 3049, 4004.
    • Expected class: private-admin.
    • Preferred fix: auth gate/private route and no raw public port.
  4. Caddy-backed app/API direct bypass ports: 3000, 3003, 3030, 3085, 4010-4025.
    • Expected class: public-caddy.
    • Preferred fix: keep Caddy public, remove raw direct public binds.
  5. SSH: 22.
    • Expected class: public-direct.
    • Preferred fix: keep public only after key-only and root-login hardening.

Verification Commands

date -Is
ss -ltnp
docker ps --format '{{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}'
docker ps -q | xargs -r docker inspect --format '{{.Name}}\tproject={{index .Config.Labels "com.docker.compose.project"}}\tservice={{index .Config.Labels "com.docker.compose.service"}}\tworkdir={{index .Config.Labels "com.docker.compose.project.working_dir"}}\tconfig={{index .Config.Labels "com.docker.compose.project.config_files"}}'
docker exec caddy caddy validate --config /etc/caddy/Caddyfile
sed -n '1,260p' /opt/bytelyst/Caddyfile
iptables -S DOCKER-USER