Skip to content

Commit a61e775

Browse files
hyperpolymathclaude
andcommitted
feat: federation auto-bootstrap in container entrypoint
- Auto-generate stable BOJ_NODE_ID from hostname + random suffix, persist to data volume across restarts - Background seed bootstrap: wait for REST health, then POST each seed node to /umoja/peers (BOJ_SEED_NODES env or built-in defaults) - Quadlet updated with seed node and node ID documentation One-click node setup now auto-joins the Umoja federation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent ba4b363 commit a61e775

2 files changed

Lines changed: 79 additions & 7 deletions

File tree

container/boj-community-node.container

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ Environment=APP_DATA_DIR=/data
4545
Environment=APP_LOG_FORMAT=json
4646
Environment=BOJ_QUIC=1
4747
Environment=BOJ_FEDERATION_PORT=9999
48+
# Node auto-joins the Umoja federation on startup using these seed nodes.
49+
# Override with a comma-separated list if running a private network.
50+
# Environment=BOJ_SEED_NODES=eu.boj.hyperpolymath.dev:9999,us.boj.hyperpolymath.dev:9999
51+
# Node ID is auto-generated and persisted in the data volume.
52+
# Set explicitly to override: Environment=BOJ_NODE_ID=my-node-name
4853

4954
# Security hardening
5055
ReadOnly=true

container/entrypoint.sh

Lines changed: 74 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,17 +47,84 @@ if [ -d "${APP_DATA_DIR:-/data}" ]; then
4747
fi
4848
fi
4949

50+
# ---------------------------------------------------------------------------
51+
# Node ID auto-generation
52+
# ---------------------------------------------------------------------------
53+
#
54+
# If BOJ_NODE_ID is not set, generate a stable one from the hostname.
55+
# Persist to DATA_DIR so identity survives restarts.
56+
57+
DATA_DIR="${APP_DATA_DIR:-/data}"
58+
NODE_ID_FILE="${DATA_DIR}/.node-id"
59+
60+
if [ -z "${BOJ_NODE_ID:-}" ]; then
61+
if [ -f "$NODE_ID_FILE" ]; then
62+
BOJ_NODE_ID=$(cat "$NODE_ID_FILE")
63+
else
64+
# Generate from hostname + random suffix
65+
SUFFIX=$(head -c 4 /dev/urandom | od -An -tx1 | tr -d ' \n')
66+
BOJ_NODE_ID="node-$(hostname -s)-${SUFFIX}"
67+
if [ -w "$DATA_DIR" ]; then
68+
echo "$BOJ_NODE_ID" > "$NODE_ID_FILE"
69+
fi
70+
fi
71+
export BOJ_NODE_ID
72+
fi
73+
echo " Node: ${BOJ_NODE_ID}"
74+
75+
# ---------------------------------------------------------------------------
76+
# Federation auto-seed (background)
77+
# ---------------------------------------------------------------------------
78+
#
79+
# After the main process starts, wait for the REST API to be healthy,
80+
# then POST each seed node to /umoja/peers. Uses BOJ_SEED_NODES env
81+
# (comma-separated host:port) or falls back to the built-in seeds.
82+
83+
DEFAULT_SEEDS="eu.boj.hyperpolymath.dev:9999,de.boj.hyperpolymath.dev:9999,us.boj.hyperpolymath.dev:9999,ap.boj.hyperpolymath.dev:9999"
84+
SEEDS="${BOJ_SEED_NODES:-$DEFAULT_SEEDS}"
85+
REST_PORT="${APP_PORT:-7700}"
86+
87+
bootstrap_federation() {
88+
# Wait for the REST API to become available
89+
TRIES=0
90+
MAX_TRIES=20
91+
while [ "$TRIES" -lt "$MAX_TRIES" ]; do
92+
if curl -sf "http://localhost:${REST_PORT}/health" > /dev/null 2>&1; then
93+
break
94+
fi
95+
TRIES=$((TRIES + 1))
96+
sleep 1
97+
done
98+
99+
if [ "$TRIES" -ge "$MAX_TRIES" ]; then
100+
echo "WARNING: REST API did not become healthy — skipping federation bootstrap"
101+
return
102+
fi
103+
104+
# Add each seed node as a peer
105+
echo "Bootstrapping federation with seed nodes..."
106+
IFS=','
107+
for seed in $SEEDS; do
108+
host=$(echo "$seed" | cut -d: -f1)
109+
port=$(echo "$seed" | cut -d: -f2)
110+
curl -sf -X POST "http://localhost:${REST_PORT}/umoja/peers" \
111+
-H "Content-Type: application/json" \
112+
-d "{\"host\":\"${host}\",\"port\":${port}}" > /dev/null 2>&1 \
113+
&& echo " Seeded: ${host}:${port}" \
114+
|| echo " Seed unreachable: ${host}:${port} (will retry via gossip)"
115+
done
116+
unset IFS
117+
echo "Federation bootstrap complete"
118+
}
119+
120+
# Run bootstrap in background so exec happens immediately
121+
bootstrap_federation &
122+
50123
# ---------------------------------------------------------------------------
51124
# Exec into main process
52125
# ---------------------------------------------------------------------------
53126
#
54127
# Replace the entrypoint shell with the application process so that
55128
# signals are delivered directly and PID 1 is the application.
56-
#
57-
# TODO: Replace the command below with your application binary.
58-
# Examples:
59-
# exec /app/boj-server
60-
# exec /app/release/bin/boj-server start
61-
# exec /app/boj-server serve --host "${APP_HOST}" --port "${APP_PORT}"
62129

63-
exec "$@"
130+
exec /app/boj-server serve --host "${APP_HOST:-[::]}" --port "${REST_PORT}"

0 commit comments

Comments
 (0)