Files
itpp-infrastructure/docs/infrastructure/core-service-inventory-2026-09-15.md
T
ShoNuff cac3cde372 docs(infra): app4 + core-bu provisioning, migration plan, verified inventory
- migration-plan-app4-core-bu-2026-09-15.md: 8-phase plan (Nuremberg decision,
  provider-diversity gap, acceptance criteria, rollback, DNS/Caddy checklist)
- core-service-inventory-2026-09-15: verified Core inventory, ~30 customer-facing
  services (the Aug 15 plan listed 5), 3 DocuSeal instances, TimeTrex Postgres,
  dead Caddy routes
- reference-update-matrix-2026-09-15: 52 artifacts that name a host
- fix naming collision: 6 files called the Hetzner box core-bu, the name core-bu
  now claims; app1-bu = 5.161.225.131, core-bu = 159.195.204.203 (netcup Nuremberg)
- correct the false provider-diversity claim (the standby is now netcup too)
- supersede app4-migration-plan.md (wrong region reported, silent on core-bu)
2026-09-15 10:19:46 -04:00

32 KiB

Core Service Inventory — 2026-09-15

Host: Core (152.53.192.33, netcup RS 2000, 8 vCPU / 15 GB / 503 GB, Debian 13, Manassas VA) Purpose: exhaustive, evidence-based inventory to plan the app4 migration (see app4-migration-plan.md). Method: read-only commands only, run directly on Core. No service was started, stopped, or restarted. hermes-maintenance was never invoked. The Hermes gateway (hermes-gateway.service, user unit) was left untouched and observed only via systemctl --user status.

All commands quoted below were actually executed on Core on 2026-09-15. Numbers are copy-pasted from real output, not estimated.

Headline counts (verified)

