Skip to content

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

Description

@mhenrixon

Problem / Goal

Gem-side plumbing for zoolutions/dash-proxy#21 (dynamic domain source for automatic TLS). Expose the proxy's new per-service flags in deploy.yml and give operators a CLI for the refresh nudge. Blocked by kamal-proxy#21 — implement after the proxy release that ships it, and pin MINIMUM_VERSION to that tag (proxy-before-gem ordering per .claude/rules/upstream-sync.md).

deploy.yml surface

proxy:
  ssl: true
  tls_domains:
    source: /api/v1/kamal/domains   # path (resolved against a healthy app target) or absolute URL
    interval: 300                    # seconds, optional
    batch_size: 1                    # 1 (default, per-domain certs) .. 25 (stable SAN batching)

Emits --tls-domains-source, --tls-domains-interval=<n>s, --tls-domains-batch-size via Proxy#deploy_options.

Context (read these first)

  • lib/kamal/configuration/proxy.rb:91-123 — deploy_options flag map; add the three keys following the healthcheck/buffering nesting pattern. One code file for the per-app path.
  • lib/kamal/configuration/validator/proxy.rb — validate: source is a /path or http(s) URL; interval positive integer; batch_size integer 1..25. Also: when tls_domains.source is set, relax the "Must set a host to enable automatic SSL" rule (the proxy allows host-less TLS with a domain source — mirror the loadbalancer bypass already present at the top of the validator).
  • lib/kamal/commands/loadbalancer.rb + lib/kamal/configuration/loadbalancer.rb — after PR fix(loadbalancer): propagate full proxy deploy options to the LB #14, the LB reuses Proxy#deploy_options, so these flags reach the LB tier automatically (that is the wm3 topology: TLS + domain source live on the LB). Add one LB test asserting the flags propagate.
  • lib/kamal/cli/proxy.rb — add kamal proxy domains <refresh|list|stats> passthrough (docker exec kamal-proxy kamal-proxy domains ...), following the existing loadbalancer subcommand dispatch pattern.
  • lib/kamal/configuration/docs/proxy.yml — document the new keys (fix stale examples while there is covered by kamal#8; don't duplicate).
  • Tests: test/configuration/proxy_test.rb, test/commands/proxy_test.rb, test/commands/loadbalancer_test.rb — interpolate MINIMUM_VERSION, never hardcode proxy tags.

Secrets note

The poll bearer token and refresh token are env vars on the proxy container (KAMAL_PROXY_DOMAINS_TOKEN, KAMAL_PROXY_REFRESH_TOKEN), set via proxy.run.options.env — document this; do NOT add token values as deploy flags (they leak into process listings/audit logs). Reference apps should rotate any token previously committed to deploy.yml in plaintext.

Verification gates

  • bundle exec ruby -Itest -e 'Dir["test/**/*_test.rb"].grep_v(/integration/).each { |f| require File.expand_path(f) }' — green (ignore the two known Apple-Silicon-only builder failures)
  • bundle exec rubocop --parallel — no offenses
  • New unit tests: flag emission (all three keys), validator rejections (bad URL, batch_size 0/26), host-less ssl allowed with source, LB propagation.

Out of scope

  • The proxy-side implementation (kamal-proxy#21). No MINIMUM_VERSION bump until the proxy tag exists on ghcr. No edits to kamal.gemspec/bin/release; no commits to main; fork tags only (dash-v*).

Execution

Fresh implementation session (sonnet tier) in ~/Code/mhenrixon/kamal. Branch off dash, PR into dash — these flags target a proxy feature that will never exist upstream (no upstream-PR-ability to preserve), and the loadbalancer propagation test depends on dash-only code.

Activity

  1. added
    enhancementNew feature or request
    gemLegacy: the gem side of cross-repo work (every item in this repo is)
    size:SSmall: hours
    on Jul 5, 2026
  2. mhenrixon commented on Jul 5, 2026

    @mhenrixon
    CollaboratorAuthor

    Proxy-side implementation (blocker): zoolutions/dash-proxy#21

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestgemLegacy: the gem side of cross-repo work (every item in this repo is)size:SSmall: hours

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions