Review: Wire up palinks.app custom domain via Hetzner edge proxy (re-review)

review-1392-2026-06-13-b Review

review approved

Verdict: APPROVED

Re-review of board item #1392. Previous review (review-1392-2026-06-13) flagged 4 items -- all 4 have been addressed.

Previous Findings Resolution

  • [x] [DECOMPOSE] -- Resolved. Umbrella #28 now has 3 sub-tickets: #50 (DNS), pal-e-platform #425 (Caddy), #51 (Rails + Keycloak)
  • [x] [BODY] Missing Caddy path -- Resolved. Sub-ticket #425 specifies /etc/caddy/Caddyfile and salt/states/caddy/ with both manual SSH and Salt state options
  • [x] [BODY] Keycloak mechanism -- Resolved. Sub-ticket #51 specifies Terraform via terraform/k3s.tfvars with tofu plan / tofu apply
  • [x] [LABEL] Points mismatch -- Resolved. Board item updated from 1 to 5 points

Template Completeness

  • [x] Type
  • [x] Lineage
  • [x] Repo -- correctly declares Multi-repo umbrella
  • [x] User Story
  • [x] Context
  • [x] Sub-Tickets -- new section listing execution order with repo and scope per sub-ticket
  • [x] File Targets -- defers to sub-tickets with summary pointers
  • [x] Feature Flag
  • [x] Acceptance Criteria
  • [x] Test Expectations
  • [x] Constraints
  • [x] Checklist -- maps to sub-ticket completion
  • [x] Related
All required sections for template-issue-feature are present. The umbrella format with a Sub-Tickets table is a good pattern for multi-repo work.

Traceability

  • [x] story:custom-domain label -- "Route palinks.app to production"
  • [x] story note verified -- found in project-palinks user-stories section
  • [x] arch:palinks label -- palinks architecture component
  • [ ] arch note MISSING -- arch-palinks not found in pal-e-docs. This has been flagged in multiple prior reviews (review-1400, review-1381, review-1377, etc.) and remains an open [SCOPE] item. However, this is a persistent platform-level gap, not a blocker for this specific ticket's scope.
  • [x] Forgejo issue -- ldraney/palinks#28, state: open

Sub-Ticket Verification

  • [x] #50 (GoDaddy DNS) -- well-scoped manual task, clear AC (dig verification), no code changes
  • [x] pal-e-platform #425 (Caddy config) -- concrete file paths, includes Caddyfile template from architecture doc, offers both manual SSH and Salt state options
  • [x] #51 (Rails + Keycloak) -- specifies config/environments/production.rb and terraform/k3s.tfvars with clear instructions
Minor issue in #425: Constraints section references "palinks #49" as the DNS dependency, but the DNS sub-ticket is actually #50. Issue #49 is an unrelated closed bug ("Open external links in new tabs"). This is cosmetic -- the dependency logic is correct, just the reference number is wrong.

File Targets

  • [x] config/environments/production.rb -- verified: file exists, config.hosts is commented out at lines 63-66. Development.rb shows the pattern: config.hosts << "hostname"
  • [x] docs/custom-domain.md -- verified: file exists with Remaining Work section (5 items) to be updated
  • [x] /etc/caddy/Caddyfile on edge-proxy -- correctly specified in #425 with sample Caddyfile content
  • [x] terraform/k3s.tfvars in pal-e-services -- specified in #51 with clear instructions on which arrays to update

Repo Placement

Umbrella is correctly filed on palinks (the primary app). Sub-tickets are filed on correct repos: #50 on palinks (manual task), #425 on pal-e-platform (Caddy infra), #51 on palinks (Rails changes + cross-repo Terraform). The multi-repo scope is now properly decomposed so each sub-ticket can be executed by an agent cloned into the correct repo.

Dependencies

  • [x] pal-e-platform #419 (Hetzner edge provisioning) -- done, on board in done column (item #1417)
  • [x] Spike #15 (custom domain routing) -- done, on board in done column (item #1377)
  • [x] Execution order documented: #50 (DNS) before #425 (Caddy TLS), #51 independent but must deploy before e2e

Acceptance Criteria

5 AC on the umbrella, each is an end-to-end verification. Sub-tickets have their own per-component AC. The umbrella AC serves as the final integration gate. All are verifiable with curl and browser testing. This is well-structured.

Blast Radius

  • docs/custom-domain.md lists landscaping-assistant.app and westsidekingsandqueens.com as future domains using the same pattern. This ticket establishes the reusable template.
  • No downstream consumers affected beyond the app itself.
  • Keycloak redirect URI change: sub-ticket #51 correctly specifies adding to existing arrays, not replacing.

Decomposition Assessment

Already decomposed into 3 sub-tickets across 2 repos + 1 manual task. Each sub-ticket is appropriately scoped for a single agent pass. The umbrella serves as a coordination and verification checkpoint. No further decomposition needed.

Recommendations

  • [BODY] Fix cross-reference in pal-e-platform #425: Constraints says "palinks #49" but should be "palinks #50" (DNS sub-ticket). Minor cosmetic issue, not a blocker.
  • [SCOPE] Create architecture note arch-palinks -- persistent gap flagged across 5+ reviews. Not blocking this ticket but should be addressed as platform housekeeping.
Neither recommendation is a blocker. The ticket is well-scoped and ready for execution.