Deploys handshake-node — the Go implementation of a Handshake (HNS) blockchain full node from Blink Labs — as a Kubernetes StatefulSet.
This chart is distinct from the hsd chart, which packages the
JavaScript reference implementation from handshake-org.
helm install my-handshake-node oci://ghcr.io/blinklabs-io/helm-charts/charts/handshake-nodeThe chart deploys a single-replica StatefulSet running handshake-node
against the Handshake mainnet by default. Persistent state is stored in a
PersistentVolumeClaim mounted at /home/handshake/.handshake-node.
Security-relevant defaults:
- The container runs as the non-root
handshakeuser baked into the upstream image withrunAsNonRoot: true, all Linux capabilities dropped, no privilege escalation,readOnlyRootFilesystem: true, and theRuntimeDefaultseccomp profile. - The peer-to-peer Service is
ClusterIPby default. Public exposure viaNodePortorLoadBalanceris an explicit operator choice. - The authenticated TLS RPC listener (port
12037) is not exposed unlessrpc.enabled=true. When enabled, the chart renders aClusterIP-only Service that stays inside the cluster. - RPC credentials are read from an existing Kubernetes Secret via
secretKeyRef— they are never taken fromvalues.yamlor a ConfigMap. - The Prometheus metrics endpoint (
12039) and Stratum server (12040) are disabled by default. When enabled they are kept on privateClusterIPServices.
- Kubernetes 1.24+
- Helm 3.8+ (for OCI registry support)
- A default StorageClass (or set
persistence.storageClass)
helm install my-handshake-node \
oci://ghcr.io/blinklabs-io/helm-charts/charts/handshake-node \
--namespace handshake --create-namespacehelm uninstall my-handshake-node --namespace handshakeThe PVC is retained by default (persistence.retention.whenDeleted=Retain).
Delete it manually if you no longer need the chain data:
kubectl -n handshake delete pvc data-my-handshake-node-0The chart provisions a PVC via volumeClaimTemplates:
persistence:
enabled: true
accessModes:
- ReadWriteOnce
size: 200Gi
# storageClass: fast-ssd
retention:
whenDeleted: Retain
whenScaled: RetainTo bind an existing claim instead, set persistence.existingClaim:
persistence:
enabled: true
existingClaim: my-existing-handshake-pvcRPC is disabled by default. To enable it, create a Kubernetes Secret with the
credentials, then reference it from values.yaml:
kubectl -n handshake create secret generic handshake-node-rpc \
--from-literal=rpcuser=hnsuser \
--from-literal=rpcpass='replace-with-a-strong-password'rpc:
enabled: true
existingSecret: handshake-node-rpc
userKey: rpcuser
passwordKey: rpcpassThe chart injects these values as the HANDSHAKE_NODE_RPCUSER and
HANDSHAKE_NODE_RPCPASS environment variables via secretKeyRef. It also
renders a ClusterIP-only Service named <release>-handshake-node-rpc on
port 12037. If you need remote clients to reach RPC, port-forward the
service or front it with an in-cluster gateway that terminates its own TLS —
do not change the Service type in this chart.
Enable the Prometheus endpoint (disabled by default):
metrics:
enabled: true
# Keep this bound to a routable interface inside the pod. The daemon
# rejects non-loopback binds unless allowPublic is true.
listen: "0.0.0.0:12039"
allowPublic: true
serviceMonitor:
enabled: true
interval: 30sThe metrics Service is always ClusterIP. Prometheus Operator users can
turn on the ServiceMonitor with metrics.serviceMonitor.enabled=true.
Stratum is disabled by default. To turn it on for internal miners:
kubectl -n handshake create secret generic handshake-node-stratum \
--from-literal=stratumuser=worker \
--from-literal=stratumpass='replace-with-a-strong-password'stratum:
enabled: true
listen: "0.0.0.0:12040"
allowPublic: true
miningAddress: hs1qyourhandshakeaddress
auth:
existingSecret: handshake-node-stratum
service:
enabled: true
type: ClusterIPPublic exposure requires stratum.allowPublic=true and both
stratumuser/stratumpass per the upstream daemon.
The peer-to-peer Service defaults to ClusterIP. To advertise the node on the
public Handshake network you must explicitly opt in:
service:
p2p:
type: LoadBalancer
port: 12038
loadBalancerSourceRanges:
- 0.0.0.0/0Use NodePort if you route through an external load balancer:
service:
p2p:
type: NodePort
port: 12038
nodePort: 32038Full mainnet node with private RPC, metrics enabled, and a beefier PVC:
network: main
image:
tag: "0.1.1-rc1"
persistence:
size: 400Gi
storageClass: fast-ssd
rpc:
enabled: true
existingSecret: handshake-node-rpc
metrics:
enabled: true
serviceMonitor:
enabled: true
resources:
requests:
cpu: 1
memory: 2Gi
limits:
cpu: 4
memory: 8Gi
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app.kubernetes.io/name: handshake-nodehelm upgrade my-handshake-node \
oci://ghcr.io/blinklabs-io/helm-charts/charts/handshake-node \
--namespace handshake \
--reuse-values \
--version <new-chart-version>The PVC survives an upgrade because retention defaults to Retain. Chart
upgrades that only change the container image trigger a rolling restart of
the single StatefulSet pod.
See values.yaml for the full list of tunables. Key knobs:
| Key | Description | Default |
|---|---|---|
image.repository |
Image name | ghcr.io/blinklabs-io/handshake-node |
image.tag |
Image tag (never latest) |
0.1.1-rc1 |
network |
Handshake network: main, regtest, simnet |
main |
persistence.size |
PVC size | 200Gi |
persistence.storageClass |
StorageClass name | cluster default |
rpc.enabled |
Enable authenticated RPC | false |
rpc.existingSecret |
Name of Secret with rpcuser/rpcpass |
"" |
metrics.enabled |
Enable Prometheus endpoint | false |
metrics.serviceMonitor.enabled |
Create ServiceMonitor | false |
stratum.enabled |
Enable Stratum server | false |
service.p2p.type |
P2P Service type | ClusterIP |
podSecurityContext |
Pod-level security context | non-root, seccomp RuntimeDefault |
securityContext |
Container security context | drop ALL, no privilege escalation |
resources |
CPU/memory requests/limits | {} |