Skip to content

feat(proxy): plumb tls_domains (dynamic domain source) into deploy.yml + kamal proxy domains CLI - #17

Merged
mhenrixon merged 1 commit into
dashfrom
issue-15-tls-domains
Jul 11, 2026
Merged

mhenrixon merged 1 commit into
dashfrom
issue-15-tls-domains

Conversation

@mhenrixon

Copy link
Copy Markdown
Collaborator

Summary

Gem-side plumbing for zoolutions/dash-proxy#21 (dynamic domain source for automatic TLS):

  • proxy.tls_domains in deploy.yml (source, interval, batch_size) emitted as --tls-domains-source, --tls-domains-interval=<n>s, --tls-domains-batch-size via Proxy#deploy_options (lib/kamal/configuration/proxy.rb)
  • Validation (lib/kamal/configuration/validator/proxy.rb): source must be a /-path or http(s) URL and is required when tls_domains is set; interval positive integer; batch_size 1..25. Host-less ssl: true is now allowed when tls_domains.source is set (mirrors the loadbalancer bypass — the proxy serves host-less TLS from the dynamic set)
  • Load balancing: the per-app deploy strips the tls-domains flags together with host/tls (TLS terminates at the LB); Kamal::Configuration::Loadbalancer#deploy_options re-adds them, so the flags reach the LB tier (wm3 topology)
  • New kamal proxy domains <refresh|list|stats> passthrough (docker exec <container> kamal-proxy domains ...), targeting the LB host/container when load balancing, all proxy hosts otherwise
  • Docs (lib/kamal/configuration/docs/proxy.yml): new keys documented (this also feeds the schema validator); poll/refresh tokens documented as KAMAL_PROXY_DOMAINS_TOKEN / KAMAL_PROXY_REFRESH_TOKEN env vars via proxy.run.options.env — never deploy flags

No MINIMUM_VERSION bump — per the issue, that waits for the proxy release that ships kamal-proxy#21.

An adversarial multi-agent review of the diff (4 lenses, every finding verified by live reproduction/mutation testing) produced 6 confirmed findings; 5 fixed in this PR:

  • tls_domains: {} no longer silently disables the feature (validator guard was .present?, which skips empty hashes)
  • interval/batch_size integer checks hardened in the semantic validator (previously the type guarantee rested solely on the docs example staying an Integer; drift would have raised an uncaught ArgumentError)
  • test gaps closed: domains under LB-on-proxy-host (auto-activation default — container resolves to kamal-proxy), multi-host fan-out pin for domains refresh, stats whitelist pin (mutation-tested green before)

Accepted as consistent-by-design (not fixed): a role-level tls_domains snippet that only overrides interval fails validation before the deep-merge with the global proxy config — identical to upstream's existing ssl/host validate-before-merge behavior (a role snippet with only ssl: true fails the same way). Workaround: restate source at the role level.

Closes #15

Test plan

  • Flag emission: all three keys, s-suffixed interval, absent when unset, source-only variant (test/configuration/proxy_test.rb)
  • Validator rejections: bad source formats, interval 0/-300, batch_size 0/26; accepts http/https URLs, batch_size 1/25; rejects tls_domains without source and unknown keys
  • Host-less ssl: true allowed with tls_domains.source
  • LB propagation: full LB deploy command string includes the flags (test/commands/loadbalancer_test.rb)
  • Per-app deploy strips the flags when load balancing
  • CLI: domains refresh on proxy hosts, domains list against the LB container when load balancing, unknown subcommand message (test/cli/proxy_test.rb); command builders for both containers
  • bundle exec rubocop --parallel — no offenses
  • Unit suite: 906 runs, only the two known Apple-Silicon builder failures (reproduced on pristine dash with these changes stashed)

…l + kamal proxy domains CLI

## Summary
Gem-side plumbing for zoolutions/dash-proxy#21. New proxy.tls_domains keys
(source, interval, batch_size) emit --tls-domains-source,
--tls-domains-interval=<n>s and --tls-domains-batch-size via
Proxy#deploy_options; host-less ssl: true is allowed when a source is set
(the proxy serves host-less TLS from the dynamic set). When load balancing,
the flags are stripped from the per-app deploy alongside host/tls and
re-added by the Loadbalancer config, so TLS + domain source land on the LB
tier. New kamal proxy domains <refresh|list|stats> passthrough targets the
LB container when load balancing, all proxy hosts otherwise. Tokens are
documented as env vars (proxy.run.options.env), never deploy flags.

No MINIMUM_VERSION bump - that waits for the proxy release shipping
kamal-proxy#21 (proxy-before-gem ordering).

## Test Coverage
- flag emission incl. s-suffixed interval, absence when unset, source-only
- validator: source format (path/http(s) URL, required), interval positive
  integer, batch_size integer 1..25, empty-hash rejection, unknown keys
- host-less ssl allowed with tls_domains.source
- LB deploy command propagates the flags; per-app deploy strips them
- domains CLI: proxy-hosts fan-out, LB dispatch, LB-on-proxy-host container
  name, stats whitelist pin, unknown subcommand message

## Verification
- [x] bundle exec rubocop --parallel passes (182 files, no offenses)
- [x] unit suite: 909 runs, only the two known Apple-Silicon builder
      failures (reproduced on pristine dash with these changes stashed)

Refs #15
@mhenrixon mhenrixon self-assigned this Jul 11, 2026
@mhenrixon
mhenrixon merged commit f044c02 into dash Jul 11, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

R4: Plumb proxy tls_domains (dynamic domain source) into deploy.yml + kamal proxy domains CLI

1 participant