Review: Caddy reverse-proxy block for mail.landscaping-assistant.app

review-1734-2026-07-04 Review

review needs-refinement

Verdict: NEEDS_REFINEMENT

Template Completeness

  • [x] Type: Feature
  • [x] Lineage
  • [x] Repo
  • [x] User Story
  • [x] Context
  • [x] File Targets
  • [x] Feature Flag (none)
  • [x] Acceptance Criteria
  • [x] Test Expectations
  • [x] Constraints
  • [x] Checklist
  • [x] Related
All required sections present.

Traceability

  • [x] story:self-hosted label — "As platform admin, I want zero third-party email dependencies so I control deliverability and avoid Gmail app password rotation"
  • [x] story note verified — found in project-stalwart-email user-stories section (key: self-hosted, role: Platform Admin)
  • [x] arch:edge-vps label — Hetzner Edge VPS component
  • [x] arch note verified — arch-edge-vps note exists in pal-e-docs (Architecture: Hetzner Edge VPS)
  • [x] Forgejo issue — ldraney/pal-e-platform#499, state: open

File Targets

  • [x] salt/pillar/caddy.sls — verified: file exists, contains caddy:sites pillar structure with domain/proxy_target/www_redirect keys
  • [ ] salt/states/caddy/Caddyfile.j2 — MISSING FROM TICKET: template hardcodes :443 and tls_server_name for ALL upstreams. Adding proxy_target: localhost:8080 to the pillar would render as reverse_proxy localhost:8080:443 with tls_server_name localhost:8080, which is broken. Template needs a conditional for local targets.

Repo Placement

Correct. Issue filed on ldraney/pal-e-platform, which contains both salt/pillar/caddy.sls and salt/states/caddy/Caddyfile.j2. Single-repo fix.

Dependencies

  • Blocking: Issue #498 ("Salt: Install Stalwart binary + systemd service on edge VPS") — board item #1733, currently in backlog, state: open. Stalwart must be installed before the reverse proxy can route to it.
  • Dependency is documented in the issue Lineage section.

Acceptance Criteria

3 AC + 1 test expectation = 4 total. All are verifiable by an agent post-implementation:
  • "https://mail.landscaping-assistant.app loads Stalwart web admin" — curl + status code
  • "TLS auto-provisioned by Caddy" — cert issuer check
  • "Admin protected by Stalwart's built-in auth" — verify unauthenticated access returns 401/redirect
  • curl -I returns 200/302 — straightforward
Missing AC: No regression criterion for existing sites after template change.

Blast Radius

HIGH concern: The Caddyfile.j2 template renders ALL sites. Introducing a conditional for local vs Tailscale targets must preserve backward compatibility for existing sites (palinks.app, landscaping-assistant.app, westsidekingsandqueens.com). A template error would break ALL public-facing services.

Decomposition Assessment

  • 2 file targets in 1 repo — acceptable
  • 4 acceptance criteria — under threshold
  • Estimated agent work: under 5 minutes (pillar entry + template conditional)
  • No decomposition needed

Recommendation

  • [BODY] Add missing file target: salt/states/caddy/Caddyfile.j2 — needs conditional to skip :443 suffix and tls_server_name transport for local targets
  • [BODY] Specify pillar schema extension: add local: true flag to the mail site entry in caddy.sls to differentiate localhost targets from Tailscale targets
  • [BODY] Add acceptance criterion: "Existing sites (palinks.app, landscaping-assistant.app, westsidekingsandqueens.com) continue to function after Caddyfile.j2 template change"