diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 8be9519..1c313f9 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: @@ -42,3 +43,34 @@ 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: + fail-fast: false + matrix: + image: + - ghcr.io/gridlabs-science/boot-protocol@sha256:1a302f3d1a1de3df7b26a4cde6372bf31d453bd024ca697bd6f0a1a09c90b601 + - ghcr.io/gridlabs-science/gridpool-sv2-pool@sha256:3de54e9c51953d2b5089ae90a25b9a0fa046c18c4ae0139b768b77a4cf0ed50e + 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..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:sha-bba078e + image: ghcr.io/gridlabs-science/gridpool-sv2-pool@sha256:3de54e9c51953d2b5089ae90a25b9a0fa046c18c4ae0139b768b77a4cf0ed50e 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:3de54e9c51953d2b5089ae90a25b9a0fa046c18c4ae0139b768b77a4cf0ed50e 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..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.4" +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: >- @@ -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. + Keeps native SV2 idle while attached Bitcoin work is unsafe and adds + digest-pinned appliance release checks. 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"