From a961ec85d53348eb3ce2ab08c405c31852d00f4a Mon Sep 17 00:00:00 2001 From: keegreil Date: Wed, 19 Aug 2026 00:11:58 -0400 Subject: [PATCH 1/5] release: harden Umbrel appliance lifecycle --- .github/workflows/check.yml | 30 +++++++++++++++ .github/workflows/release-candidate.yml | 38 +++++++++++++++++++ README.md | 26 +++++++++++++ gridlabs-gridpool/docker-compose.yml | 6 +-- .../boot_portal_config.json.template | 2 +- gridlabs-gridpool/umbrel-app.yml | 7 ++-- scripts/verify-package.sh | 30 +++++++++++++++ 7 files changed, 131 insertions(+), 8 deletions(-) create mode 100644 .github/workflows/release-candidate.yml create mode 100755 scripts/verify-package.sh diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 8be9519..d708584 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -42,3 +42,33 @@ jobs: ! grep -Fq '${GRIDPOOL_PAYOUT_ADDRESS:-}' gridlabs-gridpool/templates/init.sh grep -q 'boot_portal_config.local.json' gridlabs-gridpool/templates/gridpool-entrypoint.sh grep -q 'boot_portal_config.local.json' gridlabs-gridpool/templates/sv2-entrypoint.sh + - name: Verify release inputs and package contract + run: ./scripts/verify-package.sh + + secrets: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Scan repository history for secrets + run: >- + docker run --rm -v "$PWD:/repo" + ghcr.io/gitleaks/gitleaks@sha256:c00b6bd0aeb3071cbcb79009cb16a60dd9e0a7c60e2be9ab65d25e6bc8abbb7f + detect --source=/repo --redact --no-banner + + image-scan: + runs-on: ubuntu-latest + strategy: + matrix: + image: + - ghcr.io/gridlabs-science/boot-protocol@sha256:1a302f3d1a1de3df7b26a4cde6372bf31d453bd024ca697bd6f0a1a09c90b601 + - ghcr.io/gridlabs-science/gridpool-sv2-pool@sha256:3ab32ca79583fd81acaef5f7aae27ba8175d0fde2f6a80d2968d92349f91af4e + steps: + - uses: aquasecurity/trivy-action@v0.36.0 + with: + image-ref: ${{ matrix.image }} + format: table + severity: CRITICAL,HIGH + ignore-unfixed: true + exit-code: '1' diff --git a/.github/workflows/release-candidate.yml b/.github/workflows/release-candidate.yml new file mode 100644 index 0000000..89cecd5 --- /dev/null +++ b/.github/workflows/release-candidate.yml @@ -0,0 +1,38 @@ +name: Package release candidate + +on: + workflow_dispatch: + push: + tags: ['v*-beta.*'] + +permissions: + contents: read + id-token: write + attestations: write + +jobs: + package: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - run: ./scripts/verify-package.sh + - name: Create source package + run: | + tar -czf gridpool-umbrel.tar.gz gridlabs-gridpool + sha256sum gridpool-umbrel.tar.gz > gridpool-umbrel.tar.gz.sha256 + - name: Generate SPDX SBOM + uses: anchore/sbom-action@v0.24.0 + with: + path: gridlabs-gridpool + format: spdx-json + output-file: gridpool-umbrel.spdx.json + - uses: actions/attest-build-provenance@v2 + with: + subject-path: gridpool-umbrel.tar.gz + - uses: actions/upload-artifact@v4 + with: + name: gridpool-umbrel + path: | + gridpool-umbrel.tar.gz + gridpool-umbrel.tar.gz.sha256 + gridpool-umbrel.spdx.json diff --git a/README.md b/README.md index a7fe41f..924296a 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,9 @@ This is a sideload beta. The application UI remains behind Umbrel authentication, the node participates outbound-only by default, and only the SV2 miner port plus the UDP peer-relay port are published. +The wrapper pins immutable, attested multi-architecture OCI image digests. It +does not follow a mutable branch or `latest` image. + ## Configure and install Clone and install the package, then open GridPool from the Umbrel dashboard. On @@ -47,8 +50,31 @@ token, SV2 authority keys, and the durable proof spool. Back it up before uninstalling or moving the app. Bitcoin chain data is owned by the separate Bitcoin app and is not duplicated. +Before upgrading or uninstalling, stop GridPool and back up the complete data +directory with ownership and permissions preserved. Record the node-ID +fingerprint shown by the GridPool UI. Never copy Bitcoin RPC credentials from +the generated config into a support bundle. + +Upgrade by replacing the app definition and recreating the app containers while +leaving `gridlabs-gridpool/data` in place. After startup, verify that the node ID, +payout address, Bitcoin authority, and SV2 authority are unchanged. + +For recovery, reinstall the same or a compatible package version, stop it, +restore the saved data directory, then start the app. Deleting `pool_state.json` +is not a supported recovery procedure. A normal Umbrel uninstall may remove app +data, so retain a verified external backup first. + +The legacy dashboard is disabled in the appliance package. Native SV2 is the +only supported miner-facing service; DATUM and raw SV1 remain absent. + ## Current limitations - Core IPC is intentionally not mounted across the app boundary. Standard `getblocktemplate`/`submitblock` RPC is used for both Core and Knots. - DATUM and Stratum V1 adapters are not included in the initial appliance beta. + +## Release verification + +```bash +./scripts/verify-package.sh +``` diff --git a/gridlabs-gridpool/docker-compose.yml b/gridlabs-gridpool/docker-compose.yml index 7dc1d01..33ceafa 100644 --- a/gridlabs-gridpool/docker-compose.yml +++ b/gridlabs-gridpool/docker-compose.yml @@ -5,7 +5,7 @@ services: APP_PORT: 5000 init: - image: ghcr.io/gridlabs-science/gridpool-sv2-pool:sha-bba078e + image: ghcr.io/gridlabs-science/gridpool-sv2-pool@sha256:3ab32ca79583fd81acaef5f7aae27ba8175d0fde2f6a80d2968d92349f91af4e user: "0:0" entrypoint: ["/bin/sh", "/templates/init.sh"] environment: @@ -20,7 +20,7 @@ services: - ${APP_DATA_DIR}/templates:/templates:ro gridpool: - image: ghcr.io/gridlabs-science/boot-protocol:sha-9ac862a + image: ghcr.io/gridlabs-science/boot-protocol@sha256:1a302f3d1a1de3df7b26a4cde6372bf31d453bd024ca697bd6f0a1a09c90b601 depends_on: init: condition: service_completed_successfully @@ -43,7 +43,7 @@ services: retries: 4 sv2: - image: ghcr.io/gridlabs-science/gridpool-sv2-pool:sha-bba078e + image: ghcr.io/gridlabs-science/gridpool-sv2-pool@sha256:3ab32ca79583fd81acaef5f7aae27ba8175d0fde2f6a80d2968d92349f91af4e depends_on: init: condition: service_completed_successfully diff --git a/gridlabs-gridpool/templates/boot_portal_config.json.template b/gridlabs-gridpool/templates/boot_portal_config.json.template index 0a457c8..572bb37 100644 --- a/gridlabs-gridpool/templates/boot_portal_config.json.template +++ b/gridlabs-gridpool/templates/boot_portal_config.json.template @@ -9,7 +9,7 @@ "bitcoin_zmq_rawblock_endpoint": "${BITCOIN_ZMQ_RAWBLOCK}", "bitcoin_network": "mainnet", "enable_web_ui": true, - "enable_legacy_ui": true, + "enable_legacy_ui": false, "boot_network_id": "mainnet-beta", "boot_protocol_version": 22, "v22_activation_block_height": 959500, diff --git a/gridlabs-gridpool/umbrel-app.yml b/gridlabs-gridpool/umbrel-app.yml index 5a8e36e..b36d48e 100644 --- a/gridlabs-gridpool/umbrel-app.yml +++ b/gridlabs-gridpool/umbrel-app.yml @@ -3,7 +3,7 @@ id: gridlabs-gridpool implements: [] category: bitcoin name: GridPool -version: "0.1.0-beta.4" +version: "0.1.0-beta.5" icon: https://raw.githubusercontent.com/gridlabs-science/gridpool-umbrel/main/gridlabs-gridpool/icon.svg tagline: Sovereign, non-custodial pooled mining description: >- @@ -21,7 +21,6 @@ path: "" defaultUsername: "" defaultPassword: "" releaseNotes: >- - Makes first-run payout setup entirely UI-driven. GridPool restarts itself - after setup and native SV2 starts from the persisted address without custom - Umbrel environment variables. + Pins immutable multi-architecture images, disables the legacy dashboard, and + documents tested backup, upgrade, and recovery procedures. gallery: [] diff --git a/scripts/verify-package.sh b/scripts/verify-package.sh new file mode 100755 index 0000000..2034765 --- /dev/null +++ b/scripts/verify-package.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +set -euo pipefail + +compose="gridlabs-gridpool/docker-compose.yml" +template="gridlabs-gridpool/templates/boot_portal_config.json.template" + +for script in gridlabs-gridpool/templates/*.sh; do sh -n "$script"; done +ruby -e 'require "yaml"; YAML.load_file(ARGV[0])' "$compose" +ruby -e 'require "yaml"; YAML.load_file(ARGV[0])' gridlabs-gridpool/umbrel-app.yml + +references="$(grep -oE 'ghcr\.io/[^ @]+@sha256:[0-9a-f]{64}' "$compose" | sort -u)" +[[ "$(printf '%s\n' "$references" | sed '/^$/d' | wc -l)" -eq 2 ]] +! grep -Eq 'image: .*:sha-' "$compose" + +grep -q '"enable_legacy_ui": false' "$template" +grep -q '"enable_admin_api": false' "$template" +! grep -q 'env_file' "$compose" +! grep -Eq '(^|[[:space:]])(8332|28332|28333|34290|5000):' "$compose" +grep -q '34265:34265/tcp' "$compose" +grep -q '5001:5001/udp' "$compose" + +for reference in $references; do + if command -v docker >/dev/null 2>&1; then + inspection="$(docker buildx imagetools inspect "$reference")" + grep -q 'linux/amd64' <<<"$inspection" + grep -q 'linux/arm64' <<<"$inspection" + fi +done + +echo "Umbrel package contract and immutable release inputs verified" From a3838504d548315e24e2a1fa62b637dd3c5920ea Mon Sep 17 00:00:00 2001 From: keegreil Date: Wed, 19 Aug 2026 00:46:58 -0400 Subject: [PATCH 2/5] release: pin fail-safe native SV2 image --- .github/workflows/check.yml | 2 +- gridlabs-gridpool/docker-compose.yml | 4 ++-- gridlabs-gridpool/umbrel-app.yml | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index d708584..fc9c60c 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -63,7 +63,7 @@ jobs: matrix: image: - ghcr.io/gridlabs-science/boot-protocol@sha256:1a302f3d1a1de3df7b26a4cde6372bf31d453bd024ca697bd6f0a1a09c90b601 - - ghcr.io/gridlabs-science/gridpool-sv2-pool@sha256:3ab32ca79583fd81acaef5f7aae27ba8175d0fde2f6a80d2968d92349f91af4e + - ghcr.io/gridlabs-science/gridpool-sv2-pool@sha256:6bea980aa517f308ee176d1a85d5e274e69a92fe99df1b321a7a720ae5db3e70 steps: - uses: aquasecurity/trivy-action@v0.36.0 with: diff --git a/gridlabs-gridpool/docker-compose.yml b/gridlabs-gridpool/docker-compose.yml index 33ceafa..d855b3d 100644 --- a/gridlabs-gridpool/docker-compose.yml +++ b/gridlabs-gridpool/docker-compose.yml @@ -5,7 +5,7 @@ services: APP_PORT: 5000 init: - image: ghcr.io/gridlabs-science/gridpool-sv2-pool@sha256:3ab32ca79583fd81acaef5f7aae27ba8175d0fde2f6a80d2968d92349f91af4e + image: ghcr.io/gridlabs-science/gridpool-sv2-pool@sha256:6bea980aa517f308ee176d1a85d5e274e69a92fe99df1b321a7a720ae5db3e70 user: "0:0" entrypoint: ["/bin/sh", "/templates/init.sh"] environment: @@ -43,7 +43,7 @@ services: retries: 4 sv2: - image: ghcr.io/gridlabs-science/gridpool-sv2-pool@sha256:3ab32ca79583fd81acaef5f7aae27ba8175d0fde2f6a80d2968d92349f91af4e + image: ghcr.io/gridlabs-science/gridpool-sv2-pool@sha256:6bea980aa517f308ee176d1a85d5e274e69a92fe99df1b321a7a720ae5db3e70 depends_on: init: condition: service_completed_successfully diff --git a/gridlabs-gridpool/umbrel-app.yml b/gridlabs-gridpool/umbrel-app.yml index b36d48e..2c7da62 100644 --- a/gridlabs-gridpool/umbrel-app.yml +++ b/gridlabs-gridpool/umbrel-app.yml @@ -3,7 +3,7 @@ id: gridlabs-gridpool implements: [] category: bitcoin name: GridPool -version: "0.1.0-beta.5" +version: "0.1.0-beta.6" icon: https://raw.githubusercontent.com/gridlabs-science/gridpool-umbrel/main/gridlabs-gridpool/icon.svg tagline: Sovereign, non-custodial pooled mining description: >- @@ -21,6 +21,6 @@ path: "" defaultUsername: "" defaultPassword: "" releaseNotes: >- - Pins immutable multi-architecture images, disables the legacy dashboard, and - documents tested backup, upgrade, and recovery procedures. + Keeps native SV2 idle while attached Bitcoin work is unsafe and adds + digest-pinned appliance release checks. gallery: [] From 24fa3ea40ba00bc2898b7b3c182730c21f40c2d2 Mon Sep 17 00:00:00 2001 From: keegreil Date: Thu, 20 Aug 2026 21:30:04 -0400 Subject: [PATCH 3/5] ci: preserve all appliance image scan results --- .github/workflows/check.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index fc9c60c..ee49d6c 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -60,6 +60,7 @@ jobs: image-scan: runs-on: ubuntu-latest strategy: + fail-fast: false matrix: image: - ghcr.io/gridlabs-science/boot-protocol@sha256:1a302f3d1a1de3df7b26a4cde6372bf31d453bd024ca697bd6f0a1a09c90b601 From a368a9284627f484347f1de659e1007e683a4f0d Mon Sep 17 00:00:00 2001 From: keegreil Date: Thu, 20 Aug 2026 21:35:13 -0400 Subject: [PATCH 4/5] ci: allow candidate verification on demand --- .github/workflows/check.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index ee49d6c..034aaaa 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -1,6 +1,7 @@ name: Check on: + workflow_dispatch: push: branches: [main] pull_request: From 4e8afdeee6bb2e664199e9cf8dad91ebac9710a5 Mon Sep 17 00:00:00 2001 From: keegreil Date: Thu, 20 Aug 2026 21:46:23 -0400 Subject: [PATCH 5/5] release: pin security-updated native SV2 image --- .github/workflows/check.yml | 2 +- gridlabs-gridpool/docker-compose.yml | 4 ++-- gridlabs-gridpool/umbrel-app.yml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 034aaaa..1c313f9 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -65,7 +65,7 @@ jobs: matrix: image: - ghcr.io/gridlabs-science/boot-protocol@sha256:1a302f3d1a1de3df7b26a4cde6372bf31d453bd024ca697bd6f0a1a09c90b601 - - ghcr.io/gridlabs-science/gridpool-sv2-pool@sha256:6bea980aa517f308ee176d1a85d5e274e69a92fe99df1b321a7a720ae5db3e70 + - ghcr.io/gridlabs-science/gridpool-sv2-pool@sha256:3de54e9c51953d2b5089ae90a25b9a0fa046c18c4ae0139b768b77a4cf0ed50e steps: - uses: aquasecurity/trivy-action@v0.36.0 with: diff --git a/gridlabs-gridpool/docker-compose.yml b/gridlabs-gridpool/docker-compose.yml index d855b3d..f5fe050 100644 --- a/gridlabs-gridpool/docker-compose.yml +++ b/gridlabs-gridpool/docker-compose.yml @@ -5,7 +5,7 @@ services: APP_PORT: 5000 init: - image: ghcr.io/gridlabs-science/gridpool-sv2-pool@sha256:6bea980aa517f308ee176d1a85d5e274e69a92fe99df1b321a7a720ae5db3e70 + image: ghcr.io/gridlabs-science/gridpool-sv2-pool@sha256:3de54e9c51953d2b5089ae90a25b9a0fa046c18c4ae0139b768b77a4cf0ed50e user: "0:0" entrypoint: ["/bin/sh", "/templates/init.sh"] environment: @@ -43,7 +43,7 @@ services: retries: 4 sv2: - image: ghcr.io/gridlabs-science/gridpool-sv2-pool@sha256:6bea980aa517f308ee176d1a85d5e274e69a92fe99df1b321a7a720ae5db3e70 + image: ghcr.io/gridlabs-science/gridpool-sv2-pool@sha256:3de54e9c51953d2b5089ae90a25b9a0fa046c18c4ae0139b768b77a4cf0ed50e depends_on: init: condition: service_completed_successfully diff --git a/gridlabs-gridpool/umbrel-app.yml b/gridlabs-gridpool/umbrel-app.yml index 2c7da62..cacd637 100644 --- a/gridlabs-gridpool/umbrel-app.yml +++ b/gridlabs-gridpool/umbrel-app.yml @@ -3,7 +3,7 @@ id: gridlabs-gridpool implements: [] category: bitcoin name: GridPool -version: "0.1.0-beta.6" +version: "0.1.0-beta.7" icon: https://raw.githubusercontent.com/gridlabs-science/gridpool-umbrel/main/gridlabs-gridpool/icon.svg tagline: Sovereign, non-custodial pooled mining description: >-