Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
72da934
fix(cli): point operator messages at published docs, not repo paths (…
VijitSingh97 Aug 16, 2026
594f149
Merge remote-tracking branch 'origin/develop' into fix/1024-doc-point…
VijitSingh97 Aug 16, 2026
782933e
Merge remote-tracking branch 'origin/develop' into fix/1024-doc-point…
VijitSingh97 Aug 17, 2026
314f76c
Merge pull request #1080 from p2pool-starter-stack/main
VijitSingh97 Aug 17, 2026
00c2f42
fix(cli): doctor names control units that point at another install
VijitSingh97 Aug 18, 2026
5330a96
fix(cli): keep check_stratum_exposure's comment on its own function
VijitSingh97 Aug 18, 2026
8e709ab
fix(cli): do not fail from a superseded install dir, and make the fix…
VijitSingh97 Aug 18, 2026
9deb876
Merge pull request #1097 from p2pool-starter-stack/fix/doctor-control…
VijitSingh97 Aug 18, 2026
a85354b
chore: stop hardcoding a home directory in the research analysis scripts
VijitSingh97 Aug 18, 2026
b54d78c
Merge pull request #1101 from p2pool-starter-stack/chore/scrub-home-p…
VijitSingh97 Aug 18, 2026
bc9964a
Merge remote-tracking branch 'origin/develop' into fix/1024-doc-point…
VijitSingh97 Aug 18, 2026
14e8f4a
fix(cli): the linter could not read the one place it missed — show_he…
VijitSingh97 Aug 18, 2026
485de4b
fix(cli): decide the caddy restart by comparing the rendered file, no…
VijitSingh97 Aug 18, 2026
344df8a
fix(cli): an empty pre-existing Caddyfile is a change, not an absent …
VijitSingh97 Aug 18, 2026
1211436
Merge pull request #1107 from p2pool-starter-stack/fix/1052-caddy-res…
VijitSingh97 Aug 18, 2026
07b9d64
Merge branch 'develop' into fix/1024-doc-pointers-in-operator-text
VijitSingh97 Aug 18, 2026
6ad13c3
Merge pull request #1027 from p2pool-starter-stack/fix/1024-doc-point…
VijitSingh97 Aug 18, 2026
a49f244
fix(release): make the cosign chain one contract, producer to consume…
VijitSingh97 Aug 18, 2026
a7af071
refactor(release): probe --tlog-upload once, not per subcommand
VijitSingh97 Aug 18, 2026
8dbe552
Merge pull request #1110 from p2pool-starter-stack/fix/1108-cosign-chain
VijitSingh97 Aug 18, 2026
0e84feb
chore(release): prepare v1.19.2
VijitSingh97 Aug 18, 2026
20111d6
Merge pull request #1112 from p2pool-starter-stack/release/v1.19.2-prep
VijitSingh97 Aug 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 17 additions & 14 deletions .github/workflows/release-gate.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,27 @@
name: Release gate (self-hosted)
name: Release gate (self-hosted, manual dispatch only)

# Tier-4 end-to-end validation against the REAL synced Monero + Tari nodes — the pre-release
# gate (#54). It runs on the dedicated, self-hosted release server (which holds real wallet /
# onion keys), so it MUST only ever run code we trust.
#
# DISPATCH ONLY. This workflow does not gate `main` and does not claim to (#1048). It carried a
# `push: [main]` trigger behind an `if:` that no repo variable ever set, so every merge to main
# recorded a *skipped* run — and a skipped job is green/neutral, so `main` displayed a passing
# release gate that had never once executed. The gating that is real happens elsewhere and is named
# in docs/dev/releasing.md § Which gates are automated: `scripts/release.sh` stage 2 runs `make test`
# and the #54 live matrix at cut time and aborts on failure, plus the operator-run bench e2e.
#
# To make this automatic, a self-hosted runner has to exist first: register the release server with
# the `self-hosted, pithead-release` labels (docs/dev/release-server.md), then add back
# push:
# branches: [main]
# Do not add the trigger before the runner — a queued job no runner can claim is auto-cancelled
# after 24h and shows as a permanent red ✗ on main.
#
# SECURITY: there is deliberately NO `pull_request` trigger. A fork PR's code running on this
# runner could steal the box's keys or persist a backdoor (GitHub recommends against self-hosted
# runners on public repos for exactly this reason). The gate runs only on:
# - workflow_dispatch — a maintainer manually runs it on a ref they've reviewed, OR
# - push to main — post-merge, on trusted code.
# To end-to-end a specific fork PR, review it first, then dispatch this workflow on that ref.
# See docs/dev/release-server.md.
# runners on public repos for exactly this reason). To end-to-end a specific fork PR, review it
# first, then dispatch this workflow on that ref.
on:
workflow_dispatch:
inputs:
Expand All @@ -24,8 +35,6 @@ on:
default: "check"
type: choice
options: [check, matrix]
push:
branches: [main]

# Never run two gates against the one shared box at the same time.
concurrency:
Expand All @@ -39,12 +48,6 @@ permissions:
jobs:
release-gate:
name: Tier-4 live matrix (real nodes)
# Only run automatically once a self-hosted runner is actually wired up — set the repo variable
# ENABLE_RELEASE_GATE=true when one is registered. Without this guard, every push to main queues
# a job no runner can claim, and GitHub auto-cancels it after a 24h timeout — a permanent red ✗
# on main. A skipped job is green/neutral instead. A manual workflow_dispatch ALWAYS runs, so a
# maintainer can still validate a reviewed ref on a registered runner on demand.
if: ${{ github.event_name == 'workflow_dispatch' || vars.ENABLE_RELEASE_GATE == 'true' }}
# Register the server with these labels: `pithead-release` scopes the gate to the dedicated
# box; prefer an ephemeral / just-in-time runner in its own runner group.
runs-on: [self-hosted, pithead-release]
Expand Down
57 changes: 57 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,63 @@ Pithead ships as **one product, one version** — the version lives in the top-l
[`VERSION`](VERSION) file and every released image is tagged with it. Releases are cut
per the process in [`docs/dev/releasing.md`](docs/dev/releasing.md).

## [1.19.2] - 2026-08-18

### Fixed

- **A release can no longer ship unsigned when no install would accept it
([#1108](https://github.com/p2pool-starter-stack/pithead/issues/1108),
[#960](https://github.com/p2pool-starter-stack/pithead/issues/960)).** The release pipeline
treated signing as optional while every install treats it as mandatory: once `cosign.pub` is
committed it ships in every bundle, and the one-click upgrade refuses any release with no
`pithead.tar.gz.sig`. A cut made on a box without the signing key therefore published a bundle
the whole fleet rejects — and release assets are immutable, so the signature could never be
attached afterwards. That is why v1.18.0 had to be withdrawn. The cut now aborts instead, naming
what is missing; `--unsigned` publishes one deliberately. `COSIGN_PASSWORD` is checked too, which
it never was — cosign would otherwise have prompted for it after the images were promoted.
- **A release rehearsal now reports the decision the real cut will make.** The signing check sat
inside the dry-run guard, so `--dry-run` always printed "Release signing OFF" whatever the box was
configured to do. The one check that exists to protect a cut could only ever report failure.
- **The pinned signature verifier is proven before a release is published
([#1084](https://github.com/p2pool-starter-stack/pithead/issues/1084)).** Every install verifies
its images and its upgrade bundle by running one digest-pinned cosign container, and nothing
validated that pin. A bad digest would have shipped a stack that cannot start, reported to
operators as *signature verification failed* — tampering, rather than an image we could not fetch.
The cut now signs a probe blob, verifies it through that container, and requires a tampered blob
to be refused. The same round trip catches a `cosign.pub` that no longer matches the release key.
- **`pithead doctor` answers whether this box can take an upgrade, before one is attempted.** It
reports whether the pinned verifier image is present and how to pre-fetch it, and says plainly
that a source checkout cannot take the dashboard's one-click upgrade at all.
- **Caddy restarts whenever its rendered configuration changes
([#1052](https://github.com/p2pool-starter-stack/pithead/issues/1052)).** The restart was decided
from a list of config keys, so any setting missing from that list re-rendered the Caddyfile and
left the running Caddy on the old one. The decision is now a comparison of the rendered file.
- **`doctor` reports a control channel pointing at another install
([#1097](https://github.com/p2pool-starter-stack/pithead/issues/1097)).** A failed upgrade could
leave the systemd control units aimed at a directory the stack no longer runs from, which silently
disabled every dashboard action, and nothing reported it.
- **Operator messages point at published documentation rather than repository paths
([#1024](https://github.com/p2pool-starter-stack/pithead/issues/1024)).** Release bundles ship
lean, so the `docs/dev/...` paths several refusals named did not exist on an operator's machine.

### Changed

- The self-hosted release gate no longer claims to gate `main`
([#1048](https://github.com/p2pool-starter-stack/pithead/issues/1048)). It recorded a skipped —
and therefore green — run on every merge of a job that had never once executed, because no runner
is registered. It is manual-dispatch only now, and `docs/dev/releasing.md` names in one table
which gates are automated and which are run by hand.
- Release signing is documented as what it is: mandatory to publish, because it is mandatory to
consume. The docs had described it as opt-in since before installs began failing closed.

### Upgrade note

Installs still on **v1.18.1 or v1.19.0** verify releases with a host `cosign` binary rather than the
container introduced in v1.19.1, and an upgrade runs the code the box is already on — so those
installs refuse the upgrade until cosign is installed once. Use the pinned **v2.6.3**, not the
newest: cosign v3 satisfies the check and then fails the verification. See
[Releasing › Upgrading an install older than v1.19.1](docs/dev/releasing.md#upgrading-an-install-older-than-v1191).

## [1.19.1] - 2026-08-17

### Fixed
Expand Down
4 changes: 3 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ runs `ruff` (plus a few hygiene hooks) on your changed files. If you change depe
- **lint** — every file surface gets a linter/formatter check (`make lint` runs them all; run one
with `make lint-<surface>`): `lint-sh` (shellcheck + shfmt), `lint-py` (ruff), `lint-js` (Biome),
`lint-yaml` (yamllint), `lint-md` (markdownlint), `lint-docs-voice` (banned-word check),
`lint-operator-strings` (no issue/PR numbers in operator-facing `pithead`/dashboard text),
`lint-operator-strings` (no issue/PR numbers in operator-facing `pithead`/dashboard text, and
no bare `docs/` paths in `pithead` operator text — release bundles ship no `docs/`, so point at
`$DOCS_URL/docs/<file>.md#anchor` instead; comments keep the plain path),
`lint-proto` (buf), `lint-toml` (taplo). The
non-Python tools run via `npx`/`uvx`/`docker`, so a contributor needs **Node, uv, and Docker**
on PATH (plus `shfmt`); `pre-commit` runs the same checks on changed files. Link-checking
Expand Down
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ lint-md: ## markdownlint over all Markdown (config: .markdownlint-cli2.jsonc)
lint-docs-voice: ## Fail if banned marketing words appear in prose docs (house voice: docs/dev/STYLE.md)
bash scripts/lint-docs-voice.sh

lint-operator-strings: ## Fail if a #NNN issue/PR number leaks into pithead or dashboard operator-facing text (#755)
lint-operator-strings: ## Fail if a #NNN issue/PR number or a bare docs/ path leaks into pithead or dashboard operator-facing text (#755, #1024)
bash scripts/lint-operator-strings.sh --self-test
bash scripts/lint-operator-strings.sh

lint-proto: ## buf lint + build on the vendored Tari protos (config: .../tari/proto/buf.yaml)
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.19.1
1.19.2
2 changes: 1 addition & 1 deletion build/dashboard/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ name = "mining-dashboard"
# Keep in lockstep with the top-level VERSION file — the single source of truth for the stack version
# (#44). A shell test (tests/stack/run.sh) fails if these drift; the dashboard *displays* the version
# from VERSION (baked in as PITHEAD_VERSION, #58), so this is packaging metadata only.
version = "1.19.1"
version = "1.19.2"
description = "Monitoring dashboard and XvB switching engine for Pithead"
readme = "README.md"
requires-python = ">=3.11"
Expand Down
2 changes: 1 addition & 1 deletion build/dashboard/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions docs/dashboard.md
Original file line number Diff line number Diff line change
Expand Up @@ -981,6 +981,15 @@ CLI upgrade. The result names the restore point ([#637](https://github.com/p2poo
on the versioned layout, the previous `pithead-vX.Y.Z` dir; in place, the pre-upgrade
`config.json`/`.env` copies.

**If the button does nothing at all — no result, no error, no modal — the control units are
pointing at a different directory than the dashboard writes to.** The dashboard drops each request
into its own install's spool and a systemd path unit runs the host-side runner when a file lands
there. The unit names an absolute path and is shared box-wide, so an upgrade that aborted partway
can leave it watching a tree that is no longer the install. Nothing reports the mismatch: requests
queue up unread, and the config editor and the upgrade button both sit there. `./pithead doctor`
names it under **Dashboard control channel**, printing the directory the units point at next to the
one you ran it from, and `./pithead apply` from the install directory repoints them.

## Tips

- **First visit certificate warning.** With `dashboard.secure: true` (the default), Caddy uses a
Expand Down
17 changes: 14 additions & 3 deletions docs/dev/release-server.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,22 @@ The pipeline signs every promoted image digest and the install bundle with cosig
([#376](https://github.com/p2pool-starter-stack/pithead/issues/376), key-based — see
[Releasing › Signed releases](releasing.md#signed-releases) for what installs verify and how).
The private key lives only on this box, like the GHCR token; the public key is committed in the
repo as `cosign.pub`. The release preflight refuses to run without cosign, `COSIGN_KEY`, and
`cosign.pub` all in place.
repo as `cosign.pub`. The release preflight refuses to run without cosign, `COSIGN_KEY`,
`COSIGN_PASSWORD`, and `cosign.pub` all in place — signing is mandatory to publish because it is
mandatory to consume ([#960](https://github.com/p2pool-starter-stack/pithead/issues/960)), so a
missing piece aborts the cut rather than warning past it. `--dry-run` reaches the same decision and
skips only the signing, so a rehearsal on this box tells you whether the real cut would sign.

Preflight then proves the key end to end: it signs a probe blob and verifies it through the same
digest-pinned cosign container installs use. A `cosign.pub` that is no longer the public half of
`COSIGN_KEY` fails here rather than in the field
([#1084](https://github.com/p2pool-starter-stack/pithead/issues/1084)).

Install cosign (pinned — there is no Ubuntu apt package; the same snippet works on any host that
wants to verify):
wants to verify). Take **v2.6.3**, not the newest: cosign v3 removed the `--tlog-upload` flag both
signing calls pass, and a drifted box would otherwise pass every other check and die at the signing
stage with the images already promoted. Preflight probes for the flag and aborts early if it is
gone.

```bash
curl -fsSL -o /tmp/cosign https://github.com/sigstore/cosign/releases/download/v2.6.3/cosign-linux-amd64
Expand Down
Loading