#!/bin/bash # Single-container entrypoint: generate the FIPS config, apply iptables # isolation, start the Nostr relay (strfry + nginx), start the Nym SOCKS5 # client (only when the FIPS config enables the nym transport), and launch # FIPS last — so the mixnet proxy is provably up before FIPS dials its peer. set -e # --- Generate FIPS config from environment variables --- FIPS_NSEC="${FIPS_NSEC:?FIPS_NSEC is required}" FIPS_UDP_BIND="${FIPS_UDP_BIND:-0.0.0.0:2121}" FIPS_TCP_BIND="${FIPS_TCP_BIND:-0.0.0.0:8443}" FIPS_TUN_MTU="${FIPS_TUN_MTU:-1280}" FIPS_UDP_MTU="${FIPS_UDP_MTU:-1472}" FIPS_PEER_TRANSPORT="${FIPS_PEER_TRANSPORT:-nym}" FIPS_NYM_SOCKS5_ADDR="${FIPS_NYM_SOCKS5_ADDR:-127.0.0.1:1080}" NYM_CLIENT_ID="${NYM_CLIENT_ID:-fips-nym-client}" NYM_STARTUP_TIMEOUT="${NYM_STARTUP_TIMEOUT:-180}" mkdir -p /etc/fips # Build peers section PEERS_SECTION="" if [ -n "$FIPS_PEER_NPUB" ] && [ -n "$FIPS_PEER_ADDR" ]; then FIPS_PEER_ALIAS="${FIPS_PEER_ALIAS:-peer}" PEERS_SECTION=" - npub: \"${FIPS_PEER_NPUB}\" alias: \"${FIPS_PEER_ALIAS}\" addresses: - transport: ${FIPS_PEER_TRANSPORT} addr: \"${FIPS_PEER_ADDR}\" connect_policy: auto_connect" fi # The nym transport block is emitted only in nym mode; the SOCKS5 client # below starts only when this block is present in the config. NYM_SECTION="" if [ "$FIPS_PEER_TRANSPORT" = "nym" ]; then NYM_SECTION=" nym: socks5_addr: \"${FIPS_NYM_SOCKS5_ADDR}\" startup_timeout_secs: 120" fi cat > /etc/fips/fips.yaml </dev/null \ | jq -r '[.items[] | select(.routing_score == 1.0)] | sort_by(.last_updated_utc) | last | .service_provider_client_id // empty' 2>/dev/null \ || true) if [ -z "$NYM_SERVICE_PROVIDER" ]; then # Last resort: a provider known to work at the time of # writing (2026-06). Providers are volatile community # infra — if the mixnet connects but no traffic flows # ('no node with identity … is known' warnings), this # fallback has gone stale: pick a current one from # https://harbourmaster.nymtech.net/ and set it in .env. NYM_SERVICE_PROVIDER="${NYM_FALLBACK_PROVIDER:-7sfw3sEtSPwhWLmEasVmPXKxqioCo4GaXRkm9bW6yWGZ.CkhMoH85wfNcV2fwoBjc6QDbcaFZHzKqFFvXWfYMw19y@4ScsM6AVowhKTMWaH98NLntKDwbu2ZMEycUk4mZiZppG}" echo "WARNING: harbourmaster auto-discovery failed — using the" >&2 echo "baked-in fallback provider (may be stale; see .env):" >&2 echo " ${NYM_SERVICE_PROVIDER}" >&2 else echo "Auto-selected service provider: ${NYM_SERVICE_PROVIDER}" fi fi echo "Initializing Nym SOCKS5 client '${NYM_CLIENT_ID}' …" nym-socks5-client init \ --id "${NYM_CLIENT_ID}" \ --provider "${NYM_SERVICE_PROVIDER}" \ --port "${NYM_PORT}" \ --host "${NYM_HOST}" else # The provider is baked into the client state at init time — a value # set or discovered now does NOT apply to an existing client. Surface # the one actually in effect so a stale/dead provider isn't chased # silently (symptom: 'no node with identity … is known' warnings). STORED_PROVIDER=$(grep -m1 -oE '[1-9A-HJ-NP-Za-km-z]{20,}\.[1-9A-HJ-NP-Za-km-z]{20,}@[1-9A-HJ-NP-Za-km-z]{20,}' \ "${HOME}/.nym/socks5-clients/${NYM_CLIENT_ID}/config/config.toml" 2>/dev/null || true) echo "Reusing existing Nym client state (provider: ${STORED_PROVIDER:-unknown})." echo "To switch provider, remove the nym-data volume: docker compose down -v" fi echo "Starting Nym SOCKS5 client (mixnet bootstrap may take a minute) …" nym-socks5-client run \ --id "${NYM_CLIENT_ID}" \ --port "${NYM_PORT}" \ --host "${NYM_HOST}" & # FIPS must not start dialing before the proxy accepts connections. elapsed=0 until nc -z "$NYM_HOST" "$NYM_PORT" 2>/dev/null; do if [ "$elapsed" -ge "$NYM_STARTUP_TIMEOUT" ]; then echo "ERROR: Nym SOCKS5 proxy not ready after ${NYM_STARTUP_TIMEOUT}s" >&2 exit 1 fi sleep 2 elapsed=$((elapsed + 2)) done echo "Nym SOCKS5 proxy ready at ${FIPS_NYM_SOCKS5_ADDR} (after ~${elapsed}s)" fi # --- Launch FIPS (container lifecycle follows the daemon) --- echo "Starting FIPS daemon..." exec fips --config /etc/fips/fips.yaml