Skip to content

Stage 3 (epic): rename server artifacts with a rolling-upgrade bridge #118

Description

@mhenrixon

Stage 3: rename server artifacts with a rolling-upgrade bridge

Epic. Follow-up to #115 (final stage), depends on stage 2 (#117, landed in a2273854).
Split into three executable sub-stages after a full inventory of the artifact surface.

Problem / Goal

Everything an operator or a server can see still says kamal: the project's .kamal/
directory, the KAMAL_* env vars handed to hooks and injected into app containers, the
.kamal/ run directory on every host, the kamal-proxy container, the kamal docker
network, the kamal-proxy-config volume, and the org.* labels the prune filters key on.
Stage 3 renames all of it to dash.

Done looks like: a fresh dash init produces .dash/, a deployed host has .dash/ and a
dash-proxy container on a dash network, hooks and app containers see DASH_*, and an
operator upgrading from 3.2.0 gets there without a manual server intervention.

Full artifact inventory

# Artifact Where it lives Visible to Risk Sub-stage
1 .kamal/secrets, .kamal/hooks/ operator's own git repo high low 3a
2 KAMAL_* hook env + Dash::Tags#env + KAMAL_HOST accessory env hook scripts, deployed app containers high low 3a
3 .kamal/ run directory on the host every server low medium 3b
4 org.kamal.proxy-config-digest label proxy container none low 3b
5 kamal-docker-registry container operator's laptop low trivial 3b
6 kamal-proxy / kamal-proxy-net container names every server medium high 3c
7 kamal docker network every server low high 3c
8 kamal-proxy-config, kamal-loadbalancer-config volumes (routing table + ACME cache) every proxy host none high 3c
9 org.opencontainers.image.title=kamal-proxy label (prune filter) dash-proxy image none medium 3c
10 in-image binary kamal-proxy, user + /home/kamal-proxy/.config/kamal-proxy, /home/kamal-proxy/.apps-config dash-proxy image none medium 3c
11 KAMAL_* emission removed for good everywhere high breaking 3d (5.0)

Not in scope as a rename: KAMAL_REGISTRY_PASSWORD and friends in lib/dash/cli/templates/
and lib/dash/configuration/docs/*.yml are operator-chosen secret names, not identifiers
the gem enforces. They get renamed in the sample text only (3a), never read by name in code.
KAMAL_PROXY_DOMAINS_TOKEN / KAMAL_PROXY_REFRESH_TOKEN and the /.kamal-proxy/redirects/refresh
HTTP route are owned by the proxy, not the gem — track them in zoolutions/dash-proxy.

Sub-stages

Stage Scope Repos Target
3a Local project files + DASH_* env dual-emit + dash migrate gem 3.3.0
3b Server .kamal/.dash/, config-digest label, local registry container gem 3.4.0
3c Container identity: proxy container, network, volumes, image title label, in-image paths dash-proxy then gem proxy v1.1.0.0 + gem 4.0.0
3d Drop KAMAL_* emission and the .kamal/ local fallback gem 5.0.0

3b and 3c both force one proxy container replacement. If they land close together they may be
released as one gem version; the sub-stage split is about reviewable PRs, not mandatory releases.

Settled in interview (do not re-litigate)

  • Split into 3a/3b/3c, each independently shippable. KAMAL_* removal happens last (3d), not
    bundled into any of them.
  • Rename the docker network AND the volumes (inventory 7 and 8) rather than freezing them.
    Rationale from the user: no major site is live on dash yet, load balancing and ACME have
    effectively one user, so a clean break now beats carrying legacy identifiers forever. The
    migration must still be written defensively — see 3c.
  • KAMAL_* env vars: emit both. DASH_* and KAMAL_* are written side by side from 3a.
    Reads (ENV["KAMAL_LOCK"], ENV["KAMAL_SERVER_LOCK"]) accept either, preferring DASH_*.
    Old names are removed only at 5.0 (3d).
  • Server .kamal/.dash/: one guarded mv on boot. Not a dual-read fallback, not a
    fresh start. [ -d .kamal ] && [ ! -d .dash ] && mv .kamal .dash, idempotent.
  • Proxy image: rename + MINIMUM_VERSION floor. No symlink compatibility layer, no runtime
    probing. dash-proxy releases the renamed image first; the gem then bumps MINIMUM_VERSION to
    that tag and switches every path in one commit. The existing Dash::Utils.older_version? gate
    in lib/dash/cli/proxy.rb:35 already refuses older images.
  • Local .kamal/secrets and .kamal/hooks: fall back, with an explicit opt-in move. Defaults
    become .dash/*; if .dash/ is absent and .kamal/ is present, use .kamal/ and warn. A new
    dash migrate command performs the local move when the operator asks for it — the gem never
    rewrites the operator's working tree on its own.

Hard constraints carried into every sub-stage

  • Proxy image before gem, always. MINIMUM_VERSION must name a published, public
    ghcr.io/zoolutions/dash-proxy tag before the gem releases (rake release gates on it).
  • Never a -suffix version tag: Gem::Version reads - as prerelease and sorts it older,
    which hard-fails dash proxy boot's minimum-version check.
  • Test assertions interpolate Dash::Configuration::Proxy::Run::MINIMUM_VERSION, never a literal.
  • No direct pushes to main; no manual lib/dash/version.rb bumps (only rake release writes it).
  • docs/ is a dash-deployed app with its own .kamal/ — it is a real consumer of 3a and should
    be migrated in the same PR as a dogfooding check.

Child issues

Each child issue is self-contained — hand one to a fresh implementation session on the sonnet
tier without needing this epic for context.

Execution order

3a → 3b → 3c, each a separate release. 3b and 3c may be collapsed into one gem version if they
land close together; the split exists to keep PRs reviewable, not because the releases must be
distinct. 3c is the only stage that requires a zoolutions/dash-proxy release first, and the only
one that causes a proxy outage.

Activity

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions