add low priority mining

This commit is contained in:
Brad Stein 2025-08-13 01:00:20 -05:00
parent cbb477ad45
commit 75825c9794
16 changed files with 667 additions and 330 deletions

View File

@ -1,16 +1,18 @@
# infrastructure/kustomization-harbor.yaml
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: harbor
name: xmr-miner
namespace: flux-system
spec:
interval: 10m
path: ./services/harbor
targetNamespace: harbor
path: ./services/crypto/xmr-miner
targetNamespace: crypto
prune: true
sourceRef:
kind: GitRepository
name: flux-system
namespace: flux-system
wait: true
dependsOn:
- name: crypto
- name: monerod
wait: true

8
scripts/gitea_recovery.fish Executable file → Normal file
View File

@ -1,14 +1,11 @@
#!/usr/bin/env fish
# run from your workstation
set jump titan-db # alias from ~/.ssh/config (192.168.22.10:2277)
# grab host list from titan-dbs ~/.ssh/config
set jump titan-db
set nodes (ssh $jump 'grep -E "^Host titan-" ~/.ssh/config | awk "{print \$2}"')
for n in $nodes
echo "=== $n ==="
# list volumes
ssh $jump "ssh $n 'ls /dev/longhorn'" 2>/dev/null
# look for app.ini or repos inside each volume
@ -19,4 +16,3 @@ for n in $nodes
(ls /mnt/git/repositories 2>/dev/null || true); sudo umount /mnt'" 2>/dev/null
end
end

View File