Metric Count Command
Docker containers (all, all running) 13 docker ps -a --format ...
systemd services in running state 57 systemctl list-units --type=service --state=running
Caddy site blocks in /etc/caddy/Caddyfile 49 grep -cE '^[a-zA-Z0-9].*\{$|^http://.*\{$' /etc/caddy/Caddyfile
PostgreSQL databases (excl. template0/1/postgres) 1 (hotnow) sudo -u postgres psql -c '\l+'
Hermes cron jobs (/root/.hermes/cron/jobs.json) 87 Python count of jobs[]
Listening TCP sockets 66 ss -tlnp
Listening UDP sockets 14 ss -ulnp
TLS certs, real ACME (Let's Encrypt / ZeroSSL) 47 find .../certificates -mindepth 2 -maxdepth 2 ! -path '*/local/*'
TLS certs, Caddy tls internal self-signed 7 find .../certificates/local -mindepth 1 -maxdepth 1
System crontab entries (root) 12 crontab -l
/etc/cron.d/* files 6 (all stock Debian/PHP/sysstat except reap-chrome) ls /etc/cron.d

Hostile surprises found (read this section first)

  1. The app4 migration plan's "stays on Core" and "moves to app4" lists are both incomplete. Core is running roughly 30 additional customer-facing FastAPI/Node/systemd services that the plan document never mentions: DigLocate API, DRE MCP + DRE Portal, FT360 MCP, HotNow API (with its own Postgres DB hotnow and its own Redis DB 1), IntelSight API, OSINT API, OSINT Person MCP, Outlook upload receiver, Pipeline API, PRY (OSINT search backend), PTA registration + survey, Rally family calendar, SeeMyTrip, Shark Attack Fantasy Game, Shopping Cart Builder, SNMP metrics server, Transitpin WebSocket relay, Twilio MCP, VerdictTank API + worker, Voice Agent + STT, Auth API, plus two disabled-but-present services (crawl4ai, hermes-control-deck). See ambiguities[] and the systemd table below. Every one of these has a live Caddy site (see caddy_sites) and needs an explicit stays/moves decision.
  2. DocuSeal did NOT move to App1 as the backup-plan.md's "Migration History" section claims. Three DocuSeal containers (docuseal, docuseal-dre, docuseal-modelortho) are running on Core right now on ports 8091/8094/8092, backed by bind-mounted ./data dirs, each with its own .env. backup-plan.md still lists DocuSeal backups running against App1 (app1/docuseal/). The app4-migration-plan.md's "Inventory caveat" already flagged this: confirmed here, DocuSeal is on Core, not migrated. Same finding for SearXNG: it is running on Core (searxng container, port 127.0.0.1:8888, container up 2 hours) despite backup-plan.md marking core/searxng/ as a stale/removed path.
  3. PostgreSQL on Core has essentially nothing "shared." Only one real customer database exists: hotnow (16 MB, owned by role hotnow_app). TimeTrex runs its own separate PostgreSQL 16 instance inside its own Docker container (bind-mounted /root/docker/timetrex/database), not the host Postgres 17 instance. The migration plan's assumption of "a shared Postgres/Redis instance... provisioned fresh on app4" understates this: there are two independent Postgres engines on Core (host 17.10 for HotNow, containerized 16 for TimeTrex), and they do not share data.
  4. Redis on Core is nearly idle (used_memory 741 KB, 0 keys in DB0, DBSIZE 0), but HotNow's .env explicitly points at redis://localhost:6379/1, meaning host Redis has at least one active customer consumer despite the migration plan describing Redis only as "shared cache" with "consumers TBD in Phase 0." HotNow is that consumer, but note DB1 was also empty at scan time (idle app or nothing warm).
  5. crm.debtrecoveryexperts.com and crm.intelsight.io are dead Caddy routes. Both proxy to localhost:3003 (Twenty CRM), but nothing is listening on port 3003 — ss -tlnp | grep 3003 returned nothing. Twenty CRM migrated to App1 per backup-plan.md, but the Caddy site blocks pointing at it were never removed from Core's Caddyfile. These will 502 today; they are not "moving" apps, they are stale entries to clean up (or confirm App1 handles them via a different route/CNAME not visible from Core).
  6. UFW has two apparently-orphaned rules: 8080/tcp on tailscale0 labeled "Vaultwarden via Tailscale" (Vaultwarden migrated off Core, port not listening) and 8890/tcp labeled "temp map server" (also not listening). Neither is a running risk today but both are stale firewall state that should not be copied to app4.
  7. 13 .env/config files carry live secrets (SMTP creds, LetterStream API keys, Docuseal API tokens, Hexclave keys, Twilio SIDs, provider API keys for a dozen LLM vendors, RingLogix creds, netcup creds) all in /root/.hermes/.env, /opt/dre-portal/.env, /etc/verdicttank.env, /root/hotnow-api/.env, /opt/voice-agent/.env, /opt/hermes-voice/.env. None of these are backed up in plaintext by policy (auth-api-backup.sh explicitly redacts values), but they must be manually and securely transferred to app4, not rsynced as part of a generic root-essentials backup.
  8. Backup coverage gaps confirmed for the newly-discovered services. No backup script exists for: DigLocate, HotNow, IntelSight, Pipeline, PRY, PTA registration, PTA survey, SeeMyTrip, Shark Game, Shopping Cart, Survey (pta-survey duplicate), Voice Agent, DRE Portal. Each of these has its own SQLite DB (see data_paths[]) with zero S3 backup today. backup-plan.md's "Unbacked Services: none" claim (dated 2026-08-08) is now stale; it predates all of these services appearing in systemctl list-units.
  9. Postgres cluster mismatch: host runs PostgreSQL 17.10 (postgresql@17-main.service); TimeTrex's containerized instance is PostgreSQL 16. A wholesale pg_dumpall migration approach from the plan's section 5 will not work across both without per-instance handling.

containers[] — Docker (13 total, all running)

name image ports restart policy bind mounts / volumes compose file classification
docuseal docuseal/docuseal:latest 127.0.0.1:8091->3000 always bind /root/docker/docuseal/data -> /data /root/docker/docuseal/docker-compose.yml move-to-app4
docuseal-dre docuseal/docuseal:latest 127.0.0.1:8094->3000 always bind /root/docker/docuseal-dre/data -> /data /root/docker/docuseal-dre/docker-compose.yml move-to-app4
docuseal-modelortho docuseal/docuseal:latest 127.0.0.1:8092->3000 always bind /root/docker/docuseal-modelortho/data -> /data /root/docker/docuseal-modelortho/docker-compose.yml move-to-app4
timetrex skewll/timetrex:latest 127.0.0.1:8085->80 unless-stopped bind storage/logs/database/ini.php /root/docker/timetrex/docker-compose.yml move-to-app4
microbin danielszabo99/microbin:latest 127.0.0.1:8260->8080 unless-stopped bind /opt/microbin/data -> /app/pasta_data /opt/microbin/docker-compose.yml move-to-app4
uptime-kuma louislam/uptime-kuma:latest 0.0.0.0:3001->3001 unless-stopped bind /root/docker/uptime-kuma/data -> /app/data /root/docker/uptime-kuma/docker-compose.yml move-to-app4
grafana grafana/grafana:11.4.0 0.0.0.0:3002 (host network, exposed by container itself) unless-stopped named volume grafana_data_final -> /var/lib/grafana none found (manual docker run?) stays-on-Core
prometheus prom/prometheus:latest none published (host network via container) 9090 exposed by container unless-stopped bind prometheus.yml, named volume prometheus_data, bind textfile dir /root/docker/monitoring/... (compose not directly inspected) stays-on-Core
telegraf telegraf:latest 9273 (container) unless-stopped bind /root/docker/monitoring/telegraf/telegraf.conf monitoring compose stays-on-Core
mikrotik-exporter swoga/mikrotik-exporter:latest 127.0.0.1:9436->9436 unless-stopped bind config.yml monitoring compose stays-on-Core
searxng searxng/searxng:latest 127.0.0.1:8888->8080 always bind searxng-data, searxng-themes; named cache volume /root/docker/searxng/docker-compose.yml stays-on-Core (Super Search backend; NOTE: backup-plan.md incorrectly marks this removed)
browserless browserless/chrome:latest 0.0.0.0:3000->3000 always none none found stays-on-Core (Hermes browser dependency)
camofox-browser camofox-browser:dataimpulse 0.0.0.0:9377->9377 unless-stopped none none found stays-on-Core (Hermes stealth browser dependency)

Sizes (docker system df -v): total image space several GB (largest: browserless 3.06GB, camofox 2.27GB, timetrex 1.7GB, twentycrm 1.12GB image present but unused/0 containers, kokoro-fastapi 3.63GB image present but unused). Named volumes of note: prometheus_data 118.9MB, grafana_data_final 14.65MB, grafana_data 50.13MB (older/orphaned), grafana_data_v3 14.59MB (orphaned), twenty_db-data 71.46MB (orphaned, Twenty CRM no longer runs on Core).

systemd_units[] — running, non-stock (57 running total; below are the added/customer-relevant ones)

unit purpose exec port env file data path classification
auth-api.service ITPP Auth API (SSO) uvicorn server:app :8500 127.0.0.1:8500 /root/projects/auth/.env /root/projects/auth/auth.db (32M dir) needs-decision (auth.itpropartner.com is customer SSO; plan doesn't mention it)
diglocate-api.service 811 locate ticket mgmt API uvicorn main:app :8000 127.0.0.1:8000 none found /root/projects/diglocate (118M) move-to-app4
dre-mcp.service DRE MCP server (Hermes tool) python server.py n/a (stdio/mcp) /root/.hermes/.env /root/docker/dre-mcp (171M) needs-decision (Hermes MCP but customer=DRE data)
dre-portal.service DRE customer portal API uvicorn app.main:app :8093 127.0.0.1:8093 /opt/dre-portal/.env /opt/dre-portal/data/dre.db (110M dir) move-to-app4
ft360-mcp.service FT360 (FleetTracker360) MCP server python server.py n/a none found /root/docker/ft360-mcp (60K) needs-decision
gitea-runner.service Gitea Actions Runner (core) act_runner daemon :35849 none found /var/lib/gitea-runner stays-on-Core (CI runner tied to Core, not customer-facing)
hermes-assistant.service Hermes Assistant PWA backend python server.py 127.0.0.1:8082 (python3 pid 1098) none found /root/hermes-assistant (169M) stays-on-Core (Hermes-related)
hermes-browser.service Headless Chromium (CDP) chrome --remote-debugging-port=9222 127.0.0.1:9222 n/a n/a stays-on-Core (Hermes dependency)
hermes-socat-8787.service port forward for HermesX mobile socat 8787->8642 0.0.0.0:8787 n/a n/a stays-on-Core
hermes-voice.service Hermes Voice (SvelteKit) node build/index.js 127.0.0.1:4331 /opt/hermes-voice/.env /opt/hermes-voice (123M) needs-decision (named "Hermes" but proxied at voice.itpropartner.com, a customer-facing plan item)
host-metrics-exporter.service systemd/docker/disk/mem exporter python n/a (writes textfile) n/a n/a stays-on-Core
hotnow-api.service HotNow API backend uvicorn main:app :8001 127.0.0.1:8001 /root/hotnow-api/.env /root/hotnow-api (66M) + Postgres db hotnow (16MB) + Redis DB1 move-to-app4
intelsight-api.service IntelSight API python server.py :8099 127.0.0.1:8099 none found /root/intelsight-api/intelsight.db (64M dir) move-to-app4
node_exporter.service Prometheus node exporter node_exporter 0.0.0.0:9100 n/a n/a stays-on-Core
ops-portal.service ITPP Ops Portal backend uvicorn server:app :8090 127.0.0.1:8090 /root/.hermes/.env /opt/ops-portal/ops.db (148M dir) move-to-app4 (per plan)
osint-api.service OSINT Tool Daily Discovery API uvicorn api:app :8100 127.0.0.1:8100 /root/.hermes/.env /opt/osint-api (323M) needs-decision
osint-person.service OSINT Person MCP server python server.py n/a /root/.hermes/.env /root/docker/osint-person-mcp (314M) needs-decision (Hermes MCP tool)
outlook-upload.service Outlook folder upload receiver python upload_server.py 127.0.0.1:8240 none /root/upload-staging needs-decision
pipeline-api.service Project Pipeline API (customer portal backend) python server.py :8200 127.0.0.1:8200 none found /root/projects/pipeline/pipeline.db (30M dir) move-to-app4
pry.service PRY unified OSINT search backend python server.py :8905 127.0.0.1:8905 /root/.hermes/.env /root/docker/pry (66M) needs-decision
pta-registration.service TIMAPTA Membership Registration uvicorn server:app :8114 127.0.0.1:8114 none /opt/pta-registration/pta.db (53M dir) move-to-app4
rally.service Rally Family Calendar python run.py :8105 127.0.0.1:8105 none found /opt/rally/data/rally.db (294M dir) move-to-app4
seemytrip.service SeeMyTrip media pipeline uvicorn server:app :8113 127.0.0.1:8113 none /opt/seemytrip/data/seemytrip.db (212M dir) move-to-app4
shark-game.service Shark Attack Fantasy Game backend python server.py :8083 0.0.0.0:8083 none /root/shark-game/backend/game.db (127M dir) move-to-app4
shopping-cart.service Shopping Cart Builder uvicorn app:app :8101 127.0.0.1:8101 none /opt/shopping-cart (147M) move-to-app4
snmp-metrics.service SNMP metrics HTTP server python snmp-http-server.py :8105-adjacent (0.0.0.0:8105) 0.0.0.0:8105 n/a n/a stays-on-Core
super-search.service Super Search MCP server python server.py :8899 0.0.0.0:8899 /root/.hermes/.env /root/docker/super-search (1.1G) stays-on-Core (Hermes MCP)
survey-registration.service TIMA Location Survey uvicorn server:app :8115 127.0.0.1:8115 none /opt/pta-survey/survey.db (50M dir) move-to-app4
transitpin.service TransitPin WebSocket relay node server.js 127.0.0.1:8210 none /opt/transitpin (496K) move-to-app4
twilio-mcp.service Twilio MCP server python server.py n/a /root/.hermes/.env /root/docker/twilio-mcp (171M) stays-on-Core (Hermes MCP; also feeds voice stack)
verdicttank-api.service VerdictTank form handler + PDF gen python api.py :8201 127.0.0.1:8201 /etc/verdicttank.env /opt/verdicttank/users.db (15M dir) move-to-app4
verdicttank-worker.service VerdictTank review worker (multi-model panel) python worker.py n/a /etc/verdicttank.env /opt/verdicttank move-to-app4
voice-agent-stt.service Voice Agent STT (faster-whisper) uvicorn stt_server:app :9000 127.0.0.1:9000 none /opt/voice-agent (470M) needs-decision (plan lists voice stack for app4 but not this STT sub-service explicitly)
voice-agent.service Voice Agent (open-source stack) uvicorn agent_server:app :9101 127.0.0.1:9101 /opt/voice-agent/.env /opt/voice-agent move-to-app4 (voice.itpropartner.com / voice-open.itpropartner.com)
wazuh-agent.service Wazuh SIEM agent (Wazuh binary) n/a n/a n/a stays-on-Core (security agent, host-level)
caddy.service reverse proxy caddy run 152.53.192.33:80/443 /etc/caddy/Caddyfile /var/lib/caddy (TLS store) stays-on-Core per plan (routes to be trimmed after cutover)
postgresql@17-main.service Postgres host instance postgres 127.0.0.1:5432, [::1]:5432 n/a /var/lib/postgresql (71M) needs-decision (only DB is hotnow, which is moving)
redis-server.service Redis redis-server 127.0.0.1:6379, [::1]:6379 n/a /var/lib/redis (8K, no persistence file written; appendonly no, RDB save schedule set) needs-decision (HotNow is the only confirmed consumer found)

Disabled-but-present units (not running, found via unit files, no .service entry in the running list): crawl4ai.service (Crawl4AI extraction microservice, port 8910, disabled/inactive) and hermes-control-deck.service (backend API, port 8200 conflicts with pipeline-api's port 8200 if ever enabled — flagged in ambiguities). Both should be accounted for even though inactive.

Hermes-related systemd units confirmed via systemctl --user list-units (separate user-level manager, NOT touched or restarted): hermes-gateway.service (active running, PID 100998, 5.3G RAM) and ssh-agent.service. These stay on Core by definition of the migration and were only observed, never controlled.

ports[] — Listening sockets (66 TCP, 14 UDP)

Representative table (full list captured via ss -tlnp/ss -ulnp, see raw command output in this task's tool log for the complete 80-row set):

port bind process service
443 152.53.192.33 caddy Caddy HTTPS (customer + core sites)
80 152.53.192.33 caddy Caddy HTTP redirect
443 100.71.155.7 (Tailscale) tailscaled Tailscale-only HTTPS
22 0.0.0.0 / [::] sshd SSH
5432 127.0.0.1 / [::1] postgres Host PostgreSQL 17
6379 127.0.0.1 / [::1] redis-server Redis
3000 0.0.0.0 / [::] docker-proxy browserless
3001 0.0.0.0 / [::] docker-proxy uptime-kuma
3002 * (all interfaces) grafana Grafana (not port-mapped through Caddy's default_bind; exposed directly)
9377 0.0.0.0 / [::] docker-proxy camofox-browser
8090-8115, 8200-8210, 8500, 8899-8905 range mostly 127.0.0.1 uvicorn/python see systemd_units table above
8787 0.0.0.0 socat Hermes API forward for mobile
8642 0.0.0.0 hermes Hermes gateway internal API (do not touch)
9090 * prometheus Prometheus
9100 * node_exporter Node exporter
9273 * telegraf Telegraf
9436 127.0.0.1 docker-proxy mikrotik-exporter
9222 127.0.0.1 chrome Hermes headless browser CDP
25 127.0.0.1 / [::1] exim4 local mail relay
500, 4500 (UDP) 0.0.0.0 / [::] charon (strongSwan) IPsec (unclear purpose — see ambiguities)
1701 (UDP) 0.0.0.0 xl2tpd L2TP (unclear purpose — see ambiguities)
51821 (UDP) 0.0.0.0 / [::] (no owning process shown) WireGuard, per UFW comment
41641 (UDP) 0.0.0.0 / [::] tailscaled Tailscale
5353 (UDP) 0.0.0.0 / [::] avahi-daemon mDNS

caddy_sites[] — 49 site blocks

Extracted from /etc/caddy/Caddyfile (513 lines; global block sets default_bind 152.53.192.33, email info@itpropartner.com). Full list of hostnames and their backend targets:

hostname(s) backend notes
core.itpropartner.com 127.0.0.1:8240, 127.0.0.1:8201, static /var/www multi-path handler; Ops Portal / VerdictTank API paths mixed in
sign.itpropartner.com 127.0.0.1:8091 DocuSeal
sign.modelortho.com 127.0.0.1:8092 DocuSeal (modelortho)
sign.debtrecoveryexperts.com 127.0.0.1:8094 DocuSeal (DRE)
ops.itpropartner.com 127.0.0.1:8090, 127.0.0.1:8100, static Ops Portal + OSINT API
shark.iamgmb.com 127.0.0.1:8083 Shark Game
internal.debtrecoveryexperts.com 127.0.0.1:8093, static, basic_auth DRE Portal internal, password-protected
portal.debtrecoveryexperts.com redirect to my.debtrecoveryexperts.com/start
pay.debtrecoveryexperts.com static
my.debtrecoveryexperts.com 127.0.0.1:8093, static DRE Portal
crm.debtrecoveryexperts.com localhost:3003 DEAD — nothing listening on 3003
dig.iamgmb.com 127.0.0.1:8000, static DigLocate
uptimekuma.itpropartner.com localhost:3001 Uptime Kuma
gps.fleettracker360.com 152.53.39.202:8082 (App2, remote proxy) Traccar on App2, not Core
my.itpropartner.com 152.53.241.111:8090 (App3, remote), 127.0.0.1:8200 (pipeline), static Mixed remote+local
status.itpropartner.com 127.0.0.1:8210, 127.0.0.1:3001, static Transitpin relay + Uptime Kuma API
track.fleettracker360.com (http only) 152.53.39.202:5055 (App2, remote)
hear.fleettracker360.com static
voice.itpropartner.com 127.0.0.1:4331 Hermes Voice / SvelteKit
voice-open.itpropartner.com 127.0.0.1:9101 Voice Agent
auth.itpropartner.com 127.0.0.1:8500, static Auth API
my.intelsight.io 127.0.0.1:8099, static IntelSight
intelsight.io static landing page
intelsight.iamgmb.com static landing page
schedule.iamgmb.com static
seemytrip.iamgmb.com 127.0.0.1:8113, static SeeMyTrip
rally.iamgmb.com 127.0.0.1:8105, static Rally
shopping.iamgmb.com 127.0.0.1:8101, 127.0.0.1:8210, static Shopping Cart
pry.iamgmb.com (http only) 127.0.0.1:8905, static PRY
crm.intelsight.io localhost:3003 DEAD — same as above
www.hotnow.io redirect to hotnow.io
hotnow.io static
app.hotnow.io static
api.hotnow.io 127.0.0.1:8001 HotNow API
admin.hotnow.io static
timetrex.iamgmb.com 127.0.0.1:8085 TimeTrex
webmail.timapta.org, webmail.transitpin.com, webmail.rfptank.com, webmail.radartank.com, webmail.verdicttank.com redirect to heracles.mxrouting.net, tls internal 5 sites, self-signed certs
share.itpropartner.com 127.0.0.1:8260 Microbin
verdicttank.com, www.verdicttank.com 127.0.0.1:8201, static VerdictTank
voipsimplicity.itpropartner.com static, tls internal
forefront.itpropartner.com static, tls internal
ops.verdicttank.com 127.0.0.1:8201, static
register.timapta.org 127.0.0.1:8114 PTA registration
pta.iamgmb.com 127.0.0.1:8114 PTA registration (dup route)
survey.iamgmb.com 127.0.0.1:8115 PTA survey

caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile returned Valid configuration (with a formatting-only warning, no functional errors).

databases[] — PostgreSQL + Redis

PostgreSQL 17.10 (host, postgresql@17-main.service, port 5432):

database owner size
hotnow hotnow_app 16 MB
postgres postgres 7510 kB
template0 postgres 7353 kB
template1 postgres 7582 kB

Roles: hotnow_app (no special attrs), postgres (superuser).

TimeTrex has its own containerized PostgreSQL 16 instance (visible only via the bind-mounted /root/docker/timetrex/database PGDATA directory, type=postgres, host=localhost, user=timetrex in timetrex.ini.php). Not queryable from the host psql since it lives inside the container's own network namespace / port, and the container does not publish 5432 to the host.

Redis 8.0.2 (redis-server.service, port 6379): used_memory 741 KB, used_memory_rss 17 MB, save 3600 1 300 100 60 10000, appendonly no. DBSIZE on DB0 and DB1 both returned 0 at scan time. HotNow's .env references redis://localhost:6379/1 confirming at least one real customer consumer even though it was empty when sampled.

cron[] — system cron + Hermes cron

Root system crontab (crontab -l, 12 real entries beyond blank/env lines): boys-mail-monitor (hourly + daily), shark-game scraper (daily), shark-draft-reminder (15 min), hermes-backup.sh (1 AM), backup-audit-check.sh (2 AM), root-essentials-backup.sh (3 AM), system-config-sync.sh (4 AM), snmp-collect.sh (every minute), core-services-backup.sh (1:30 AM), watchdog-wg-tunnel.sh (10 min), ops-report-collect/send (23:30).

/etc/crontab: only stock Debian run-parts hourly/daily/weekly/monthly entries.

/etc/cron.d/: e2scrub_all, kernel (fstrim), php (session cleanup), sysstat — all stock Debian. One custom entry: reap-chrome (every 30 min, reaps orphaned Playwright Chrome processes, added per DR note 2026-09-09).

No other Linux user has a crontab (checked every user in /etc/passwd).

Hermes cron (/root/.hermes/cron/jobs.json): 87 jobs total, last_status: ok for the great majority; 5 jobs currently show last_status: error (Doc-Live Verify, Security Compliance Check, OSINT Tool Daily Discovery, Super Search Daily Discovery, Nous LLM Pricing Weekly Report) — these are Hermes-internal automation, not customer-facing infra, and are flagged for the Hermes team separately, not part of this migration's scope. Backup-relevant Hermes cron jobs (docuseal-backup, docuseal-dre-backup, docuseal-modelortho-backup, timetrex-backup, auth-api-backup, and dozens of others for App1/2/3 services) confirm the backup-plan.md schedule is implemented as documented, with the caveat in surprise #8 above (several newly-found Core services have no corresponding job at all).

certs[] — TLS

47 real ACME-issued certificates under /var/lib/caddy/.local/share/caddy/certificates/acme-v02.api.letsencrypt.org-directory/ and one under the ZeroSSL CA path (api.hotnow.io). All checked with openssl x509 -noout -enddate; none are expired, nearest expiry is status.itpropartner.com and voipsimplicity-adjacent (self-signed, see below) in mid-October 2026, furthest is pta.iamgmb.com (Dec 9 2026). Full list of hostname -> expiry captured in the JSON companion file.

7 self-signed (tls internal) certs for internal/webmail redirect stubs: webmail.verdicttank.com, webmail.rfptank.com, webmail.radartank.com, webmail.timapta.org, webmail.transitpin.com, voipsimplicity.itpropartner.com, forefront.itpropartner.com — these expire within 1 year of issuance and are low-stakes redirect-only stubs.

Migration note: app4 must pre-issue its own certs for every moving hostname before DNS cutover (per the plan's checklist); the existing Core certs cannot be copied over and reused as-is without importing Caddy's storage, which the plan does not currently call for.

data_paths[] — sizes (du -sh, all real measurements)

path size owner service
/root/docker/uptime-kuma/data 487M uptime-kuma
/root/docker/super-search/ 1.1G super-search MCP (venv-heavy)
/opt/voice-agent 470M voice-agent/voice-agent-stt
/opt/rally/data/rally.db (dir 294M) 294M rally
/opt/osint-api 323M osint-api
/root/docker/osint-person-mcp 314M osint-person MCP
/opt/seemytrip/data within 212M dir seemytrip
/opt/ops-portal 148M ops-portal
/opt/shopping-cart 147M shopping-cart
/root/docker/dre-mcp 171M dre-mcp
/root/docker/twilio-mcp 171M twilio-mcp
/root/shark-game 127M shark-game
/root/hermes-assistant 169M hermes-assistant
/opt/hermes-voice 123M hermes-voice
/root/projects/diglocate 118M diglocate-api
/opt/dre-portal 110M dre-portal
/var/lib/docker/volumes/prometheus_data/_data 114.9M prometheus
/opt/pta-registration 53M pta-registration
/opt/pta-survey 50M survey-registration
/root/docker/timetrex (container data) 50M timetrex
/root/hotnow-api 66M hotnow-api
/root/intelsight-api 64M intelsight-api
/root/docker/pry 66M pry
/root/projects/pipeline 30M pipeline-api
/root/projects/auth 32M auth-api
/var/lib/docker/volumes/grafana_data_final/_data 15M grafana
/opt/verdicttank 15M verdicttank-api/worker
/var/lib/postgresql 71M host PostgreSQL
/root/docker/docuseal-dre/data 2.5M docuseal-dre
/root/docker/docuseal-modelortho/data 1.4M docuseal-modelortho
/root/docker/docuseal/data 688K docuseal
/opt/transitpin 496K transitpin
/opt/microbin/data 4.0K microbin
/var/lib/redis 8.0K redis-server (no RDB dump on disk at scan time)

Overall disk: /dev/vda4 503G total, 157G used (33%), 326G available. /var/lib/docker 31G, /root/docker 5.8G, /opt 6.4G.

Memory/CPU snapshot at scan time: 15Gi RAM total, 4.9Gi used, 11Gi buff/cache, 8Gi swap configured with 1.9Gi in use; 8 vCPUs.

ambiguities[]

  1. grafana, prometheus, telegraf, mikrotik-exporter containers have no discoverable docker-compose.yml in the paths their bind mounts imply beyond /root/docker/monitoring/... for the latter three; Grafana's compose file was not found at all (likely started via a one-off docker run or a compose file elsewhere not searched). Confirm before any monitoring-stack changes.
  2. crawl4ai.service and hermes-control-deck.service are defined but disabled/inactive. hermes-control-deck binds port 8200, the same port pipeline-api.service is actively using — if hermes-control-deck is ever enabled it will fail to bind. Needs a decision on whether either is still needed; neither showed up in the migration plan.
  3. IPsec (charon/strongSwan) on UDP 500/4500 and L2TP (xl2tpd) on UDP 1701 are running with no obvious owner in the docs reviewed. Unclear if this is a legacy VPN endpoint for a customer or leftover config. Flag for the infra team, do not assume it is decommission-safe.
  4. UFW rule 8890/tcp "temp map server" — nothing is listening on this port today. Unclear what service this was for or whether it is scheduled to return.
  5. crm.debtrecoveryexperts.com and crm.intelsight.io Caddy blocks point to localhost:3003 with nothing listening. Either Twenty CRM on App1 is reached by a different mechanism not visible from Core (e.g. these routes are actually dead code) or there's a missing local proxy. Needs confirmation before deciding whether these Caddy blocks move, get deleted, or get repointed to App1 directly.
  6. gitea-runner.service listens on *:35849 — purpose of this port (act_runner's own control port) unconfirmed from Core alone; likely not customer-facing, but flagged since it's a nonstandard high port bound to all interfaces.
  7. Grafana bound to *:3002 on all interfaces (not proxied through Caddy's default_bind and not restricted to localhost or Tailscale) — worth checking if this is intentionally public; it wasn't found behind any Caddy site block in the scanned Caddyfile.
  8. TimeTrex's internal PostgreSQL 16 could not be queried from the host psql (different engine version/instance inside the container, not published to host network). A migration plan needs a container-internal pg_dump step (documented in timetrex-backup.sh, which already does docker exec timetrex ... pg_dump), not a host-level one.
  9. Several running services have no .env and no discoverable database config found in a shallow scan (diglocate-api, ft360-mcp, intelsight-api — wait, intelsight-api does have intelsight.db, pipeline-api, pry, rally, seemytrip, shark-game, shopping-cart, transitpin) beyond the SQLite files already listed; deeper source inspection would be needed to confirm each has no hidden Postgres/Redis dependency the shallow .env grep missed.

Backup coverage summary (cross-checked against backup-plan.md)

Covered by an existing script (verified script exists, not verified last-run freshness beyond what backup-plan.md states): Hermes (full + live sync), /root essentials, Grafana, Uptime Kuma, Docker volumes, Prometheus, Auth API, DocuSeal (x1 base + x2 named backups for -dre and -modelortho), TimeTrex.

No backup coverage found for (all newly-discovered in this audit): DigLocate API, FT360 MCP (only stats/export scripts found, not a DB backup), HotNow API + its Postgres DB + Redis DB1, IntelSight API, OSINT API, OSINT Person MCP, Pipeline API, PRY, PTA Registration, PTA Survey, Rally (only debug/dump scripts found, not a scheduled backup job matching this DB path), SeeMyTrip, Shark Game backend DB, Shopping Cart, DRE Portal (dre.db), Voice Agent, Voice Agent STT, VerdictTank's users.db (VerdictTank does have hello-*-collect.py cron jobs but not a DB backup job).

These gaps should be closed on app4 as part of Phase 1/3 of the migration, not carried over as-is.