Skip to content

Releases: smallhoursorg/hotserve

v0.3.1

Choose a tag to compare

@github-actions github-actions released this 15 Oct 20:46
ae6a260

Secrets are commits. An app's secrets live in the box's repository, one age-encrypted file per variable. They reach the box over the same signed channel as the Caddyfile, and the box decrypts them into /etc/hotserve/secrets/<app>.env. GitHub and CI only ever hold ciphertext. The reference is docs/secrets.md. This release is the commit that passed as v0.3.1-rc2, after an acceptance run of rc1 and an upgrade from rc1 to rc2 on a real box.

Upgrading:

  • On the box, follow docs/upgrading.md. The upgrade no longer asks about /etc/hotserve/Caddyfile (below).
  • A box repository made from v0.3.0's template copies in the new files (docs/secrets.md, "From an earlier template").
  • If a box repository's HOTSERVE_VERSION variable pinned a candidate, delete it, so its checks use this release.

Behaviour changes since v0.3.0

Secrets are commits. An app's secrets can now live in the box's repository: one age-encrypted file per variable under <host>/secrets/<app>/. The box decrypts them into /etc/hotserve/secrets/<app>.env when it applies a push, and the app reads them at its next launch.

Getting it:

  • Update your box repository from the template: Makefile, bin/secrets, bin/signer, bin/apply, bin/check, .gitattributes. The commented env_file line in its Caddyfile names the old path: add the line make secret prints instead (docs/secrets.md, "From an earlier template").
  • On the laptop, brew install age (or apt install age).
  • Read docs/secrets.md.
  • The package makes the box's key when it is installed or upgraded on a running system; hotserve box key, as root, prints it.

Env files you made by hand keep working unchanged. To move one into the repository:

  1. ssh root@box cat /etc/hotserve/<app>.env | make secrets-import APP=<app> BOX_KEY=$(ssh root@box hotserve box key)
  2. Set env_file /etc/hotserve/secrets/<app>.env in the app's block, and push.
  3. Once that run is green, remove the old file.

<host>/secrets is reserved. Beside a box's Caddyfile, secrets is now the box's own: a repository that already has its own secrets entry there is refused until it is moved.

The template's sudoers file is gone. An administrator needs only the adm group. Once your secrets are in the repository, remove the old grant as root: rm /etc/sudoers.d/hotserve-admin && groupdel hotserve-admin.

Upgrades no longer ask about /etc/hotserve/Caddyfile. The starter ships at /usr/share/hotserve/Caddyfile. The package installs it at /etc/hotserve/Caddyfile only when nothing is there, and replaces it only while that file is byte-identical to the starter the package last wrote. Any other file is never touched.