@ -55,6 +55,18 @@ function _sc_list --description "list storageclasses as menu"
' 2>/dev/null
end
# convert XMR -> piconero (atomic units) with Python (exact
function _xmr_to_atomic -a AMT
_need python3; or return 1
printf '%s' 'from decimal import Decimal, getcontext
import sys
getcontext().prec = 50
amt = Decimal(sys.argv[1])
atomic = int((amt * Decimal("1000000000000")).to_integral_value(rounding="ROUND_HALF_UP"))
print(atomic)
' | python3 - "$AMT"
end
# Choose SC (menu -> stderr; selection -> stdout)
function _choose_sc --description "interactive StorageClass picker"
set lines (_sc_list)
@ -141,8 +153,6 @@ function _rpc_call --description "call wallet JSON-RPC via localhost pf" -a RPCU
else
set payload (printf '{"jsonrpc":"2.0","id":"0","method":"%s","params":%s}' $METHOD "$PARAMS")
end
# Use --digest so we satisfy the server's challenge
# Note: callers should check for an empty response (curl --fail returns nothing).
curl -s --fail --digest -u "$RPCUSER:$RPCPASS" \
-H "Content-Type: application/json" \
-d "$payload" "$url"
@ -156,65 +166,97 @@ function _probe_monerod -a ADDR
return 1
end
# Loud early block on suspicious names
if string match -qi '*qubic*' -- $ADDR
echo (set_color red)"Refusing daemon '$ADDR' (contains 'qubic'). This is NOT a Monero node."(set_color normal) >&2
return 1
end
# Try /get_info (HTTP) first (most public nodes support this)
set raw (curl -s --max-time 7 "http://$ADDR/get_info" 2>/dev/null)
set ok 0
if test -n "$raw"
set status (echo $raw | jq -r '.status // "OK"')
set testnet (echo $raw | jq -r '.testnet // false')
set stagenet (echo $raw | jq -r '.stagenet // false')
set height (echo $raw | jq -r '.height // .target_height // 0')
if test "$status" = "OK"
set ok 1
end
end
# Fallback to JSON-RPC get_info (some nodes only expose /json_rpc)
if test $ok -eq 0
set raw (curl -s --max-time 7 -H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":"0","method":"get_info"}' "http://$ADDR/json_rpc" \
| jq -r '.result? | @json' 2>/dev/null)
# Try up to 3 short attempts
for t in (seq 1 3)
set ok 0
set raw (curl -s --max-time 7 "http://$ADDR/get_info" 2>/dev/null)
if test -n "$raw"
set status (echo $raw | jq -r '.status // "OK"')
set testnet (echo $raw | jq -r '.testnet // false')
set mstatus (echo $raw | jq -r '.status // "OK"')
set testnet (echo $raw | jq -r '.testnet // false')
set stagenet (echo $raw | jq -r '.stagenet // false')
set height (echo $raw | jq -r '.height // .target_height // 0')
if test "$status" = "OK"
set height (echo $raw | jq -r '.height // .target_height // 0')
if test "$mstatus" = "OK"
set ok 1
end
end
if test $ok -eq 0
set raw (curl -s --max-time 7 -H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":"0","method":"get_info"}' "http://$ADDR/json_rpc" \
| jq -r '.result? | @json' 2>/dev/null)
if test -n "$raw"
set mstatus (echo $raw | jq -r '.status // "OK"')
set testnet (echo $raw | jq -r '.testnet // false')
set stagenet (echo $raw | jq -r '.stagenet // false')
set height (echo $raw | jq -r '.height // .target_height // 0')
if test "$mstatus" = "OK"
set ok 1
end
end
end
if test $ok -eq 1
if test "$testnet" = "true" -o "$stagenet" = "true"
echo (set_color red)"Refusing non-mainnet daemon (testnet=$testnet stagenet=$stagenet) at $ADDR."(set_color normal) >&2
return 1
end
echo "Daemon OK: Monero mainnet, height=$height"
return 0
end
sleep 1
end
if test $ok -eq 0
echo (set_color red)"Could not speak Monero /get_info to $ADDR. Not a monerod (or blocked). Aborting."(set_color normal) >&2
echo "Tip: run your own monerod and use its ClusterIP:Port, or set WALLETSVC_SKIP_DAEMON_CHECK=1 to bypass." >&2
echo (set_color red)"Could not speak Monero /get_info to $ADDR. Not a monerod (or blocked)."(set_color normal) >&2
echo "Tip: run your own monerod or set WALLETSVC_SKIP_DAEMON_CHECK=1 to bypass." >&2
return 1
end
# Wait for wallet RPC to accept connections (digest auth get_version)
function walletsvc_wait_ready -a NS SVC SECS
if test -z "$NS"; set NS crypto; end
if test -z "$SVC"; set SVC wallet-rpc; end
if test -z "$SECS"; set SECS 60; end
set RPCUSER (kubectl -n $NS get secret {$SVC}-rpc-auth -o jsonpath='{.data.username}' | base64 -d 2>/dev/null)
set RPCPASS (kubectl -n $NS get secret {$SVC}-rpc-auth -o jsonpath='{.data.password}' | base64 -d 2>/dev/null)
if test -z "$RPCUSER" -o -z "$RPCPASS"
echo (set_color red)"Error:"(set_color normal)" missing RPC creds in secret {$SVC}-rpc-auth" >&2
return 1
end
if test "$testnet" = "true" -o "$stagenet" = "true"
echo (set_color red)"Refusing non-mainnet daemon (testnet=$testnet stagenet=$stagenet) at $ADDR."(set_color normal) >&2
return 1
_pf_start $NS $SVC 18083 18083
set tries (math "ceil($SECS/2)")
for i in (seq 1 $tries)
set code (curl -s -o /dev/null -w "%{http_code}" --digest -u "$RPCUSER:$RPCPASS" \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":"0","method":"get_version"}' \
http://127.0.0.1:18083/json_rpc)
if test "$code" = "200"
_pf_stop
return 0
end
sleep 2
end
echo "Daemon OK: Monero mainnet, height=$height"
_pf_stop
echo (set_color red)"RPC not ready after $SECS seconds."(set_color normal) >&2
return 1
end
### ------- main workflow ---------------------------------------------------
function walletsvc_bootstrap --description "Interactive setup of monero-wallet-rpc (multi-wallet) with PVC + secrets"
function walletsvc_bootstrap --description "Interactive setup of monero-wallet-rpc with PVC+secrets; creates default wallet and prints recovery info"
_need kubectl jq curl awk; or return 1
_banner "Monero wallet RPC bootstrap"
# --- Namespace
read -P "Namespace [monero]: " ns_raw
if test -z "$ns_raw"; set ns_raw monero; end
read -P "Namespace [crypto]: " ns_raw
if test -z "$ns_raw"; set ns_raw crypto; end
set ns (_k8s_name $ns_raw)
_require "Namespace" $ns; or return 1
@ -231,28 +273,64 @@ function walletsvc_bootstrap --description "Interactive setup of monero-wallet-r
echo "TIP: Prefer a class with reclaimPolicy=Retain so a PVC delete won't delete the PV."
set sc (_choose_sc); or return 1
# --- Names + sizes
read -P "PVC name (for wallet data) [wallet-data]: " pvc_name_raw
if test -z "$pvc_name_raw"; set pvc_name_raw wallet-data; end
set pvc_name (_k8s_name $pvc_name_raw)
_require "PVC name" $pvc_name; or return 1
read -P "PVC size [5Gi]: " pvc_size
if test -z "$pvc_size"; set pvc_size 5Gi; end
_require "PVC size" $pvc_size; or return 1
read -P "App label / Deployment name [wallet-rpc]: " app_raw
if test -z "$app_raw"; set app_raw wallet-rpc; end
set app_name (_k8s_name $app_raw)
_require "App/Deployment name" $app_name; or return 1
read -P "Service name (blank = same as app): " svc_raw
if test -z "$svc_raw"
set svc_name $app_name
else
set svc_name (_k8s_name $svc_raw)
# --- Unified name (prefix wallet-monero-)
read -P "Wallet name (no spaces, e.g. 'brad', base will be wallet-monero-brad): " wallet_raw
_require "Wallet name" $wallet_raw; or return 1
if string match -rq '\s' -- $wallet_raw
echo (set_color red)"Error:"(set_color normal)" Wallet name cannot contain spaces." >&2
return 1
end
set base (_k8s_name $wallet_raw)
set base (string replace -r '^wallet-monero-' '' -- $base)
set name "wallet-monero-$base"
# Bind all k8s object names to the unified base
set pvc_name $name
set app_name $name
set svc_name $name
set rpc_secret_name "$app_name-rpc-auth"
set wpass_secret_name "$app_name-wallet-pass"
# --- Detect existing resources (all of them)
set exists_any 0
set found_list
for r in "deploy/$app_name" "svc/$svc_name" "netpol/{$app_name}-ingress"
if kubectl -n $ns get $r >/dev/null 2>&1
set -a found_list $r
set exists_any 1
end
end
if kubectl -n $ns get pvc $pvc_name >/dev/null 2>&1
set -a found_list "pvc/$pvc_name"
set exists_any 1
end
if kubectl -n $ns get secret $rpc_secret_name >/dev/null 2>&1
set -a found_list "secret/$rpc_secret_name"
set exists_any 1
end
if kubectl -n $ns get secret $wpass_secret_name >/dev/null 2>&1
set -a found_list "secret/$wpass_secret_name"
set exists_any 1
end
if test $exists_any -eq 1
_banner "Existing resources detected for $name"
for r in $found_list
echo " - $ns/$r"
end
read -P "Remove EVERYTHING (deploy/svc/netpol/PVC/SECRETS) and recreate? (y/N): " wipe
if string match -qi 'y*' -- $wipe
kubectl -n $ns delete deploy $app_name --ignore-not-found --wait=true >/dev/null 2>&1; or true
kubectl -n $ns delete svc $svc_name --ignore-not-found --wait=true >/dev/null 2>&1; or true
kubectl -n $ns delete netpol {$app_name}-ingress --ignore-not-found --wait=true >/dev/null 2>&1; or true
kubectl -n $ns delete pvc $pvc_name --ignore-not-found --wait=true >/dev/null 2>&1; or true
kubectl -n $ns delete secret $rpc_secret_name --ignore-not-found >/dev/null 2>&1; or true
kubectl -n $ns delete secret $wpass_secret_name --ignore-not-found >/dev/null 2>&1; or true
echo "Cleaned up all previous resources."
else
echo "Reusing existing resources; they will be applied in-place."
end
end
_require "Service name" $svc_name; or return 1
# --- Node scheduling (worker-only by default)
read -P "Schedule only on worker nodes? (y/N) [y]: " pin_workers
@ -266,17 +344,25 @@ function walletsvc_bootstrap --description "Interactive setup of monero-wallet-r
if test -z "$node_label_val"; set node_label_val true; end
end
# --- Daemon + image
# --- Daemon (auto-use local monerod)
echo
echo "Monero node (daemon) the wallet will talk to."
echo " - Best: your own monerod (trusted). Example (same ns): monerod:18081"
echo " - Remote nodes work, but reduce privacy and add trust."
read -P "Daemon address (host:port) [REQUIRED]: " daemon_addr
_require "Daemon address" $daemon_addr; or return 1
echo "Monero daemon: using your cluster-local monerod by default."
if test "$ns" = "crypto"
set daemon_addr monerod:18081
else
set daemon_addr monerod.crypto.svc.cluster.local:18081
end
echo "Daemon address (for the Deployment): $daemon_addr"
if test -z "$WALLETSVC_SKIP_DAEMON_CHECK"
_banner "Probing daemon"
_probe_monerod $daemon_addr; or return 1
_banner "Probing daemon via temporary port-forward"
set PF_LOCAL 28081
_pf_start crypto monerod $PF_LOCAL 18081
if not _probe_monerod 127.0.0.1:$PF_LOCAL
_pf_stop
return 1
end
_pf_stop
else
echo "Skipping daemon probe due to WALLETSVC_SKIP_DAEMON_CHECK=1"
end
@ -286,28 +372,49 @@ function walletsvc_bootstrap --description "Interactive setup of monero-wallet-r
if test -z "$image"; set image docker.io/sethsimmons/simple-monero-wallet-rpc:latest; end
_require "Container image" $image; or return 1
# --- RPC + wallet credentials
read -P "RPC username [walletrpc]: " rpc_user
if test -z "$rpc_user"; set rpc_user walletrpc; end
# --- Secrets (defaults: RPC user=wallet name, passwords auto if missing)
set existing_rpc_user (kubectl -n $ns get secret $rpc_secret_name -o jsonpath='{.data.username}' 2>/dev/null | base64 -d 2>/dev/null)
set existing_rpc_pass (kubectl -n $ns get secret $rpc_secret_name -o jsonpath='{.data.password}' 2>/dev/null | base64 -d 2>/dev/null)
set existing_wpass (kubectl -n $ns get secret $wpass_secret_name -o jsonpath='{.data.password}' 2>/dev/null | base64 -d 2>/dev/null)
set rpc_user_default $base
if test -n "$existing_rpc_user"
set rpc_user_default $existing_rpc_user
end
read -P "RPC username [$rpc_user_default]: " rpc_user
if test -z "$rpc_user"; set rpc_user $rpc_user_default; end
_require "RPC username" $rpc_user; or return 1
read -s -P "RPC password [auto-generate 32 alnum if blank]: " rpc_pass
read -s -P "RPC password [leave blank to keep current or auto-generate if none]: " rpc_pass
echo
if test -z "$rpc_pass"; set rpc_pass (_rand_alnum32); end
if test -z "$rpc_pass"
if test -n "$existing_rpc_pass"
set rpc_pass $existing_rpc_pass
else
set rpc_pass (_rand_alnum32)
end
end
read -P "Wallet filename (no path) [main]: " wallet_file_raw
if test -z "$wallet_file_raw"; set wallet_file_raw main; end
set wallet_file (_k8s_name $wallet_file_raw)
_require "Wallet filename" $wallet_file; or return 1
read -s -P "Wallet password [auto-generate 32 alnum if blank]: " wallet_pass
read -s -P "Wallet password [leave blank to keep current or auto-generate if none]: " wallet_pass
echo
if test -z "$wallet_pass"; set wallet_pass (_rand_alnum32); end
if test -z "$wallet_pass"
if test -n "$existing_wpass"
set wallet_pass $existing_wpass
else
set wallet_pass (_rand_alnum32)
end
end
_banner "Summary"
echo " Namespace: $ns"
echo " StorageClass: $sc"
echo " PVC: $pvc_name ($pvc_size)"
echo " Base name: $name"
echo " PVC: $pvc_name"
echo " App/Deployment: $app_name"
echo " Service: $svc_name"
echo " Daemon address: $daemon_addr"
@ -323,9 +430,6 @@ function walletsvc_bootstrap --description "Interactive setup of monero-wallet-r
return 1
end
set rpc_secret_name "$app_name-rpc-auth"
set wpass_secret_name "$app_name-wallet-pass"
_banner "Applying secrets"
kubectl -n $ns create secret generic $rpc_secret_name \
--from-literal=username="$rpc_user" \
@ -336,29 +440,35 @@ function walletsvc_bootstrap --description "Interactive setup of monero-wallet-r
--from-literal=password="$wallet_pass" \
--dry-run=client -o yaml | kubectl -n $ns apply -f -; or return 1
# Annotate with timestamp so pod restarts when only secrets change
set ts (date -Is)
kubectl -n $ns annotate secret $rpc_secret_name walletsvc.titan/update-ts="$ts" --overwrite >/dev/null 2>&1
kubectl -n $ns annotate secret $wpass_secret_name walletsvc.titan/update-ts="$ts" --overwrite >/dev/null 2>&1
echo "RPC creds: user='$rpc_user' pass='$rpc_pass'"
echo "Wallet pass: $wallet_pass"
_banner "Applying PVC"
set pvcfile (mktemp -t {$pvc_name}-pvc.XXXX.yaml)
begin
echo "apiVersion: v1"
echo "kind: PersistentVolumeClaim"
echo "metadata:"
echo " name: $pvc_name"
echo " namespace: $ns"
echo "spec:"
echo " accessModes: [\"ReadWriteOnce\"]"
echo " storageClassName: $sc"
echo " resources:"
echo " requests:"
echo " storage: $pvc_size"
end > $pvcfile
echo "--- PVC manifest ($pvcfile) ---"
cat $pvcfile
kubectl apply -f $pvcfile; or return 1
if kubectl -n $ns get pvc $pvc_name >/dev/null 2>&1
echo "PVC $ns/$pvc_name exists; keeping as-is."
else
set pvcfile (mktemp -t {$pvc_name}-pvc.XXXX.yaml)
begin
echo "apiVersion: v1"
echo "kind: PersistentVolumeClaim"
echo "metadata:"
echo " name: $pvc_name"
echo " namespace: $ns"
echo "spec:"
echo " accessModes: [\"ReadWriteOnce\"]"
echo " storageClassName: $sc"
echo " resources:"
echo " requests:"
echo " storage: 5Gi"
end > $pvcfile
echo "--- PVC manifest ($pvcfile) ---"
cat $pvcfile
kubectl apply -f $pvcfile; or return 1
end
_banner "Applying Deployment"
set dply (mktemp -t {$app_name}-deploy.XXXX.yaml)
@ -483,7 +593,6 @@ function walletsvc_bootstrap --description "Interactive setup of monero-wallet-r
cat $np
kubectl apply -f $np; or return 1
# If Deployment existed and only secrets changed, force a restart so new creds are read
_banner "Ensuring pod picks up latest secrets"
kubectl -n $ns rollout restart deploy/$app_name >/dev/null 2>&1; or true
@ -491,61 +600,80 @@ function walletsvc_bootstrap --description "Interactive setup of monero-wallet-r
if not kubectl -n $ns rollout status deploy/$app_name --timeout=180s
echo (set_color red)"Rollout did not finish in time. Recent events:"(set_color normal) >&2
kubectl -n $ns get events --sort-by=.lastTimestamp | tail -n 40
echo (set_color yellow)"Hint:"(set_color normal)" if you see Multi-Attach for the PVC, delete any older pod (e.g. 'kubectl -n $ns delete pod <oldpod>') and the new one will attach."
echo (set_color yellow)"Hint:"(set_color normal)" if you see Multi-Attach for the PVC, delete any older pod."
return 1
end
_banner "Creating/opening wallet via JSON-RPC"
_banner "Waiting for wallet RPC readiness"
walletsvc_wait_ready $ns $svc_name 60; or return 1
# sanity: verify /data is writable before we try to create anything
_banner "Creating/opening wallet via JSON-RPC"
if not walletsvc_check_write $ns $app_name >/dev/null
echo (set_color red)"PVC is not writable; aborting."(set_color normal)
return 1
end
# create (idempotent) then open
# Always attempt create; if it exists, open it.
if not walletsvc_create_wallet $ns $svc_name $wallet_file $wallet_pass
echo (set_color red)"Failed to create wallet $wallet_file. See pod logs."(set_color normal)
echo (set_color yellow)"create_wallet returned non-zero; attempting open anyway…"(set_color normal)
end
if not walletsvc_open $ns $svc_name $wallet_file $wallet_pass
echo (set_color red)"Failed to open wallet $wallet_file. If this wallet already exists, the on-disk password may differ from the Secret. Use walletsvc_open with the correct password or walletsvc_change_wallet_password."(set_color normal)
return 1
end
if not walletsvc_open $ns $svc_name $wallet_file $wallet_pass
echo (set_color red)"Failed to open wallet $wallet_file. See pod logs."(set_color normal)
return 1
end
# --- Gather summary data (address, seed if enabled)
set PRIMARY (walletsvc_primary_address $ns $svc_name 2>/dev/null)
set NEWADDR (walletsvc_new_address $ns $svc_name "bootstrap-deposit" 0 2>/dev/null | awk '/New address:/{print $3}' )
set SEEDRAW (walletsvc_show_seed $ns $svc_name 2>&1)
set SEED (echo $SEEDRAW | awk '/Mnemonic seed:/,0{if($0!~/Mnemonic seed:/)print}' )
_banner "Done"
echo "Namespace: $ns"
echo "App/Deployment: $app_name"
echo "Service: $svc_name (ClusterIP)"
echo "Base name: $name"
echo "Service (in-cluster): $svc_name:18083"
echo "PVC: $pvc_name via StorageClass '$sc'"
echo "RPC auth: user='$rpc_user'"
echo "RPC pass: $rpc_pass"
echo "Wallet pass: $wallet_pass"
echo "Wallet file: /data/$wallet_file"
echo "Primary address (acct 0):"
echo " $PRIMARY"
if test -n "$NEWADDR"
echo "Fresh deposit subaddress (acct 0):"
echo " $NEWADDR"
end
if string match -qi '*Mnemonic seed:*' -- $SEEDRAW
echo (set_color yellow)"Mnemonic seed (WRITE THIS DOWN, OFFLINE):"(set_color normal)
echo $SEED
else
echo "Mnemonic seed not retrievable via RPC (image may disable it)."
echo "If needed and supported by your image: walletsvc_show_seed $ns $svc_name"
end
echo
echo "Access locally: walletsvc_portforward $ns $svc_name"
echo "Show seed (if enabled by image): walletsvc_show_seed $ns $svc_name"
echo "Access locally: walletsvc_portforward $ns $svc_name # then http://127.0.0.1:18083/json_rpc"
echo "Quick overview: walletsvc_overview $ns $svc_name"
end
### ------- utilities -------------------------------------------------------
function walletsvc_portforward --description "port-forward RPC to localhost:18083" -a NS SVC
if test -z "$NS"; set NS monero; end
if test -z "$NS"; set NS crypto; end
if test -z "$SVC"; set SVC wallet-rpc; end
echo "Forwarding $NS/$SVC → http://127.0.0.1:18083 (Ctrl+C to stop)…"
kubectl -n $NS port-forward svc/$SVC 18083:18083
end
function walletsvc_status --description "show pod/service status" -a NS APP
if test -z "$NS"; set NS monero; end
if test -z "$NS"; set NS crypto; end
if test -z "$APP"; set APP wallet-rpc; end
kubectl -n $NS get deploy,po,svc,netpol -l app=$APP -o wide
end
function walletsvc_stop --description "stop the RPC but keep PVC + secrets" -a NS APP SVC
if test -z "$NS"; set NS monero; end
if test -z "$NS"; set NS crypto; end
if test -z "$APP"; set APP wallet-rpc; end
if test -z "$SVC"; set SVC $APP; end
kubectl -n $NS delete deploy $APP --ignore-not-found
@ -554,7 +682,7 @@ function walletsvc_stop --description "stop the RPC but keep PVC + secrets" -a N
end
function walletsvc_purge --description "IRREVERSIBLY delete PVC + secrets (keys on disk!)" -a NS APP PVC SVC
if test -z "$NS"; set NS monero; end
if test -z "$NS"; set NS crypto; end
if test -z "$APP"; set APP wallet-rpc; end
if test -z "$PVC"; set PVC wallet-data; end
if test -z "$SVC"; set SVC $APP; end
@ -574,7 +702,7 @@ end
function walletsvc_show_seed --description "Try to fetch mnemonic via RPC query_key (wallet must be open)" -a NS SVC RPCUSER RPCPASS
_need jq curl; or return 1
if test -z "$NS"; set NS monero; end
if test -z "$NS"; set NS crypto; end
if test -z "$SVC"; set SVC wallet-rpc; end
if test -z "$RPCUSER"
set RPCUSER (kubectl -n $NS get secret {$SVC}-rpc-auth -o jsonpath='{.data.username}' | base64 -d)
@ -597,7 +725,7 @@ end
# ---------- core JSON-RPC wrapper (raw JSON to stdout) ----------
function walletsvc_rpc_call --description "RPC call with secrets (auto port-forward)" -a NS SVC METHOD PARAMS_JSON
if test -z "$NS"; set NS monero; end
if test -z "$NS"; set NS crypto; end
if test -z "$SVC"; set SVC wallet-rpc; end
if test -z "$METHOD"
echo "Usage: walletsvc_rpc_call [ns] [svc] <method> [params_json]" >&2
@ -614,7 +742,6 @@ function walletsvc_rpc_call --description "RPC call with secrets (auto port-forw
set payload (printf '{"jsonrpc":"2.0","id":"0","method":"%s"}' $METHOD)
end
# capture HTTP code and body without using --fail so we always get the body
set tmp (mktemp)
set code (curl -s --digest -u "$RPCUSER:$RPCPASS" \
-H 'Content-Type: application/json' \
@ -648,7 +775,7 @@ end
# Rotate RPC username/password (updates Secret & restarts Deploy)
function walletsvc_set_rpc_credentials --description "rotate RPC Basic creds" -a NS APP
_need kubectl; or return 1
if test -z "$NS"; set NS monero; end
if test -z "$NS"; set NS crypto; end
if test -z "$APP"; set APP wallet-rpc; end
set SNAME {$APP}-rpc-auth
@ -676,7 +803,7 @@ end
# Change wallet file password via RPC
function walletsvc_change_wallet_password --description "change password of a wallet file via RPC" -a NS SVC
if test -z "$NS"; set NS monero; end
if test -z "$NS"; set NS crypto; end
if test -z "$SVC"; set SVC wallet-rpc; end
read -P "Wallet filename (under /data): " WFILE
@ -690,18 +817,14 @@ function walletsvc_change_wallet_password --description "change password of a wa
echo
if test -z "$NEW"; set NEW (_rand_alnum32); end
# open the wallet (in case it's not already)
set res (walletsvc_rpc_call $NS $SVC open_wallet (printf '{"filename":"%s","password":"%s"}' $WFILE $OLD))
# ignore error here; we try change anyway so user can see the RPC error if wrong
# change password
walletsvc_rpc_call $NS $SVC change_wallet_password (printf '{"old_password":"%s","new_password":"%s"}' $OLD $NEW)
echo "New wallet password: $NEW"
end
# --- logs from the deployment (quick tail) ---
function walletsvc_logs -a NS APP LINES
if test -z "$NS"; set NS monero; end
if test -z "$NS"; set NS crypto; end
if test -z "$APP"; set APP wallet-rpc; end
if test -z "$LINES"; set LINES 200; end
kubectl -n $NS logs deploy/$APP --tail=$LINES
@ -709,7 +832,7 @@ end
# --- list files in /data inside the pod ---
function walletsvc_wallet_ls -a NS APP
if test -z "$NS"; set NS monero; end
if test -z "$NS"; set NS crypto; end
if test -z "$APP"; set APP wallet-rpc; end
echo "Listing /data in $NS/$APP" >&2
kubectl -n $NS exec deploy/$APP -- sh -lc 'ls -la /data || true'
@ -717,7 +840,7 @@ end
# --- create a wallet (uses Secret password if PASS omitted) ---
function walletsvc_create_wallet -a NS SVC FILE PASS LANG
if test -z "$NS"; set NS monero; end
if test -z "$NS"; set NS crypto; end
if test -z "$SVC"; set SVC wallet-rpc; end
if test -z "$FILE"
echo "Usage: walletsvc_create_wallet [ns] [svc] <filename> [pass] [language]" >&2
@ -751,7 +874,7 @@ end
# --- improved opener: closes first, checks files, hints if password mismatch ---
function walletsvc_open -a NS SVC FILE PASS
if test -z "$NS"; set NS monero; end
if test -z "$NS"; set NS crypto; end
if test -z "$SVC"; set SVC wallet-rpc; end
if test -z "$FILE"
echo "Usage: walletsvc_open [ns] [svc] <filename> [password]" >&2
@ -804,31 +927,84 @@ function walletsvc_open -a NS SVC FILE PASS
end
function walletsvc_check_write -a NS APP
if test -z "$NS"; set NS monero; end
if test -z "$NS"; set NS crypto; end
if test -z "$APP"; set APP wallet-rpc; end
kubectl -n $NS exec deploy/$APP -- sh -lc 'echo test-$(date +%s) > /data/.write-test && ls -l /data/.write-test && rm -f /data/.write-test'
end
# --- ensure wallet is open (create if missing) ---
function walletsvc_ensure_open -a NS SVC FILE
if test -z "$NS"; set NS monero; end
if test -z "$NS"; set NS crypto; end
if test -z "$SVC"; set SVC wallet-rpc; end
if test -z "$FILE"; set FILE main; end
# If files missing, create using secret password
set exists (kubectl -n $NS exec deploy/$SVC -- sh -lc (printf 'if [ -e "/data/%s" ] || [ -e "/data/%s.keys" ]; then echo yes; else echo no; fi' $FILE $FILE) 2>/dev/null)
if test "$exists" = "no"
echo "No /data/$FILE found. Creating…" >&2
walletsvc_create_wallet $NS $SVC $FILE; or return 1
end
# Open with secret password
walletsvc_open $NS $SVC $FILE; or return 1
end
# --- quick primary address print (account 0)
function walletsvc_primary_address -a NS SVC
if test -z "$NS"; set NS crypto; end
if test -z "$SVC"; set SVC wallet-rpc; end
_pf_start $NS $SVC 18083 18083
set resp (curl -s --fail --digest \
-u (kubectl -n $NS get secret {$SVC}-rpc-auth -o jsonpath='{.data.username}' | base64 -d):(kubectl -n $NS get secret {$SVC}-rpc-auth -o jsonpath='{.data.password}' | base64 -d) \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":"0","method":"get_address","params":{"account_index":0}}' \
http://127.0.0.1:18083/json_rpc)
_pf_stop
echo $resp | jq -r '.result.address'
end
# --- show unlocked balance quickly
function walletsvc_unlocked -a NS SVC
_need python3; or return 1
if test -z "$NS"; set NS crypto; end
if test -z "$SVC"; set SVC wallet-rpc; end
set res (walletsvc_rpc_call $NS $SVC get_balance '{"account_index":0}')
set bal (echo $res | jq -r '.result.balance // 0')
set unl (echo $res | jq -r '.result.unlocked_balance // 0')
# Convert piconero -> XMR using Python (fish-safe)
set balx (python3 -c 'from decimal import Decimal; import sys; print(Decimal(sys.argv[1]) / Decimal("1e12"))' -- $bal)
set unlx (python3 -c 'from decimal import Decimal; import sys; print(Decimal(sys.argv[1]) / Decimal("1e12"))' -- $unl)
echo "balance=$balx XMR unlocked=$unlx XMR"
end
# --- wait until unlocked balance >= threshold (XMR), timeout seconds
function walletsvc_wait_unlocked -a NS SVC XMR_MIN TIMEOUT
_need python3; or return 1
if test -z "$NS"; set NS crypto; end
if test -z "$SVC"; set SVC wallet-rpc; end
if test -z "$XMR_MIN"; set XMR_MIN 0; end
if test -z "$TIMEOUT"; set TIMEOUT 300; end
set start (date +%s)
while true
set res (walletsvc_rpc_call $NS $SVC get_balance '{"account_index":0}')
set unl (echo $res | jq -r '.result.unlocked_balance // 0')
set unlx (python3 -c 'from decimal import Decimal; import sys; print(Decimal(sys.argv[1]) / Decimal("1e12"))' -- $unl)
set ge (python3 -c 'from decimal import Decimal; import sys; print(1 if Decimal(sys.argv[1]) >= Decimal(sys.argv[2]) else 0)' -- $unlx $XMR_MIN)
if test $ge -eq 1
echo "Unlocked: $unlx XMR (≥ $XMR_MIN)"
return 0
end
if test (math (date +%s) - $start) -ge $TIMEOUT
echo "Timed out waiting for unlocked balance ≥ $XMR_MIN XMR (current $unlx XMR)."
return 1
end
sleep 6
end
end
# ---------- friendly overview ----------
function walletsvc_overview -a NS SVC ACCOUNT
if test -z "$NS"; set NS monero; end
if test -z "$NS"; set NS crypto; end
if test -z "$SVC"; set SVC wallet-rpc; end
if test -z "$ACCOUNT"; set ACCOUNT 0; end
@ -838,28 +1014,25 @@ function walletsvc_overview -a NS SVC ACCOUNT
_pf_start $NS $SVC 18083 18083
echo "== Wallet RPC overview (ns=$NS svc=$SVC acct=$ACCOUNT)"
# Version
set resp (curl -s --fail --digest -u "$RPCUSER:$RPCPASS" \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":"0","method":"get_version"}' \
http://127.0.0.1:18083/json_rpc)
echo $resp | jq -r '"-- Version:\n version=\(.result.version) release=\(.result.release)"'
# Height (show error clearly if wallet not open)
set resp (curl -s --fail --digest -u "$RPCUSER:$RPCPASS" \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":"0","method":"get_height"}' \
http://127.0.0.1:18083/json_rpc)
set err (echo $resp | jq -r '.error.message // empty')
echo "-- Height:"
if test -n "$err"
echo "-- Height:"
echo " ERROR: $err"
echo " (Tip: open your wallet: walletsvc_open $NS $SVC main)"
else
echo $resp | jq -r '"-- Height:\n wallet_height=\(.result.height)"'
echo $resp | jq -r '" wallet_height=\(.result.height)"'
end
# Accounts
set resp (curl -s --fail --digest -u "$RPCUSER:$RPCPASS" \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":"0","method":"get_accounts"}' \
@ -873,7 +1046,6 @@ function walletsvc_overview -a NS SVC ACCOUNT
| " [\(.account_index)] \(.label) total=\(.balance/1000000000000) XMR unlocked=\(.unlocked_balance/1000000000000) XMR"'
end
# Balance for selected account
set BODY (printf '{"account_index":%s}' $ACCOUNT)
set resp (curl -s --fail --digest -u "$RPCUSER:$RPCPASS" \
-H 'Content-Type: application/json' \
@ -887,7 +1059,6 @@ function walletsvc_overview -a NS SVC ACCOUNT
echo $resp | jq -r '" balance=\(.result.balance/1000000000000) XMR unlocked=\(.result.unlocked_balance/1000000000000) XMR"'
end
# Primary address
set resp (curl -s --fail --digest -u "$RPCUSER:$RPCPASS" \
-H 'Content-Type: application/json' \
-d (printf '{"jsonrpc":"2.0","id":"0","method":"get_address","params":{"account_index":%s}}' $ACCOUNT) \
@ -900,7 +1071,6 @@ function walletsvc_overview -a NS SVC ACCOUNT
echo $resp | jq -r '.result.address'
end
# First 5 subaddresses
echo "-- First 5 subaddresses:"
if test -n "$err"
echo " (skipped due to error above)"
@ -908,7 +1078,6 @@ function walletsvc_overview -a NS SVC ACCOUNT
echo $resp | jq -r '.result.addresses[:5][] | " [\(.address_index)] \(.address) label=\(.label) used=\(.used)"'
end
# Recent transfers
set BODY (printf '{"account_index":%s,"in":true}' $ACCOUNT)
set in (curl -s --fail --digest -u "$RPCUSER:$RPCPASS" \
-H 'Content-Type: application/json' \
@ -938,7 +1107,7 @@ end
# ---------- list addresses ----------
function walletsvc_list_addresses -a NS SVC ACCOUNT
if test -z "$NS"; set NS monero; end
if test -z "$NS"; set NS crypto; end
if test -z "$SVC"; set SVC wallet-rpc; end
if test -z "$ACCOUNT"; set ACCOUNT 0; end
set RPCUSER (kubectl -n $NS get secret {$SVC}-rpc-auth -o jsonpath='{.data.username}' | base64 -d)
@ -960,7 +1129,7 @@ end
# ---------- new subaddress ----------
function walletsvc_new_address -a NS SVC LABEL ACCOUNT
if test -z "$NS"; set NS monero; end
if test -z "$NS"; set NS crypto; end
if test -z "$SVC"; set SVC wallet-rpc; end
if test -z "$ACCOUNT"; set ACCOUNT 0; end
if test -z "$LABEL"; set LABEL (printf "deposit-%s" (date +%Y%m%d-%H%M%S)); end
@ -981,47 +1150,116 @@ function walletsvc_new_address -a NS SVC LABEL ACCOUNT
echo $resp | jq -r --arg L "$LABEL" '"New address: \(.result.address) [index=\(.result.address_index)] label=" + $L'
end
# --- send a specific amount XMR to a single address (optionally payment_id)
# Usage: walletsvc_send [ns] [svc] <to_address> <amount_xmr> [payment_id_hex]
function walletsvc_send -a NS SVC TO AMT_XMR PID
_need jq curl python3; or return 1
if test -z "$NS"; set NS crypto; end
if test -z "$SVC"; set SVC wallet-rpc; end
if test -z "$TO" -o -z "$AMT_XMR"
echo "Usage: walletsvc_send [ns] [svc] <to_address> <amount_xmr> [payment_id_hex]" >&2
return 1
end
# Convert amount to atomic units
set ATOMIC (_xmr_to_atomic $AMT_XMR)
if test -z "$ATOMIC"
echo "Failed to convert amount '$AMT_XMR' to piconero." >&2
return 1
end
# Build JSON
set body_base (printf '{"destinations":[{"amount":%s,"address":"%s"}],"account_index":0,"priority":1,"get_tx_key":true,"do_not_relay":false}' $ATOMIC $TO)
if test -n "$PID"
set body (string replace '}' ',"payment_id":"'$PID'"}' -- $body_base)
else
set body $body_base
end
set res (walletsvc_rpc_call $NS $SVC transfer $body)
set err (echo $res | jq -r '.error.message // empty')
if test -n "$err"
echo "ERROR: $err" >&2
return 1
end
set fee (echo $res | jq -r '.result.fee // 0')
set tx (echo $res | jq -r '.result.tx_hash // (.result.tx_hash_list[0] // "")')
# piconero -> XMR without heredoc
set fee_xmr (python3 -c 'from decimal import Decimal; import sys; print(Decimal(sys.argv[1]) / Decimal("1e12"))' -- $fee)
echo "Sent transaction:"
echo " tx: $tx"
echo " fee: $fee_xmr XMR ($fee piconero)"
end
# --- sweep all unlocked funds to an address (account 0)
# Usage: walletsvc_sweep_all [ns] [svc] <to_address>
function walletsvc_sweep_all -a NS SVC TO
_need jq curl; or return 1
if test -z "$NS"; set NS crypto; end
if test -z "$SVC"; set SVC wallet-rpc; end
if test -z "$TO"
echo "Usage: walletsvc_sweep_all [ns] [svc] <to_address>" >&2
return 1
end
set body (printf '{"address":"%s","account_index":0,"priority":1,"do_not_relay":false,"get_tx_keys":true}' $TO)
set res (walletsvc_rpc_call $NS $SVC sweep_all $body)
set err (echo $res | jq -r '.error.message // empty')
if test -n "$err"
echo "ERROR: $err" >&2
return 1
end
echo "Sweep transactions:"
echo $res | jq -r '.result.tx_hash_list[]? | " tx: " + .'
end
# Optional: non-sensitive cluster snapshot to help tune manifests.
function cluster_probe
set OUT cluster-snapshot.txt
echo "# Cluster snapshot "(date -Is) > $OUT
echo -e "\n## kubectl version" >> $OUT
printf "# Cluster snapshot %s\n" (date -Is) > $OUT
printf "\n## kubectl version\n" >> $OUT
kubectl version --short >> $OUT 2>&1
echo -e "\n## Nodes" >> $OUT
printf "\n## Nodes\n" >> $OUT
kubectl get nodes -o wide >> $OUT
echo -e "\n### Node resources" >> $OUT
printf "\n### Node resources\n" >> $OUT
kubectl describe nodes | egrep -i 'Name:|Roles:|Capacity:|Allocatable:|cpu|memory|ephemeral' >> $OUT
echo -e "\n## Namespaces" >> $OUT
printf "\n## Namespaces\n" >> $OUT
kubectl get ns >> $OUT
echo -e "\n## StorageClasses (summary)" >> $OUT
printf "\n## StorageClasses (summary)\n" >> $OUT
kubectl get storageclass >> $OUT
echo -e "\n## StorageClasses (yaml)" >> $OUT
printf "\n## StorageClasses (yaml)\n" >> $OUT
kubectl get storageclass -o yaml >> $OUT
echo -e "\n## PV/PVC" >> $OUT
kubectl get pv >> $OUT 2>&1
kubectl get pvc -A >> $OUT 2>&1
printf "\n## PV/PVC\n" >> $OUT
kubectl get pv >> $OUT 2>/dev/null
kubectl get pvc -A >> $OUT 2>/dev/null
echo -e "\n## Ingress / Services" >> $OUT
kubectl get ingressclass >> $OUT 2>&1
kubectl get ingress -A >> $OUT 2>&1
printf "\n## Ingress / Services\n" >> $OUT
kubectl get ingressclass >> $OUT 2>/dev/null
kubectl get ingress -A >> $OUT 2>/dev/null
kubectl get svc -A | egrep -i 'NAMESPACE|LoadBalancer|EXTERNAL-IP|traefik|ingress' >> $OUT
echo -e "\n## kube-system pods (CNI hint)" >> $OUT
kubectl -n kube-system get pods -o wide >> $OUT 2>&1
printf "\n## kube-system pods (CNI hint)\n" >> $OUT
kubectl -n kube-system get pods -o wide >> $OUT 2>/dev/null
echo -e "\n## Default limits/quotas" >> $OUT
kubectl -n default get limitrange,resourcequota >> $OUT 2>&1
printf "\n## Default limits/quotas\n" >> $OUT
kubectl -n default get limitrange,resourcequota >> $OUT 2>/dev/null
echo -e "\n## Tools present on your machine" >> $OUT
printf "\n## Tools present on your machine\n" >> $OUT
for t in kubectl helm jq curl awk envsubst fish
if type -q $t
echo "$t: OK" >> $OUT
printf "%s: OK\n" $t >> $OUT
else
echo "$t: MISSING" >> $OUT
printf "%s: MISSING\n" $t >> $OUT
end
end
@ -1030,8 +1268,8 @@ end
# --- one-line command reference (keep in sync) ---
function walletsvc_help
echo "walletsvc_bootstrap # interactive deploy + secrets + PVC + first wallet (multiwallet RPC)"
echo "walletsvc_status [ns] [app] # show deploy, pods, service, netpol"
echo "walletsvc_bootstrap # interactive deploy + secrets + PVC + first wallet (multiwallet RPC), idempotent"
echo "walletsvc_status [ns] [app] # show deploy, pods, service, netpol (default ns=crypto)"
echo "walletsvc_portforward [ns] [svc] # port-forward RPC to localhost:18083 (CTRL+C to stop)"
echo "walletsvc_logs [ns] [app] [N] # tail N (default 200) lines of RPC pod logs"
echo "walletsvc_wallet_ls [ns] [app] # list files under /data in the RPC pod (check wallet files)"
@ -1044,67 +1282,19 @@ function walletsvc_help
echo "walletsvc_list_addresses [ns] [svc] [acct=0] # list all subaddresses for account"
echo "walletsvc_new_address [ns] [svc] [label] [acct=0] # create labeled subaddress"
echo "walletsvc_set_rpc_credentials [ns] [app] # rotate RPC basic auth (secret + rollout)"
echo "walletsvc_change_wallet_password [ns] [svc] # change password of a wallet file via RPC"
echo "walletsvc_change_wallet_password [ns] [svc] # change wallet file password via RPC"
echo "walletsvc_stop [ns] [app] [svc] # delete deployment+service only"
echo "walletsvc_purge [ns] [app] [pvc] [svc] # delete deploy+svc+PVC+secrets (danger!)"
end
function walletsvc_help_detailed
echo "Commands (defaults: ns=monero, svc/app=wallet-rpc)"
echo "Commands (defaults: ns=crypto, svc/app=wallet-rpc)"
echo
echo "walletsvc_bootstrap"
echo " Interactive deploy: creates namespace, secrets, PVC, Deployment (fsGroup + init perms), Service, NetPolicy."
echo " Prompts you for a *Monero* daemon (monerod) with NO default; probes it via get_info to ensure mainnet Monero."
echo " Set WALLETSVC_SKIP_DAEMON_CHECK=1 to bypass the probe (not recommended)."
echo
echo "walletsvc_status [ns] [app]"
echo " Show Deployment/Pod/Service/NetPolicy for the app label."
echo
echo "walletsvc_portforward [ns] [svc]"
echo " Port-forward RPC to http://127.0.0.1:18083 (Ctrl+C to stop)."
echo
echo "walletsvc_logs [ns] [app] [N]"
echo " Tail N lines (default 200) from the wallet RPC pod logs."
echo
echo "walletsvc_wallet_ls [ns] [app]"
echo " List /data contents inside the pod (quick sanity for wallet files)."
echo
echo "walletsvc_check_write [ns] [app]"
echo " Touch/remove a temp file under /data to prove the PVC is writable."
echo
echo "walletsvc_rpc_test [ns] [svc]"
echo " JSON-RPC get_version (sanity)."
echo
echo "walletsvc_rpc_call [ns] [svc] <method> [params_json]"
echo " Raw JSON-RPC with digest auth; prints body; non-200 returns non-zero."
echo
echo "walletsvc_create_wallet [ns] [svc] <file> [pass] [language]"
echo " Create /data/<file>{,.keys}. Pass defaults to Secret <svc>-wallet-pass. Language defaults to English."
echo
echo "walletsvc_open [ns] [svc] <file> [pass]"
echo " Close any open wallet, verify files exist, then open with the given (or Secret) password."
echo
echo "walletsvc_ensure_open [ns] [svc] [file]"
echo " If files missing, create with Secret pass, then open."
echo
echo "walletsvc_overview [ns] [svc] [acct=0]"
echo " Pretty view: version, height, accounts, balances, addresses, recent transfers."
echo
echo "walletsvc_list_addresses [ns] [svc] [acct=0]"
echo " List all subaddresses for the account."
echo
echo "walletsvc_new_address [ns] [svc] [label] [acct=0]"
echo " Create a new subaddress with optional label (default: timestamped)."
echo
echo "walletsvc_set_rpc_credentials [ns] [app]"
echo " Rotate RPC basic auth (updates Secret and restarts Deployment)."
echo
echo "walletsvc_change_wallet_password [ns] [svc]"
echo " Change wallet file password via RPC."
echo
echo "Notes:"
echo " - Your wallet must talk to a Monero daemon (monerod). p2pool is a mining pool, not a daemon."
echo " - For the most trust-minimized setup, run your own monerod and point the wallet at it."
echo " - This script blocks suspicious daemon addresses (e.g. containing 'qubic') and probes that the daemon"
echo " speaks Monero mainnet before proceeding."
echo " Interactive deploy: creates/updates secrets, PVC, Deployment (fsGroup+init perms), Service, NetPolicy."
echo " Idempotent: if deploy/svc/netpol already exist, you can clean them up (PVC+secrets kept) and continue."
echo " Uses your in-cluster monerod by default (monerod:18081 in ns=crypto)."
echo " Probes it via a temporary port-forward so it works from your workstation."
echo " Set WALLETSVC_SKIP_DAEMON_CHECK=1 to bypass the daemon probe (not recommended)."
end

View File

@ -57,9 +57,17 @@ spec:
- --confirm-external-bind
- --p2p-bind-ip=0.0.0.0
- --p2p-bind-port=18080
- --zmq-pub=tcp://0.0.0.0:18083
- --out-peers=32
- --in-peers=64
- --add-priority-node=p2pmd.xmrvsbeast.com:18080
- --add-priority-node=nodes.hashvault.pro:18080
- --disable-dns-checkpoints
- --enable-dns-blocklist
ports:
- { name: rpc, containerPort: 18081 }
- { name: p2p, containerPort: 18080 }
- { name: p2pool, containerPort: 18083 }
# securityContext:
# allowPrivilegeEscalation: false
# readOnlyRootFilesystem: true

View File

@ -0,0 +1,21 @@
# services/crypto/xmr-miner/configmap-sources.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: xmr-miner-sources
namespace: crypto
data:
# REQUIRED: set to the official p2pool ARM64 tarball URL
P2POOL_URL: "https://EXAMPLE/p2pool-linux-arm64.tar.gz"
# OPTIONAL: p2pool SHA256 (exact 64-hex chars). Leave blank to skip verification.
P2POOL_SHA256: ""
# REQUIRED: set to the official xmrig ARM64 tarball URL (static build preferred)
XMRIG_URL: "https://EXAMPLE/xmrig-linux-static-arm64.tar.gz"
# OPTIONAL: xmrig SHA256. Leave blank to skip verification.
XMRIG_SHA256: ""
# Threads for xmrig (default 1 to avoid RAM spikes; override after testing)
XMRIG_THREADS: "1"
# Extra args for xmrig if you want (space-separated)
XMRIG_EXTRA_ARGS: ""

View File

@ -0,0 +1,84 @@
# services/crypto/xmr-miner/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: p2pool
namespace: crypto
labels: { app: p2pool }
spec:
replicas: 1
selector:
matchLabels: { app: p2pool }
template:
metadata:
labels: { app: p2pool }
spec:
nodeSelector:
kubernetes.io/arch: arm64
priorityClassName: scavenger
volumes:
- name: tools
emptyDir: {}
- name: payout
secret:
secretName: xmr-payout
- name: sources
configMap:
name: xmr-miner-sources
initContainers:
- name: fetch-tools
image: debian:bookworm-slim
command: ["/bin/sh","-lc"]
args:
- |
set -eux
apt-get update
apt-get install -y --no-install-recommends ca-certificates curl xz-utils tar coreutils
update-ca-certificates
P2POOL_URL="$(cat /cfg/P2POOL_URL)"; : "${P2POOL_URL:?P2POOL_URL required}"
XMRIG_URL="$(cat /cfg/XMRIG_URL)"; : "${XMRIG_URL:?XMRIG_URL required}"
P2POOL_SHA="$(cat /cfg/P2POOL_SHA256)"; true
XMRIG_SHA="$(cat /cfg/XMRIG_SHA256)"; true
mkdir -p /opt/bin
# --- p2pool ---
curl -fL "$P2POOL_URL" -o /tmp/p2pool.tgz
if [ -n "${P2POOL_SHA:-}" ]; then echo "${P2POOL_SHA} /tmp/p2pool.tgz" | sha256sum -c -; fi
tar -x -C /tmp -f /tmp/p2pool.tgz || true
# pick the p2pool binary from whatever layout the tarball uses
P2=$(find /tmp -maxdepth 3 -type f -name 'p2pool*' -perm -u+x | head -n1)
test -n "$P2" && cp "$P2" /opt/bin/p2pool && chmod 0755 /opt/bin/p2pool
# --- xmrig (for quick local self-checks; DaemonSet also downloads its own) ---
curl -fL "$XMRIG_URL" -o /tmp/xmrig.tgz
if [ -n "${XMRIG_SHA:-}" ]; then echo "${XMRIG_SHA} /tmp/xmrig.tgz" | sha256sum -c -; fi
tar -x -C /tmp -f /tmp/xmrig.tgz || true
XR=$(find /tmp -maxdepth 3 -type f -name 'xmrig*' -perm -u+x | head -n1)
test -n "$XR" && cp "$XR" /opt/bin/xmrig && chmod 0755 /opt/bin/xmrig
ls -l /opt/bin
volumeMounts:
- { name: tools, mountPath: /opt/bin }
- { name: sources, mountPath: /cfg, readOnly: true }
containers:
- name: p2pool
image: debian:bookworm-slim
command: ["/bin/sh","-lc"]
args:
- |
set -eu
ADDR="$(cat /run/xmr/address)"
# be nice to the node; p2pool will connect to your in-cluster monerod
exec nice -n 19 ionice -c3 /opt/bin/p2pool \
--wallet "$ADDR" \
--host monerod.crypto.svc.cluster.local \
--rpc-port 18081 \
--zmq-port 18083 \
--stratum 0.0.0.0:3333
ports:
- { containerPort: 3333, name: stratum, protocol: TCP }
volumeMounts:
- { name: tools, mountPath: /opt/bin }
- { name: payout, mountPath: /run/xmr, readOnly: true }
# BestEffort QoS: no requests/limits → yields CPU when others need it

View File

@ -0,0 +1,10 @@
# services/crypto/xmr-miner/kustomization/yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- secret-wallet.yaml
- configmap-sources.yaml
- deployment.yaml
- service.yaml
- xmrig-daemonset.yaml
- priority-class.yaml

View File

@ -0,0 +1,8 @@
# services/crypto/xmr-miner/priority-class.yaml
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: scavenger
value: -10
globalDefault: false
description: "Preemptible background miners."

View File

@ -0,0 +1,9 @@
# services/crypto/xmr-miner/secret-wallet.yaml
apiVersion: v1
kind: Secret
metadata:
name: xmr-payout
namespace: crypto
type: Opaque
stringData:
address: "459owYDrJFESexUGTGgxoNZQ7VwWYrd8pfspdH15GStBeyr653N5qRHdqdVSaQ5UWCYQ42ueRNoNjPTeHR7LDisE67SrzDH"

View File

@ -0,0 +1,23 @@
# services/crypto/xmr-miner/service.yaml
apiVersion: v1
kind: Service
metadata:
name: p2pool
namespace: crypto
labels: { app: p2pool }
spec:
type: ClusterIP
selector: { app: p2pool }
ports:
- name: stratum
port: 3333
targetPort: 3333
- name: rpc
port: 18081
targetPort: 18081
- name: p2p
port: 18080
targetPort: 18080
- name: zmq
port: 18083
targetPort: 18083

View File

@ -0,0 +1,86 @@
# services/crypto/xmr-miner/xmrig-daemonset.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: xmr-xmrig
namespace: crypto
labels: { app: xmr-xmrig }
spec:
selector:
matchLabels: { app: xmr-xmrig }
updateStrategy:
type: RollingUpdate
template:
metadata:
labels: { app: xmr-xmrig }
spec:
nodeSelector:
kubernetes.io/arch: arm64
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: node-role.kubernetes.io/control-plane
operator: DoesNotExist
- key: node-role.kubernetes.io/master
operator: DoesNotExist
volumes:
- name: tools
emptyDir: {}
- name: payout
secret:
secretName: xmr-payout
- name: sources
configMap:
name: xmr-miner-sources
initContainers:
- name: fetch-xmrig
image: debian:bookworm-slim
command: ["/bin/sh","-lc"]
args:
- |
set -eux
apt-get update
apt-get install -y --no-install-recommends ca-certificates curl xz-utils tar coreutils
update-ca-certificates
XMRIG_URL="$(cat /cfg/XMRIG_URL)"; : "${XMRIG_URL:?XMRIG_URL required}"
XMRIG_SHA="$(cat /cfg/XMRIG_SHA256)"; true
mkdir -p /opt/bin
curl -fL "$XMRIG_URL" -o /tmp/xmrig.tgz
if [ -n "${XMRIG_SHA:-}" ]; then echo "${XMRIG_SHA} /tmp/xmrig.tgz" | sha256sum -c -; fi
tar -x -C /tmp -f /tmp/xmrig.tgz || true
XR=$(find /tmp -maxdepth 3 -type f -name 'xmrig*' -perm -u+x | head -n1)
test -n "$XR" && cp "$XR" /opt/bin/xmrig && chmod 0755 /opt/bin/xmrig
ls -l /opt/bin
volumeMounts:
- { name: tools, mountPath: /opt/bin }
- { name: sources, mountPath: /cfg, readOnly: true }
containers:
- name: xmrig
image: debian:bookworm-slim
env:
- { name: XMRIG_THREADS, valueFrom: { configMapKeyRef: { name: xmr-miner-sources, key: XMRIG_THREADS } } }
- { name: XMRIG_EXTRA_ARGS,valueFrom: { configMapKeyRef: { name: xmr-miner-sources, key: XMRIG_EXTRA_ARGS } } }
command: ["/bin/sh","-lc"]
args:
- |
set -eu
ADDR="$(cat /run/xmr/address)"
THR="${XMRIG_THREADS:-1}"
EXTRA="${XMRIG_EXTRA_ARGS:-}"
# Lowest CPU/IO priority; connect to cluster p2pool
exec nice -n 19 ionice -c3 /opt/bin/xmrig \
-o p2pool.crypto.svc.cluster.local:3333 \
-u x+10000 \
-a rx \
-k \
--donate-level 0 \
--cpu-priority 1 \
--threads "${THR}" ${EXTRA}
volumeMounts:
- { name: tools, mountPath: /opt/bin }
- { name: payout, mountPath: /run/xmr, readOnly: true }
# BestEffort QoS: no requests/limits → yields CPU when others need it

View File

@ -1,12 +0,0 @@
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: harbor-tls
namespace: harbor
spec:
secretName: harbor-tls
issuerRef:
kind: ClusterIssuer
name: letsencrypt-prod
dnsNames:
- registry.bstein.dev

View File

@ -1,69 +0,0 @@
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: harbor
namespace: flux-system
spec:
interval: 15m
targetNamespace: harbor
install:
createNamespace: false
chart:
spec:
chart: harbor
version: 1.17.1
sourceRef:
kind: HelmRepository
name: harbor
namespace: flux-system
values:
expose:
type: ingress
ingress:
className: traefik
hosts:
core: registry.bstein.dev
annotations:
cert-manager.io/cluster-issuer: letsencrypt-production
tls:
enabled: true
certSource: secret
secret:
secretName: harbor-tls
externalURL: https://registry.bstein.dev
notary:
enabled: false
harborAdminPassword: "ENCRYPT-ME-WITH-SOPS"
database:
type: external
external:
host: postgres-service.postgres.svc.cluster.local
port: 5432
username: harbor
password: "ENCRYPT-ME-WITH-SOPS"
sslmode: disable
coreDatabase: harbor
redis:
type: internal
persistence:
persistentVolumeClaim:
registry:
storageClass: astreae
accessMode: ReadWriteOnce
size: 100Gi
jobservice:
storageClass: astreae
accessMode: ReadWriteOnce
size: 5Gi
redis:
storageClass: astreae
accessMode: ReadWriteOnce
size: 2Gi
trivy:
storageClass: astreae
accessMode: ReadWriteOnce
size: 5Gi
chartmuseum:
enabled: false
trivy:
enabled: true

View File

@ -1,8 +0,0 @@
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: harbor
namespace: flux-system
spec:
interval: 1h
url: https://helm.goharbor.io

View File

@ -1,7 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- namespace.yaml
- certificate.yaml
- helmrepository.yaml
- helmrelease.yaml

View File

@ -1,4 +0,0 @@
apiVersion: v1
kind: Namespace
metadata:
name: harbor