Repository navigation
Description
Finding (PR diff review, 2026-10-24)
Merged PR #764 (pm-stack Phase 2, merge commit 15f4885f on main) hard-codes one host's tailnet IP address into both new compose files, which breaks the stack's own documented move-hosts procedure.
Evidence (verified on current main 3184df61)
pm-stack/pad/compose.yml:22— port binding"100.99.113.103:7777:7777"pm-stack/grist/compose.yml:26— port binding"100.99.113.103:8484:8484"pm-stack/README.md:23-24repeats100.99.113.103as the tailnet bind for both services.
That address is the current laptop host's (amd-6's) Tailscale IP. It is not a value any other host can be assumed to have.
This contradicts the stack's own contracts, added in the same PR:
pm-stack/pad/deploy.sh:16andpm-stack/grist/deploy.sh:16— "Host specifics (the ONLY host-specific values)" are listed asPM_STACK_SECRETSandSOPS_AGE_KEY_FILE. The tailnet bind address is a third host-specific value, and it lives in neither.pm-stack/README.md:278-281— "Move hosts: copy this directory +../secrets, ensure the new host's age key is a SOPS recipient, run./deploy.sh, install the unit, done." On any host whose tailnet IP is not100.99.113.103,docker compose upfails to bind that address (or, worse, the address belongs to a different tailnet node), so the documented procedure fails at the compose step with no hint that a tracked file needs editing.- Same pattern, lower stakes:
pm-stack/grist/compose.yml:30hard-codesGRIST_DEFAULT_EMAIL: drewrypope@gmail.comas a tracked literal rather than a rendered env value.
Why it matters
The pm-stack is explicitly designed to be host-portable (token-based tunnel, SOPS-rendered .env, "only one connector should run at a time"). A hard-coded host IP in version-controlled compose files is the kind of thing that only surfaces during a host migration — the worst time to discover it.
Acceptance criteria
- The tailnet bind address for Pad and Grist comes from the rendered
.env/ an env variable (e.g.PM_STACK_TAILNET_IP) produced bydeploy.sh, with the loopback-only binding as the safe default when it is unset. -
pm-stack/README.md's Move hosts section and services table no longer assume the current host's address, and the deploy.sh "ONLY host-specific values" headers are true again. -
GRIST_DEFAULT_EMAILis likewise rendered from configuration, or the hard-coding is explicitly documented as intentional. - Verified: rendering/deploying with a different (or unset) tailnet IP produces compose bindings matching that value, and the current host's deployment is unchanged in behaviour.
Filed by the pr-diff-review job from PR #764's diff, verified against current main; this job files only.
Activity
dezren39 commentedon Oct 26, 2026
Intake review
Still live: re-verified this run against current main —
pm-stack/pad/compose.yml:22still binds100.99.113.103:7777:7777andpm-stack/grist/compose.yml:26still binds100.99.113.103:8484:8484. No merged PR since #764 has touched the pm-stack compose files. Acceptance criteria were already present (checkbox form), so the body is unchanged.Related: PR #764 (introduced the hard-coding); the deploy.sh host-specifics contract and the README Move hosts procedure quoted in the body.
Notes / risks: the failure only surfaces during a host migration, and the same literal in the README services table must move with the compose files or the docs stay wrong.
GRIST_DEFAULT_EMAILis the lower-stakes second half of the criteria — document-or-render, either satisfies it.Suggested approach: render a
PM_STACK_TAILNET_IPvalue throughdeploy.shinto the compose bindings with loopback as the unset default, update the README table + Move hosts section, and verify a render with a different/unset IP as the criteria require.Labels normalized by the fleet monitor: kind/infrastructure + app/pm-stack (both coined this run per the label taxonomy), source/pr-diff-review, status/needs-triage, priority/normal, effort/s, meta/enriched.
dezren39 commentedon Nov 1, 2026
Transition: picked up
Worker: repo-worker-code (openchamber scheduled lane)
Task: 2207
Next: Revalidate the issue and current main; triage only.
dezren39 commentedon Nov 1, 2026
Transition: triage verdict
Worker: repo-worker-code (openchamber scheduled lane)
Task: 2207
Verdict: Confirmed on main
1a7366f54cdc83ef8e4eefcf3cfea2ce90acf3d4: Pad compose line 22 and Grist compose line 26 still bind100.99.113.103; Grist line 30 still hard-codes the email. The documented move-hosts path is not portable. No linked PR or matching open PR owns this fix; #1181 concerns a different service.Next: Queue a bounded small fix for configurable bindings, safe loopback default, documentation and render verification; preserve current-host behavior when explicitly configured.
9 remaining items
dezren39 commentedon Nov 1, 2026
🤖 Agent note · picked up (resumed)
Merge pull request #3293 from developing-today/queue/4854-781-tailnet…
dezren39 commentedon Nov 1, 2026
🤖 Agent note · done
${PM_STACK_TAILNET_IP:-127.0.0.1}(loopback-only when unset, never public); both deploy.sh scripts render PM_STACK_TAILNET_IP fromtailscale ip -4(env override wins, line omitted when no tailnet address); GRIST_DEFAULT_EMAIL rendered from the SOPSgrist_default_emailkey with the documented operator fallback; README services table + move-hosts updated; new regression testpm-stack/scripts/tailnet-bindings.test.sh.