Skip to main content

max / makenotwork

8.2 KB · 184 lines History Blame Raw
1 # Sando topology config.
2 #
3 # Tiers run in declaration order. Each tier lists the gates that must pass to
4 # unlock promotion *to* the next tier, the nodes it ships to, and the canary
5 # policy for shipping within the tier.
6 #
7 # Day-one wiring: host (fw13, local) -> A (testnot.work) -> B (prod-1). C is
8 # declared but not provisioned; adding the second prod node later is a config
9 # edit (set provisioned = true, fill in [[tier.node]]).
10 #
11 # The first tier is "host" — it refers to whatever machine sandod runs on
12 # (currently fw13). Renamed from the legacy "mm" name in Session 1 of
13 # the sando bundle redesign.
14
15 [repo]
16 bare_path = "/srv/sando/mnw.git"
17 branch = "main"
18 # Canonical remote sandod fetches the deploy branch from before worktree-ing a
19 # sha (pull-based /rebuild). Authorized via the `sando-deploy` read-only
20 # service account on the makenot.work git server (fetch-only collaborator).
21 # Leave unset for push-based hosts (commits arrive via the bare-repo hook).
22 upstream = "git@ssh.makenot.work:max/makenotwork.git"
23
24 # ---- auxiliary repos ----
25 # Extra repos fetched and checked out beside the per-sha worktree so a cross-repo
26 # path dependency resolves at build time. The mnw-cli companion (built from the
27 # MNW worktree) carries `synckit-client = { path = "../../synckit/synckit-client" }`
28 # after synckit moved to its own repo; from <workdir>/<sha>/mnw-cli that resolves
29 # to <workdir>/synckit, so synckit must be checked out there. checkout_dir is a
30 # path under the workdir, nesting allowed; the checkout is shared across shas and
31 # refreshed to `branch` HEAD each build. See the maintainer wiki, sando-overview.
32 [[aux_repo]]
33 name = "synckit"
34 bare_path = "/srv/sando/synckit.git"
35 upstream = "git@ssh.makenot.work:max/synckit.git"
36 branch = "main"
37 checkout_dir = "synckit"
38
39 # The server and multithreaded both carry
40 # `docengine = { path = "../../Libraries/docengine" }` after docengine left
41 # MNW/shared on 2026-07-30. From <workdir>/<sha>/server that resolves to
42 # <workdir>/Libraries/docengine, hence the nested checkout_dir.
43 [[aux_repo]]
44 name = "docengine"
45 bare_path = "/srv/sando/docengine.git"
46 upstream = "git@ssh.makenot.work:max/docengine.git"
47 branch = "main"
48 checkout_dir = "Libraries/docengine"
49
50 [backup]
51 # Source of the prod-backup clone used by migration_dry_run on the Sando host.
52 # For localhost dev this can be a file:// path to a fixture dump. In prod we
53 # pull directly from alpha-west-1 via a scoped `backup-puller` rrsync user.
54 source = "ssh://backup-puller@alpha-west-1:2200/latest.sql.gz"
55 local_path = "/srv/sando/backups/latest.sql.gz"
56
57 # ---- host: fw13 local pre-staging gate ----
58 [[tier]]
59 name = "host"
60 provisioned = true
61 canary = "sequential"
62 # code_smoke runs FIRST: it boots the freshly-built binary against a throwaway
63 # empty DB it migrates from scratch + seeds (the example catalog), then probes
64 # /health. Fast + infra-light (no prod-dump restore), so a green here proves the
65 # code is sound and isolates a later cargo_test / migration_dry_run red as an
66 # environment problem rather than a code one.
67 #
68 # hardening_test is cargo_test's blind spot: cargo_test builds with
69 # --features fast-tests, which relaxes the auth/sandbox rate limits and argon2
70 # cost AND #[ignore]s the whole rate-limiting suite, so the gate never touched
71 # the auth hardening it exists to protect. hardening_test re-runs that suite
72 # with no features, single-threaded, against production constants. It pays for
73 # a second compile of the server's test binary; that is the cost of the
74 # coverage.
75 #
76 # The lint + supply-chain gates. `-D warnings` used to be enforced in exactly
77 # one place (server/deploy/run-ci.sh, which died with the astra pipeline) and
78 # `cargo fmt --check` nowhere at all. fmt runs first: it needs no compilation,
79 # so a formatting red comes back in seconds instead of after a full build.
80 # cargo_audit and cargo_deny only run in crates carrying a triaged
81 # .cargo/audit.toml / deny.toml -- four crates in this repo fail cargo audit
82 # purely for lack of a reviewed advisory posture, and a permanently red gate
83 # teaches everyone to ignore it.
84 gates = [
85 { kind = "fmt" },
86 { kind = "code_smoke" },
87 { kind = "cargo_test" },
88 { kind = "hardening_test" },
89 { kind = "clippy" },
90 { kind = "cargo_audit" },
91 { kind = "cargo_deny" },
92 { kind = "migration_dry_run" },
93 { kind = "boot_smoke" },
94 ]
95 # Host is the daemon's own machine (fw13); no remote node row.
96
97 # ---- A: testnot.work staging ----
98 [[tier]]
99 name = "a"
100 provisioned = true
101 canary = "sequential"
102 # node_health (post-deploy) probes the deployed node over its executor — the
103 # gate boot_smoke used to stand in for, but boot_smoke runs on the build host and
104 # proves nothing about testnot-1. boot_smoke stays a host build-time gate only.
105 #
106 # A tier's gates guard promotion *out* of it, so THIS list is what stands between
107 # testnot and production. manual_confirm lives here for that reason. It used to
108 # sit only on tier b, where it guards b -> c — and c is not provisioned, so it
109 # gated nothing: a prod ship was cleared by node_health + burn_in alone, and
110 # hotfix: true skips burn_in, leaving one gate on a production deploy.
111 #
112 # hotfix does NOT skip manual_confirm (only burn_in), so every ship to prod-1
113 # now needs an explicit POST /confirm/a naming the version. That is the point:
114 # there was no human sign-off anywhere on the path to production.
115 gates = [
116 { kind = "node_health" },
117 { kind = "burn_in", hours = 48 },
118 { kind = "manual_confirm" },
119 ]
120 [[tier.node]]
121 name = "testnot-1"
122 ssh_target = "deploy@testnot" # tailnet name; never the public IP / testnot.work hostname
123 release_root = "/opt/mnw"
124 service_name = "makenotwork.service"
125 # Pre-swap config-drift guard: source this node's env and run the freshly-rsynced
126 # binary in MNW_CHECK_CONFIG=1 mode BEFORE the symlink swap. A required var
127 # missing here (how prod crash-looped on CDN_BASE_URL, postmortem 2026-07-09 #2)
128 # fails the promote with the running service intact. The node runs 0.10.14, which
129 # supports the mode; validated end-to-end 2026-07-10.
130 config_check_env_file = "/etc/mnw/makenotwork.env"
131 # Readiness probe on top of `systemctl is-active`. Without this, node_health
132 # proves only that systemd thinks the unit is running — which a crash-looping
133 # binary can satisfy between restarts. The node binds HOST=127.0.0.1 PORT=8080
134 # (/etc/mnw/makenotwork.env); verified serving 200 on 2026-07-21.
135 health_url = "http://127.0.0.1:8080/health"
136
137 # ---- B: prod-1 ----
138 [[tier]]
139 name = "b"
140 provisioned = true
141 canary = "sequential"
142 # Guards b -> c. Kept for the day prod-2 is provisioned; it gates nothing today
143 # (c has provisioned = false). The sign-off that matters for shipping to THIS
144 # tier lives on tier a.
145 gates = [
146 { kind = "node_health" },
147 { kind = "manual_confirm" },
148 ]
149 [[tier.node]]
150 name = "prod-1"
151 # Tailnet name; port 2200 supplied via /srv/sando/.ssh/config Host block.
152 # Service user is "makenotwork" (pre-existing on prod), not "deploy" — chose
153 # not to chown 885M of backups + redo postgres peer auth for a cosmetic rename.
154 ssh_target = "makenotwork@alpha-west-1"
155 release_root = "/opt/mnw"
156 service_name = "makenotwork.service"
157 # Pre-swap config-drift guard (see testnot-1 above). This node is the one the
158 # 0.10.14 deploy crash-looped on a missing CDN_BASE_URL — the exact miss this
159 # gate closes. First real exercise is the next prod promote.
160 config_check_env_file = "/etc/mnw/makenotwork.env"
161 # Readiness probe (see testnot-1). The unit binds HOST=0.0.0.0 PORT=3000, so the
162 # probe dials it on loopback; verified serving 200 on 2026-07-21. This is the
163 # node whose crash-loop `systemctl is-active` alone would have missed.
164 health_url = "http://127.0.0.1:3000/health"
165 # Companion: install the mnw-cli built in this same promote (see [[companion]] in
166 # sando-daemon.toml) after the server is up, and restart its unit. Closes the
167 # drift that broke git hosting during the 0.10.14 deploy — mnw-cli now ships from
168 # the same sha as the server. Needs the node-side wrapper + sudoers grant
169 # (deploy/install-companion.sh, deploy/mnw-companion.sudoers).
170 [[tier.node.companion]]
171 name = "mnw-cli"
172 install_path = "/opt/mnw-cli/mnw-cli"
173 service_name = "mnw-cli.service"
174
175 # ---- C: prod-2 (declared, not yet provisioned) ----
176 [[tier]]
177 name = "c"
178 provisioned = false
179 canary = "sequential"
180 gates = [
181 { kind = "node_health" },
182 ]
183 # [[tier.node]] entries to be added when the second prod node ships.
184