From 9a72c8d3ec461af238213858aa78daca976f3c32 Mon Sep 17 00:00:00 2001
From: centra
Date: Fri, 19 Dec 2025 08:14:44 +0900
Subject: [PATCH] fix(nix): Bind REST APIs to 0.0.0.0 for cluster join
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- chainfire.nix: CHAINFIRE__NETWORK__HTTP_ADDR env var
- flaredb.nix: FLAREDB_HTTP_ADDR env var
- first-boot-automation.nix: jq-based config reading
Fixes ChainFire crash: "unexpected argument '--http-addr' found"
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5
---
nix/modules/chainfire.nix | 11 +++++++++
nix/modules/first-boot-automation.nix | 33 +++++++++++++++++++--------
nix/modules/flaredb.nix | 10 ++++++++
3 files changed, 44 insertions(+), 10 deletions(-)
diff --git a/nix/modules/chainfire.nix b/nix/modules/chainfire.nix
index 11f45a6..7f6216b 100644
--- a/nix/modules/chainfire.nix
+++ b/nix/modules/chainfire.nix
@@ -25,6 +25,12 @@ in
description = "Port for chainfire gossip protocol";
};
+ httpPort = lib.mkOption {
+ type = lib.types.port;
+ default = 8081;
+ description = "Port for chainfire HTTP/admin API (used for cluster join)";
+ };
+
dataDir = lib.mkOption {
type = lib.types.path;
default = "/var/lib/chainfire";
@@ -61,6 +67,11 @@ in
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
+ environment = {
+ # Set HTTP admin address via environment variable (config-rs format: CHAINFIRE__NETWORK__HTTP_ADDR)
+ CHAINFIRE__NETWORK__HTTP_ADDR = "0.0.0.0:${toString cfg.httpPort}";
+ };
+
serviceConfig = {
Type = "simple";
User = "chainfire";
diff --git a/nix/modules/first-boot-automation.nix b/nix/modules/first-boot-automation.nix
index e0af24b..b326da7 100644
--- a/nix/modules/first-boot-automation.nix
+++ b/nix/modules/first-boot-automation.nix
@@ -76,6 +76,19 @@ let
log "INFO" "Starting cluster join process for ${serviceName}"
+ # Read cluster config at runtime
+ CONFIG_FILE="${cfg.configFile}"
+ if [ -f "$CONFIG_FILE" ]; then
+ IS_BOOTSTRAP=$(${pkgs.jq}/bin/jq -r '.bootstrap // false' "$CONFIG_FILE")
+ LEADER_URL=$(${pkgs.jq}/bin/jq -r '.leader_url // "https://localhost:${toString port}"' "$CONFIG_FILE")
+ NODE_ID=$(${pkgs.jq}/bin/jq -r '.node_id // "unknown"' "$CONFIG_FILE")
+ RAFT_ADDR=$(${pkgs.jq}/bin/jq -r '.raft_addr // "127.0.0.1:${toString (port + 1)}"' "$CONFIG_FILE")
+ log "INFO" "Loaded config: bootstrap=$IS_BOOTSTRAP, node_id=$NODE_ID"
+ else
+ log "ERROR" "Config file not found: $CONFIG_FILE"
+ exit 1
+ fi
+
# Wait for local service health
log "INFO" "Waiting for local ${serviceName} to be healthy"
@@ -91,7 +104,7 @@ let
exit 1
fi
- HTTP_CODE=$(${pkgs.curl}/bin/curl -k -s -o /dev/null -w "%{http_code}" "${healthUrl}" 2>/dev/null || echo "000")
+ HTTP_CODE=$(${pkgs.curl}/bin/curl -s -o /dev/null -w "%{http_code}" "${healthUrl}" 2>/dev/null || echo "000")
if [ "$HTTP_CODE" = "200" ]; then
log "INFO" "Local ${serviceName} is healthy"
@@ -103,7 +116,7 @@ let
done
# Check if this is a bootstrap node
- if [ "${toString isBootstrap}" = "true" ]; then
+ if [ "$IS_BOOTSTRAP" = "true" ]; then
log "INFO" "Bootstrap node detected, cluster already initialized"
# Create marker to indicate initialization complete
@@ -121,7 +134,7 @@ let
# Join existing cluster
log "INFO" "Attempting to join existing cluster"
- log "INFO" "Leader URL: ${leaderUrl}, Node ID: ${nodeId}, Raft Addr: ${raftAddr}"
+ log "INFO" "Leader URL: $LEADER_URL, Node ID: $NODE_ID, Raft Addr: $RAFT_ADDR"
MAX_ATTEMPTS=5
RETRY_DELAY=10
@@ -131,10 +144,10 @@ let
# Make join request
RESPONSE_FILE=$(mktemp)
- HTTP_CODE=$(${pkgs.curl}/bin/curl -k -s -w "%{http_code}" -o "$RESPONSE_FILE" \
- -X POST "${leaderUrl}${leaderUrlPath}" \
+ HTTP_CODE=$(${pkgs.curl}/bin/curl -s -w "%{http_code}" -o "$RESPONSE_FILE" \
+ -X POST "$LEADER_URL${leaderUrlPath}" \
-H "Content-Type: application/json" \
- -d "{\"id\":\"${nodeId}\",\"raft_addr\":\"${raftAddr}\"}" 2>/dev/null || echo "000")
+ -d "{\"id\":\"$NODE_ID\",\"raft_addr\":\"$RAFT_ADDR\"}" 2>/dev/null || echo "000")
RESPONSE_BODY=$(cat "$RESPONSE_FILE" 2>/dev/null || echo "")
rm -f "$RESPONSE_FILE"
@@ -242,7 +255,7 @@ in
systemd.services.chainfire-cluster-join = lib.mkIf cfg.enableChainfire (
mkClusterJoinService {
serviceName = "chainfire";
- healthUrl = "https://localhost:${toString cfg.chainfirePort}/health";
+ healthUrl = "http://localhost:8081/health"; # Health endpoint on admin port
leaderUrlPath = "/admin/member/add";
port = cfg.chainfirePort;
description = "Chainfire";
@@ -253,7 +266,7 @@ in
systemd.services.flaredb-cluster-join = lib.mkIf cfg.enableFlareDB (
mkClusterJoinService {
serviceName = "flaredb";
- healthUrl = "https://localhost:${toString cfg.flaredbPort}/health";
+ healthUrl = "http://localhost:8082/health"; # Health endpoint on admin port
leaderUrlPath = "/admin/member/add";
port = cfg.flaredbPort;
description = "FlareDB";
@@ -378,7 +391,7 @@ in
# Check Chainfire
if systemctl is-active chainfire.service > /dev/null 2>&1; then
- HTTP_CODE=$(${pkgs.curl}/bin/curl -k -s -o /dev/null -w "%{http_code}" "https://localhost:${toString cfg.chainfirePort}/health" 2>/dev/null || echo "000")
+ HTTP_CODE=$(${pkgs.curl}/bin/curl -s -o /dev/null -w "%{http_code}" "http://localhost:8081/health" 2>/dev/null || echo "000")
if [ "$HTTP_CODE" = "200" ]; then
log "INFO" "Chainfire health check: PASSED"
@@ -390,7 +403,7 @@ in
# Check FlareDB
if systemctl is-active flaredb.service > /dev/null 2>&1; then
- HTTP_CODE=$(${pkgs.curl}/bin/curl -k -s -o /dev/null -w "%{http_code}" "https://localhost:${toString cfg.flaredbPort}/health" 2>/dev/null || echo "000")
+ HTTP_CODE=$(${pkgs.curl}/bin/curl -s -o /dev/null -w "%{http_code}" "http://localhost:8082/health" 2>/dev/null || echo "000")
if [ "$HTTP_CODE" = "200" ]; then
log "INFO" "FlareDB health check: PASSED"
diff --git a/nix/modules/flaredb.nix b/nix/modules/flaredb.nix
index 9767baa..471065e 100644
--- a/nix/modules/flaredb.nix
+++ b/nix/modules/flaredb.nix
@@ -19,6 +19,12 @@ in
description = "Port for flaredb Raft protocol";
};
+ httpPort = lib.mkOption {
+ type = lib.types.port;
+ default = 8082;
+ description = "Port for flaredb HTTP/admin API (used for cluster join)";
+ };
+
dataDir = lib.mkOption {
type = lib.types.path;
default = "/var/lib/flaredb";
@@ -56,6 +62,10 @@ in
after = [ "network.target" "chainfire.service" ];
requires = [ "chainfire.service" ];
+ environment = {
+ FLAREDB_HTTP_ADDR = "0.0.0.0:${toString cfg.httpPort}";
+ };
+
serviceConfig = {
Type = "simple";
User = "flaredb";