- 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)
288 lines
32 KiB
Markdown
288 lines
32 KiB
Markdown
# 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.
|