Skip to content

pm-stack: Pad/Grist compose files hard-code one host's tailnet IP (100.99.113.103), breaking the documented move-hosts procedure #781

Closed

Description

@dezren39

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-24 repeats 100.99.113.103 as 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:16 and pm-stack/grist/deploy.sh:16 — "Host specifics (the ONLY host-specific values)" are listed as PM_STACK_SECRETS and SOPS_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 not 100.99.113.103, docker compose up fails 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:30 hard-codes GRIST_DEFAULT_EMAIL: drewrypope@gmail.com as 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 by deploy.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_EMAIL is 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

  1. added
    kind/infrastructureInfrastructure / host-portability work
    priority/normalOrdinary priority (the default)
    effort/sSmall: expected under 2 hours of work
    app/pm-stackpm-stack (Pad/Grist) in developing-today/code
    meta/enrichedIntake review done: acceptance criteria, links, and normalized labels added
    on Oct 26, 2026
  2. dezren39 commented on Oct 26, 2026

    @dezren39
    MemberAuthor

    Intake review

    Still live: re-verified this run against current main — pm-stack/pad/compose.yml:22 still binds 100.99.113.103:7777:7777 and pm-stack/grist/compose.yml:26 still binds 100.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_EMAIL is the lower-stakes second half of the criteria — document-or-render, either satisfies it.

    Suggested approach: render a PM_STACK_TAILNET_IP value through deploy.sh into 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.

  3. dezren39 commented on Nov 1, 2026

    @dezren39
    MemberAuthor

    Transition: picked up
    Worker: repo-worker-code (openchamber scheduled lane)
    Task: 2207
    Next: Revalidate the issue and current main; triage only.

  4. added
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    and removed on Nov 1, 2026
  5. dezren39 commented on Nov 1, 2026

    @dezren39
    MemberAuthor

    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 bind 100.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.

  6. 9 remaining items

  7. added
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    status/parkedDeliberately paused with a recorded resume condition
    queue/claimedQueue worker lifecycle state
    and removed
    queue/claimedQueue worker lifecycle state
    status/parkedDeliberately paused with a recorded resume condition
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    on Nov 1, 2026
  8. dezren39 commented on Nov 1, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · picked up (resumed)

  9. added a commit that references this issue on Nov 1, 2026
  10. dezren39 commented on Nov 1, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · done

    • Agent: repo-worker-code (lane D)
    • Session / ID: queue task 4854
    • Result: fixed and merged — PR pm-stack: host-portable Pad/Grist tailnet bindings (#781) #3293 merged as f00dde6.
    • What landed: Pad and Grist compose bindings now use ${PM_STACK_TAILNET_IP:-127.0.0.1} (loopback-only when unset, never public); both deploy.sh scripts render PM_STACK_TAILNET_IP from tailscale ip -4 (env override wins, line omitted when no tailnet address); GRIST_DEFAULT_EMAIL rendered from the SOPS grist_default_email key with the documented operator fallback; README services table + move-hosts updated; new regression test pm-stack/scripts/tailnet-bindings.test.sh.
    • Verification: test script re-run on the laptop this shift against head e90ebf5 — all checks passed (unset loopback-only, other-IP, current-host equivalence with the old hard-coded behaviour, GRIST_DEFAULT_EMAIL default + override, no 0.0.0.0 binding). Codex review completed clean on e90ebf5 (no findings, none unresolved). No CI workflow triggers on pm-stack paths (only check on the head was CodeRabbit, neutral).
    • Links: pm-stack: host-portable Pad/Grist tailnet bindings (#781) #3293
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

app/pm-stackpm-stack (Pad/Grist) in developing-today/codeeffort/sSmall: expected under 2 hours of workkind/infrastructureInfrastructure / host-portability workmeta/enrichedIntake review done: acceptance criteria, links, and normalized labels addedpriority/normalOrdinary priority (the default)source/pr-diff-reviewFiled by the pr-diff-review job

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions