- commit
- 6110f23
- parent
- be74d58
- author
- im_wower
- date
- 2026-03-22 03:32:36 +0800 CST
Merge feat/T-028-real-rollout-v2 into main
7 files changed,
+270,
-9
+58,
-9
1@@ -1,10 +1,10 @@
2 ---
3 task_id: T-028
4 title: 真实 Cloudflare / VPS 上线执行
5-status: todo
6-branch: feat/T-028-real-rollout
7+status: review
8+branch: feat/T-028-real-rollout-v2
9 repo: /Users/george/code/baa-conductor
10-base_ref: main
11+base_ref: main@be74d58
12 depends_on:
13 - T-018
14 - T-019
15@@ -22,7 +22,7 @@ write_scope:
16 - apps/control-api-worker/**
17 - scripts/ops/**
18 - scripts/runtime/**
19-updated_at: 2026-03-22
20+updated_at: 2026-03-22T03:29:42+0800
21 ---
22
23 # T-028 真实 Cloudflare / VPS 上线执行
24@@ -79,23 +79,72 @@ updated_at: 2026-03-22
25
26 ## files_changed
27
28-- 待填写
29+- `coordination/tasks/T-028-real-rollout.md`
30+- `scripts/runtime/check-node.sh`
31+- `docs/runtime/launchd.md`
32+- `docs/runtime/node-verification.md`
33+- `docs/runtime/real-rollout-2026-03-22.md`
34+- `docs/ops/README.md`
35+- `docs/ops/real-rollout-2026-03-22.md`
36
37 ## commands_run
38
39-- 待填写
40+- 本地干净 worktree:`git -C /Users/george/code/baa-conductor-main-merge worktree add -b feat/T-028-real-rollout-v2 /Users/george/code/baa-conductor-T028-v2 be74d585251f20ff5bd74ae17f6f6b011ff0bd34`
41+- 本地新 worktree 首命令:`cd /Users/george/code/baa-conductor-T028-v2 && npx --yes pnpm install`
42+- 本地构建:`cd /Users/george/code/baa-conductor-T028-v2 && npx --yes pnpm -r build`
43+- `mini` 本机:`./scripts/runtime/bootstrap.sh --repo-dir /Users/george/code/baa-conductor-T028-v2`
44+- `mini` 本机:`set -a; source /Users/george/.config/baa-conductor/control-api-worker.secrets.env; set +a; ./scripts/runtime/install-launchd.sh --repo-dir /Users/george/code/baa-conductor-T028-v2 --node mini --service conductor --service status-api --install-dir /Users/george/Library/LaunchAgents --local-api-base http://100.71.210.78:4317 --local-api-allowed-hosts 100.71.210.78 --status-api-host 100.71.210.78`
45+- `mini` 本机:`./scripts/runtime/check-launchd.sh --repo-dir /Users/george/code/baa-conductor-T028-v2 --node mini --service conductor --service status-api --install-dir /Users/george/Library/LaunchAgents --local-api-base http://100.71.210.78:4317 --local-api-allowed-hosts 100.71.210.78 --status-api-host 100.71.210.78`
46+- `mini` 本机:`./scripts/runtime/reload-launchd.sh --service conductor --service status-api --install-dir /Users/george/Library/LaunchAgents`
47+- `mini` 本机:`./scripts/runtime/check-node.sh --repo-dir /Users/george/code/baa-conductor-T028-v2 --node mini --service conductor --service status-api --install-dir /Users/george/Library/LaunchAgents --local-api-base http://100.71.210.78:4317 --local-api-allowed-hosts 100.71.210.78 --status-api-base http://100.71.210.78:4318 --status-api-host 100.71.210.78 --expected-rolez leader --check-loaded`
48+- 本地到双节点探活:`curl http://100.71.210.78:4317/healthz`、`curl http://100.71.210.78:4318/healthz`、`curl http://100.112.239.13:4317/healthz`、`curl http://100.112.239.13:4318/healthz`
49+- 同步到 `mac`:`rsync -az --delete --exclude '.git' --exclude 'node_modules' /Users/george/code/baa-conductor-T028-v2/ george@100.112.239.13:/Users/george/code/baa-conductor-T028-v2/`
50+- `mac` 远端:`cd /Users/george/code/baa-conductor-T028-v2 && npx --yes pnpm install`
51+- `mac` 远端:`cd /Users/george/code/baa-conductor-T028-v2 && npx --yes pnpm -r build`
52+- `mac` 远端:`./scripts/runtime/bootstrap.sh --repo-dir /Users/george/code/baa-conductor-T028-v2`
53+- `mac` 远端:`set -a; source /Users/george/.config/baa-conductor/control-api-worker.secrets.env; set +a; ./scripts/runtime/install-launchd.sh --repo-dir /Users/george/code/baa-conductor-T028-v2 --node mac --service conductor --service status-api --install-dir /Users/george/Library/LaunchAgents --local-api-base http://100.112.239.13:4317 --local-api-allowed-hosts 100.112.239.13 --status-api-host 100.112.239.13`
54+- `mac` 远端:`./scripts/runtime/check-launchd.sh --repo-dir /Users/george/code/baa-conductor-T028-v2 --node mac --service conductor --service status-api --install-dir /Users/george/Library/LaunchAgents --local-api-base http://100.112.239.13:4317 --local-api-allowed-hosts 100.112.239.13 --status-api-host 100.112.239.13`
55+- `mac` 远端:`./scripts/runtime/reload-launchd.sh --service conductor --service status-api --install-dir /Users/george/Library/LaunchAgents`
56+- `mac` 远端:`./scripts/runtime/check-node.sh --repo-dir /Users/george/code/baa-conductor-T028-v2 --node mac --service conductor --service status-api --install-dir /Users/george/Library/LaunchAgents --local-api-base http://100.112.239.13:4317 --local-api-allowed-hosts 100.112.239.13 --status-api-base http://100.112.239.13:4318 --status-api-host 100.112.239.13 --expected-rolez standby --check-loaded`
57+- 生成 DNS / Nginx 计划:`scripts/ops/cloudflare-dns-plan.sh --env /Users/george/.config/baa-conductor/ops.env --fetch-current --emit-shell .tmp/ops/cloudflare-dns-preview.sh --output .tmp/ops/cloudflare-dns-plan.json`、`scripts/ops/nginx-sync-plan.sh --env /Users/george/.config/baa-conductor/ops.env --bundle-dir .tmp/ops/baa-conductor-nginx`
58+- VPS 基线:`ssh -p 2222 root@192.210.137.113 'hostname; tailscale ip -4; nginx -v; ss -ltnp | grep -E ":(80|443|2222)\\b"'`
59+- VPS upstream 探活:`ssh -p 2222 root@192.210.137.113 'curl http://100.71.210.78:4317/healthz && curl http://100.112.239.13:4317/healthz'`
60+- VPS status-api 探活:`ssh -p 2222 root@192.210.137.113 'curl http://100.71.210.78:4318/healthz && curl http://100.112.239.13:4318/healthz'`
61+- VPS 依赖:`ssh -p 2222 root@192.210.137.113 'apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y apache2-utils python3-certbot-dns-cloudflare'`
62+- VPS Cloudflare DNS challenge 凭据:`printf 'dns_cloudflare_api_token = %s\n' "$CLOUDFLARE_API_TOKEN" | ssh -p 2222 root@192.210.137.113 'install -d -m 700 /root/.secrets/certbot && cat > /root/.secrets/certbot/cloudflare.ini && chmod 600 /root/.secrets/certbot/cloudflare.ini'`
63+- VPS 三张证书:三次 `ssh -p 2222 root@192.210.137.113 'certbot certonly --non-interactive --agree-tos --register-unsafely-without-email --preferred-challenges dns-01 --authenticator dns-cloudflare --dns-cloudflare-credentials /root/.secrets/certbot/cloudflare.ini --dns-cloudflare-propagation-seconds 30 --cert-name <host> -d <host>'`
64+- 本地直连域名密码文件:`/Users/george/.config/baa-conductor/direct-node-basic-auth.env`
65+- VPS htpasswd:`printf '%s\n' "$BAA_DIRECT_NODE_BASIC_AUTH_PASSWORD" | ssh -p 2222 root@192.210.137.113 "htpasswd -i -c /etc/nginx/.htpasswd-baa-conductor conductor-ops && chmod 640 /etc/nginx/.htpasswd-baa-conductor && chown root:www-data /etc/nginx/.htpasswd-baa-conductor"`
66+- Nginx bundle 同步:`rsync -az -e 'ssh -p 2222' /Users/george/code/baa-conductor-T028-v2/.tmp/ops/baa-conductor-nginx/ root@192.210.137.113:/tmp/baa-conductor-nginx/`
67+- VPS 安装 / 校验 / reload:`ssh -p 2222 root@192.210.137.113 'cd /tmp/baa-conductor-nginx && ./deploy-on-vps.sh'`、`ssh -p 2222 root@192.210.137.113 'cd /tmp/baa-conductor-nginx && ./deploy-on-vps.sh --reload'`
68+- Cloudflare DNS 初次切换:`bash /Users/george/code/baa-conductor-T028-v2/.tmp/ops/cloudflare-dns-preview.sh`
69+- Cloudflare DNS 改为 DNS-only:`scripts/ops/cloudflare-dns-plan.sh --env /Users/george/.config/baa-conductor/ops.env --fetch-current --emit-shell .tmp/ops/cloudflare-dns-proxy-off.sh --output .tmp/ops/cloudflare-dns-plan-proxy-off.json`、`bash /Users/george/code/baa-conductor-T028-v2/.tmp/ops/cloudflare-dns-proxy-off.sh`
70+- 验收:`curl -H "Authorization: Bearer $CONTROL_API_OPS_ADMIN_TOKEN" https://control-api.makefile.so/v1/system/state`、`curl --resolve conductor.makefile.so:443:192.210.137.113 https://conductor.makefile.so/healthz`、`curl --resolve conductor.makefile.so:443:192.210.137.113 https://conductor.makefile.so/rolez`、`curl -u conductor-ops:... --resolve mini-conductor.makefile.so:443:192.210.137.113 https://mini-conductor.makefile.so/rolez`、`curl -u conductor-ops:... --resolve mac-conductor.makefile.so:443:192.210.137.113 https://mac-conductor.makefile.so/rolez`
71+- 收尾校验:`bash -n scripts/ops/*.sh scripts/runtime/*.sh`、`git diff --check`
72+- 提交与推送:`git commit -m "Complete T-028 real rollout"`、`git push -u origin feat/T-028-real-rollout-v2`
73
74 ## result
75
76-- 待填写
77+- `mini` 与 `mac` 已从旧的 loopback-only 配置切到各自的 Tailscale `100.x` 监听:`mini 100.71.210.78:4317/4318`、`mac 100.112.239.13:4317/4318`,两台节点都通过了真实 `check-node.sh --check-loaded`。
78+- VPS `racknerd-ff37952` 已真实打通到两台节点的 `4317` 与 `4318`;`curl http://100.71.210.78:4317/healthz`、`curl http://100.112.239.13:4317/healthz` 以及对应 `4318` 探针都返回 `ok`。
79+- VPS 已安装 `apache2-utils` 与 `python3-certbot-dns-cloudflare`,并通过 Cloudflare DNS challenge 签发三张证书:`conductor.makefile.so`、`mini-conductor.makefile.so`、`mac-conductor.makefile.so`,到期日均为 `2026-06-19`。
80+- `/etc/nginx/.htpasswd-baa-conductor` 已创建,仓库生成的 Nginx bundle 已同步到 VPS,`./deploy-on-vps.sh` 与 `./deploy-on-vps.sh --reload` 都真实通过,`nginx -t` 成功。
81+- Cloudflare DNS 已真实切换。三条 conductor 记录最终状态为 `A -> 192.210.137.113 proxied=false`;Cloudflare API 回读计划为 `noop`,权威 NS `giancarlo.ns.cloudflare.com` 对三个 host 都返回 `192.210.137.113`。
82+- 公网验收闭环完成:`https://control-api.makefile.so/v1/system/state` 返回 `ok=true` 且 `holder_id=mini-main`;`conductor.makefile.so` 入口返回 `healthz=ok`、`rolez=leader`;`mini-conductor.makefile.so` / `mac-conductor.makefile.so` 未认证为 `401`,带 Basic Auth 后分别返回 `leader` / `standby`。
83+- 为了让 Tailscale rollout 的静态校验真实可用,本分支顺手修复了 `scripts/runtime/check-node.sh`,让它把 `--local-api-allowed-hosts` 和 `--status-api-host` 正确传给 `check-launchd.sh`。
84+- 分支已推送:`origin/feat/T-028-real-rollout-v2`
85
86 ## risks
87
88-- 待填写
89+- 这三条 conductor DNS 记录当前是 `proxied=false`。原因不是源站问题,而是当前 Cloudflare token 只有 DNS 权限,无法把 zone SSL mode 从疑似 `Flexible` 改到 `Full` / `Full (strict)`;若重新开代理,公网会回到 `301 Location: https://$host$request_uri` 循环。
90+- `mini` 与 `mac` 运行时现在都指向 `/Users/george/code/baa-conductor-T028-v2`,不是文档默认的 `/Users/george/code/baa-conductor`;如果后续切回 canonical 路径,需要重新渲染并 reload launchd 副本。
91+- `mini/mac` 直连域名的 Basic Auth 密码只保存在仓库外的 `/Users/george/.config/baa-conductor/direct-node-basic-auth.env` 和 VPS htpasswd 文件里,没有进入 repo;交接时需要确保整合者知道这份私有文件的位置。
92
93 ## next_handoff
94
95-- 待填写
96+- 如需把 `conductor.makefile.so`、`mini-conductor.makefile.so`、`mac-conductor.makefile.so` 重新放回 Cloudflare 代理,先准备带 zone settings 权限的 token,把 `makefile.so` 的 SSL mode 调到 `Full` 或 `Full (strict)`,再把私有 inventory `/Users/george/.config/baa-conductor/ops.env` 里的 `BAA_CF_PROXY_*` 改回 `true` 并重新执行 DNS 计划脚本。
97+- 观察三张证书的首次自动续期是否正常;若要显式演练,可在 VPS 上执行 `certbot renew --dry-run`。
98+- 后续若进入 `T-029`,就直接基于当前 `mini leader / mac standby / VPS ingress` 现网做 smoke、切换与长时间稳定性回归,不需要再重新铺节点监听或公网入口。
99
100 开始时建议直接把 `status` 改为 `in_progress`。
101
+5,
-0
1@@ -133,6 +133,11 @@ scripts/ops/cloudflare-dns-plan.sh \
2 - 脚本本身不会发 `POST` / `PATCH` / `DELETE`
3 - `--emit-shell` 只是把预览命令写到文件里,不会自动执行
4
5+实战注意:
6+
7+- 如果 `conductor*.makefile.so` 已经有有效源站证书、`--resolve` 直打 VPS 的 `https://.../healthz` 正常,但经 Cloudflare 代理访问时返回 `301 Location: https://$host$request_uri` 自重定向,通常说明 zone 仍在 `Flexible`。
8+- 当前 token 如果没有 zone settings 权限,无法直接把 SSL mode 改到 `Full` / `Full (strict)`;这种情况下先把 `BAA_CF_PROXY_*` 设成 `false`,切成 DNS-only,避免公网入口卡在 Cloudflare 边缘循环。
9+
10 ## Nginx 渲染与部署 bundle
11
12 ### 1. 渲染并打包
+101,
-0
1@@ -0,0 +1,101 @@
2+# 2026-03-22 Real Rollout Record
3+
4+## Snapshot
5+
6+| Surface | Target | Actual state |
7+| --- | --- | --- |
8+| `control-api.makefile.so` | Cloudflare Worker + D1 | Already deployed and verified |
9+| `conductor.makefile.so` | VPS `192.210.137.113` -> `mini/mac 100.x:4317` | Live |
10+| `mini-conductor.makefile.so` | VPS `192.210.137.113` -> `mini 100.71.210.78:4317` | Live with Basic Auth |
11+| `mac-conductor.makefile.so` | VPS `192.210.137.113` -> `mac 100.112.239.13:4317` | Live with Basic Auth |
12+
13+## Cloudflare
14+
15+- zone: `makefile.so`
16+- zone id: `f3507ab962df815d93e7ad3f1a390615`
17+- public IPv4: `192.210.137.113`
18+- Worker custom domain kept as-is: `https://control-api.makefile.so`
19+
20+DNS records created on 2026-03-22 CST:
21+
22+- `conductor.makefile.so` record id `535f46affdb393e30a24e5f9fb5b95c5`
23+- `mini-conductor.makefile.so` record id `41a6d2fab5071ea6de09eacabd5ffca6`
24+- `mac-conductor.makefile.so` record id `e7011abac5c5684e22a047f5e2ced5d7`
25+
26+Final record state:
27+
28+- `A conductor.makefile.so -> 192.210.137.113 proxied=false`
29+- `A mini-conductor.makefile.so -> 192.210.137.113 proxied=false`
30+- `A mac-conductor.makefile.so -> 192.210.137.113 proxied=false`
31+
32+Why DNS-only:
33+
34+- With `proxied=true`, `--resolve ...:443:192.210.137.113` direct-to-origin checks were healthy, but public requests through Cloudflare returned `301 Location: https://$host$request_uri`.
35+- The token available to this rollout could edit DNS but could not read or change zone SSL settings; `GET /zones/<zone>/settings/ssl` returned `9109 Unauthorized`.
36+- The observed behavior is consistent with the zone still being on `Flexible`, so the rollout switched these three conductor records to DNS-only instead of leaving public traffic on a redirect loop.
37+
38+Authoritative DNS confirmation after the final PATCH:
39+
40+- `@giancarlo.ns.cloudflare.com conductor.makefile.so -> 192.210.137.113`
41+- `@giancarlo.ns.cloudflare.com mini-conductor.makefile.so -> 192.210.137.113`
42+- `@giancarlo.ns.cloudflare.com mac-conductor.makefile.so -> 192.210.137.113`
43+
44+## VPS / Nginx
45+
46+VPS facts:
47+
48+- host: `racknerd-ff37952`
49+- SSH: `root@192.210.137.113 -p 2222`
50+- Tailscale IPv4: `100.68.201.85`
51+- Nginx: `1.24.0`
52+
53+Packages installed during this rollout:
54+
55+- `apache2-utils`
56+- `python3-certbot-dns-cloudflare`
57+
58+TLS and auth:
59+
60+- Cloudflare DNS challenge credentials written to `/root/.secrets/certbot/cloudflare.ini`
61+- `certbot certonly --authenticator dns-cloudflare ... --cert-name conductor.makefile.so -d conductor.makefile.so`
62+- `certbot certonly --authenticator dns-cloudflare ... --cert-name mini-conductor.makefile.so -d mini-conductor.makefile.so`
63+- `certbot certonly --authenticator dns-cloudflare ... --cert-name mac-conductor.makefile.so -d mac-conductor.makefile.so`
64+- all three certificates expire on `2026-06-19`
65+- Basic Auth file created at `/etc/nginx/.htpasswd-baa-conductor`
66+- local secret copy stored outside the repo at `/Users/george/.config/baa-conductor/direct-node-basic-auth.env`
67+
68+Nginx deployment:
69+
70+- bundle rendered from `/Users/george/code/baa-conductor-T028-v2/.tmp/ops/baa-conductor-nginx`
71+- synced to `/tmp/baa-conductor-nginx` on the VPS
72+- `./deploy-on-vps.sh` passed `nginx -t`
73+- `./deploy-on-vps.sh --reload` passed and reloaded Nginx
74+
75+## Public verification
76+
77+Control plane:
78+
79+- `curl -H "Authorization: Bearer $CONTROL_API_OPS_ADMIN_TOKEN" https://control-api.makefile.so/v1/system/state`
80+- result: `ok=true`, `holder_id=mini-main`, `mode=running`
81+
82+Pre-cutover ingress verification against the fresh VPS config:
83+
84+- `curl --resolve conductor.makefile.so:443:192.210.137.113 https://conductor.makefile.so/healthz -> ok`
85+- `curl --resolve conductor.makefile.so:443:192.210.137.113 https://conductor.makefile.so/rolez -> leader`
86+- `curl --resolve mini-conductor.makefile.so:443:192.210.137.113 https://mini-conductor.makefile.so/healthz -> 401` without auth
87+- `curl -u conductor-ops:... --resolve mini-conductor.makefile.so:443:192.210.137.113 https://mini-conductor.makefile.so/healthz -> ok`
88+- `curl -u conductor-ops:... --resolve mini-conductor.makefile.so:443:192.210.137.113 https://mini-conductor.makefile.so/rolez -> leader`
89+- `curl --resolve mac-conductor.makefile.so:443:192.210.137.113 https://mac-conductor.makefile.so/healthz -> 401` without auth
90+- `curl -u conductor-ops:... --resolve mac-conductor.makefile.so:443:192.210.137.113 https://mac-conductor.makefile.so/healthz -> ok`
91+- `curl -u conductor-ops:... --resolve mac-conductor.makefile.so:443:192.210.137.113 https://mac-conductor.makefile.so/rolez -> standby`
92+
93+VPS upstream probes:
94+
95+- `curl http://100.71.210.78:4317/healthz -> ok`
96+- `curl http://100.112.239.13:4317/healthz -> ok`
97+- `curl http://100.71.210.78:4318/healthz -> ok`
98+- `curl http://100.112.239.13:4318/healthz -> ok`
99+
100+## Follow-up
101+
102+- If these three conductor domains need to go back behind Cloudflare proxy, use a token that can change zone SSL settings and switch the zone from `Flexible` to `Full` or `Full (strict)` before turning `proxied=true` back on.
+4,
-0
1@@ -163,7 +163,9 @@ AGENTS_DIR="$HOME/Library/LaunchAgents"
2 --all-services \
3 --install-dir "$AGENTS_DIR" \
4 --local-api-base http://100.71.210.78:4317 \
5+ --local-api-allowed-hosts 100.71.210.78 \
6 --status-api-base http://100.71.210.78:4318 \
7+ --status-api-host 100.71.210.78 \
8 --expected-rolez leader
9 ```
10
11@@ -238,7 +240,9 @@ AGENTS_DIR="$HOME/Library/LaunchAgents"
12 --all-services \
13 --install-dir "$AGENTS_DIR" \
14 --local-api-base http://100.112.239.13:4317 \
15+ --local-api-allowed-hosts 100.112.239.13 \
16 --status-api-base http://100.112.239.13:4318 \
17+ --status-api-host 100.112.239.13 \
18 --expected-rolez standby
19 ```
20
+12,
-0
1@@ -70,7 +70,9 @@ AGENTS_DIR="$HOME/Library/LaunchAgents"
2 --all-services \
3 --install-dir "$AGENTS_DIR" \
4 --local-api-base http://100.71.210.78:4317 \
5+ --local-api-allowed-hosts 100.71.210.78 \
6 --status-api-base http://100.71.210.78:4318 \
7+ --status-api-host 100.71.210.78 \
8 --expected-rolez leader
9 ```
10
11@@ -82,6 +84,10 @@ AGENTS_DIR="$HOME/Library/LaunchAgents"
12 --node mini \
13 --all-services \
14 --install-dir "$AGENTS_DIR" \
15+ --local-api-base http://100.71.210.78:4317 \
16+ --local-api-allowed-hosts 100.71.210.78 \
17+ --status-api-base http://100.71.210.78:4318 \
18+ --status-api-host 100.71.210.78 \
19 --expected-rolez leader \
20 --check-loaded
21 ```
22@@ -134,7 +140,9 @@ AGENTS_DIR="$HOME/Library/LaunchAgents"
23 --all-services \
24 --install-dir "$AGENTS_DIR" \
25 --local-api-base http://100.112.239.13:4317 \
26+ --local-api-allowed-hosts 100.112.239.13 \
27 --status-api-base http://100.112.239.13:4318 \
28+ --status-api-host 100.112.239.13 \
29 --expected-rolez standby
30 ```
31
32@@ -151,6 +159,10 @@ sudo ./scripts/runtime/check-node.sh \
33 --scope daemon \
34 --install-dir /Library/LaunchDaemons \
35 --username george \
36+ --local-api-base http://100.112.239.13:4317 \
37+ --local-api-allowed-hosts 100.112.239.13 \
38+ --status-api-base http://100.112.239.13:4318 \
39+ --status-api-host 100.112.239.13 \
40 --expected-rolez standby \
41 --check-loaded
42 ```
+75,
-0
1@@ -0,0 +1,75 @@
2+# 2026-03-22 Runtime Rollout Record
3+
4+## Nodes
5+
6+| Node | Host | Repo path used for rollout | Result |
7+| --- | --- | --- | --- |
8+| `mini` | local machine `Mac`, Tailscale `100.71.210.78` | `/Users/george/code/baa-conductor-T028-v2` | launchd reloaded, on-node checks passed, `rolez=leader` |
9+| `mac` | remote `MacBookPro`, Tailscale `100.112.239.13` | `/Users/george/code/baa-conductor-T028-v2` | repo synced, launchd reloaded, on-node checks passed, `rolez=standby` |
10+
11+## Shared runtime inputs
12+
13+- repo path used for this rollout: `/Users/george/code/baa-conductor-T028-v2`
14+- launchd install dir: `/Users/george/Library/LaunchAgents`
15+- shared token source: `/Users/george/.config/baa-conductor/control-api-worker.secrets.env`
16+- control API base: `https://control-api.makefile.so`
17+
18+## mini
19+
20+Executed locally on 2026-03-22 CST:
21+
22+1. `npx --yes pnpm -r build`
23+2. `./scripts/runtime/bootstrap.sh --repo-dir /Users/george/code/baa-conductor-T028-v2`
24+3. `./scripts/runtime/install-launchd.sh --repo-dir /Users/george/code/baa-conductor-T028-v2 --node mini --service conductor --service status-api --install-dir /Users/george/Library/LaunchAgents --local-api-base http://100.71.210.78:4317 --local-api-allowed-hosts 100.71.210.78 --status-api-host 100.71.210.78`
25+4. `./scripts/runtime/check-launchd.sh ...`
26+5. `./scripts/runtime/reload-launchd.sh --service conductor --service status-api --install-dir /Users/george/Library/LaunchAgents`
27+6. `./scripts/runtime/check-node.sh --repo-dir /Users/george/code/baa-conductor-T028-v2 --node mini --service conductor --service status-api --install-dir /Users/george/Library/LaunchAgents --local-api-base http://100.71.210.78:4317 --local-api-allowed-hosts 100.71.210.78 --status-api-base http://100.71.210.78:4318 --status-api-host 100.71.210.78 --expected-rolez leader --check-loaded`
28+
29+Observed state after reload:
30+
31+- conductor PID: `79697`
32+- status-api PID: `79700`
33+- `http://100.71.210.78:4317/healthz -> ok`
34+- `http://100.71.210.78:4317/rolez -> leader`
35+- `http://100.71.210.78:4318/healthz -> ok`
36+
37+## mac
38+
39+Executed remotely over `ssh george@100.112.239.13` on 2026-03-22 CST:
40+
41+1. `rsync -az --delete --exclude '.git' --exclude 'node_modules' /Users/george/code/baa-conductor-T028-v2/ george@100.112.239.13:/Users/george/code/baa-conductor-T028-v2/`
42+2. `cd /Users/george/code/baa-conductor-T028-v2 && npx --yes pnpm install`
43+3. `cd /Users/george/code/baa-conductor-T028-v2 && npx --yes pnpm -r build`
44+4. `./scripts/runtime/bootstrap.sh --repo-dir /Users/george/code/baa-conductor-T028-v2`
45+5. `./scripts/runtime/install-launchd.sh --repo-dir /Users/george/code/baa-conductor-T028-v2 --node mac --service conductor --service status-api --install-dir /Users/george/Library/LaunchAgents --local-api-base http://100.112.239.13:4317 --local-api-allowed-hosts 100.112.239.13 --status-api-host 100.112.239.13`
46+6. `./scripts/runtime/check-launchd.sh ...`
47+7. `./scripts/runtime/reload-launchd.sh --service conductor --service status-api --install-dir /Users/george/Library/LaunchAgents`
48+8. `./scripts/runtime/check-node.sh --repo-dir /Users/george/code/baa-conductor-T028-v2 --node mac --service conductor --service status-api --install-dir /Users/george/Library/LaunchAgents --local-api-base http://100.112.239.13:4317 --local-api-allowed-hosts 100.112.239.13 --status-api-base http://100.112.239.13:4318 --status-api-host 100.112.239.13 --expected-rolez standby --check-loaded`
49+
50+Observed state after reload:
51+
52+- conductor PID: `60338`
53+- status-api PID: `60341`
54+- `http://100.112.239.13:4317/healthz -> ok`
55+- `http://100.112.239.13:4317/rolez -> standby`
56+- `http://100.112.239.13:4318/healthz -> ok`
57+
58+## Cross-node reachability
59+
60+Validated after both nodes switched to Tailscale listeners:
61+
62+- local host:
63+ - `curl http://100.71.210.78:4317/healthz -> ok`
64+ - `curl http://100.71.210.78:4318/healthz -> ok`
65+ - `curl http://100.112.239.13:4317/healthz -> ok`
66+ - `curl http://100.112.239.13:4318/healthz -> ok`
67+- VPS `root@192.210.137.113 -p 2222`:
68+ - `curl http://100.71.210.78:4317/healthz -> ok`
69+ - `curl http://100.112.239.13:4317/healthz -> ok`
70+ - `curl http://100.71.210.78:4318/healthz -> ok`
71+ - `curl http://100.112.239.13:4318/healthz -> ok`
72+
73+## Notes
74+
75+- This rollout replaced the old `/Users/george/code/baa-conductor-T028` runtime path with `/Users/george/code/baa-conductor-T028-v2` on both nodes.
76+- `check-node.sh` now forwards `--local-api-allowed-hosts` and `--status-api-host` to `check-launchd.sh`; without that fix, Tailscale rollout checks would fail even though the installed plist values were correct.
+15,
-0
1@@ -22,7 +22,10 @@ Options:
2 --shared-token-file PATH Read the expected token from a file.
3 --control-api-base URL Expected BAA_CONTROL_API_BASE in installed copies.
4 --local-api-base URL Conductor local API base URL. Defaults to 127.0.0.1:4317.
5+ --local-api-allowed-hosts CSV
6+ Expected BAA_CONDUCTOR_LOCAL_API_ALLOWED_HOSTS in installed copies.
7 --status-api-base URL Status API base URL. Defaults to 127.0.0.1:4318.
8+ --status-api-host HOST Expected BAA_STATUS_API_HOST in installed copies.
9 --username NAME Expected UserName for LaunchDaemons.
10 --domain TARGET launchctl domain target for --check-loaded.
11 --check-loaded Also require launchctl print to succeed for each service.
12@@ -55,7 +58,9 @@ shared_token=""
13 shared_token_file=""
14 control_api_base="${BAA_RUNTIME_DEFAULT_CONTROL_API_BASE}"
15 local_api_base="${BAA_RUNTIME_DEFAULT_LOCAL_API}"
16+local_api_allowed_hosts="${BAA_CONDUCTOR_LOCAL_API_ALLOWED_HOSTS:-}"
17 status_api_base="${BAA_RUNTIME_DEFAULT_STATUS_API}"
18+status_api_host="${BAA_STATUS_API_HOST:-127.0.0.1}"
19 username="$(default_username)"
20 domain_target=""
21 check_loaded="0"
22@@ -120,10 +125,18 @@ while [[ $# -gt 0 ]]; do
23 local_api_base="$2"
24 shift 2
25 ;;
26+ --local-api-allowed-hosts)
27+ local_api_allowed_hosts="$2"
28+ shift 2
29+ ;;
30 --status-api-base)
31 status_api_base="$2"
32 shift 2
33 ;;
34+ --status-api-host)
35+ status_api_host="$2"
36+ shift 2
37+ ;;
38 --username)
39 username="$2"
40 shift 2
41@@ -323,6 +336,8 @@ run_static_checks() {
42 --install-dir "$install_dir"
43 --control-api-base "$control_api_base"
44 --local-api-base "$local_api_base"
45+ --local-api-allowed-hosts "$local_api_allowed_hosts"
46+ --status-api-host "$status_api_host"
47 --username "$username"
48 )
49