[serve] interval_secs = 300 prune_days = 30 listen = "0.0.0.0:9100" peer_heartbeat_secs = 60 route_check_interval_secs = 300 dashboard = false # api_token loaded from POM_API_TOKEN env var # The database is at a fixed absolute path, not wherever XDG_DATA_HOME happens # to point. The unit sets XDG_DATA_HOME=/var/lib and an interactive shell does # not, so before this was configured `pom serve` and a hand-run `pom test` on # this host opened two different files and neither said so: the suites ran, # passed, and reported into a database nothing served. [storage] db_path = "/var/lib/pom/pom.db" [instance] name = "hetzner" [targets.mnw] label = "Makenotwork Production" expected_routes = ["/", "/discover", "/login", "/docs"] [[targets.mnw.dns]] name = "makenot.work" record_type = "A" expected = [] [[targets.mnw.dns]] name = "forums.makenot.work" record_type = "A" expected = [] [[targets.mnw.dns]] name = "git.makenot.work" record_type = "A" expected = [] [[targets.mnw.cors]] url = "https://fsn1.your-objectstorage.com/makenotwork-files/cors-probe" origin = "https://makenot.work" method = "PUT" [targets.mnw.whois] domain = "makenot.work" warn_days = 30 [targets.mnw.health] url = "https://makenot.work/api/health" timeout_secs = 10 [targets.mnw.health.expect] status_code = 200 json_fields = { "status" = "operational", "checks.database" = "true" } [targets.mnw.health.trending] baseline_window_hours = 168 spike_threshold = 2.0 [targets.mnw.tls] host = "makenot.work" # NO `[targets.mnw.tests]` HERE, and this is the correction rather than an # omission. Removed 2026-08-23 (infra `0db0a6a0`). # # What stood here was a remote CI check, `ssh = "max@100.106.221.39"` running # `/home/max/staging/run-ci.sh`, carried in this file because prod's live config # had it and the two were meant to stay in step. **It never once succeeded.** # Prod's `test_runs` table held three rows, all of them: # # exit_code 255, 0 seconds # hostkeys_find_by_key_hostfile: hostkeys_foreach failed for # /home/pom/.ssh/known_hosts: Permission denied # Host key verification failed. # # The first is dated 2026-08-20 22:55, which is when the check was added rather # than when it broke. It reported `tests: last test run failed` on both mnw and # mt from that minute onward. # # It cannot be repaired in place, which is why this is a removal and not a fixed # path. `pom.service` sets `ProtectHome=true` and `ProtectSystem=strict`, so the # daemon cannot read or write `/home/pom` at all: no `known_hosts`, no identity # file, no ssh out of this host by any key. That is the hardening working, not a # misconfiguration to relax. # # Nor should it be relaxed. An outbound SSH identity here would give the public # production box credentials into the build host, which is the wrong direction # for that trust to run. # # WHERE THE VERDICT LIVES: astra, which runs the suites locally as the `pom` # user (`/var/lib/pom/staging/run-ci.sh `, see `pom-astra.toml`) and # reports them. On 2026-08-23 that instance read `mnw tests ok, 3337 passed` and # `mt tests ok, 500 passed`. Read it through the peer mesh; this instance # watches the platform, and CI belongs to the machine that can see the output. [targets.mnw.backups] # Where the backups actually are, verified on prod 2026-07-29: the nightly job # writes /var/lib/mnw/backups/makenotwork/makenotwork-.sql.gz. This said # /opt/makenotwork/backups, a path that does not exist on that host, so the # check reported the target degraded for a directory it could not find while the # real backups went unwatched. The app lives at /opt/mnw/current, not # /opt/makenotwork; the old path looks like a pre-rename leftover. # # pom matches files by the "-" or "_" prefix inside this # directory, so the directory is the per-database one, not its parent. directory = "/var/lib/mnw/backups/makenotwork" databases = ["makenotwork"] max_age_hours = 25 interval_secs = 3600 [targets.mnw.scan_pipeline] # Polls /admin/uploads/health.json for queue depth, stuck-scan count, # held backlog, and per-layer error rates. Thresholds per # scan-pipeline-audit.md ยง 6. Localhost on the makenotwork port to # skip the Caddy + Cloudflare path (internal-only signal). base_url = "http://127.0.0.1:3000" interval_secs = 300 timeout_secs = 10 [targets.mnw.synckit_fleet] # Which SyncKit SDK versions are actually syncing. SyncKit is client-side # only, so there is no deployed version to poll; the server aggregates the # version off each sync request's User-Agent and this reads that. Authed # with alerts.alerts_ingest_token, so the check does not spawn without one. # Informational: an old version in the field never degrades the target, only # a readout PoM cannot take does. Localhost for the same reason as above. base_url = "http://127.0.0.1:3000" window_days = 30 interval_secs = 3600 timeout_secs = 10 [targets.mnw-cli] label = "MNW CLI SSH Server" [[targets.mnw-cli.dns]] name = "cli.makenot.work" record_type = "A" expected = [] [targets.mnw-cli.ssh_banner] host = "127.0.0.1" port = 22 timeout_secs = 5 [targets.mt] label = "Multithreaded Forum" expected_routes = ["/"] [targets.mt.health] url = "http://127.0.0.1:3400/api/health" timeout_secs = 5 [targets.mt.health.expect] status_code = 200 # `tls_trust_anchors` is whether the host CA bundle gave mt any outbound TLS # anchors. mt ships none of its own, so a thin or stale bundle takes out the # OAuth token exchange that logs users in, while the box still boots, serves # pages, and answers 200 here. mt deliberately does not degrade `status` for it # (a bad bundle is a whole-fleet condition, and failing the load-balancer check # would turn a login outage into a total one), so this assertion is the only # thing that makes the condition visible before a user finds it. json_fields = { "status" = "operational", "database" = "true", "tls_trust_anchors" = "true" } [targets.mt.tls] host = "forums.makenot.work" # No `[targets.mt.tests]` either, removed in the same pass and for the same # reason. The argument is written out under `[targets.mnw.tls]` above. [targets.htpy] label = "htpy.app" [[targets.htpy.dns]] name = "htpy.app" record_type = "A" expected = [] [targets.htpy.whois] domain = "htpy.app" warn_days = 30 [targets.htpy.health] # htpy-web moved twice and this URL followed neither move: it listens on 3200, # not 8080, and /archive/ now 308s to /viewer/. Checking the old # address failed on connect, so the target has been reading "unreachable" with an # open incident since 2026-03-29 while htpy.app itself served fine. Verified # 2026-07-29: this returns 200 with "htpy" in the body from fw13, astra and # hetzner, and 3200 is in the tailnet ACL's inter-server port list. url = "http://100.99.153.68:3200/viewer/S_2" timeout_secs = 10 [targets.htpy.health.expect] status_code = 200 body_contains = "htpy" [targets.htpy.tls] host = "htpy.app" [peers.astra] # By tailnet name. MagicDNS resolves it from this host: verified 2026-08-23, # `astra:9100` and the old literal both answered 401 identically. address = "astra:9100" on_missing = "alert" # token = "" [peers.macbook] # See the note in `pom-astra.toml`: the literal that stood here was not in the # tailnet at all, so this peer resolved to nothing. address = "mbp:9100" on_missing = "log" # token = "" [alerts] # postmark_token loaded from POM_POSTMARK_TOKEN env var to = "pom-alerts@makenot.work" # MNW operator-log sink: also pushes alerts to POST {mnw_url}/api/internal/alerts. # The token loads from POM_ALERTS_INGEST_TOKEN and must match ALERTS_INGEST_TOKEN # in /etc/mnw/makenotwork.env; both mnw_url and the token must be present, else # the sink stays disabled. # # Not the public URL. Caddy answers 404 to /api/internal/* by design (see the # @internal matcher in the Caddyfile), so the sink has to address the app port # directly. Measured 2026-08-15: POST https://makenot.work/api/internal/alerts # is 404, POST http://127.0.0.1:3000/api/internal/alerts is 401 without a token. mnw_url = "http://127.0.0.1:3000" # WAM ticket sink: failure alerts become tickets instead of email, and # recoveries stay on email. WAM runs on this host; its token loads from # POM_WAM_TOKEN and must match the value in /etc/wam/env on the WAM node. # # Enabled 2026-08-15, once WAM actually enforced auth. Until that day the # deployed binary was an April build predating its own require_auth middleware, # so a token-sending client would have been talking to a server that ignored # tokens. Both nodes now answer an unauthenticated /tickets with 401. wam_url = "http://127.0.0.1:7890"