commit d48035b366af2971a42901231b4305283c25aa64 Author: satoshi Date: Sun Apr 12 19:41:13 2026 +0300 Phase 1: CKPool build infrastructure Forks CKPool-solo at upstream commit cfb0f83b (v1.0) via a multi-stage Docker build, with an env-driven entrypoint that renders ckpool.conf from a template. The pinned commit already includes every fix referenced in Bassin issue #29 (workbase_id double increment, extended low-power timeouts, configurable dropidle, vardiff burst handling). No patches are applied yet — the patches/ directory holds the workflow and build wiring so Kamado-specific patches can be added incrementally. Build is portable across aarch64/x86_64: yasm is intentionally omitted so CKPool falls back to its C SHA256, and CFLAGS override drops upstream's default -march=native. Runtime image ships ckpool and ckpmsg for socket debugging; share logging (-L) is enabled by default. Co-Authored-By: Claude Opus 4.6 diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..ae6c5f9 --- /dev/null +++ b/.env.example @@ -0,0 +1,6 @@ +# Copy to .env and fill in for `docker compose up` +POOL_BTCADDRESS=tb1qexamplereplacewithyourownaddressforrewards +BITCOIN_RPC_USER=kamado +BITCOIN_RPC_PASSWORD=changeme +# Optional — ZMQ endpoint for instant block notifications from bitcoind +# ZMQ_BLOCK=tcp://host.docker.internal:28332 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5f4ed8d --- /dev/null +++ b/.gitignore @@ -0,0 +1,41 @@ +# Build artifacts +build/ +dist/ +*.o +*.a +*.so +*.tar +*.tar.gz + +# Docker +docker-images/ + +# CKPool source (cloned at build time) +ckpool/src/ + +# Go +api/bin/ +api/vendor/ +*.exe + +# Node / Svelte +ui/node_modules/ +ui/.svelte-kit/ +ui/build/ + +# Runtime / data +data/ +logs/ +*.log +*.pid + +# Editor +.vscode/ +.idea/ +*.swp +*.swo +.DS_Store + +# Environment +.env +.env.local diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e13c2cf --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +Kamado Pool +Copyright (C) 2026 Kamado Pool contributors + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +--- + +This project is built on top of CKPool by Con Kolivas, which is itself +licensed under GPL-3.0. See https://bitbucket.org/ckolivas/ckpool for +the upstream source. The full GPL-3.0 text is available at: +https://www.gnu.org/licenses/gpl-3.0.txt diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b9e3e60 --- /dev/null +++ b/Makefile @@ -0,0 +1,32 @@ +# ---------------------------------------------------------------------------- +# Kamado Pool — top-level Makefile +# ---------------------------------------------------------------------------- +.PHONY: help ckpool ckpool-shell up down logs clean + +help: + @echo "Kamado Pool targets:" + @echo " make ckpool Build the ckpool-solo docker image" + @echo " make ckpool-shell Open a shell in the built ckpool image" + @echo " make up docker compose up -d" + @echo " make down docker compose down" + @echo " make logs Tail ckpool logs" + @echo " make clean Remove build artifacts and data" + +ckpool: + docker build -t kamado/ckpool:dev ./ckpool + +ckpool-shell: ckpool + docker run --rm -it --entrypoint /bin/sh kamado/ckpool:dev + +up: + docker compose up -d --build + +down: + docker compose down + +logs: + docker compose logs -f ckpool + +clean: + docker compose down -v || true + rm -rf data/ build/ dist/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..75adb27 --- /dev/null +++ b/README.md @@ -0,0 +1,56 @@ +# Kamado Pool + +A modern, feature-complete solo Bitcoin mining pool built on a patched fork of CKPool, with a real-time web dashboard and a Go-based API middleware that surfaces everything CKPool knows. + +## Why Kamado? + +Existing CKPool-based solutions (like Bassin for Umbrel) read only a handful of periodic stats files and miss most of CKPool's rich data. Kamado talks directly to CKPool's Unix socket API to expose: + +- Real-time per-client data: hashrate, difficulty, user agent, hardware detection +- Full block-found history with height, hash, reward, and solving worker +- Per-worker and per-client best share tracking (current + all-time) +- Network difficulty, pool efficiency, expected time to block +- Live dashboard updates via WebSocket (no 60-second file polls) + +## Architecture + +``` +┌────────────────────────────────────────────────────┐ +│ ckpool-solo (C) ──Unix socket──► kamado-api (Go) │ +│ ports: 3333 ports: 80 │ +│ │ +│ ▲ stratum ▲ HTTP/WS │ +│ │ │ │ +│ Miners Browser │ +│ │ +│ bitcoind ◄──── RPC + ZMQ ────── ckpool + kamado-api│ +└────────────────────────────────────────────────────┘ +``` + +Three main components: + +| Component | Language | Purpose | +| -------------- | -------- | -------------------------------------------------- | +| `ckpool/` | C | Stratum server, share validation, block submission | +| `api/` | Go | Socket client, REST/WebSocket API, persistence | +| `ui/` | Svelte | Real-time dashboard | + +## Phases + +- [x] **Phase 1** — Fork & fix CKPool, build infrastructure +- [ ] **Phase 2** — Go API middleware (socket client, REST, WebSocket, SQLite) +- [ ] **Phase 3** — Svelte UI dashboard +- [ ] **Phase 4** — Monorepo Docker build, full stack integration +- [ ] **Phase 5** — Testing (regtest, testnet4), polish + +StartOS packaging lives in a separate repository. + +## Upstream + +CKPool by Con Kolivas: https://bitbucket.org/ckolivas/ckpool + +Pinned commit: see [ckpool/CKPOOL_COMMIT](ckpool/CKPOOL_COMMIT) + +## License + +GPL-3.0. CKPool itself is distributed under GPL-3. diff --git a/ckpool/CKPOOL_COMMIT b/ckpool/CKPOOL_COMMIT new file mode 100644 index 0000000..d2d7e88 --- /dev/null +++ b/ckpool/CKPOOL_COMMIT @@ -0,0 +1 @@ +cfb0f83b70d7b382b85d2bd0710cf4cb2dda4007 diff --git a/ckpool/CKPOOL_REPO b/ckpool/CKPOOL_REPO new file mode 100644 index 0000000..32fc8aa --- /dev/null +++ b/ckpool/CKPOOL_REPO @@ -0,0 +1 @@ +https://bitbucket.org/ckolivas/ckpool.git diff --git a/ckpool/Dockerfile b/ckpool/Dockerfile new file mode 100644 index 0000000..435f4e2 --- /dev/null +++ b/ckpool/Dockerfile @@ -0,0 +1,82 @@ +# ============================================================================ +# CKPool-solo build stage +# ============================================================================ +# Builds a patched ckpool binary from upstream source at a pinned commit. +# Binaries land in /build/ckpool/src/ — we copy `ckpool` and `ckpmsg` into +# the runtime image. We do NOT `make install` because upstream's +# install-exec-hook calls setcap which fails inside a docker build. +# +# Portability: +# - No yasm installed -> ckpool falls back to its portable C SHA256 +# implementation. This trades some per-core hashrate validation +# throughput for a binary that runs on any x86_64/aarch64 CPU +# regardless of the build machine's feature flags. +# - We override CFLAGS to drop the upstream default `-march=native` +# for the same reason. +# ---------------------------------------------------------------------------- +FROM debian:bookworm-slim AS ckpool-build + +ARG CKPOOL_REPO=https://bitbucket.org/ckolivas/ckpool.git +ARG CKPOOL_COMMIT=cfb0f83b70d7b382b85d2bd0710cf4cb2dda4007 + +RUN apt-get update && apt-get install --no-install-recommends -y \ + build-essential \ + autoconf \ + automake \ + libtool \ + pkg-config \ + libzmq3-dev \ + ca-certificates \ + git \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /build + +RUN git clone "${CKPOOL_REPO}" ckpool \ + && cd ckpool \ + && git checkout "${CKPOOL_COMMIT}" + +# Apply Kamado patches in alphabetical order, if any exist +COPY patches/ /tmp/kamado-patches/ +RUN set -eux; \ + cd /build/ckpool; \ + for p in /tmp/kamado-patches/*.patch; do \ + [ -f "$p" ] || continue; \ + echo "Applying $(basename "$p")"; \ + git apply --verbose "$p"; \ + done + +# Configure and build. Explicit CFLAGS override upstream's default +# `-O2 -Wall -march=native`. We keep -O2 -Wall but drop -march=native. +RUN cd /build/ckpool \ + && ./autogen.sh \ + && CFLAGS="-O2 -Wall -pipe" ./configure --prefix=/usr/local \ + && make -j"$(nproc)" + +# Sanity check +RUN test -x /build/ckpool/src/ckpool \ + && test -x /build/ckpool/src/ckpmsg \ + && file /build/ckpool/src/ckpool || true + +# ============================================================================ +# Runtime stage (minimal) +# ============================================================================ +FROM debian:bookworm-slim AS ckpool-runtime + +RUN apt-get update && apt-get install --no-install-recommends -y \ + libzmq5 \ + ca-certificates \ + tini \ + && rm -rf /var/lib/apt/lists/* + +COPY --from=ckpool-build /build/ckpool/src/ckpool /usr/local/bin/ckpool +COPY --from=ckpool-build /build/ckpool/src/ckpmsg /usr/local/bin/ckpmsg +COPY config/ckpool.conf.template /etc/ckpool/ckpool.conf.template +COPY entrypoint.sh /usr/local/bin/ckpool-entrypoint.sh +RUN chmod +x /usr/local/bin/ckpool-entrypoint.sh + +RUN mkdir -p /run/ckpool /var/lib/ckpool /var/log/ckpool + +EXPOSE 3333 + +ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/ckpool-entrypoint.sh"] diff --git a/ckpool/config/ckpool.conf.template b/ckpool/config/ckpool.conf.template new file mode 100644 index 0000000..f9d0334 --- /dev/null +++ b/ckpool/config/ckpool.conf.template @@ -0,0 +1,23 @@ +{ + "btcd": [ + { + "url": "${BITCOIN_RPC_HOST}:${BITCOIN_RPC_PORT}", + "auth": "${BITCOIN_RPC_USER}", + "pass": "${BITCOIN_RPC_PASSWORD}", + "notify": ${BITCOIN_NOTIFY} + } + ], + "btcaddress": "${POOL_BTCADDRESS}", + "btcsig": "${POOL_BTCSIG}", + "blockpoll": ${BLOCKPOLL_MS}, + "update_interval": ${UPDATE_INTERVAL_S}, + "serverurl": [ + "0.0.0.0:${STRATUM_PORT}" + ], + "mindiff": ${MINDIFF}, + "startdiff": ${STARTDIFF}, + "maxdiff": ${MAXDIFF}, + "dropidle": ${DROPIDLE}, + "zmqblock": "${ZMQ_BLOCK}", + "logdir": "${LOGDIR}" +} diff --git a/ckpool/entrypoint.sh b/ckpool/entrypoint.sh new file mode 100644 index 0000000..f7ee992 --- /dev/null +++ b/ckpool/entrypoint.sh @@ -0,0 +1,96 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# ckpool-solo entrypoint for Kamado Pool +# +# Reads configuration from environment variables, renders the ckpool.conf +# from the template, and execs ckpool-solo. +# +# Required env: +# POOL_BTCADDRESS - Bitcoin address that receives block rewards +# BITCOIN_RPC_HOST - e.g. bitcoind, 127.0.0.1 +# BITCOIN_RPC_PORT - e.g. 8332 (mainnet) / 48332 (testnet4) +# BITCOIN_RPC_USER - RPC username +# BITCOIN_RPC_PASSWORD - RPC password +# +# Optional env (with defaults): +# STRATUM_PORT=3333 +# POOL_BTCSIG="/Kamado Pool/" +# BLOCKPOLL_MS=100 +# UPDATE_INTERVAL_S=30 +# MINDIFF=1 +# STARTDIFF=42 +# MAXDIFF=0 (0 = unlimited) +# DROPIDLE=0 (seconds; 0 = never drop idle clients) +# BITCOIN_NOTIFY=true (use bitcoind blocknotify/walletnotify) +# ZMQ_BLOCK="" (e.g. tcp://bitcoind:28332) +# LOGDIR=/var/log/ckpool +# SOCKET_DIR=/run/ckpool +# SHARE_LOG=1 (1 = enable -L share logging) +# ---------------------------------------------------------------------------- +set -eu + +: "${POOL_BTCADDRESS:?POOL_BTCADDRESS is required}" +: "${BITCOIN_RPC_HOST:?BITCOIN_RPC_HOST is required}" +: "${BITCOIN_RPC_PORT:?BITCOIN_RPC_PORT is required}" +: "${BITCOIN_RPC_USER:?BITCOIN_RPC_USER is required}" +: "${BITCOIN_RPC_PASSWORD:?BITCOIN_RPC_PASSWORD is required}" + +STRATUM_PORT="${STRATUM_PORT:-3333}" +POOL_BTCSIG="${POOL_BTCSIG:-/Kamado Pool/}" +BLOCKPOLL_MS="${BLOCKPOLL_MS:-100}" +UPDATE_INTERVAL_S="${UPDATE_INTERVAL_S:-30}" +MINDIFF="${MINDIFF:-1}" +STARTDIFF="${STARTDIFF:-42}" +MAXDIFF="${MAXDIFF:-0}" +DROPIDLE="${DROPIDLE:-0}" +BITCOIN_NOTIFY="${BITCOIN_NOTIFY:-true}" +ZMQ_BLOCK="${ZMQ_BLOCK:-}" +LOGDIR="${LOGDIR:-/var/log/ckpool}" +SOCKET_DIR="${SOCKET_DIR:-/run/ckpool}" +SHARE_LOG="${SHARE_LOG:-1}" + +mkdir -p "$LOGDIR" "$SOCKET_DIR" + +TEMPLATE=/etc/ckpool/ckpool.conf.template +CONF=/etc/ckpool/ckpool.conf + +# Simple placeholder substitution (no envsubst dependency). +# We intentionally don't use eval/printf tricks — sed is safe and predictable. +sed \ + -e "s|\${BITCOIN_RPC_HOST}|${BITCOIN_RPC_HOST}|g" \ + -e "s|\${BITCOIN_RPC_PORT}|${BITCOIN_RPC_PORT}|g" \ + -e "s|\${BITCOIN_RPC_USER}|${BITCOIN_RPC_USER}|g" \ + -e "s|\${BITCOIN_RPC_PASSWORD}|${BITCOIN_RPC_PASSWORD}|g" \ + -e "s|\${BITCOIN_NOTIFY}|${BITCOIN_NOTIFY}|g" \ + -e "s|\${POOL_BTCADDRESS}|${POOL_BTCADDRESS}|g" \ + -e "s|\${POOL_BTCSIG}|${POOL_BTCSIG}|g" \ + -e "s|\${BLOCKPOLL_MS}|${BLOCKPOLL_MS}|g" \ + -e "s|\${UPDATE_INTERVAL_S}|${UPDATE_INTERVAL_S}|g" \ + -e "s|\${STRATUM_PORT}|${STRATUM_PORT}|g" \ + -e "s|\${MINDIFF}|${MINDIFF}|g" \ + -e "s|\${STARTDIFF}|${STARTDIFF}|g" \ + -e "s|\${MAXDIFF}|${MAXDIFF}|g" \ + -e "s|\${DROPIDLE}|${DROPIDLE}|g" \ + -e "s|\${LOGDIR}|${LOGDIR}|g" \ + -e "s|\${ZMQ_BLOCK}|${ZMQ_BLOCK}|g" \ + "$TEMPLATE" > "$CONF" + +# If ZMQ is not configured, strip the zmqblock line entirely. +# The template keeps zmqblock as a non-final key with a trailing comma, +# so removing the whole line leaves valid JSON. +if [ -z "$ZMQ_BLOCK" ]; then + sed -i '/"zmqblock":/d' "$CONF" +fi + +echo "[kamado] rendered ckpool.conf:" +sed "s|\"pass\": \".*\"|\"pass\": \"\"|" "$CONF" +echo + +# Build command line +CMD="/usr/local/bin/ckpool --btcsolo --config $CONF --sockdir $SOCKET_DIR" +if [ "$SHARE_LOG" = "1" ]; then + CMD="$CMD --log-shares" +fi + +echo "[kamado] exec: $CMD" +exec $CMD diff --git a/ckpool/patches/README.md b/ckpool/patches/README.md new file mode 100644 index 0000000..c737c72 --- /dev/null +++ b/ckpool/patches/README.md @@ -0,0 +1,58 @@ +# CKPool Patches + +Patches applied on top of the upstream CKPool commit pinned in `../CKPOOL_COMMIT`. + +Patches are applied in alphabetical order by filename. Use a numeric prefix to enforce ordering: + +- `0001-short-description.patch` +- `0002-another-fix.patch` + +## Current state + +**No patches are applied.** The pinned upstream commit (`cfb0f83b`, tagged +as version 1.0) already includes every fix that Bassin issue #29 asked to +backport, plus several improvements: + +| Upstream commit | What it fixes | +| --------------- | -------------------------------------------------------------- | +| `a439cf96` | workbase_id double increment bug | +| `590fb2a2` | Extended timeouts for low-powered miners (NerdMiner, ESP32) | +| `b13f3eee` | Configurable `dropidle` timeout (exposed via our env var) | +| `66db3aa3` | Better vardiff for bursty hashers | +| `130c755d` | Fix unlikely fopen segfault | +| `0bd3d751` | Consistent error field in mining.submit rejections | +| `988b2687` | Version 1.0 — longstanding stability bump | + +Kamado therefore starts from a CKPool that is strictly ahead of what Bassin +ships today. + +## When to add a patch + +Use this directory for: + +1. Fixes needed before they land upstream (and only after attempting + to submit upstream first). +2. Kamado-specific behavior changes that would not be accepted upstream — + e.g. tighter integration hooks with `kamado-api`. +3. Temporary workarounds with a clear removal plan, documented in the + patch commit message. + +Do NOT use this directory for: + +- Pure configuration changes (expose via the entrypoint env vars instead). +- Build system tweaks (put those in the Dockerfile). + +## Creating a patch + +From a clean clone of upstream at the pinned commit: + +```sh +git clone https://bitbucket.org/ckolivas/ckpool.git +cd ckpool +git checkout $(cat /path/to/KamadoPool/ckpool/CKPOOL_COMMIT) +# ... make your changes ... +git diff > /path/to/KamadoPool/ckpool/patches/0001-my-fix.patch +``` + +The Dockerfile applies each `*.patch` file in this directory with +`git apply --verbose` during the build. diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..f3fbe53 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,51 @@ +# ---------------------------------------------------------------------------- +# Kamado Pool — development compose +# +# Spins up ckpool-solo pointing at a bitcoind. Expects bitcoind to be +# reachable at the host/port set in the `env` block below. For local +# testing, run bitcoind on regtest/testnet4 and set these accordingly. +# +# The ckpool socket dir is bind-mounted to ./data/run so the (future) +# kamado-api can talk to it from outside the container during development. +# ---------------------------------------------------------------------------- +services: + ckpool: + build: + context: ./ckpool + dockerfile: Dockerfile + container_name: kamado-ckpool + restart: unless-stopped + ports: + - "3333:3333" + volumes: + - ./data/run:/run/ckpool + - ./data/logs:/var/log/ckpool + environment: + # --- Bitcoin Core connection --- + BITCOIN_RPC_HOST: "host.docker.internal" + BITCOIN_RPC_PORT: "48332" # testnet4 default + BITCOIN_RPC_USER: "${BITCOIN_RPC_USER:-kamado}" + BITCOIN_RPC_PASSWORD: "${BITCOIN_RPC_PASSWORD:-kamado}" + BITCOIN_NOTIFY: "true" + # Optional ZMQ for instant block notifications (recommended) + ZMQ_BLOCK: "${ZMQ_BLOCK:-}" # e.g. tcp://host.docker.internal:28332 + + # --- Pool settings --- + POOL_BTCADDRESS: "${POOL_BTCADDRESS:?set POOL_BTCADDRESS in .env}" + POOL_BTCSIG: "/Kamado Pool dev/" + STRATUM_PORT: "3333" + + # --- Difficulty --- + MINDIFF: "1" + STARTDIFF: "1" + MAXDIFF: "0" + # Drop clients idle longer than N seconds (0 = never drop) + DROPIDLE: "0" + + # --- Logging --- + SHARE_LOG: "1" + LOGDIR: "/var/log/ckpool" + SOCKET_DIR: "/run/ckpool" + extra_hosts: + # Allow containers to reach bitcoind running on the host + - "host.docker.internal:host-gateway"