Skip to main content

max / makenotwork

8.5 KB · 238 lines History Blame Raw
1 [serve]
2 interval_secs = 300
3 prune_days = 30
4 listen = "0.0.0.0:9100"
5 peer_heartbeat_secs = 60
6 route_check_interval_secs = 300
7 dashboard = false
8 # api_token loaded from POM_API_TOKEN env var
9
10 # The database is at a fixed absolute path, not wherever XDG_DATA_HOME happens
11 # to point. The unit sets XDG_DATA_HOME=/var/lib and an interactive shell does
12 # not, so before this was configured `pom serve` and a hand-run `pom test` on
13 # this host opened two different files and neither said so: the suites ran,
14 # passed, and reported into a database nothing served.
15 [storage]
16 db_path = "/var/lib/pom/pom.db"
17
18 [instance]
19 name = "hetzner"
20
21 [targets.mnw]
22 label = "Makenotwork Production"
23 expected_routes = ["/", "/discover", "/login", "/docs"]
24
25 [[targets.mnw.dns]]
26 name = "makenot.work"
27 record_type = "A"
28 expected = []
29
30 [[targets.mnw.dns]]
31 name = "forums.makenot.work"
32 record_type = "A"
33 expected = []
34
35 [[targets.mnw.dns]]
36 name = "git.makenot.work"
37 record_type = "A"
38 expected = []
39
40 [[targets.mnw.cors]]
41 url = "https://fsn1.your-objectstorage.com/makenotwork-files/cors-probe"
42 origin = "https://makenot.work"
43 method = "PUT"
44
45 [targets.mnw.whois]
46 domain = "makenot.work"
47 warn_days = 30
48
49 [targets.mnw.health]
50 url = "https://makenot.work/api/health"
51 timeout_secs = 10
52
53 [targets.mnw.health.expect]
54 status_code = 200
55 json_fields = { "status" = "operational", "checks.database" = "true" }
56
57 [targets.mnw.health.trending]
58 baseline_window_hours = 168
59 spike_threshold = 2.0
60
61 [targets.mnw.tls]
62 host = "makenot.work"
63
64 # NO `[targets.mnw.tests]` HERE, and this is the correction rather than an
65 # omission. Removed 2026-08-23 (infra `0db0a6a0`).
66 #
67 # What stood here was a remote CI check, `ssh = "max@100.106.221.39"` running
68 # `/home/max/staging/run-ci.sh`, carried in this file because prod's live config
69 # had it and the two were meant to stay in step. **It never once succeeded.**
70 # Prod's `test_runs` table held three rows, all of them:
71 #
72 # exit_code 255, 0 seconds
73 # hostkeys_find_by_key_hostfile: hostkeys_foreach failed for
74 # /home/pom/.ssh/known_hosts: Permission denied
75 # Host key verification failed.
76 #
77 # The first is dated 2026-08-20 22:55, which is when the check was added rather
78 # than when it broke. It reported `tests: last test run failed` on both mnw and
79 # mt from that minute onward.
80 #
81 # It cannot be repaired in place, which is why this is a removal and not a fixed
82 # path. `pom.service` sets `ProtectHome=true` and `ProtectSystem=strict`, so the
83 # daemon cannot read or write `/home/pom` at all: no `known_hosts`, no identity
84 # file, no ssh out of this host by any key. That is the hardening working, not a
85 # misconfiguration to relax.
86 #
87 # Nor should it be relaxed. An outbound SSH identity here would give the public
88 # production box credentials into the build host, which is the wrong direction
89 # for that trust to run.
90 #
91 # WHERE THE VERDICT LIVES: astra, which runs the suites locally as the `pom`
92 # user (`/var/lib/pom/staging/run-ci.sh <target>`, see `pom-astra.toml`) and
93 # reports them. On 2026-08-23 that instance read `mnw tests ok, 3337 passed` and
94 # `mt tests ok, 500 passed`. Read it through the peer mesh; this instance
95 # watches the platform, and CI belongs to the machine that can see the output.
96
97 [targets.mnw.backups]
98 # Where the backups actually are, verified on prod 2026-07-29: the nightly job
99 # writes /var/lib/mnw/backups/makenotwork/makenotwork-<date>.sql.gz. This said
100 # /opt/makenotwork/backups, a path that does not exist on that host, so the
101 # check reported the target degraded for a directory it could not find while the
102 # real backups went unwatched. The app lives at /opt/mnw/current, not
103 # /opt/makenotwork; the old path looks like a pre-rename leftover.
104 #
105 # pom matches files by the "<database>-" or "<database>_" prefix inside this
106 # directory, so the directory is the per-database one, not its parent.
107 directory = "/var/lib/mnw/backups/makenotwork"
108 databases = ["makenotwork"]
109 max_age_hours = 25
110 interval_secs = 3600
111
112 [targets.mnw.scan_pipeline]
113 # Polls /admin/uploads/health.json for queue depth, stuck-scan count,
114 # held backlog, and per-layer error rates. Thresholds per
115 # scan-pipeline-audit.md ยง 6. Localhost on the makenotwork port to
116 # skip the Caddy + Cloudflare path (internal-only signal).
117 base_url = "http://127.0.0.1:3000"
118 interval_secs = 300
119 timeout_secs = 10
120
121 [targets.mnw.synckit_fleet]
122 # Which SyncKit SDK versions are actually syncing. SyncKit is client-side
123 # only, so there is no deployed version to poll; the server aggregates the
124 # version off each sync request's User-Agent and this reads that. Authed
125 # with alerts.alerts_ingest_token, so the check does not spawn without one.
126 # Informational: an old version in the field never degrades the target, only
127 # a readout PoM cannot take does. Localhost for the same reason as above.
128 base_url = "http://127.0.0.1:3000"
129 window_days = 30
130 interval_secs = 3600
131 timeout_secs = 10
132
133 [targets.mnw-cli]
134 label = "MNW CLI SSH Server"
135
136 [[targets.mnw-cli.dns]]
137 name = "cli.makenot.work"
138 record_type = "A"
139 expected = []
140
141 [targets.mnw-cli.ssh_banner]
142 host = "127.0.0.1"
143 port = 22
144 timeout_secs = 5
145
146 [targets.mt]
147 label = "Multithreaded Forum"
148 expected_routes = ["/"]
149
150 [targets.mt.health]
151 url = "http://127.0.0.1:3400/api/health"
152 timeout_secs = 5
153
154 [targets.mt.health.expect]
155 status_code = 200
156 # `tls_trust_anchors` is whether the host CA bundle gave mt any outbound TLS
157 # anchors. mt ships none of its own, so a thin or stale bundle takes out the
158 # OAuth token exchange that logs users in, while the box still boots, serves
159 # pages, and answers 200 here. mt deliberately does not degrade `status` for it
160 # (a bad bundle is a whole-fleet condition, and failing the load-balancer check
161 # would turn a login outage into a total one), so this assertion is the only
162 # thing that makes the condition visible before a user finds it.
163 json_fields = { "status" = "operational", "database" = "true", "tls_trust_anchors" = "true" }
164
165 [targets.mt.tls]
166 host = "forums.makenot.work"
167
168 # No `[targets.mt.tests]` either, removed in the same pass and for the same
169 # reason. The argument is written out under `[targets.mnw.tls]` above.
170
171 [targets.htpy]
172 label = "htpy.app"
173
174 [[targets.htpy.dns]]
175 name = "htpy.app"
176 record_type = "A"
177 expected = []
178
179 [targets.htpy.whois]
180 domain = "htpy.app"
181 warn_days = 30
182
183 [targets.htpy.health]
184 # htpy-web moved twice and this URL followed neither move: it listens on 3200,
185 # not 8080, and /archive/<name> now 308s to /viewer/<name>. Checking the old
186 # address failed on connect, so the target has been reading "unreachable" with an
187 # open incident since 2026-03-29 while htpy.app itself served fine. Verified
188 # 2026-07-29: this returns 200 with "htpy" in the body from fw13, astra and
189 # hetzner, and 3200 is in the tailnet ACL's inter-server port list.
190 url = "http://100.99.153.68:3200/viewer/S_2"
191 timeout_secs = 10
192
193 [targets.htpy.health.expect]
194 status_code = 200
195 body_contains = "htpy"
196
197 [targets.htpy.tls]
198 host = "htpy.app"
199
200 [peers.astra]
201 # By tailnet name. MagicDNS resolves it from this host: verified 2026-08-23,
202 # `astra:9100` and the old literal both answered 401 identically.
203 address = "astra:9100"
204 on_missing = "alert"
205 # token = "<astra's POM_API_TOKEN value>"
206
207 [peers.macbook]
208 # See the note in `pom-astra.toml`: the literal that stood here was not in the
209 # tailnet at all, so this peer resolved to nothing.
210 address = "mbp:9100"
211 on_missing = "log"
212 # token = "<macbook's POM_API_TOKEN value>"
213
214 [alerts]
215 # postmark_token loaded from POM_POSTMARK_TOKEN env var
216 to = "pom-alerts@makenot.work"
217
218 # MNW operator-log sink: also pushes alerts to POST {mnw_url}/api/internal/alerts.
219 # The token loads from POM_ALERTS_INGEST_TOKEN and must match ALERTS_INGEST_TOKEN
220 # in /etc/mnw/makenotwork.env; both mnw_url and the token must be present, else
221 # the sink stays disabled.
222 #
223 # Not the public URL. Caddy answers 404 to /api/internal/* by design (see the
224 # @internal matcher in the Caddyfile), so the sink has to address the app port
225 # directly. Measured 2026-08-15: POST https://makenot.work/api/internal/alerts
226 # is 404, POST http://127.0.0.1:3000/api/internal/alerts is 401 without a token.
227 mnw_url = "http://127.0.0.1:3000"
228
229 # WAM ticket sink: failure alerts become tickets instead of email, and
230 # recoveries stay on email. WAM runs on this host; its token loads from
231 # POM_WAM_TOKEN and must match the value in /etc/wam/env on the WAM node.
232 #
233 # Enabled 2026-08-15, once WAM actually enforced auth. Until that day the
234 # deployed binary was an April build predating its own require_auth middleware,
235 # so a token-sending client would have been talking to a server that ignored
236 # tokens. Both nodes now answer an unauthenticated /tickets with 401.
237 wam_url = "http://127.0.0.1:7890"
238