diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..c296876 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,39 @@ +# Build artifacts +bin/ +data/ +logs/ + +# Git +.git/ +.gitignore + +# Docker +Dockerfile* +docker-compose*.yml +.dockerignore + +# Documentation +*.md +docs/ + +# IDE +.vscode/ +.idea/ +*.swp +*.swo + +# OS +.DS_Store +Thumbs.db + +# Temporary files +*.tmp +*.temp +*~ + +# Test files +*_test.go +e2e/ + +# Examples (optional - remove if you want to include them) +examples/ diff --git a/.gitignore b/.gitignore index 4eff86c..e7c2469 100644 --- a/.gitignore +++ b/.gitignore @@ -73,3 +73,6 @@ data/bootstrap/rqlite/ .env.* configs/ + +# PR documentation (temporary files) +PR_CHANGES.md diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..49af40e --- /dev/null +++ b/Dockerfile @@ -0,0 +1,67 @@ +# Multi-stage build for DeBros Network +# Stage 1: Build stage +FROM golang:1.23-alpine AS builder + +# Install build dependencies +RUN apk add --no-cache git make gcc musl-dev wget tar + +# Set working directory +WORKDIR /app + +# Build RQLite from source for Alpine compatibility +RUN RQLITE_VERSION="8.43.0" && \ + git clone --depth 1 --branch v${RQLITE_VERSION} https://github.com/rqlite/rqlite.git /tmp/rqlite && \ + cd /tmp/rqlite && \ + go build -o /usr/local/bin/rqlited ./cmd/rqlited && \ + go build -o /usr/local/bin/rqlite ./cmd/rqlite && \ + chmod +x /usr/local/bin/rqlited /usr/local/bin/rqlite && \ + rm -rf /tmp/rqlite + +# Copy go mod files first for better caching +COPY go.mod go.sum ./ +RUN go mod download + +# Copy source code +COPY . . + +# Build only node and gateway executables (CLI is built separately on host) +RUN mkdir -p bin && \ + go build -ldflags "-X 'main.version=0.50.1-beta' -X 'main.commit=unknown' -X 'main.date=2025-09-23T15:40:00Z'" -o bin/node ./cmd/node && \ + go build -ldflags "-X 'main.version=0.50.1-beta' -X 'main.commit=unknown' -X 'main.date=2025-09-23T15:40:00Z' -X 'github.com/DeBrosOfficial/network/pkg/gateway.BuildVersion=0.50.1-beta' -X 'github.com/DeBrosOfficial/network/pkg/gateway.BuildCommit=unknown' -X 'github.com/DeBrosOfficial/network/pkg/gateway.BuildTime=2025-09-23T15:40:00Z'" -o bin/gateway ./cmd/gateway + +# Stage 2: Runtime stage +FROM alpine:latest + +# Install runtime dependencies +RUN apk add --no-cache ca-certificates tzdata wget + +# Create non-root user +RUN addgroup -g 1001 -S debros && \ + adduser -u 1001 -S debros -G debros + +# Create necessary directories +RUN mkdir -p /app/bin /app/data /app/configs /app/logs && \ + chown -R debros:debros /app + +# Copy built executables from builder stage +COPY --from=builder /app/bin/ /app/bin/ + +# Copy RQLite binaries from builder stage +COPY --from=builder /usr/local/bin/rqlited /usr/local/bin/rqlited +COPY --from=builder /usr/local/bin/rqlite /usr/local/bin/rqlite + +# Set working directory +WORKDIR /app + +# Switch to non-root user +USER debros + +# Expose ports +# 4001: LibP2P P2P communication +# 5001: RQLite HTTP API +# 6001: Gateway HTTP/WebSocket +# 7001: RQLite Raft consensus +EXPOSE 4001 5001 6001 7001 + +# Default command (can be overridden in docker-compose) +CMD ["./bin/node"] diff --git a/WINDOWS_DOCKER_SETUP.md b/WINDOWS_DOCKER_SETUP.md new file mode 100644 index 0000000..8db374d --- /dev/null +++ b/WINDOWS_DOCKER_SETUP.md @@ -0,0 +1,250 @@ +# DeBros Network - Windows Docker Setup Guide + +Simple step-by-step guide to run the DeBros Network locally on Windows using Docker. + +## Prerequisites + +✅ **Docker Desktop for Windows** - Must be installed and running +✅ **PowerShell** - Comes with Windows +✅ **Git** - For cloning the repository + +## Step 1: Clone and Build + +```powershell +# Clone the repository +git clone https://github.com/DeBrosOfficial/network.git +cd network + +# Build the CLI for local testing +go build -ldflags "-X 'main.version=0.50.1-beta'" -o bin/network-cli.exe cmd/cli/main.go +``` + +## Step 2: Start the Docker Network + +```powershell +# Start all services (this will take 1-2 minutes) +docker-compose up -d + +# Check that all containers are running +docker-compose ps +``` + +**Expected output:** 4 containers running: +- `debros-bootstrap` (Bootstrap Node) +- `debros-node-2` (Node 2) +- `debros-node-3` (Node 3) +- `debros-gateway` (HTTP Gateway) + +## Step 3: Wait for Network Initialization + +```powershell +# Wait for all services to start up +Start-Sleep -Seconds 30 + +# Verify bootstrap node is running +docker logs debros-bootstrap --tail 5 +``` + +**Look for:** `"current_peers": 2` (means node-2 and node-3 connected) + +## Step 4: Setup CLI Environment + +```powershell +# Set these environment variables (required for every new terminal) +$env:DEBROS_GATEWAY_URL="http://localhost:6001" +$BOOTSTRAP_PEER="/ip4/127.0.0.1/tcp/4001/p2p/12D3KooWQX6jcPTVSsBuVuxdkbMbau3DAqZT4pc7UgGh2FvDxrKr" +``` + +## Step 5: Test the Network + +### Basic Health Check +```powershell +# Check if network is healthy +.\bin\network-cli.exe health --bootstrap $BOOTSTRAP_PEER +``` +**Expected:** `Status: 🟢 healthy` + +### View Connected Peers +```powershell +# List connected peers +.\bin\network-cli.exe peers --bootstrap $BOOTSTRAP_PEER +``` +**Expected:** 2 peers (your CLI + bootstrap node) - **This is normal!** + +### Test Messaging (Requires Authentication) +```powershell +# Send a message (first time will prompt for wallet authentication) +.\bin\network-cli.exe pubsub publish test-topic "Hello DeBros Network!" --bootstrap $BOOTSTRAP_PEER + +# Listen for messages (in another terminal) +.\bin\network-cli.exe pubsub subscribe test-topic 10s --bootstrap $BOOTSTRAP_PEER +``` + +### Test Database (Requires Authentication) +```powershell +# Query the database +.\bin\network-cli.exe query "SELECT * FROM namespaces" --bootstrap $BOOTSTRAP_PEER +``` + +## Network Architecture + +Your local network consists of: + +``` +┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐ +│ Bootstrap │◄──►│ Node-2 │◄──►│ Node-3 │ +│ localhost:4001 │ │ localhost:4002 │ │ localhost:4003 │ +│ localhost:5001 │ │ localhost:5002 │ │ localhost:5003 │ +└─────────────────┘ └──────────────────┘ └─────────────────┘ + ▲ + │ + ▼ +┌─────────────────┐ ┌──────────────────┐ +│ CLI Client │ │ Gateway │ +│ (Your Terminal) │ │ localhost:6001 │ +└─────────────────┘ └──────────────────┘ +``` + +**Ports Used:** +- **4001-4003**: P2P communication between nodes +- **5001-5003**: Database HTTP API +- **6001**: Gateway HTTP API +- **7001-7003**: Database internal communication + +## Daily Usage Commands + +### Start Your Development Session +```powershell +# Start the network +docker-compose up -d + +# Setup environment (run in every new terminal) +$env:DEBROS_GATEWAY_URL="http://localhost:6001" +$BOOTSTRAP_PEER="/ip4/127.0.0.1/tcp/4001/p2p/12D3KooWQX6jcPTVSsBuVuxdkbMbau3DAqZT4pc7UgGh2FvDxrKr" + +# Quick health check +.\bin\network-cli.exe health --bootstrap $BOOTSTRAP_PEER +``` + +### Common CLI Commands +```powershell +# Network status +.\bin\network-cli.exe status --bootstrap $BOOTSTRAP_PEER + +# Send message +.\bin\network-cli.exe pubsub publish my-topic "Hello Network!" --bootstrap $BOOTSTRAP_PEER + +# Listen for messages +.\bin\network-cli.exe pubsub subscribe my-topic 15s --bootstrap $BOOTSTRAP_PEER + +# Database query +.\bin\network-cli.exe query "SELECT datetime('now') as current_time" --bootstrap $BOOTSTRAP_PEER + +# List all database tables +.\bin\network-cli.exe query "SELECT name FROM sqlite_master WHERE type='table'" --bootstrap $BOOTSTRAP_PEER +``` + +### End Your Development Session +```powershell +# Stop network (keeps all data) +docker-compose stop + +# OR stop and remove all data (clean slate) +docker-compose down -v +``` + +## Troubleshooting + +### Check Container Status +```powershell +# View all containers +docker-compose ps + +# Check specific container logs +docker logs debros-bootstrap --tail 20 +docker logs debros-gateway --tail 20 +docker logs debros-node-2 --tail 20 +``` + +### Common Issues and Solutions + +**❌ "Connection refused" error** +```powershell +# Solution: Check containers are running and wait longer +docker-compose ps +Start-Sleep -Seconds 60 +``` + +**❌ "Authentication failed"** +```powershell +# Solution: Check gateway is running +docker logs debros-gateway --tail 10 +``` + +**❌ Only seeing 2 peers instead of 4** +``` +✅ This is NORMAL! The CLI only sees itself + bootstrap node. + Node-2 and Node-3 are connected to bootstrap but not visible to CLI. + Your network is working correctly! +``` + +### Restart Services +```powershell +# Restart all services +docker-compose restart + +# Restart specific service +docker-compose restart bootstrap-node + +# Complete clean restart +docker-compose down -v +docker-compose up -d +``` + +### View Real-time Logs +```powershell +# Follow logs for all services +docker-compose logs -f + +# Follow logs for specific service +docker-compose logs -f bootstrap-node +``` + +## Important Notes + +### Authentication +- First CLI command will prompt you to connect your wallet +- You'll receive an API key for future commands +- API key is stored locally and reused automatically + +### Network Isolation +- **Your Docker Network**: Completely isolated for development +- **Production Network**: Live DeBros Network (separate) +- **Default CLI**: Connects to production (without `--bootstrap` flag) +- **CLI with `--bootstrap`**: Connects to your local Docker network + +### Data Persistence +- All data is stored in Docker volumes +- Survives container restarts +- Use `docker-compose down -v` to reset everything + +## Quick Reference Card + +```powershell +# Essential daily commands +docker-compose up -d # Start +$env:DEBROS_GATEWAY_URL="http://localhost:6001" # Setup +$BOOTSTRAP_PEER="/ip4/127.0.0.1/tcp/4001/p2p/12D3KooWQX6jcPTVSsBuVuxdkbMbau3DAqZT4pc7UgGh2FvDxrKr" +.\bin\network-cli.exe health --bootstrap $BOOTSTRAP_PEER # Test +docker-compose down # Stop +``` + +--- + +🎉 **Your local DeBros Network is ready for development and testing!** + +The network is completely isolated from production and perfect for: +- Testing new features +- Learning the DeBros Network +- Developing applications +- Experimenting safely diff --git a/cmd/cli/main.go b/cmd/cli/main.go index c3fd8a6..7cc4a15 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -414,6 +414,11 @@ func handlePeerID() { func createClient() (client.NetworkClient, error) { config := client.DefaultClientConfig("network-cli") + // Override bootstrap peers if specified via flag + if bootstrapPeer != "/ip4/127.0.0.1/tcp/4001" { + config.BootstrapPeers = []string{bootstrapPeer} + } + // Check for existing credentials using enhanced authentication creds, err := auth.GetValidEnhancedCredentials() if err != nil { diff --git a/cmd/gateway/config.go b/cmd/gateway/config.go index cc8f345..e626372 100644 --- a/cmd/gateway/config.go +++ b/cmd/gateway/config.go @@ -100,11 +100,15 @@ func parseGatewayConfig(logger *logging.ColoredLogger) *gateway.Config { } cfg.BootstrapPeers = bp } + if v := strings.TrimSpace(os.Getenv("RQLITE_DSN")); v != "" { + cfg.RQLiteDSN = v + } // 3) Flags (override env) addr := flag.String("addr", "", "HTTP listen address (e.g., :6001)") ns := flag.String("namespace", "", "Client namespace for scoping resources") peers := flag.String("bootstrap-peers", "", "Comma-separated bootstrap peers for network client") + rqliteDSN := flag.String("rqlite-dsn", "", "RQLite database DSN (e.g., http://localhost:5001)") // Do not call flag.Parse() elsewhere to avoid double-parsing flag.Parse() @@ -126,6 +130,9 @@ func parseGatewayConfig(logger *logging.ColoredLogger) *gateway.Config { } cfg.BootstrapPeers = bp } + if r := strings.TrimSpace(*rqliteDSN); r != "" { + cfg.RQLiteDSN = r + } logger.ComponentInfo(logging.ComponentGeneral, "Loaded gateway configuration", zap.String("addr", cfg.ListenAddr), diff --git a/cmd/node/main.go b/cmd/node/main.go index 53fb562..6c531ba 100644 --- a/cmd/node/main.go +++ b/cmd/node/main.go @@ -31,7 +31,7 @@ func setup_logger(component logging.Component) (logger *logging.ColoredLogger) { } // parse_and_return_network_flags it initializes all the network flags coming from the .yaml files -func parse_and_return_network_flags() (configPath *string, dataDir, nodeID *string, p2pPort, rqlHTTP, rqlRaft *int, rqlJoinAddr *string, advAddr *string, help *bool) { +func parse_and_return_network_flags() (configPath *string, dataDir, nodeID *string, p2pPort, rqlHTTP, rqlRaft *int, rqlJoinAddr *string, advAddr, httpAdvAddr, raftAdvAddr *string, help *bool) { logger := setup_logger(logging.ComponentNode) configPath = flag.String("config", "", "Path to config YAML file (overrides defaults)") @@ -42,45 +42,15 @@ func parse_and_return_network_flags() (configPath *string, dataDir, nodeID *stri rqlRaft = flag.Int("rqlite-raft-port", 7001, "RQLite Raft port") rqlJoinAddr = flag.String("rqlite-join-address", "", "RQLite address to join (e.g., /ip4/)") advAddr = flag.String("adv-addr", "127.0.0.1", "Default Advertise address for rqlite and rafts") + httpAdvAddr = flag.String("http-adv-addr", "", "HTTP advertise address (overrides adv-addr for HTTP)") + raftAdvAddr = flag.String("raft-adv-addr", "", "Raft advertise address (overrides adv-addr for Raft)") help = flag.Bool("help", false, "Show help") flag.Parse() logger.Info("Successfully parsed all flags and arguments.") - if *configPath != "" { - cfg, err := LoadConfigFromYAML(*configPath) - if err != nil { - logger.Error("Failed to load config from YAML", zap.Error(err)) - os.Exit(1) - } - logger.ComponentInfo(logging.ComponentNode, "Configuration loaded from YAML file", zap.String("path", *configPath)) - - // Instead of returning flag values, return config values - // For ListenAddresses, extract port from multiaddr string if possible, else use default - var p2pPortVal int - if len(cfg.Node.ListenAddresses) > 0 { - // Try to parse port from multiaddr string - var port int - _, err := fmt.Sscanf(cfg.Node.ListenAddresses[0], "/ip4/0.0.0.0/tcp/%d", &port) - if err == nil { - p2pPortVal = port - } else { - p2pPortVal = 4001 - } - } else { - p2pPortVal = 4001 - } - return configPath, - &cfg.Node.DataDir, - &cfg.Node.ID, - &p2pPortVal, - &cfg.Database.RQLitePort, - &cfg.Database.RQLiteRaftPort, - &cfg.Database.RQLiteJoinAddress, - &cfg.Discovery.HttpAdvAddress, - help - } - + // Always return the parsed command line flags + // Config file loading will be handled separately in main() return } @@ -180,9 +150,13 @@ func load_args_into_config(cfg *config.Config, p2pPort, rqlHTTP, rqlRaft *int, r logger.ComponentInfo(logging.ComponentNode, "Setting RQLite join address", zap.String("address", *rqlJoinAddr)) } - if *advAddr != "" { + // Only override advertise addresses if they're not already set in config and advAddr is not the default + if *advAddr != "" && *advAddr != "127.0.0.1" { cfg.Discovery.HttpAdvAddress = fmt.Sprintf("%s:%d", *advAddr, *rqlHTTP) cfg.Discovery.RaftAdvAddress = fmt.Sprintf("%s:%d", *advAddr, *rqlRaft) + logger.ComponentInfo(logging.ComponentNode, "Overriding advertise addresses", + zap.String("http_adv_addr", cfg.Discovery.HttpAdvAddress), + zap.String("raft_adv_addr", cfg.Discovery.RaftAdvAddress)) } if *dataDir != "" { @@ -190,21 +164,89 @@ func load_args_into_config(cfg *config.Config, p2pPort, rqlHTTP, rqlRaft *int, r } } +// load_args_into_config_from_yaml applies only explicit command line overrides when loading from YAML +func load_args_into_config_from_yaml(cfg *config.Config, p2pPort, rqlHTTP, rqlRaft *int, rqlJoinAddr, advAddr, httpAdvAddr, raftAdvAddr, dataDir *string) { + logger := setup_logger(logging.ComponentNode) + + // Only override if explicitly set via command line (check if flag was actually provided) + if *dataDir != "" { + cfg.Node.DataDir = *dataDir + logger.ComponentInfo(logging.ComponentNode, "Overriding data directory from command line", zap.String("dataDir", *dataDir)) + } + + // Check environment variable for RQLite join address (Docker-specific) + if rqlJoinEnv := os.Getenv("RQLITE_JOIN_ADDRESS"); rqlJoinEnv != "" { + cfg.Database.RQLiteJoinAddress = rqlJoinEnv + logger.ComponentInfo(logging.ComponentNode, "Setting RQLite join address from environment", zap.String("address", rqlJoinEnv)) + } + + // Handle RQLite join address override from command line (takes precedence over env var) + if *rqlJoinAddr != "" { + cfg.Database.RQLiteJoinAddress = *rqlJoinAddr + logger.ComponentInfo(logging.ComponentNode, "Setting RQLite join address from command line", zap.String("address", *rqlJoinAddr)) + } + + // Check environment variables for advertise addresses (Docker-specific) + if httpAdvEnv := os.Getenv("HTTP_ADV_ADDRESS"); httpAdvEnv != "" { + cfg.Discovery.HttpAdvAddress = httpAdvEnv + logger.ComponentInfo(logging.ComponentNode, "Setting HTTP advertise address from environment", zap.String("http_adv_addr", httpAdvEnv)) + } + if raftAdvEnv := os.Getenv("RAFT_ADV_ADDRESS"); raftAdvEnv != "" { + cfg.Discovery.RaftAdvAddress = raftAdvEnv + logger.ComponentInfo(logging.ComponentNode, "Setting Raft advertise address from environment", zap.String("raft_adv_addr", raftAdvEnv)) + } + + // Handle specific advertise address overrides from command line (takes precedence over env vars) + if *httpAdvAddr != "" { + cfg.Discovery.HttpAdvAddress = *httpAdvAddr + logger.ComponentInfo(logging.ComponentNode, "Overriding HTTP advertise address from command line", zap.String("http_adv_addr", *httpAdvAddr)) + } + if *raftAdvAddr != "" { + cfg.Discovery.RaftAdvAddress = *raftAdvAddr + logger.ComponentInfo(logging.ComponentNode, "Overriding Raft advertise address from command line", zap.String("raft_adv_addr", *raftAdvAddr)) + } + + // Handle general advertise address (only if specific ones weren't set) + if *advAddr != "" && *advAddr != "127.0.0.1" && *httpAdvAddr == "" && *raftAdvAddr == "" && os.Getenv("HTTP_ADV_ADDRESS") == "" && os.Getenv("RAFT_ADV_ADDRESS") == "" { + cfg.Discovery.HttpAdvAddress = fmt.Sprintf("%s:%d", *advAddr, *rqlHTTP) + cfg.Discovery.RaftAdvAddress = fmt.Sprintf("%s:%d", *advAddr, *rqlRaft) + logger.ComponentInfo(logging.ComponentNode, "Overriding advertise addresses from adv-addr flag", + zap.String("http_adv_addr", cfg.Discovery.HttpAdvAddress), + zap.String("raft_adv_addr", cfg.Discovery.RaftAdvAddress)) + } + + logger.ComponentInfo(logging.ComponentNode, "YAML configuration loaded with overrides applied") +} + func main() { logger := setup_logger(logging.ComponentNode) - _, dataDir, nodeID, p2pPort, rqlHTTP, rqlRaft, rqlJoinAddr, advAddr, help := parse_and_return_network_flags() + configPath, dataDir, nodeID, p2pPort, rqlHTTP, rqlRaft, rqlJoinAddr, advAddr, httpAdvAddr, raftAdvAddr, help := parse_and_return_network_flags() check_if_should_open_help(help) select_data_dir(dataDir, nodeID) // Load Node Configuration var cfg *config.Config - cfg = config.DefaultConfig() - logger.ComponentInfo(logging.ComponentNode, "Default configuration loaded successfully") + if *configPath != "" { + // Load config from YAML file + var err error + cfg, err = LoadConfigFromYAML(*configPath) + if err != nil { + logger.Error("Failed to load config from YAML", zap.Error(err)) + os.Exit(1) + } + logger.ComponentInfo(logging.ComponentNode, "Configuration loaded from YAML file", zap.String("path", *configPath)) - // Apply command line argument overrides - load_args_into_config(cfg, p2pPort, rqlHTTP, rqlRaft, rqlJoinAddr, advAddr, dataDir) + // Only apply command line overrides if they were explicitly set (not defaults) + load_args_into_config_from_yaml(cfg, p2pPort, rqlHTTP, rqlRaft, rqlJoinAddr, advAddr, httpAdvAddr, raftAdvAddr, dataDir) + } else { + cfg = config.DefaultConfig() + logger.ComponentInfo(logging.ComponentNode, "Default configuration loaded successfully") + + // Apply command line argument overrides + load_args_into_config(cfg, p2pPort, rqlHTTP, rqlRaft, rqlJoinAddr, advAddr, dataDir) + } logger.ComponentInfo(logging.ComponentNode, "Command line arguments applied to configuration") // LibP2P uses configurable port (default 4001); RQLite uses 5001 (HTTP) and 7001 (Raft) diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..7f9dbe4 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,165 @@ +# Docker Compose file for DeBros Network + +services: + # Bootstrap Node - First node in the network + bootstrap-node: + build: . + container_name: debros-bootstrap + hostname: bootstrap-node + command: ["./bin/node", "--config", "/app/configs/bootstrap.docker.yaml"] + ports: + - "4001:4001" # LibP2P P2P + - "5001:5001" # RQLite HTTP API + - "7001:7001" # RQLite Raft + volumes: + - bootstrap_data:/app/data + - ./configs:/app/configs:ro + - bootstrap_logs:/app/logs + environment: + - NODE_ID=bootstrap + - P2P_PORT=4001 + - RQLITE_PORT=5001 + - RQLITE_RAFT_PORT=7001 + - DATA_DIR=/app/data/bootstrap + - LOG_LEVEL=info + networks: + - debros-network + restart: unless-stopped + healthcheck: + test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:5001/status", "||", "exit", "1"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 45s + + # Second Node - Joins the bootstrap node + node-2: + build: . + container_name: debros-node-2 + hostname: node-2 + command: ["./bin/node", "--config", "/app/configs/node.docker.yaml"] + ports: + - "4002:4001" # LibP2P P2P (mapped to different host port) + - "5002:5001" # RQLite HTTP API + - "7002:7001" # RQLite Raft + volumes: + - node2_data:/app/data + - ./configs:/app/configs:ro + - node2_logs:/app/logs + environment: + - NODE_ID=node-2 + - P2P_PORT=4001 + - RQLITE_PORT=5001 + - RQLITE_RAFT_PORT=7001 + - RQLITE_JOIN_ADDRESS=bootstrap-node:7001 + - BOOTSTRAP_PEERS=/ip4/172.20.0.2/tcp/4001/p2p/12D3KooWQX6jcPTVSsBuVuxdkbMbau3DAqZT4pc7UgGh2FvDxrKr + - DATA_DIR=/app/data/node2 + - LOG_LEVEL=info + - HTTP_ADV_ADDRESS=node-2:5001 + - RAFT_ADV_ADDRESS=node-2:7001 + networks: + - debros-network + depends_on: + bootstrap-node: + condition: service_healthy + restart: unless-stopped + healthcheck: + test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:5001/status", "||", "exit", "1"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 45s + + # Third Node - Additional network participant + node-3: + build: . + container_name: debros-node-3 + hostname: node-3 + command: ["./bin/node", "--config", "/app/configs/node.docker.yaml"] + ports: + - "4003:4001" # LibP2P P2P + - "5003:5001" # RQLite HTTP API + - "7003:7001" # RQLite Raft + volumes: + - node3_data:/app/data + - ./configs:/app/configs:ro + - node3_logs:/app/logs + environment: + - NODE_ID=node-3 + - P2P_PORT=4001 + - RQLITE_PORT=5001 + - RQLITE_RAFT_PORT=7001 + - RQLITE_JOIN_ADDRESS=bootstrap-node:7001 + - BOOTSTRAP_PEERS=/ip4/172.20.0.2/tcp/4001/p2p/12D3KooWQX6jcPTVSsBuVuxdkbMbau3DAqZT4pc7UgGh2FvDxrKr + - DATA_DIR=/app/data/node3 + - LOG_LEVEL=info + - HTTP_ADV_ADDRESS=node-3:5001 + - RAFT_ADV_ADDRESS=node-3:7001 + networks: + - debros-network + depends_on: + bootstrap-node: + condition: service_healthy + restart: unless-stopped + healthcheck: + test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:5001/status", "||", "exit", "1"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 45s + + # HTTP Gateway - Provides REST API and WebSocket interface + gateway: + build: . + container_name: debros-gateway + hostname: gateway + command: ["./bin/gateway", "--addr", ":6001", "--namespace", "default", "--bootstrap-peers", "/ip4/bootstrap-node/tcp/4001/p2p/12D3KooWQX6jcPTVSsBuVuxdkbMbau3DAqZT4pc7UgGh2FvDxrKr"] + ports: + - "6001:6001" # Gateway HTTP/WebSocket + volumes: + - ./configs:/app/configs:ro + - gateway_logs:/app/logs + environment: + - GATEWAY_ADDR=:6001 + - GATEWAY_NAMESPACE=default + - GATEWAY_BOOTSTRAP_PEERS=/ip4/bootstrap-node/tcp/4001/p2p/12D3KooWQX6jcPTVSsBuVuxdkbMbau3DAqZT4pc7UgGh2FvDxrKr + - RQLITE_NODES=http://bootstrap-node:5001 + - RQLITE_DSN=http://bootstrap-node:5001 + - LOG_LEVEL=info + networks: + - debros-network + depends_on: + bootstrap-node: + condition: service_healthy + node-2: + condition: service_healthy + restart: unless-stopped + healthcheck: + test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:6001/health", "||", "exit", "1"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 30s + +networks: + debros-network: + driver: bridge + ipam: + config: + - subnet: 172.20.0.0/16 + +volumes: + bootstrap_data: + driver: local + bootstrap_logs: + driver: local + node2_data: + driver: local + node2_logs: + driver: local + node3_data: + driver: local + node3_logs: + driver: local + gateway_logs: + driver: local