This note records the intended boundary for HostKit release work before adding artifact download, activation, cleanup, or rollback behavior.
Current scope
Today release/2 is intentionally small. It is a DSL helper that emits plain, inspectable resources:
- a versions directory, by default
path(:opt, "releases/<name>"), - a current symlink, by default
path(:opt, "current/<name>"), - the symlink target
<versions_dir>/<version>.
It does not fetch artifacts, unpack archives, manage services, restart daemons, run readiness checks, prune old versions, or hide apply-time behavior.
service :gatus do
release :gatus, version: "5.36.0", owner: "deploy", group: "deploy"
daemon "gatus" do
service(exec_start: path(:opt, "current/gatus/gatus"))
end
endThe release declaration owns the filesystem pointer. Runtime declarations own how a daemon uses that pointer.
Boundary rules
Keep these concerns separate:
Release/artifact concerns
- version identity,
- artifact source and checksum,
- unpack/build output path,
- active pointer path,
- provenance manifest,
- retention/garbage collection of inactive versions.
Runtime concerns
- systemd units,
- users/groups for processes,
- environment files,
- working directories,
- hardening/isolation,
- restarts and reloads.
Verification and monitoring concerns
- apply-time readiness resources,
- external monitor metadata,
- Gatus or other provider rendering,
- health checks.
release must not define systemd/readiness/monitoring behavior. A release may expose paths or produce resources, but consumers such as daemon, ready, and providers remain separate declarations.
Future shape
A fuller release declaration may grow into artifact preparation and activation, but it should still compile to plain resources and commands:
service :gatus do
release :gatus, version: "5.36.0" do
artifact github: "TwiN/gatus",
asset: "gatus-linux-amd64.tar.gz",
checksum: "sha256:..."
unpack strip_components: 1
activate :symlink, atomic: true
keep 3
end
daemon "gatus" do
service(exec_start: path(:opt, "current/gatus/gatus"))
end
endThis future shape should still produce inspectable plan data: directories, files, symlinks, commands, manifests, and cleanup actions. It should not introduce an opaque deployment engine hidden behind the DSL.
Activation model
When HostKit eventually manages activation, prefer a prepare-then-flip model:
- prepare the new version under an inactive path,
- verify artifact checksum/provenance before activation,
- optionally write a manifest for the prepared version,
- atomically update the active pointer where the target platform supports it,
- leave inactive versions available for rollback until explicit retention cleanup.
Activation should be modeled as plan/apply data. If an atomic symlink swap needs a command, that command should be visible in the plan and should have explicit down-plan behavior.
Down-plan expectations
Rollback remains a down plan, not a release-specific rollback entity.
For future release actions:
- current symlink changes are reversible when the previous target was read into the up plan,
- artifact preparation may be irreversible unless HostKit has enough prior state or a backup/source-bundle strategy,
- retention cleanup should be conservative and must not delete the active version,
- cleanup of old versions should be explicit and inspectable,
- applying a down plan should use the same
HostKit.Applyengine as any other plan.
Shared paths
Traditional deployment tools often include shared/ paths for mutable state across versions. HostKit already has separate primitives for state/config/cache/data roots, storage, env files, and systemd writable paths. A future release API may help describe shared paths, but it must not automatically wire them into runtime behavior. Runtime declarations should still make writable paths and process behavior explicit.
Naming guidance
Use release for user-facing declarations. Avoid helper namespaces like release_path/2 unless dogfooding proves they are necessary. Prefer existing generic path conventions such as:
path(:opt, "current/gatus/gatus")If repeated current/version roots become noisy, consider generic convention roots such as :current or :versions rather than release-specific helper names.