# Knit Spool v0.2.0 — knit-spool 0.2.0 - Product: Knit Spool (https://whatsnew.fyi/product/knit-spool) - Vendor: Knit Spool - Date: 2026-09-04 - Version: v0.2.0 - Original notes: https://github.com/getknit/knit-spool/releases/tag/v0.2.0 - Permalink: https://whatsnew.fyi/product/knit-spool/releases/v0.2.0 What's New is an index, not a publisher: every entry below links to the vendor's own release notes, which are the authoritative source. Entries are labelled where they are hand-curated sample data, pre-releases, or drawn from a secondary source such as a developer blog. Reuse: the summaries, labels and curation here are © What's New. Quote freely with attribution and a link back; wholesale republication of the corpus is not permitted — terms: https://whatsnew.fyi/terms. The vendors' own release notes remain their publishers'. --- - **added** — SIGHUP reloads the configuration from SPOOL_RELOAD_FILE without restarting or dropping client connections - **added** — SPOOL_TOKEN_NEXT accepts a second credential for gradual credential rotation - **added** — Drain mode toggled by SIGUSR1 refuses new connections with 503 while serving live ones - **added** — knit-spool check validates the environment and prints resolved values without binding a port or opening a store - **added** — Effective configuration is logged at boot as one k=v line carrying every resolved value - **added** — GET /source serves the build stamp including running version, commit, and corresponding-source URL - **added** — SPOOL_SOURCE_URL allows forks to point the source offer at their repository instead of upstream - **added** — SPOOL_METRICS_TOKEN provides a separate scrape credential distinct from the connect credential - **added** — knit_spool_build_info metric exposes version and commit as labelled gauge - **added** — Commons scope per spool relayed but unreadable by clients - **changed** — Wire protocol is additive only with no records or error codes removed or changed for backward compatibility - **changed** — Drain mode reuses the same transport refusal mechanism as SPOOL_MAX_CONNS > The operator release. A spool can now be reloaded, drained, credential-rotated and > validated without dropping a connection or guessing, and it will tell you which version of > itself is running. It also gains a commons — one optional shared scope per spool, relayed > but unreadable. Pre-1.0, so every interface below is still subject to change; the wire is > additive only, with no records or error codes removed or changed, so a 0.1.0 client talks to > a 0.2.0 spool unchanged. ###### Added - **`SIGHUP` reloads the configuration** — quotas, rate limits and credentials change without restarting, so no client loses its connection to a settings change. `SPOOL_RELOAD_FILE` names a `KEY=value` file layered over the environment; the environment itself cannot be the source, because `System.getenv()` is fixed at exec and a container's variables cannot change without recreating it. What a reload can and cannot move, and when each takes effect, is in the README. Naming an unreloadable value is logged and ignored rather than fatal, and an unreadable or invalid file leaves the running configuration untouched. - **`SPOOL_TOKEN_NEXT`, a second accepted credential** — rotation becomes add-new, migrate, promote, retire instead of a cutover that locks out every client until it updates. Both tokens are accepted for as long as both are set; anything that is neither is still refused. Compared in constant time with no early exit, so the timing does not say which credential was presented. - **Drain mode, toggled by `SIGUSR1`** — new connections refused `503` with a `Retry-After` while the live ones keep being served. There was nowhere to stand between "serving" and "stopped": shutdown closes every session at once, so on a busy spool an upgrade sent every client back on the same second. Now you drain, watch the connection count fall, and then stop. `SIGUSR1` rather than a second `SIGTERM`, which is what `docker stop` sends before it `SIGKILL`s — a two-phase TERM would drain and then be killed mid-drain by the ordinary stop path. It reuses the same transport refusal `SPOOL_MAX_CONNS` already uses, and for the same reason: §7.1 has no close code that means "come back later". `/healthz` deliberately keeps answering `200` — the container HEALTHCHECK, both CI pipelines and compose's `service_healthy` gate all probe it, and a drain that failed it would restart the container mid-drain. Counted by `knit_spool_drain_refused_total`, kept apart from `knit_spool_conns_refused_total` so a planned drain never reads as a box out of room, and shown as `draining=yes` on the status line. - **`knit-spool check`**, which validates the environment, prints every resolved value, and exits `0` valid or `1` invalid — without binding a port, opening a store, or creating a directory. Confirming a configuration previously meant starting a daemon and reading its logs, which is a poor fit for provisioning that wants to know a config is good *before* a container starts, and for testing a tier template in CI. Resolved config on stdout, warnings and errors on stderr, so one can be parsed without filtering the other; `SPOOL_DATA_DIR` is checked for a writable parent and deliberately not created. It checks configuration, not store state — a commons that will not fit because the store already holds `SPOOL_MAX_SCOPES` scopes still fails at boot. - **The effective configuration is logged at boot**, one `k=v` line carrying every resolved value — defaults included, because the value an operator misremembers is always the one they never set, and across a fleet that is a support call rather than a shrug. `configFromEnv` validated all of it and then kept the answer to itself; the only startup line reported port, PoW bits, and whether a token was set. Tokens are reported as `set`/`unset` and never printed, and the commons appears as a truncated id for the same reason ` _[Truncated at 4000 characters — full notes: https://github.com/getknit/knit-spool/releases/tag/v0.2.0]_