#!/bin/bash # Kamado Pool StartOS entrypoint. # # Reads /root/.kamado/start9/config.yaml via yq, resolves the chosen # bitcoind variant (mainnet vs testnet4), exports the env vars the # upstream ckpool entrypoint expects, renders ckpool.conf from the # template shipped alongside this script, and then supervises # ckpool-solo + kamado-api as a pair. set -euo pipefail CONFIG_FILE="/root/.kamado/start9/config.yaml" if [[ ! -f "${CONFIG_FILE}" ]]; then echo "kamado-entrypoint: config file missing: ${CONFIG_FILE}" >&2 exit 1 fi q() { yq -r "$1" "${CONFIG_FILE}"; } BITCOIND_VARIANT=$(q '.bitcoind.type') case "${BITCOIND_VARIANT}" in bitcoind) export BITCOIN_RPC_HOST="bitcoind.embassy" export BITCOIN_RPC_PORT=8332 # Satoshi's genesis block coinbase address. Used only for # ckpool's startup coinbase-builder self-test; never credited # a satoshi since solo mode pays the worker's stratum address. SELFTEST_ADDRESS="1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa" ;; bitcoind-testnet) export BITCOIN_RPC_HOST="bitcoind-testnet.embassy" export BITCOIN_RPC_PORT=48332 # Testnet genesis coinbase address — valid P2PKH on testnet4. SELFTEST_ADDRESS="mipcBbFg9gMiCh81Kj8tqqdgoZub1ZJRfn" ;; *) echo "kamado-entrypoint: unknown bitcoind variant: ${BITCOIND_VARIANT}" >&2 exit 1 ;; esac export BITCOIN_RPC_USER=$(q '.bitcoind.user') export BITCOIN_RPC_PASSWORD=$(q '.bitcoind.password') export STRATUM_PORT=$(q '.stratum-port // 3333') export POOL_BTCSIG=$(q '.advanced.pool-identifier // "/Kamado/"') export STARTDIFF=$(q '.advanced.startdiff // 16384') export MINDIFF=$(q '.advanced.mindiff // 1000') export MAXDIFF=$(q '.advanced.maxdiff // 0') export DROPIDLE=$(q '.advanced.dropidle // 0') LOG_LEVEL=$(q '.advanced.log-level // "info"') ZMQ_ENABLED=$(q '.advanced.zmq-enabled // true') TLS_ENABLED=$(q '.tls.enabled // "disabled"') TLS_PORT=$(q '.tls.port // 3334') # CKPool-solo uses the worker's stratum username as the payout # address and refuses to authenticate workers whose username is not # a valid address on the active network. The conf `btcaddress` is # only consulted once at startup, for ckpool's coinbase-builder # self-test: it builds and validates a sample coinbase transaction # against bitcoind before accepting any workers. Since no worker has # connected yet at that point, we hand it the genesis block coinbase # address for the active network — it's always valid, and solo mode # never credits it a satoshi. export POOL_BTCADDRESS="${SELFTEST_ADDRESS}" # LOGDIR lives on the ckpool data volume so ckpool's own state files # (users/, workers/, pool/pool.status, daily log files) survive restart. # SOCKET_DIR is ephemeral — sockets are re-created on each start. export LOGDIR=/root/.ckpool/logs export SOCKET_DIR=/run/ckpool export BITCOIN_NOTIFY=true # ckpool itself doesn't use ZMQ in this build — zmqblock is stripped # from the rendered conf below. kamado-api subscribes separately. export ZMQ_BLOCK="" export BLOCKPOLL_MS=100 export UPDATE_INTERVAL_S=30 mkdir -p "${LOGDIR}" "${SOCKET_DIR}" /etc/ckpool # Render ckpool.conf using the same sed approach as the upstream # KamadoPool ckpool entrypoint — the template is bundled into the # image at build time. TEMPLATE=/etc/ckpool/ckpool.conf.template CONF=/etc/ckpool/ckpool.conf 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}" sed -i '/"zmqblock":/d' "${CONF}" echo "kamado-entrypoint: starting ckpool (solo, ${BITCOIND_VARIANT}) on port ${STRATUM_PORT}" /usr/local/bin/ckpool --btcsolo --config "${CONF}" --sockdir "${SOCKET_DIR}" --log-shares & CKPOOL_PID=$! # DB_PATH must live on the persisted main volume; the default # /var/lib/kamado/kamado.db is ephemeral container storage. KAMADO_DATA_DIR=/root/.kamado/data mkdir -p "${KAMADO_DATA_DIR}" export LISTEN_ADDR=":8080" export CKPOOL_SOCKDIR="${SOCKET_DIR}" export CKPOOL_LOGFILE="${LOGDIR}/ckpool.log" export DB_PATH="${KAMADO_DATA_DIR}/kamado.db" export BITCOIN_RPC_URL="http://${BITCOIN_RPC_HOST}:${BITCOIN_RPC_PORT}" export POLL_INTERVAL=5s export KAMADO_LOG_LEVEL="${LOG_LEVEL}" if [[ "${ZMQ_ENABLED}" == "true" ]]; then export BITCOIN_ZMQ_BLOCK="tcp://${BITCOIN_RPC_HOST}:28332" else export BITCOIN_ZMQ_BLOCK="" fi echo "kamado-entrypoint: starting kamado-api" /usr/local/bin/kamado-api & API_PID=$! # Optional TLS stratum via stunnel sidecar. STUNNEL_PID="" if [[ "${TLS_ENABLED}" == "enabled" ]]; then TLS_DIR=/root/.kamado/tls CERT="${TLS_DIR}/stratum.pem" mkdir -p "${TLS_DIR}" if [[ ! -f "${CERT}" ]]; then echo "kamado-entrypoint: generating self-signed stratum TLS cert" openssl req -x509 -newkey rsa:2048 -sha256 -nodes \ -keyout "${TLS_DIR}/stratum.key" \ -out "${TLS_DIR}/stratum.crt" \ -days 3650 \ -subj "/CN=kamado-pool-stratum" >/dev/null 2>&1 cat "${TLS_DIR}/stratum.key" "${TLS_DIR}/stratum.crt" > "${CERT}" chmod 600 "${TLS_DIR}/stratum.key" "${CERT}" fi FINGERPRINT=$(openssl x509 -in "${TLS_DIR}/stratum.crt" -noout -fingerprint -sha256 | cut -d= -f2) printf '%s\n' "${FINGERPRINT}" > "${TLS_DIR}/fingerprint.txt" echo "kamado-entrypoint: stratum TLS SHA256 fingerprint: ${FINGERPRINT}" STUNNEL_CONF=/etc/stunnel/stratum.conf mkdir -p /etc/stunnel cat > "${STUNNEL_CONF}" < :${STRATUM_PORT}" /usr/bin/stunnel4 "${STUNNEL_CONF}" & STUNNEL_PID=$! fi term() { echo "kamado-entrypoint: SIGTERM — shutting down" kill -TERM "${API_PID}" "${CKPOOL_PID}" ${STUNNEL_PID:-} 2>/dev/null || true wait "${API_PID}" "${CKPOOL_PID}" ${STUNNEL_PID:-} 2>/dev/null || true exit 0 } trap term TERM INT # shellcheck disable=SC2086 wait -n ${CKPOOL_PID} ${API_PID} ${STUNNEL_PID:-} EXIT_CODE=$? echo "kamado-entrypoint: a supervised process exited (${EXIT_CODE}), stopping the rest" kill -TERM "${API_PID}" "${CKPOOL_PID}" ${STUNNEL_PID:-} 2>/dev/null || true wait || true exit "${EXIT_CODE}"