The first watcher had an unbounded retry in it. A release whose deploy failed left no .done file, so the next poll two minutes later spawned a fresh headless agent, and the one after that, all night, each one costing money. The loop was careful and its logging was honest; the defect was structural. It is structural because the loop is written FRESH by an agent every time one arms. A rule that lives only in the prompt gets re-implemented from scratch each time, and the wrong implementation is the one that looks right: test the .done file. That file only exists on success, so testing it is exactly the retry storm. So scripts/deploy-claim.sh owns it. `claim <version>` exits 0 once and 1 for ever after, whatever became of the attempt that held it — a failed release is not retried, it is looked at. Handing the claim back is `rm -rf` by a person, on purpose; a loop cannot undo its own claim, which is what makes the storm impossible rather than unlikely. Three choices in it worth keeping: - `mkdir` is the claim. One atomic syscall that both tests and takes, with no window between checking and acting — two pollers a second apart is not hypothetical when a watcher gets restarted. - Bash, not bun. The thing that stops a retry storm must not need a healthy checkout; a deploy that dies on `bun install` is precisely when it has to work. - A version is digits and dots or it is refused. It arrives off a git tag and is concatenated into a path. A pre-existing .done with no claim beside it still counts as deployed, so deploy/2026.08.21.01 — rolled out by hand before any of this existed — is not re-attempted. docs/servers.md corrected from the commander's own report, which was right and specific: on macbookpro APPEND /opt/homebrew/bin for bun rather than replacing the PATH (this file said "do not help the PATH", which is right for pm2 and is what made plugins:update fail there), `ls` is aliased to an eza that is not installed, plugins:update bounces every sidecar before the plan's own restart step, and fofos cannot publish affordances for what is probably the same ownership problem as its capabilities sync. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
7.2 KiB
Servers — the running list
Every machine the owner runs, how to reach it, and what of Officer is on it. Kept here so an orchestrator ("pull and update every platform", "restart officer everywhere", "which host is behind") has one place to read rather than a fact re-derived per session.
Verified 2026-08-21 from officer-dev by SSH and pm2 jlist, except where marked assumed.
How access works
All of it is over the tailnet, key-based, no ~/.ssh/config, no passwords, no jump hosts, no ports.
The login user is NOT always pastilhas. It is per host and it is in the table below. deadpool runs
as br and always will — the owner's key is authorised for that account. An orchestrator that hard-codes
a username fails on that host and will fail on the next one like it.
ssh alpha # pastilhas, implied
ssh br@deadpool-rezio-bridge # not implied
Officer platforms
Seven, all running.
| Host | Tailnet IP | Login | Checkout | Domain |
|---|---|---|---|---|
officer-dev |
100.64.0.13 | pastilhas |
~/officer-dev/platform |
platform.officer.dev |
alpha |
100.64.0.1 | pastilhas |
~/officerdev/platform |
officer.pastilhas.dev |
fofos |
100.64.0.17 | pastilhas |
~/officerdev/platform |
officer.fofosfluffybastards.com |
macbookpro |
100.64.0.9 | pastilhas |
~/projects/officer-suite/platform |
macbookpro.pastilhas.dev |
edge-pertento-bridge |
100.64.0.15 | pastilhas |
~/officerdev/platform |
edge.pertento.ai |
production-pertento-bridge |
100.64.0.12 | pastilhas |
~/officerdev/platform |
officer.pertento.ai |
deadpool-rezio-bridge |
100.64.0.21 | br |
/home/br/officerdev/platform |
officer.rezio.dev [pending] |
Only platform.officer.dev is public (77.42.39.155). The rest resolve to tailnet addresses and are
reachable from inside only.
Names that disagree with themselves
Three hosts answer to more than one name, and tailscale status is not always the one in use:
In tailscale status |
Also | The machine's own hostname |
|---|---|---|
edge-bridge |
edge-pertento-bridge |
edge-pertento |
gitea-bridge |
production-pertento-bridge |
PPROD |
deadpool-rezio-bridge |
— | deadpool |
The middle column is what to use. edge-bridge and gitea-bridge are the old names and are being
retired; anything reading tailscale status will still see them.
Things that will catch out an orchestrator
All verified, none cosmetic.
- The checkout path differs per host — four distinct shapes across seven machines. Take it from PM2:
the
officerprocess'spm_cwd.alphacarries a SECOND, stale checkout at~/dockers/officer.dev/platform, and pulling that one succeeds while changing nothing. pm2is not on a non-interactive PATH. It lives under nvm, and nvm's setup is in.zshrc, which a login shell does not read. Usessh <host> '$SHELL -ic "pm2 jlist"'.-lcis not enough — it reports "command not found" on machines where pm2 is plainly running, which cost two wrong conclusions on 2026-08-21.- On
macbookpro, APPEND to the PATH forbun. Never replace it. This said "do not help the PATH" until 2026-08-21, and that was half right in a way that cost a deploy step:pm2is only on the interactive shell's PATH, so replacing it breakspm2— butbunis at/opt/homebrew/bin/bunand.zshrcdoes not put that directory on the interactive PATH either, sobun run plugins:updatefailscommand not foundbefore it runs a line.export PATH=$PATH:/opt/homebrew/binfor that one command shadows nothing:pm2stays at~/.local/bin/pm2andnodeatnode@22. Runpm2as a separate command. $SHELL -iconmacbookproaliaseslstoeza, which is not installed. A probe usinglsreports a missing file that is plainly there. Usecommand ls, or a non-interactive shell for file tests.- The git remotes differ.
fofospulls over HTTPS; the others over SSH. Never assume a URL — pull in place. - A "dirty tree" means TRACKED changes.
git status --porcelaincounts untracked files, and a stray script a fast-forward could never touch is not a reason to skip a host. Use--untracked-files=no. macbookprois a laptop. It may be asleep. Unreachable is a skip with a note, not a failed deploy.bun run plugins:updaterestarts sidecars itself, printingsidecar: startedper plugin — onalphathat is twelve processes bouncing during the plugins step, before any restart the plan asked for. Not a fault, but a deploy report that lists onlyofficerunder "restarted" is understating what moved.fofoscannot sync its capabilities, and could not publish gitea affordances on 2026-08-21:dubious ownershipfrom git andEACCES … mkdir '…/capabilities/affordances/officerdev'. Both point at that capabilities checkout being owned by somebody other thanpastilhas. Deliberately not papered over with asafe.directoryentry until somebody has run onestaton it. Unexplained.
Not platforms — ignore for now
A checkout may be present; no officer process runs on any of these. Whether they are retired, staging
or half-installed is not established — ask before treating them as targets.
| Host | Tailnet IP |
|---|---|
headscale |
100.64.0.2 |
omega |
100.64.0.3 |
gateway |
100.64.0.4 |
Other machines on the tailnet
- Not SSH-reachable (port 22 refused):
edge-dns(100.64.0.8),officer-bridge(100.64.0.5). Assumed service-only containers — not confirmed.edge-bridgewas listed here until 2026-08-21 and was wrong: its SSH was simply down at the time of checking. - Client devices, not servers:
iphone16,andres-ipad,pixel9a, plus other people's devices in the tailnet (diogos-ipad,iphone-diogo,iphone-bruno,fedras-iphone,guilhermes-macbook-pro,macbook-pro-de-joo).
Known wrong, waiting on someone else
officer.rezio.devresolves to 100.64.0.4 — that isgateway, notdeadpool(100.64.0.21). Not merely unavailable: pointed at a different machine, so it will serve the wrong box the moment gateway answers on 443. Ports 80 and 443 are closed ondeadpool, so nothing fronts it yet. Waiting on the domain owner.
Keeping this list true
It rots when a server is added, a checkout moves, a platform is retired, a remote changes or a login differs. Re-verify with the four commands it was built from:
tailscale status
ssh <login>@<host> '$SHELL -ic "pm2 jlist"' # the officer entry's pm_cwd
ssh <login>@<host> 'git -C <cwd> remote get-url origin'
getent hosts <domain>