A box still on a v0.3.x starter keeps it. To bring it up to date and have later upgrades follow the starter, run cp /usr/share/hotserve/Caddyfile /etc/hotserve/ and then dpkg-reconfigure hotserve, which records it. The cp alone leaves the next upgrade with a changed starter nothing to match. (Corrected after merge: the original sentence left out dpkg-reconfigure; Copilot on #240.)

apt purge hotserve no longer removes /etc/hotserve/Caddyfile on a box installed from this release on. On a box upgraded from v0.3.x, it still does.

A refused deploy token is always answered 401. Both deploy webhooks, an app's and the box's, answer every refused token with the same flat 401, however often the address has failed. There is no 429 and no Retry-After from them any more. The per-address and process-wide budgets still bound what the journal records (ten and a hundred failures logged a minute). The box template's bin/apply reports a 429, which can now only come from something in front of the box, as red with its status and body. Anything scripted against the 429 needs changing.

The .deb now requires systemd 257 or newer. apt refuses to install it on Debian 12 and earlier or Ubuntu 24.04, naming systemd (>= 257). Those hosts could never run an app: every app's sandbox needs PrivatePIDs=, which systemd 257 added. Before this, they installed and failed only when the first config that defines an app was loaded (#225).

What's Changed

  • fix(examples): a pin of just "v" is refused, and the tutorial installs the latest release by @earthlingdavey in #224
  • docs(box): what the rc2 walkthrough found: the hidden .github/, GitHub's badge, the 503 quoted, hardening on its own page by @earthlingdavey in #226
  • fix(packaging): the .deb needs systemd 257, and step 2 says what it checks by @earthlingdavey in #225
  • docs(box): secrets as commits — the design (box PR 7a) by @earthlingdavey in #227
  • feat(box): secrets' pure cores — the env-file renderer, the secrets proof, the box key (box PR 7b) by @earthlingdavey in #229
  • feat(box): root installs secrets — the set, the key, recovery (box PR 7c) by @earthlingdavey in #231
  • feat(examples): secrets as commits — make secret, secrets, rekey (box PR 7e1) by @earthlingdavey in #228
  • feat(examples): secrets ride the workflow — bundle, recipient check, e2e; the sudoers file goes (box PR 7e2) by @earthlingdavey in #233
  • docs: secrets as commits — the tutorial step and docs/secrets.md (box PR 7f) by @earthlingdavey in #230
  • fix(examples): bin/apply names a symlinked box file; two comments say what is true (#223) by @earthlingdavey in #235
  • feat(deploytrust): a refused token is always the flat 401; the throttle only samples the journal by @earthlingdavey in #236
  • docs(readme): box joins the README — built-in table, install, security, dependencies by @earthlingdavey in #238
  • fix(packaging): upgrades never ask about /etc/hotserve/Caddyfile (#237) by @earthlingdavey in #239
  • docs: one triage rule for the whole repository, as a skill, scoring the fix as well by @earthlingdavey in #240
  • docs(triage): score only failures; operator input is occurrence 5; score backlog items before building by @earthlingdavey in #246

Full Changelog: v0.3.0...v0.3.1

v0.3.1-rc2

v0.3.1-rc2 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 15 Oct 20:24
ae6a260

The second release candidate for v0.3.1, cut from main at ae6a260. rc1 (9920ad8) passed its acceptance run on 2026-10-15. Since rc1:

  • #239: /etc/hotserve/Caddyfile is no longer a dpkg conffile, so upgrades never ask about it. An upgrade from rc1 is the first time this change runs on a real box.
  • #236: a refused deploy token always gets the same flat 401.
  • #235: the template's bin/apply names a symlinked box file instead of sending it.
  • Docs: the README covers box (#238), and the triage rule is now one skill for the whole repository (#240, #246).

The reference for secrets is docs/secrets.md, and the follow-ups are tracked in #234.

To try it:

Behaviour changes since v0.3.0

Secrets are commits. An app's secrets can now live in the box's repository: one age-encrypted file per variable under <host>/secrets/<app>/. The box decrypts them into /etc/hotserve/secrets/<app>.env when it applies a push, and the app reads them at its next launch.

Getting it:

  • Update your box repository from the template: Makefile, bin/secrets, bin/signer, bin/apply, bin/check, .gitattributes. The commented env_file line in its Caddyfile names the old path: add the line make secret prints instead (docs/secrets.md, "From an earlier template").
  • On the laptop, brew install age (or apt install age).
  • Read docs/secrets.md.
  • The package makes the box's key when it is installed or upgraded on a running system; hotserve box key, as root, prints it.

Env files you made by hand keep working unchanged. To move one into the repository:

  1. ssh root@box cat /etc/hotserve/<app>.env | make secrets-import APP=<app> BOX_KEY=$(ssh root@box hotserve box key)
  2. Set env_file /etc/hotserve/secrets/<app>.env in the app's block, and push.
  3. Once that run is green, remove the old file.

<host>/secrets is reserved. Beside a box's Caddyfile, secrets is now the box's own: a repository that already has its own secrets entry there is refused until it is moved.

The template's sudoers file is gone. An administrator needs only the adm group. Once your secrets are in the repository, remove the old grant as root: rm /etc/sudoers.d/hotserve-admin && groupdel hotserve-admin.

Upgrades no longer ask about /etc/hotserve/Caddyfile. The starter ships at /usr/share/hotserve/Caddyfile. The package installs it at /etc/hotserve/Caddyfile only when nothing is there, and replaces it only while that file is byte-identical to the starter the package last wrote. Any other file is never touched.

A box still on a v0.3.x starter keeps it. To bring it up to date and have later upgrades follow the starter, run cp /usr/share/hotserve/Caddyfile /etc/hotserve/ and then dpkg-reconfigure hotserve, which records it. The cp alone leaves the next upgrade with a changed starter nothing to match. (Corrected after merge: the original sentence left out dpkg-reconfigure; Copilot on #240.)

apt purge hotserve no longer removes /etc/hotserve/Caddyfile on a box installed from this release on. On a box upgraded from v0.3.x, it still does.

A refused deploy token is always answered 401. Both deploy webhooks, an app's and the box's, answer every refused token with the same flat 401, however often the address has failed. There is no 429 and no Retry-After from them any more. The per-address and process-wide budgets still bound what the journal records (ten and a hundred failures logged a minute). The box template's bin/apply reports a 429, which can now only come from something in front of the box, as red with its status and body. Anything scripted against the 429 needs changing.

The .deb now requires systemd 257 or newer. apt refuses to install it on Debian 12 and earlier or Ubuntu 24.04, naming systemd (>= 257). Those hosts could never run an app: every app's sandbox needs PrivatePIDs=, which systemd 257 added. Before this, they installed and failed only when the first config that defines an app was loaded (#225).

What's Changed

  • fix(examples): bin/apply names a symlinked box file; two comments say what is true (#223) by @earthlingdavey in #235
  • feat(deploytrust): a refused token is always the flat 401; the throttle only samples the journal by @earthlingdavey in #236
  • docs(readme): box joins the README — built-in table, install, security, dependencies by @earthlingdavey in #238
  • fix(packaging): upgrades never ask about /etc/hotserve/Caddyfile (#237) by @earthlingdavey in #239
  • docs: one triage rule for the whole repository, as a skill, scoring the fix as well by @earthlingdavey in #240
  • docs(triage): score only failures; operator input is occurrence 5; score backlog items before building by @earthlingdavey in #246

Full Changelog: v0.3.1-rc1...v0.3.1-rc2

v0.3.1-rc1

v0.3.1-rc1 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 13 Oct 14:00
9920ad8

A release candidate for v0.3.1, cut from main at 9920ad8, for the acceptance run of secrets as commits (box PR 7). An app's secrets live in the box's repository, one age-encrypted file per variable. They reach the box over the same signed channel as the Caddyfile, and the box decrypts them into /etc/hotserve/secrets/<app>.env. The reference is docs/secrets.md, and the follow-ups are tracked in #234.

To try it:

Behaviour changes since v0.3.0

Secrets are commits. An app's secrets can now live in the box's repository: one age-encrypted file per variable under <host>/secrets/<app>/. The box decrypts them into /etc/hotserve/secrets/<app>.env when it applies a push, and the app reads them at its next launch.

Getting it:

  • Update your box repository from the template: Makefile, bin/secrets, bin/signer, bin/apply, bin/check, .gitattributes. The commented env_file line in its Caddyfile names the old path: add the line make secret prints instead (docs/secrets.md, "From an earlier template").
  • On the laptop, brew install age (or apt install age).
  • Read docs/secrets.md.
  • The package makes the box's key when it is installed or upgraded on a running system; hotserve box key, as root, prints it.

Env files you made by hand keep working unchanged. To move one into the repository:

  1. ssh root@box cat /etc/hotserve/<app>.env | make secrets-import APP=<app> BOX_KEY=$(ssh root@box hotserve box key)
  2. Set env_file /etc/hotserve/secrets/<app>.env in the app's block, and push.
  3. Once that run is green, remove the old file.

<host>/secrets is reserved. Beside a box's Caddyfile, secrets is now the box's own: a repository that already has its own secrets entry there is refused until it is moved.

The template's sudoers file is gone. An administrator needs only the adm group. Once your secrets are in the repository, remove the old grant as root: rm /etc/sudoers.d/hotserve-admin && groupdel hotserve-admin.

The .deb now requires systemd 257 or newer. apt refuses to install it on Debian 12 and earlier or Ubuntu 24.04, naming systemd (>= 257). Those hosts could never run an app: every app's sandbox needs PrivatePIDs=, which systemd 257 added. Before this, they installed and failed only when the first config that defines an app was loaded (#225).

What's Changed

  • fix(examples): a pin of just "v" is refused, and the tutorial installs the latest release by @earthlingdavey in #224
  • docs(box): what the rc2 walkthrough found: the hidden .github/, GitHub's badge, the 503 quoted, hardening on its own page by @earthlingdavey in #226
  • fix(packaging): the .deb needs systemd 257, and step 2 says what it checks by @earthlingdavey in #225
  • docs(box): secrets as commits — the design (box PR 7a) by @earthlingdavey in #227
  • feat(box): secrets' pure cores — the env-file renderer, the secrets proof, the box key (box PR 7b) by @earthlingdavey in #229
  • feat(box): root installs secrets — the set, the key, recovery (box PR 7c) by @earthlingdavey in #231
  • feat(examples): secrets as commits — make secret, secrets, rekey (box PR 7e1) by @earthlingdavey in #228
  • feat(examples): secrets ride the workflow — bundle, recipient check, e2e; the sudoers file goes (box PR 7e2) by @earthlingdavey in #233
  • docs: secrets as commits — the tutorial step and docs/secrets.md (box PR 7f) by @earthlingdavey in #230

Full Changelog: v0.3.0...v0.3.1-rc1

v0.3.0

Choose a tag to compare

@github-actions github-actions released this 11 Oct 20:41
f9ef7ee

hotserve 0.3.0 puts the box's config in git. The box's Caddyfile can live in a private repository of its own. A change reaches the box only as a commit signed by a key the box lists, sent by that repository's workflow with no stored secret, and checked by the box's own binary before it is applied. Your first deploy now sets this up on day 0, from the new hotserve-example-box template.

Upgrading from 0.2.x

Upgrading has the steps. Two things to know:

  • dpkg asks about /etc/hotserve/Caddyfile if you have edited it, because the starter file's text changed. Answer N, the default, to keep yours. Answering Y would replace your config with the starter.
  • Nothing else changes until you choose to. A box without a box repository keeps its hand-edited /etc/hotserve/Caddyfile, and a reload still applies it. The box channel starts only when you run hotserve init from a box repository: the box README's Set it up covers a box that already serves its own file.

New: the box's config in git

  • hotserve init <commit>, run as root in the box's directory from your repository, installs that file and records the commit the box starts from. hotserve box baseline resets that commit, for example after main is force-pushed. hotserve box describe and hotserve box webhook read a box's file the way the box does.
  • box_webhook, on the deploy host's site, is where the repository's workflow sends each push. The box checks the run's token and every commit's signature since the commit it runs, checks that the file is the one in the commit, validates it, and then applies it. A refused push changes nothing, and a reload that fails puts the previous file back. The workflow's run prints the box's verdict and the installed-versus-pushed diff. On the box, journalctl -u hotserve-box-apply says what it did.
  • The template: make signer makes your SSH key the first signer, make check checks each box's file, and the workflow checks every push and pull request, then sends main to each box. GitHub's merge button cannot land a change the box will apply: land with git merge --ff-only from a signer's laptop.
  • Docs: the tutorial's step 3 is the box repository. The second page runs the box first and hardens it second: SSH by key only, port 22 shut to everyone but you, and an optional administrator. Root keeps key-only SSH as the way back at 3am.

Behaviour changes

Caddy v2.11.6 (#163). Apps behind hotserve will see these upstream changes:

  • Request headers are capped at 16 KiB by default (was 1 MB); larger ones get 431. The fix is the max_header_size server option.
  • Request header names containing . are dropped, the same as names with _ since 2.11.4. Opt back in with expected_dot_headers.
  • New 1-minute idle read/write timeouts: a body read or response write that makes no progress for a minute is aborted. Pauses between SSE writes don't count.
  • A wildcard site's client_auth no longer carries over to more specific site blocks. Config validation is stricter, and method matcher values are uppercased.
  • hotserve CLI errors, from hotserve validate for example, are no longer a plain Error: ... line. They go through Caddy's logger: coloured on a terminal, and a JSON log line when piped. The message text is unchanged, but scripts that grep for Error: need adjusting.

penaltybox:

  • IPv6 clients are counted by /64 (#194). Addresses in one /64 share a budget and a box, so a client can no longer step to a fresh address to avoid one. IPv4-mapped (::ffff:…) and well-known-prefix NAT64 (64:ff9b::…) clients are counted as their IPv4 address. This applies to any key whose whole value is one IP address, a header key such as CF-Connecting-IP included. Clients sharing a /64 now share a budget: a SLAAC office or home network, customers given /128s from a shared /64, and IPv4 clients behind NAT64 with a network-specific prefix or behind Teredo. The hash that spreads keys across the store's shards is also seeded at random each time the store is built, so a client can't pick keys that fill one shard. No config change is needed.
  • A warning when max_keys is below 128 (#196). That leaves one slot per shard, so the store tracks 64 clients, not max_keys. It is a warning only, never a refusal.
  • The hint header is stripped on the empty-key path too (#199), when strip is on. Before, a client could leave a header key out and see the origin's hint. A hint sent as an HTTP trailer or on a 1xx response is still not stripped: send it as a header.
  • hint_penaltybox goes first in a route block (#201). Outside a route, Caddy sorts it after handle and after cache, so cached hits are neither counted nor box-checked. The starter Caddyfile's commented example now wraps it, cache and reverse_proxy in a route. If you uncommented the old example, wrap those lines the same way.

Recovery (#203). A state.json that is not hotserve's own regular file now stops recovery for that app, with a log line saying why. This covers a link (it is no longer followed), a dangling link, a FIFO, or a file over 64 KiB. To fix it, redeploy the app, or remove the file and restart hotserve.

New warnings (warnings only, never refusals):

  • an env_file that every account on the box can read or write, at config load and at each launch (#191);
  • a deploy_trust block wider than it reads, at config load (#189).

The deploy record names the sha256 a deploy was pinned to, and a rollback carries the pin forward (#185).

The example templates. Repositories made from a template are copies that do not update; copy the new file in to get each change.

  • scripts/deploy.sh (Node, Deno):
    • it refuses a dirty tree when VERSION defaults from git, and checks VERSION's characters before minting a token (#197);
    • it refuses a HOTSERVE_URL that is not https:// (#186).
  • bin/apply (box):
    • a push that Cloudflare could not get an answer for (520–524, 530) is asked after rather than reported red (#219);
    • a push's result is waited for up to 30 minutes (#222).

Known issues

These are tracked in #223:

  • The tutorial's steps 1 and 2 say step 2 detects a host that cannot run the sandbox. It is first detected at step 3's hotserve init. Use Debian 13: an older Debian installs and runs, then fails at step 3.
  • In the box template, a HOTSERVE_VERSION repository variable of just v installs the latest release instead of being refused.

Not in this release

Backups. v0.3.0-rc1 was cut from the backup branch, which is on hold (#179); 0.3.0 is cut from main and does not include them.

What's Changed

  • docs: the artifact byte cap is for the whole archive, not for each file in it by @earthlingdavey in #126
  • build(deps): bump go.opentelemetry.io/otel/sdk from 1.44.0 to 1.45.0 in /penaltybox by @dependabot[bot] in #127
  • build(deps): bump go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc from 1.43.0 to 1.45.0 in /penaltybox by @dependabot[bot] in #130
  • build(deps): bump go.opentelemetry.io/otel/sdk from 1.44.0 to 1.45.0 in /liveswap by @dependabot[bot] in #132
  • build(deps): bump go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp from 1.43.0 to 1.45.0 by @dependabot[bot] in #134
  • build(deps): bump github.com/darkweak/storages/otter/caddy from 0.0.19 to 0.0.20 in the go-deps group across 1 directory by @dependabot[bot] in #142
  • liveswap: an archive's symlinks cannot take extraction outside the release by @earthlingdavey in #148
  • liveswap: a failed artifact fetch does not print the hop's query string by @earthlingdavey in #149
  • liveswap: review follow-ups whose fix needed no decision (F-4, F-5, F-9, F-12, F-16 + three low) by @earthlingdavey in #150
  • build(deps): bump the go-deps group across 2 directories with 2 updates by @dependabot[bot] in #157
  • ci(vulncheck): accept unreachable GO-2026-6508 via pin-watch alert_dismissals by @earthlingdavey in #158
  • build(deps): bump caddy to v2.11.6 by @earthlingdavey in #163
  • build(deps): bump golangci/golangci-lint from v2.13.2 to v2.14.0 in the compose-images group by @dependabot[bot] in #164
  • build(deps): bump the dockerfiles group across 2 directories with 1 update by @dependabot[bot] in #156
  • ci(pin-watch): drop the GHSA-w34q and GHSA-gcjh alert dismissals by @earthlingdavey in #165
  • fix(liveswap): a failed deploy's log tail keeps the lines journald could not name a unit on (PR 7, liveswap half) by @earthlingdavey in #167
  • build(deps): bump golang.org/x/net to v0.60.0 (fixes govulncheck gate) by @earthlingdavey in #181
  • docs: introduce hotserve as a standalone product by @earthlingdavey in #180
  • build(make): package clears the stale .deb before building (#151) by @earthlingdavey in #184
  • ci(fuzz): fuzzt...
Read more

v0.3.0-rc2

v0.3.0-rc2 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 11 Oct 11:25
fb2a2cd

A release candidate for v0.3.0, cut from main at fb2a2cd, for the acceptance run of the box channel: the box's Caddyfile lives in a private repository and reaches the box only as signed commits its own binary validates (hotserve init, hotserve box …, box_webhook).

It does not include v0.3.0-rc1's backups. That candidate was cut from the backup branch, which is on hold (#179); nothing from it is in main.

Until #220 merges, the docs for this candidate are on its branch: docs/first-deploy.md. A prerelease leaves the template repositories alone, so copy examples/box from that branch, and set the box repository's Actions variable HOTSERVE_VERSION to 0.3.0-rc2. This candidate's .deb is named hotserve_0.3.0.rc2_<arch>.deb.

The behaviour changes since v0.2.1 will be written up for v0.3.0.

What's Changed

  • docs: the artifact byte cap is for the whole archive, not for each file in it by @earthlingdavey in #126
  • build(deps): bump go.opentelemetry.io/otel/sdk from 1.44.0 to 1.45.0 in /penaltybox by @dependabot[bot] in #127
  • build(deps): bump go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc from 1.43.0 to 1.45.0 in /penaltybox by @dependabot[bot] in #130
  • build(deps): bump go.opentelemetry.io/otel/sdk from 1.44.0 to 1.45.0 in /liveswap by @dependabot[bot] in #132
  • build(deps): bump go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp from 1.43.0 to 1.45.0 by @dependabot[bot] in #134
  • build(deps): bump github.com/darkweak/storages/otter/caddy from 0.0.19 to 0.0.20 in the go-deps group across 1 directory by @dependabot[bot] in #142
  • liveswap: an archive's symlinks cannot take extraction outside the release by @earthlingdavey in #148
  • liveswap: a failed artifact fetch does not print the hop's query string by @earthlingdavey in #149
  • liveswap: review follow-ups whose fix needed no decision (F-4, F-5, F-9, F-12, F-16 + three low) by @earthlingdavey in #150
  • build(deps): bump the go-deps group across 2 directories with 2 updates by @dependabot[bot] in #157
  • ci(vulncheck): accept unreachable GO-2026-6508 via pin-watch alert_dismissals by @earthlingdavey in #158
  • build(deps): bump caddy to v2.11.6 by @earthlingdavey in #163
  • build(deps): bump golangci/golangci-lint from v2.13.2 to v2.14.0 in the compose-images group by @dependabot[bot] in #164
  • build(deps): bump the dockerfiles group across 2 directories with 1 update by @dependabot[bot] in #156
  • ci(pin-watch): drop the GHSA-w34q and GHSA-gcjh alert dismissals by @earthlingdavey in #165
  • fix(liveswap): a failed deploy's log tail keeps the lines journald could not name a unit on (PR 7, liveswap half) by @earthlingdavey in #167
  • build(deps): bump golang.org/x/net to v0.60.0 (fixes govulncheck gate) by @earthlingdavey in #181
  • docs: introduce hotserve as a standalone product by @earthlingdavey in #180
  • build(make): package clears the stale .deb before building (#151) by @earthlingdavey in #184
  • ci(fuzz): fuzztime reaches go test as data, not shell (#151) by @earthlingdavey in #183
  • fix(packaging): prerm deconfigure no longer tears down the box (#151) by @earthlingdavey in #182
  • feat(liveswap): the deploy record names the sha256 a deploy was pinned to (#145) by @earthlingdavey in #185
  • fix(examples): deploy.sh refuses a non-https HOTSERVE_URL before minting (#151) by @earthlingdavey in #186
  • fix(packaging): postinstall says when the upgrade restart failed (#151) by @earthlingdavey in #187
  • docs(box): the config channel's design, and its place in the threat model (PR 0) by @earthlingdavey in #178
  • feat(liveswap): an env_file open to every account is warned about, at load and at launch (#106) by @earthlingdavey in #191
  • docs(threat-model): env_file values are unit state in the hotserve user's manager (#106) by @earthlingdavey in #192
  • feat(liveswap): config load warns about a deploy_trust block wider than it reads (#106) by @earthlingdavey in #189
  • feat(box): the proof core and the reading of the signed file (PR 1) by @earthlingdavey in #190
  • refactor(liveswap): deploy auth becomes the leaf package deploytrust, for both webhooks (box PR 2a) by @earthlingdavey in #188
  • build(deps): bump the go-deps group across 4 directories with 1 update by @dependabot[bot] in #193
  • test(sandbox): the cgroupfs probe tells read-only from absent and denied (#151) by @earthlingdavey in #195
  • fix(penaltybox): key IPv6 clients by /64 and seed the shard hash (#151) by @earthlingdavey in #194
  • fix(examples): deploy.sh refuses a dirty tree's default version and validates VERSION (#151) by @earthlingdavey in #197
  • fix(penaltybox): warn when max_keys leaves one slot per shard, and document the per-shard split and empty-key fail-open (#151) by @earthlingdavey in #196
  • ci(lint): lint and format-check integration-tagged files too by @earthlingdavey in #198
  • fix(penaltybox): strip the hint header on the empty-key path too (#151) by @earthlingdavey in #199
  • feat(box): the box option, box_webhook's GET / and hotserve box webhook (box PR 2b) by @earthlingdavey in #200
  • docs(penaltybox): the design doc describes the shipped module, counting is stated correctly, and the directive goes first in a route (#151) by @earthlingdavey in #201
  • ci: a go mod tidy -diff gate per module, grouped gomod security updates (#151) by @earthlingdavey in #202
  • fix(liveswap): state.json is written and read without following links, like the deploy records (#151) by @earthlingdavey in #203
  • feat(box): the applier core (box PR 3a) by @earthlingdavey in #205
  • feat(box): the applier's units and packaging (box PR 3b) by @earthlingdavey in #204
  • feat(box): box_webhook admits pushes and answers result polls (box PR 3c) by @earthlingdavey in #206
  • test(liveswap): the deploy-record test reads version fields, not the whole body by @earthlingdavey in #207
  • fix(box): a refusal names its commits unmasked by @earthlingdavey in #208
  • test(e2e): the box channel end to end (box PR 3d) by @earthlingdavey in #209
  • fix(box): the applier's follow-ups — left-on-disk line, own rollback words, SIGTERM by @earthlingdavey in #210
  • feat(box): hotserve init and hotserve box baseline (box PR 4) by @earthlingdavey in #216
  • test(e2e): compose names the e2e images per project by @earthlingdavey in #215
  • fix(box): the fast path asks every step that needs no signature by @earthlingdavey in #214
  • feat(box): step 6 reads the adaptation for reachability (box PR 3c-r) by @earthlingdavey in #211
  • fix(liveswap): a retired JWKS client closes its idle connections by @earthlingdavey in #217
  • feat(box): the template applies by signed push (box PR 5) by @earthlingdavey in #213
  • docs(box): "Why 202 and a poll" gives the reasons that hold by @earthlingdavey in #218
  • fix(examples): bin/apply asks after a push a CDN could not answer (Cloudflare 520-524, 530) by @earthlingdavey in #219
  • feat(box): a push is waited for 30 minutes, past root's worst case by @earthlingdavey in #222

Full Changelog: v0.2.1...v0.3.0-rc2

v0.3.0-rc1

v0.3.0-rc1 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 03 Oct 17:43
957a765

What's Changed

  • docs: the artifact byte cap is for the whole archive, not for each file in it by @earthlingdavey in #126
  • build(deps): bump go.opentelemetry.io/otel/sdk from 1.44.0 to 1.45.0 in /penaltybox by @dependabot[bot] in #127
  • build(deps): bump go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc from 1.43.0 to 1.45.0 in /penaltybox by @dependabot[bot] in #130
  • build(deps): bump go.opentelemetry.io/otel/sdk from 1.44.0 to 1.45.0 in /liveswap by @dependabot[bot] in #132
  • build(deps): bump go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp from 1.43.0 to 1.45.0 by @dependabot[bot] in #134
  • feat(liveswap): an app declares what to back up by @earthlingdavey in #138
  • feat(backups): the backup engine — plan, dump, upload, verify, clean by @earthlingdavey in #139
  • feat(backups): restore, and the restore drill by @earthlingdavey in #140
  • feat(liveswap): a backup path is 400 bytes at most by @earthlingdavey in #141
  • build(deps): bump github.com/darkweak/storages/otter/caddy from 0.0.19 to 0.0.20 in the go-deps group across 1 directory by @dependabot[bot] in #142
  • liveswap: an archive's symlinks cannot take extraction outside the release by @earthlingdavey in #148
  • liveswap: a failed artifact fetch does not print the hop's query string by @earthlingdavey in #149
  • feat(hotserve): validate and reload refuse a backup a backup run would not see by @earthlingdavey in #147
  • feat(backups): status, validate, and a listing of the repository per run by @earthlingdavey in #146
  • liveswap: review follow-ups whose fix needed no decision (F-4, F-5, F-9, F-12, F-16 + three low) by @earthlingdavey in #150
  • feat(backups): hotserve-backup setup, the account, and the credential file's move by @earthlingdavey in #152
  • feat(backups): the units, the timers, packaging, and the docs on a fresh box by @earthlingdavey in #153
  • build(deps): bump the go-deps group across 2 directories with 2 updates by @dependabot[bot] in #157
  • ci(vulncheck): accept unreachable GO-2026-6508 via pin-watch alert_dismissals by @earthlingdavey in #158
  • fix(backups): the package's lifecycle, the account's rule, and an upgrade that leaves a run alone by @earthlingdavey in #155
  • test(backups): e2e-backup from ~13 min to about 4 on CI by @earthlingdavey in #159
  • feat(backups): the repository check, the clean-run record, and D4's backstop (PR 5) by @earthlingdavey in #161
  • build(deps): bump caddy to v2.11.6 by @earthlingdavey in #163
  • build(deps): bump golangci/golangci-lint from v2.13.2 to v2.14.0 in the compose-images group by @dependabot[bot] in #164
  • build(deps): bump the dockerfiles group across 2 directories with 1 update by @dependabot[bot] in #156
  • ci(pin-watch): drop the GHSA-w34q and GHSA-gcjh alert dismissals by @earthlingdavey in #165
  • test(backups): cut upkeep — pin every third-party behaviour, one print format, the cache key (PR 6) by @earthlingdavey in #162
  • test(backups): the pin race no longer runs out of attempts under load (PR 7, backups half) by @earthlingdavey in #166
  • fix(liveswap): a failed deploy's log tail keeps the lines journald could not name a unit on (PR 7, liveswap half) by @earthlingdavey in #167
  • docs(backups): the release candidate's docs — an operator guide, a bucket no key can destroy, the checks only a real provider can answer (PR 8) by @earthlingdavey in #168

Full Changelog: v0.2.1...v0.3.0-rc1

v0.2.1

Choose a tag to compare

@github-actions github-actions released this 20 Sep 16:37
642eb94

What's Changed

  • docs: a first deploy starts from the template repository by @earthlingdavey in #116
  • liveswap, examples: a refused deploy says why — in the journal, and in the deployer's own log by @earthlingdavey in #118
  • liveswap, examples: an issuer the box cannot consult is named in the journal, whatever the budget by @earthlingdavey in #120
  • test: the systemd integration image is on the same Go image as dev, and Dependabot now watches it by @earthlingdavey in #121
  • build(deps): bump the go-deps group across 2 directories with 1 update by @dependabot[bot] in #122
  • liveswap, examples: a pull deploy can pin the artifact's sha256 (#106) by @earthlingdavey in #123
  • docs: patching the box reaches an app at its next launch, and how to give it one by @earthlingdavey in #124
  • packaging: hotserve comes back after a crash, and a refused start stays down by @earthlingdavey in #125

Full Changelog: v0.2.0...v0.2.1

v0.2.1-rc1

v0.2.1-rc1 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 16 Sep 21:17
642eb94

What's Changed

  • docs: a first deploy starts from the template repository by @earthlingdavey in #116
  • liveswap, examples: a refused deploy says why — in the journal, and in the deployer's own log by @earthlingdavey in #118
  • liveswap, examples: an issuer the box cannot consult is named in the journal, whatever the budget by @earthlingdavey in #120
  • test: the systemd integration image is on the same Go image as dev, and Dependabot now watches it by @earthlingdavey in #121
  • build(deps): bump the go-deps group across 2 directories with 1 update by @dependabot[bot] in #122
  • liveswap, examples: a pull deploy can pin the artifact's sha256 (#106) by @earthlingdavey in #123
  • docs: patching the box reaches an app at its next launch, and how to give it one by @earthlingdavey in #124
  • packaging: hotserve comes back after a crash, and a refused start stays down by @earthlingdavey in #125

Full Changelog: v0.2.0...v0.2.1-rc1

v0.2.0

Choose a tag to compare

@github-actions github-actions released this 15 Sep 21:12
1bcf6dd

What's Changed

  • release: rename ~ to . in asset filenames before checksumming by @earthlingdavey in #25
  • release: ship raw binaries as documented per-arch tar.gz by @earthlingdavey in #26
  • packaging: drop the .apk until it can ship a working service by @earthlingdavey in #27
  • liveswap: continuous watchdog — restart apps on crash or sustained health failure by @earthlingdavey in #28
  • liveswap: replace the shared deploy secret with OIDC + local-key trust by @earthlingdavey in #29
  • liveswap: push-upload & rollback deploy sources, with immutable versions by @earthlingdavey in #30
  • liveswap: match numeric OIDC identity claims by exact digits by @earthlingdavey in #31
  • liveswap: run apps as transient systemd units (systemd-only runner) by @earthlingdavey in #34
  • liveswap: non-dumpable supervisor, the shared-UID rule, and the support matrix for isolation by @earthlingdavey in #38
  • build(deps): bump the dockerfiles group across 2 directories with 1 update by @dependabot[bot] in #41
  • build(deps): bump the go-deps group across 2 directories with 2 updates by @dependabot[bot] in #43
  • build(deps): bump the compose-images group with 2 updates by @dependabot[bot] in #42
  • liveswap: decode the webhook body into its own wire type by @earthlingdavey in #39
  • build(deps): bump google.golang.org/grpc from 1.82.1 to 1.83.1 in /penaltybox by @dependabot[bot] in #44
  • liveswap: sandbox every app unit — user + PID namespaces on Debian 13 (#35 phase 1) by @earthlingdavey in #40
  • build(deps): bump google.golang.org/grpc from 1.82.1 to 1.83.1 in / and /liveswap by @earthlingdavey in #46
  • liveswap: remove the retired sandboxFilesystem tier by @earthlingdavey in #47
  • liveswap: measure the sandbox once per manager connection, not once per config load by @earthlingdavey in #48
  • liveswap: sandbox is on or off — remove auto, delete the capability invalidation by @earthlingdavey in #49
  • liveswap: fuzz the deploy-token claim matching and the webhook request parsing by @earthlingdavey in #50
  • liveswap: the sandbox is unconditional — remove sandbox off, the tier and the record by @earthlingdavey in #51
  • docs: design docs describe the present — collapse the amendment layers into history by @earthlingdavey in #53
  • build(deps): bump curlimages/curl from 8.21.0 to 8.22.0 in the compose-images group by @dependabot[bot] in #54
  • build(deps): bump the go-deps group across 2 directories with 1 update by @dependabot[bot] in #55
  • liveswap: the app contract is a unix socket, not a loopback port by @earthlingdavey in #56
  • docs: the egress residual names its issue (#57) by @earthlingdavey in #58
  • liveswap: throttle webhook auth failures per address; cap archive entries by @earthlingdavey in #59
  • ci: let the weekly soak start hotserve, and explain itself when it cannot by @earthlingdavey in #63
  • ci: soak every merge to main, not just once a week by @earthlingdavey in #64
  • liveswap: point the runner and D-Bus edges downward by @earthlingdavey in #65
  • build(deps): bump google.golang.org/grpc from 1.83.1 to 1.83.2 in /penaltybox by @dependabot[bot] in #69
  • build(deps): bump google.golang.org/grpc from 1.83.1 to 1.83.2 by @dependabot[bot] in #70
  • liveswap: test the manager re-probe measureSandbox does before measuring by @earthlingdavey in #68
  • liveswap: one view probe, shared by the three lanes that read the sandbox from inside by @earthlingdavey in #72
  • docs: point the resource-caps references at #71, not #52 by @earthlingdavey in #73
  • liveswap: move the two leaf blocks out of the files above them by @earthlingdavey in #74
  • liveswap: write the layering down, and gate gofmt by @earthlingdavey in #76
  • liveswap: put the two seams the detached sweep reads behind atomics by @earthlingdavey in #78
  • docs: cite the threat model's source by symbol, not line number (#67) by @earthlingdavey in #79
  • liveswap: report the command a unit is actually running by @earthlingdavey in #80
  • docs: fix the README's root claim, and give security its own section by @earthlingdavey in #81
  • docs: fix the module READMEs' examples and install guidance by @earthlingdavey in #82
  • docs: add an Upgrading section to the README by @earthlingdavey in #84
  • docs: fix the paved road a newcomer copies by @earthlingdavey in #85
  • liveswap: log that it started, and with how many apps by @earthlingdavey in #87
  • examples: a Deno app and a Node single executable, deployed by e2e by @earthlingdavey in #88
  • build(deps): bump the go-deps group across 2 directories with 3 updates by @dependabot[bot] in #90
  • examples/box: the box repo, a non-root administrator, and Getting started by @earthlingdavey in #89
  • liveswap: the 'app definition changed' line says next launch, not next deploy by @earthlingdavey in #91
  • docs: the paved road as the non-root administrator walks it by @earthlingdavey in #92
  • examples: private-repo deploys, and rollback from the Actions tab by @earthlingdavey in #93
  • docs: the first deploy as a numbered tutorial, and the page after it by @earthlingdavey in #95
  • docs: README points at docs/ instead of repeating it by @earthlingdavey in #96
  • examples: the READMEs walk the tutorial's order, and the sudoers file comes from the repo by @earthlingdavey in #97
  • examples/box: the administrator can create an app's env file by @earthlingdavey in #98
  • examples: deploy.sh dresses its output for the Actions job page by @earthlingdavey in #99
  • liveswap: every webhook response passes a redaction filter by @earthlingdavey in #100
  • liveswap: a failed deploy says why, from the app's side by @earthlingdavey in #101
  • liveswap: a release built for the wrong machine is refused before anything runs by @earthlingdavey in #102
  • liveswap: a deploy streams its phases to a client that asks by @earthlingdavey in #103
  • liveswap: every deploy's outcome is on record, per version by @earthlingdavey in #104
  • examples: deploy.sh accepts the single response of a box that does not stream by @earthlingdavey in #105
  • liveswap: the response filter has four rules, and #104's code is held to them by @earthlingdavey in #108
  • liveswap: the done line's comment says what order the filter leaves, and a redaction is pinned by @earthlingdavey in #110
  • liveswap: deployed_by names who deployed, not only the source that let them in by @earthlingdavey in #111
  • examples: a copy deploys with no file edited, and each release publishes the apps as templates by @earthlingdavey in #112
  • examples, packaging, docs: what the v0.2.0-rc2 walk-through on a real box found by @earthlingdavey in #114

Full Changelog: v0.1.0...v0.2.0

v0.2.0-rc3

v0.2.0-rc3 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 15 Sep 21:02
1bcf6dd

What's Changed

  • examples, packaging, docs: what the v0.2.0-rc2 walk-through on a real box found by @earthlingdavey in #114

Full Changelog: v0.2.0-rc2...v0.2.0-rc3