| 1 |
# pom's tiers and nodes. Design: wiki [[sando-bento-boundary]]. |
| 2 |
# |
| 3 |
# No [repo] and no [[backup]]-driven migration gate. pom is intake-only: Bento |
| 4 |
# builds it natively on astra and on the Hetzner box, and hands Sando finished |
| 5 |
# bytes with an artifact record. Sando fetches no source for it, so there is no |
| 6 |
# bare repo on this host to name, and declaring one would be a claim /rebuild |
| 7 |
# would then act on. |
| 8 |
backup = [] |
| 9 |
|
| 10 |
# ---- host: where an accepted artifact is gated before it goes anywhere ---- |
| 11 |
# |
| 12 |
# Not a machine pom runs on. It is the tier every intake lands on, the same way |
| 13 |
# a Sando-built product's host tier is where its build lands: gates run here, and |
| 14 |
# a green run is what makes the bundle promotable. sandod's own box (fw13) is not |
| 15 |
# a pom node and never runs pom. |
| 16 |
# |
| 17 |
# The gate list is empty on purpose, and that is the boundary rather than an |
| 18 |
# omission. Everything Sando would have run here is artifact-scoped — cargo_test, |
| 19 |
# clippy, fmt, the audits — and artifact-scoped evidence belongs to the builder |
| 20 |
# under this split. Bento already ran them, and the record carries their verdicts |
| 21 |
# (`intake::accept` reports whether the builder's gates passed). Asking Sando to |
| 22 |
# re-run them would need a source checkout it does not have, and the gates say so |
| 23 |
# rather than passing on having run nothing. |
| 24 |
# |
| 25 |
# What is left for Sando is evidence about the artifact *in an environment*, and |
| 26 |
# every one of those lives on a tier with a node in it, below. |
| 27 |
[[tier]] |
| 28 |
name = "host" |
| 29 |
provisioned = true |
| 30 |
canary = "sequential" |
| 31 |
gates = [] |
| 32 |
|
| 33 |
# ---- astra: aarch64, and pom's own CI runner ---- |
| 34 |
# |
| 35 |
# First real tier because it is the machine that matters least if pom is down for |
| 36 |
# a moment: it watches, it does not serve anyone. The Hetzner instance keeps |
| 37 |
# watching while this one restarts. |
| 38 |
# |
| 39 |
# The bootstrap caveat is why the two are a ladder and not a fan-out: pom watches |
| 40 |
# its own deploy, so deploying pom restarts the watcher mid-operation. One |
| 41 |
# instance at a time, health read from the peer over the mesh, never both at once. |
| 42 |
[[tier]] |
| 43 |
name = "astra" |
| 44 |
provisioned = true |
| 45 |
canary = "sequential" |
| 46 |
# Guards astra -> hetzner. node_health proves the restarted instance is actually |
| 47 |
# serving; burn_in makes production wait on a real interval of the new binary |
| 48 |
# running somewhere; manual_confirm is the human sign-off before the box that |
| 49 |
# watches production takes it. |
| 50 |
gates = [ |
| 51 |
{ kind = "node_health" }, |
| 52 |
{ kind = "burn_in", hours = 24 }, |
| 53 |
{ kind = "manual_confirm" }, |
| 54 |
] |
| 55 |
[[tier.node]] |
| 56 |
name = "astra" |
| 57 |
platform = "linux/aarch64" |
| 58 |
ssh_target = "max@astra" # tailnet name |
| 59 |
release_root = "/opt/pom" |
| 60 |
service_name = "pom.service" |
| 61 |
# Readiness on top of `systemctl is-active`: pom binds its API on the tailnet |
| 62 |
# address, and the dashboard is on. A crash-looping binary satisfies is-active |
| 63 |
# between restarts, which is the failure this closes. |
| 64 |
health_url = "http://127.0.0.1:9100/api/health" |
| 65 |
|
| 66 |
# ---- hetzner: x86_64, watching production from production ---- |
| 67 |
[[tier]] |
| 68 |
name = "hetzner" |
| 69 |
provisioned = true |
| 70 |
canary = "sequential" |
| 71 |
gates = [ |
| 72 |
{ kind = "node_health" }, |
| 73 |
{ kind = "manual_confirm" }, |
| 74 |
] |
| 75 |
[[tier.node]] |
| 76 |
name = "hetzner" |
| 77 |
platform = "linux/x86_64" |
| 78 |
ssh_target = "root@alpha-west-1" # Tailscale SSH; port 2200 via the ssh config |
| 79 |
release_root = "/opt/pom" |
| 80 |
service_name = "pom.service" |
| 81 |
health_url = "http://127.0.0.1:9100/api/health" |
| 82 |
|