chore: update .gitignore and CHANGELOG for new development features

- Added .dev/ directory to .gitignore to exclude development process files.
- Updated CHANGELOG.md with new entries for the one-command `make dev` target, full stack initialization, and improved configuration management.
- Simplified README instructions for generating configuration files and starting the complete network stack.
This commit is contained in:
anonpenguin23 2025-10-25 08:19:31 +03:00
parent cce5326368
commit 7c9851729e
No known key found for this signature in database
GPG Key ID: 1CBB1FE35AFBEE30
6 changed files with 417 additions and 209 deletions

2
.gitignore vendored
View File

@ -73,3 +73,5 @@ data/bootstrap/rqlite/
.env.*
configs/
.dev/

View File

@ -8,14 +8,37 @@ The format is based on [Keep a Changelog][keepachangelog] and adheres to [Semant
### Added
- One-command `make dev` target to start full development stack (bootstrap + node2 + node3 + gateway in background)
- New `network-cli config init` (no --type) generates complete development stack with all configs and identities
- Full stack initialization with auto-generated peer identities for bootstrap and all nodes
- Explicit control over LibP2P listen addresses for better localhost/development support
- Production/development mode detection for NAT services (disabled for localhost, enabled for production)
- Process management with .dev/pids directory for background process tracking
- Centralized logging to ~/.debros/logs/ for all network services
### Changed
- Simplified Makefile: removed legacy dev commands, replaced with unified `make dev` target
- Updated README with clearer getting started instructions (single `make dev` command)
- Simplified `network-cli config init` behavior: defaults to generating full stack instead of single node
- `network-cli config init` now handles bootstrap peer discovery and join addresses automatically
- LibP2P configuration: removed always-on NAT services for development environments
- Code formatting in pkg/node/node.go (indentation fixes in bootstrapPeerSource)
### Deprecated
### Removed
- Removed legacy Makefile targets: run-example, show-bootstrap, run-cli, cli-health, cli-peers, cli-status, cli-storage-test, cli-pubsub-test
- Removed verbose dev-setup, dev-cluster, and old dev workflow targets
### Fixed
- Fixed indentation in bootstrapPeerSource function for consistency
- Fixed gateway.yaml generation with correct YAML indentation for bootstrap_peers
### Security
## [0.51.6] - 2025-10-24
### Added

145
Makefile
View File

@ -21,7 +21,7 @@ test-e2e:
.PHONY: build clean test run-node run-node2 run-node3 run-example deps tidy fmt vet lint clear-ports
VERSION := 0.51.6-beta
VERSION := 0.51.7-beta
COMMIT ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo unknown)
DATE ?= $(shell date -u +%Y-%m-%dT%H:%M:%SZ)
LDFLAGS := -X 'main.version=$(VERSION)' -X 'main.commit=$(COMMIT)' -X 'main.date=$(DATE)'
@ -77,110 +77,45 @@ run-gateway:
@echo "Generate it with: network-cli config init --type gateway"
go run ./cmd/gateway
# Run basic usage example
run-example:
@echo "Running basic usage example..."
go run examples/basic_usage.go
# Show how to run with flags
show-bootstrap:
@echo "Provide join address via flags, e.g.:"
@echo " make run-node2 JOINADDR=/ip4/127.0.0.1/tcp/5001 HTTP=5002 RAFT=7002 P2P=4002"
# Run network CLI
run-cli:
@echo "Running network CLI help..."
./bin/network-cli help
# Network CLI helper commands
cli-health:
@echo "Checking network health..."
./bin/network-cli health
cli-peers:
@echo "Listing network peers..."
./bin/network-cli peers
cli-status:
@echo "Getting network status..."
./bin/network-cli status
cli-storage-test:
@echo "Testing storage operations..."
@./bin/network-cli storage put test-key "Hello Network" || echo "Storage test requires running network"
@./bin/network-cli storage get test-key || echo "Storage test requires running network"
@./bin/network-cli storage list || echo "Storage test requires running network"
cli-pubsub-test:
@echo "Testing pub/sub operations..."
@./bin/network-cli pubsub publish test-topic "Hello World" || echo "PubSub test requires running network"
@./bin/network-cli pubsub topics || echo "PubSub test requires running network"
# Download dependencies
deps:
@echo "Downloading dependencies..."
go mod download
# Tidy dependencies
tidy:
@echo "Tidying dependencies..."
go mod tidy
# Format code
fmt:
@echo "Formatting code..."
go fmt ./...
# Vet code
vet:
@echo "Vetting code..."
go vet ./...
# Lint alias (lightweight for now)
lint: fmt vet
@echo "Linting complete (fmt + vet)"
# Clear common development ports
clear-ports:
@echo "Clearing common dev ports (4001/4002, 5001/5002, 7001/7002)..."
@chmod +x scripts/clear-ports.sh || true
@scripts/clear-ports.sh
# Development setup
dev-setup: deps
@echo "Setting up development environment..."
@mkdir -p data/bootstrap data/node data/node2 data/node3
@mkdir -p data/test-bootstrap data/test-node1 data/test-node2
@echo "Development setup complete!"
# Start development cluster (requires multiple terminals)
dev-cluster:
@echo "To start a development cluster with 3 nodes:"
# One-command dev: Start bootstrap, node2, node3, and gateway in background
# Requires: configs already exist in ~/.debros
dev: build
@echo "🚀 Starting development network stack..."
@mkdir -p .dev/pids
@mkdir -p $$HOME/.debros/logs
@echo "Starting bootstrap node..."
@nohup ./bin/node --config bootstrap.yaml > $$HOME/.debros/logs/bootstrap.log 2>&1 & echo $$! > .dev/pids/bootstrap.pid
@sleep 2
@echo "Starting node2..."
@nohup ./bin/node --config node2.yaml > $$HOME/.debros/logs/node2.log 2>&1 & echo $$! > .dev/pids/node2.pid
@sleep 1
@echo "Starting node3..."
@nohup ./bin/node --config node3.yaml > $$HOME/.debros/logs/node3.log 2>&1 & echo $$! > .dev/pids/node3.pid
@sleep 1
@echo "Starting gateway..."
@nohup ./bin/gateway --config gateway.yaml > $$HOME/.debros/logs/gateway.log 2>&1 & echo $$! > .dev/pids/gateway.pid
@echo ""
@echo "1. Generate config files in ~/.debros:"
@echo " make build"
@echo " ./bin/network-cli config init --type bootstrap"
@echo " ./bin/network-cli config init --type node --name node.yaml --bootstrap-peers '<bootstrap_peer_multiaddr>'"
@echo " ./bin/network-cli config init --type node --name node2.yaml --bootstrap-peers '<bootstrap_peer_multiaddr>'"
@echo "============================================================"
@echo "✅ Development stack started!"
@echo "============================================================"
@echo ""
@echo "2. Run in separate terminals:"
@echo " Terminal 1: make run-node # Start bootstrap node (bootstrap.yaml)"
@echo " Terminal 2: make run-node2 # Start node 1 (node.yaml)"
@echo " Terminal 3: make run-node3 # Start node 2 (node2.yaml)"
@echo " Terminal 4: make run-gateway # Start gateway"
@echo "Processes:"
@echo " Bootstrap: PID=$$(cat .dev/pids/bootstrap.pid)"
@echo " Node2: PID=$$(cat .dev/pids/node2.pid)"
@echo " Node3: PID=$$(cat .dev/pids/node3.pid)"
@echo " Gateway: PID=$$(cat .dev/pids/gateway.pid)"
@echo ""
@echo "3. Or run custom node with any config file:"
@echo " go run ./cmd/node --config custom-node.yaml"
@echo "Ports:"
@echo " Bootstrap P2P: 4001, HTTP: 5001, Raft: 7001"
@echo " Node2 P2P: 4002, HTTP: 5002, Raft: 7002"
@echo " Node3 P2P: 4003, HTTP: 5003, Raft: 7003"
@echo " Gateway: 6001"
@echo ""
@echo "4. Test:"
@echo " make cli-health # Check network health"
@echo " make cli-peers # List peers"
@echo " make cli-storage-test # Test storage"
@echo " make cli-pubsub-test # Test messaging"
# Full development workflow
dev: clean build test
@echo "Development workflow complete!"
@echo "Press Ctrl+C to stop all processes"
@echo "============================================================"
@echo ""
@trap 'echo "Stopping all processes..."; kill $$(cat .dev/pids/*.pid) 2>/dev/null; rm -f .dev/pids/*.pid; exit 0' INT; \
tail -f $$HOME/.debros/logs/bootstrap.log $$HOME/.debros/logs/node2.log $$HOME/.debros/logs/node3.log $$HOME/.debros/logs/gateway.log
# Help
help:
@ -189,12 +124,14 @@ help:
@echo " clean - Clean build artifacts"
@echo " test - Run tests"
@echo ""
@echo "Development:"
@echo " dev - Start full dev stack (bootstrap + 2 nodes + gateway)"
@echo " Requires: configs in ~/.debros (run 'network-cli config init' first)"
@echo ""
@echo "Configuration (NEW):"
@echo " First, generate config files in ~/.debros with:"
@echo " make build # Build CLI first"
@echo " ./bin/network-cli config init --type bootstrap # Generate bootstrap config"
@echo " ./bin/network-cli config init --type node --bootstrap-peers '<peer_multiaddr>'"
@echo " ./bin/network-cli config init --type gateway"
@echo " ./bin/network-cli config init # Generate full stack"
@echo ""
@echo "Network Targets (requires config files in ~/.debros):"
@echo " run-node - Start bootstrap node"

126
README.md
View File

@ -174,23 +174,36 @@ cd network
make build
```
### 3. Start a Bootstrap Node
### 3. Generate Configuration Files
```bash
make run-node
# Or manually:
go run ./cmd/node --config configs/node.yaml
# Generate all configs (bootstrap, node2, node3, gateway) with one command
./bin/network-cli config init
```
### 4. Start Additional Nodes
This creates:
- `~/.debros/bootstrap.yaml` - Bootstrap node
- `~/.debros/node2.yaml` - Regular node 2
- `~/.debros/node3.yaml` - Regular node 3
- `~/.debros/gateway.yaml` - HTTP Gateway
Plus auto-generated identities for each node.
### 4. Start the Complete Network Stack
```bash
make run-node2
# Or manually:
go run ./cmd/node --config configs/node.yaml
make dev
```
### 5. Test with CLI
This starts:
- Bootstrap node (P2P: 4001, RQLite HTTP: 5001, Raft: 7001)
- Node 2 (P2P: 4002, RQLite HTTP: 5002, Raft: 7002)
- Node 3 (P2P: 4003, RQLite HTTP: 5003, Raft: 7003)
- Gateway (HTTP: 6001)
Logs stream to terminal. Press **Ctrl+C** to stop all processes.
### 5. Test with CLI (in another terminal)
```bash
./bin/network-cli health
@ -261,100 +274,103 @@ The system will **only** load config from `~/.debros/` and will error if require
Use the `network-cli config init` command to generate configuration files:
#### Generate a Node Config
### Generate Complete Stack (Recommended)
```bash
# Generate bootstrap, node2, node3, and gateway configs in one command
./bin/network-cli config init
# Force regenerate (overwrites existing configs)
./bin/network-cli config init --force
```
This is the **recommended way** to get started with a local development network.
### Generate Individual Configs (Advanced)
For custom setups or production deployments, you can generate individual configs:
#### Generate a Single Node Config
```bash
# Generate basic node config with bootstrap peers
network-cli config init --type node --bootstrap-peers "/ip4/127.0.0.1/tcp/4001/p2p/QmXxx,/ip4/127.0.0.1/tcp/4002/p2p/QmYyy"
./bin/network-cli config init --type node --bootstrap-peers "/ip4/127.0.0.1/tcp/4001/p2p/QmXxx"
# With custom ports
network-cli config init --type node --name node2.yaml --listen-port 4002 --rqlite-http-port 5002 --rqlite-raft-port 7002 --join localhost:5001 --bootstrap-peers "/ip4/127.0.0.1/tcp/4001/p2p/QmXxx"
./bin/network-cli config init --type node --name node2.yaml \
--listen-port 4002 --rqlite-http-port 5002 --rqlite-raft-port 7002 \
--join localhost:5001 --bootstrap-peers "/ip4/127.0.0.1/tcp/4001/p2p/QmXxx"
# Force overwrite existing config
network-cli config init --type node --force
./bin/network-cli config init --type node --force
```
#### Generate a Bootstrap Node Config
```bash
# Generate bootstrap node (no join address required)
network-cli config init --type bootstrap
./bin/network-cli config init --type bootstrap
# With custom ports
network-cli config init --type bootstrap --listen-port 4001 --rqlite-http-port 5001 --rqlite-raft-port 7001
./bin/network-cli config init --type bootstrap --listen-port 4001 --rqlite-http-port 5001 --rqlite-raft-port 7001
```
#### Generate a Gateway Config
```bash
# Generate gateway config
network-cli config init --type gateway
./bin/network-cli config init --type gateway
# With bootstrap peers
network-cli config init --type gateway --bootstrap-peers "/ip4/127.0.0.1/tcp/4001/p2p/QmXxx"
./bin/network-cli config init --type gateway --bootstrap-peers "/ip4/127.0.0.1/tcp/4001/p2p/QmXxx"
```
### Running Multiple Nodes on the Same Machine
### Running the Network
You can run multiple nodes on a single machine by creating separate configuration files and using the `--config` flag:
#### Create Multiple Node Configs
Once configs are generated, start the complete stack with:
```bash
# Node 1
./bin/network-cli config init --type node --name node1.yaml \
--listen-port 4001 --rqlite-http-port 5001 --rqlite-raft-port 7001 \
--bootstrap-peers "/ip4/127.0.0.1/tcp/4001/p2p/<BOOTSTRAP_ID>"
# Node 2
./bin/network-cli config init --type node --name node2.yaml \
--listen-port 4002 --rqlite-http-port 5002 --rqlite-raft-port 7002 \
--join localhost:5001 \
--bootstrap-peers "/ip4/127.0.0.1/tcp/4001/p2p/<BOOTSTRAP_ID>"
# Node 3
./bin/network-cli config init --type node --name node3.yaml \
--listen-port 4003 --rqlite-http-port 5003 --rqlite-raft-port 7003 \
--join localhost:5001 \
--bootstrap-peers "/ip4/127.0.0.1/tcp/4001/p2p/<BOOTSTRAP_ID>"
make dev
```
#### Run Multiple Nodes in Separate Terminals
Or start individual components (in separate terminals):
```bash
# Terminal 1 - Bootstrap node
go run ./cmd/node --config bootstrap.yaml
# Terminal 2 - Node 1
go run ./cmd/node --config node1.yaml
# Terminal 3 - Node 2
# Terminal 2 - Node 2
go run ./cmd/node --config node2.yaml
# Terminal 4 - Node 3
# Terminal 3 - Node 3
go run ./cmd/node --config node3.yaml
# Terminal 4 - Gateway
go run ./cmd/gateway --config gateway.yaml
```
#### Or Use Makefile Targets
### Running Multiple Nodes on the Same Machine
The default `make dev` creates a 3-node setup. For additional nodes, generate individual configs:
```bash
# Terminal 1
make run-node # Runs: go run ./cmd/node --config bootstrap.yaml
# Generate additional node configs with unique ports
./bin/network-cli config init --type node --name node4.yaml \
--listen-port 4004 --rqlite-http-port 5004 --rqlite-raft-port 7004 \
--join localhost:5001 \
--bootstrap-peers "/ip4/127.0.0.1/tcp/4001/p2p/<BOOTSTRAP_ID>"
# Terminal 2
make run-node2 # Runs: go run ./cmd/node --config node.yaml
# Terminal 3
make run-node3 # Runs: go run ./cmd/node --config node2.yaml
# Start the additional node
go run ./cmd/node --config node4.yaml
```
#### Key Points for Multiple Nodes
- **Each node needs unique ports**: P2P port, RQLite HTTP port, and RQLite Raft port must all be different
- **Join address**: Non-bootstrap nodes need `rqlite_join_address` pointing to the bootstrap or an existing node
- **Join address**: Non-bootstrap nodes need `rqlite_join_address` pointing to the bootstrap or an existing node (use Raft port)
- **Bootstrap peers**: All nodes need the bootstrap node's multiaddr in `discovery.bootstrap_peers`
- **Config files**: Store all configs in `~/.debros/` with different filenames
- **--config flag**: Specify which config file to load (defaults to `node.yaml`)
- **--config flag**: Specify which config file to load
⚠️ **Common Mistake - Same Ports:**
If all nodes use the same ports (e.g., 5001, 7001), they will try to bind to the same addresses and fail to communicate. Verify each node has unique ports:
@ -365,11 +381,11 @@ grep "rqlite_port\|rqlite_raft_port" ~/.debros/bootstrap.yaml
# Should show: rqlite_port: 5001, rqlite_raft_port: 7001
# Node 2
grep "rqlite_port\|rqlite_raft_port" ~/.debros/node.yaml
grep "rqlite_port\|rqlite_raft_port" ~/.debros/node2.yaml
# Should show: rqlite_port: 5002, rqlite_raft_port: 7002
# Node 3
grep "rqlite_port\|rqlite_raft_port" ~/.debros/node2.yaml
grep "rqlite_port\|rqlite_raft_port" ~/.debros/node3.yaml
# Should show: rqlite_port: 5003, rqlite_raft_port: 7003
```

View File

@ -15,6 +15,7 @@ import (
"github.com/DeBrosOfficial/network/pkg/auth"
"github.com/DeBrosOfficial/network/pkg/client"
"github.com/DeBrosOfficial/network/pkg/config"
"github.com/DeBrosOfficial/network/pkg/encryption"
"github.com/libp2p/go-libp2p/core/crypto"
"github.com/libp2p/go-libp2p/core/peer"
)
@ -607,30 +608,35 @@ func showConfigHelp() {
fmt.Printf("Config Management Commands\n\n")
fmt.Printf("Usage: network-cli config <subcommand> [options]\n\n")
fmt.Printf("Subcommands:\n")
fmt.Printf(" init - Generate configuration files in ~/.debros\n")
fmt.Printf(" init - Generate full network stack in ~/.debros (bootstrap + 2 nodes + gateway)\n")
fmt.Printf(" validate --name <file> - Validate a config file\n\n")
fmt.Printf("Init Default Behavior (no --type):\n")
fmt.Printf(" Generates bootstrap.yaml, node2.yaml, node3.yaml, gateway.yaml with:\n")
fmt.Printf(" - Auto-generated identities for bootstrap, node2, node3\n")
fmt.Printf(" - Correct bootstrap_peers and join addresses\n")
fmt.Printf(" - Default ports: P2P 4001-4003, HTTP 5001-5003, Raft 7001-7003\n\n")
fmt.Printf("Init Options:\n")
fmt.Printf(" --type <type> - Config type: node, bootstrap, gateway (default: node)\n")
fmt.Printf(" --name <file> - Output filename (default: node.yaml)\n")
fmt.Printf(" --type <type> - Single config type: node, bootstrap, gateway (skips stack generation)\n")
fmt.Printf(" --name <file> - Output filename (default: depends on --type or 'stack' for full stack)\n")
fmt.Printf(" --force - Overwrite existing config/stack files\n\n")
fmt.Printf("Single Config Options (with --type):\n")
fmt.Printf(" --id <id> - Node ID for bootstrap peers\n")
fmt.Printf(" --listen-port <port> - LibP2P listen port (default: 4001)\n")
fmt.Printf(" --rqlite-http-port <port> - RQLite HTTP port (default: 5001)\n")
fmt.Printf(" --rqlite-raft-port <port> - RQLite Raft port (default: 7001)\n")
fmt.Printf(" --join <host:port> - RQLite address to join (required for non-bootstrap)\n")
fmt.Printf(" --bootstrap-peers <peers> - Comma-separated bootstrap peer multiaddrs\n")
fmt.Printf(" --force - Overwrite existing config\n\n")
fmt.Printf(" --bootstrap-peers <peers> - Comma-separated bootstrap peer multiaddrs\n\n")
fmt.Printf("Examples:\n")
fmt.Printf(" network-cli config init\n")
fmt.Printf(" network-cli config init --type node --bootstrap-peers /ip4/127.0.0.1/tcp/4001/p2p/QmXxx,/ip4/127.0.0.1/tcp/4002/p2p/QmYyy\n")
fmt.Printf(" network-cli config init --type bootstrap\n")
fmt.Printf(" network-cli config init --type gateway\n")
fmt.Printf(" network-cli config init # Generate full stack\n")
fmt.Printf(" network-cli config init --force # Overwrite existing stack\n")
fmt.Printf(" network-cli config init --type bootstrap # Single bootstrap config (legacy)\n")
fmt.Printf(" network-cli config validate --name node.yaml\n")
}
func handleConfigInit(args []string) {
// Parse flags
var (
cfgType = "node"
cfgType = ""
name = "" // Will be set based on type if not provided
id string
listenPort = 4001
@ -694,6 +700,13 @@ func handleConfigInit(args []string) {
}
}
// If --type is not specified, generate full stack
if cfgType == "" {
initFullStack(force)
return
}
// Otherwise, continue with single-file generation
// Validate type
if cfgType != "node" && cfgType != "bootstrap" && cfgType != "gateway" {
fmt.Fprintf(os.Stderr, "Invalid --type: %s (expected: node, bootstrap, or gateway)\n", cfgType)
@ -799,6 +812,192 @@ func handleConfigValidate(args []string) {
fmt.Printf("✅ Config is valid: %s\n", configPath)
}
func initFullStack(force bool) {
fmt.Printf("🚀 Initializing full network stack...\n")
// Ensure ~/.debros directory exists
homeDir, err := os.UserHomeDir()
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to get home directory: %v\n", err)
os.Exit(1)
}
debrosDir := filepath.Join(homeDir, ".debros")
if err := os.MkdirAll(debrosDir, 0755); err != nil {
fmt.Fprintf(os.Stderr, "Failed to create ~/.debros directory: %v\n", err)
os.Exit(1)
}
// Step 1: Generate bootstrap identity
bootstrapIdentityDir := filepath.Join(debrosDir, "bootstrap")
bootstrapIdentityPath := filepath.Join(bootstrapIdentityDir, "identity.key")
if !force {
if _, err := os.Stat(bootstrapIdentityPath); err == nil {
fmt.Fprintf(os.Stderr, "Bootstrap identity already exists at %s (use --force to overwrite)\n", bootstrapIdentityPath)
os.Exit(1)
}
}
bootstrapInfo, err := encryption.GenerateIdentity()
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to generate bootstrap identity: %v\n", err)
os.Exit(1)
}
if err := os.MkdirAll(bootstrapIdentityDir, 0755); err != nil {
fmt.Fprintf(os.Stderr, "Failed to create bootstrap data directory: %v\n", err)
os.Exit(1)
}
if err := encryption.SaveIdentity(bootstrapInfo, bootstrapIdentityPath); err != nil {
fmt.Fprintf(os.Stderr, "Failed to save bootstrap identity: %v\n", err)
os.Exit(1)
}
fmt.Printf("✅ Generated bootstrap identity: %s (Peer ID: %s)\n", bootstrapIdentityPath, bootstrapInfo.PeerID.String())
// Construct bootstrap multiaddr
bootstrapMultiaddr := fmt.Sprintf("/ip4/127.0.0.1/tcp/4001/p2p/%s", bootstrapInfo.PeerID.String())
fmt.Printf(" Bootstrap multiaddr: %s\n", bootstrapMultiaddr)
// Step 2: Generate bootstrap.yaml
bootstrapName := "bootstrap.yaml"
bootstrapPath := filepath.Join(debrosDir, bootstrapName)
if !force {
if _, err := os.Stat(bootstrapPath); err == nil {
fmt.Fprintf(os.Stderr, "Bootstrap config already exists at %s (use --force to overwrite)\n", bootstrapPath)
os.Exit(1)
}
}
bootstrapContent := generateBootstrapConfig(bootstrapName, "", 4001, 5001, 7001)
if err := os.WriteFile(bootstrapPath, []byte(bootstrapContent), 0644); err != nil {
fmt.Fprintf(os.Stderr, "Failed to write bootstrap config: %v\n", err)
os.Exit(1)
}
fmt.Printf("✅ Generated bootstrap config: %s\n", bootstrapPath)
// Step 3: Generate node2 identity and config
node2IdentityDir := filepath.Join(debrosDir, "node2")
node2IdentityPath := filepath.Join(node2IdentityDir, "identity.key")
if !force {
if _, err := os.Stat(node2IdentityPath); err == nil {
fmt.Fprintf(os.Stderr, "Node2 identity already exists at %s (use --force to overwrite)\n", node2IdentityPath)
os.Exit(1)
}
}
node2Info, err := encryption.GenerateIdentity()
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to generate node2 identity: %v\n", err)
os.Exit(1)
}
if err := os.MkdirAll(node2IdentityDir, 0755); err != nil {
fmt.Fprintf(os.Stderr, "Failed to create node2 data directory: %v\n", err)
os.Exit(1)
}
if err := encryption.SaveIdentity(node2Info, node2IdentityPath); err != nil {
fmt.Fprintf(os.Stderr, "Failed to save node2 identity: %v\n", err)
os.Exit(1)
}
fmt.Printf("✅ Generated node2 identity: %s (Peer ID: %s)\n", node2IdentityPath, node2Info.PeerID.String())
node2Name := "node2.yaml"
node2Path := filepath.Join(debrosDir, node2Name)
if !force {
if _, err := os.Stat(node2Path); err == nil {
fmt.Fprintf(os.Stderr, "Node2 config already exists at %s (use --force to overwrite)\n", node2Path)
os.Exit(1)
}
}
node2Content := generateNodeConfig(node2Name, "", 4002, 5002, 7002, "127.0.0.1:7001", bootstrapMultiaddr)
if err := os.WriteFile(node2Path, []byte(node2Content), 0644); err != nil {
fmt.Fprintf(os.Stderr, "Failed to write node2 config: %v\n", err)
os.Exit(1)
}
fmt.Printf("✅ Generated node2 config: %s\n", node2Path)
// Step 4: Generate node3 identity and config
node3IdentityDir := filepath.Join(debrosDir, "node3")
node3IdentityPath := filepath.Join(node3IdentityDir, "identity.key")
if !force {
if _, err := os.Stat(node3IdentityPath); err == nil {
fmt.Fprintf(os.Stderr, "Node3 identity already exists at %s (use --force to overwrite)\n", node3IdentityPath)
os.Exit(1)
}
}
node3Info, err := encryption.GenerateIdentity()
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to generate node3 identity: %v\n", err)
os.Exit(1)
}
if err := os.MkdirAll(node3IdentityDir, 0755); err != nil {
fmt.Fprintf(os.Stderr, "Failed to create node3 data directory: %v\n", err)
os.Exit(1)
}
if err := encryption.SaveIdentity(node3Info, node3IdentityPath); err != nil {
fmt.Fprintf(os.Stderr, "Failed to save node3 identity: %v\n", err)
os.Exit(1)
}
fmt.Printf("✅ Generated node3 identity: %s (Peer ID: %s)\n", node3IdentityPath, node3Info.PeerID.String())
node3Name := "node3.yaml"
node3Path := filepath.Join(debrosDir, node3Name)
if !force {
if _, err := os.Stat(node3Path); err == nil {
fmt.Fprintf(os.Stderr, "Node3 config already exists at %s (use --force to overwrite)\n", node3Path)
os.Exit(1)
}
}
node3Content := generateNodeConfig(node3Name, "", 4003, 5003, 7003, "127.0.0.1:7001", bootstrapMultiaddr)
if err := os.WriteFile(node3Path, []byte(node3Content), 0644); err != nil {
fmt.Fprintf(os.Stderr, "Failed to write node3 config: %v\n", err)
os.Exit(1)
}
fmt.Printf("✅ Generated node3 config: %s\n", node3Path)
// Step 5: Generate gateway.yaml
gatewayName := "gateway.yaml"
gatewayPath := filepath.Join(debrosDir, gatewayName)
if !force {
if _, err := os.Stat(gatewayPath); err == nil {
fmt.Fprintf(os.Stderr, "Gateway config already exists at %s (use --force to overwrite)\n", gatewayPath)
os.Exit(1)
}
}
gatewayContent := generateGatewayConfig(bootstrapMultiaddr)
if err := os.WriteFile(gatewayPath, []byte(gatewayContent), 0644); err != nil {
fmt.Fprintf(os.Stderr, "Failed to write gateway config: %v\n", err)
os.Exit(1)
}
fmt.Printf("✅ Generated gateway config: %s\n", gatewayPath)
// Print summary
fmt.Printf("\n" + strings.Repeat("=", 60) + "\n")
fmt.Printf("✅ Full network stack initialized successfully!\n")
fmt.Printf(strings.Repeat("=", 60) + "\n\n")
fmt.Printf("Configuration files created in: %s\n\n", debrosDir)
fmt.Printf("Bootstrap Node:\n")
fmt.Printf(" Config: %s\n", bootstrapPath)
fmt.Printf(" Peer ID: %s\n", bootstrapInfo.PeerID.String())
fmt.Printf(" Ports: P2P=4001, HTTP=5001, Raft=7001\n\n")
fmt.Printf("Node2:\n")
fmt.Printf(" Config: %s\n", node2Path)
fmt.Printf(" Ports: P2P=4002, HTTP=5002, Raft=7002\n")
fmt.Printf(" Join: 127.0.0.1:7001\n\n")
fmt.Printf("Node3:\n")
fmt.Printf(" Config: %s\n", node3Path)
fmt.Printf(" Ports: P2P=4003, HTTP=5003, Raft=7003\n")
fmt.Printf(" Join: 127.0.0.1:7001\n\n")
fmt.Printf("Gateway:\n")
fmt.Printf(" Config: %s\n\n", gatewayPath)
fmt.Printf("To start the network:\n")
fmt.Printf(" Terminal 1: ./bin/node --config bootstrap.yaml\n")
fmt.Printf(" Terminal 2: ./bin/node --config node2.yaml\n")
fmt.Printf(" Terminal 3: ./bin/node --config node3.yaml\n")
fmt.Printf(" Terminal 4: ./bin/gateway --config gateway.yaml\n")
fmt.Printf("\n" + strings.Repeat("=", 60) + "\n")
}
func generateNodeConfig(name, id string, listenPort, rqliteHTTPPort, rqliteRaftPort int, joinAddr, bootstrapPeers string) string {
nodeID := id
if nodeID == "" {
@ -923,11 +1122,11 @@ func generateGatewayConfig(bootstrapPeers string) string {
var peersYAML strings.Builder
if len(peers) == 0 {
peersYAML.WriteString(" bootstrap_peers: []")
peersYAML.WriteString("bootstrap_peers: []")
} else {
peersYAML.WriteString(" bootstrap_peers:\n")
peersYAML.WriteString("bootstrap_peers:\n")
for _, p := range peers {
fmt.Fprintf(&peersYAML, " - \"%s\"\n", p)
fmt.Fprintf(&peersYAML, " - \"%s\"\n", p)
}
}

View File

@ -84,35 +84,35 @@ func (n *Node) startRQLite(ctx context.Context) error {
// bootstrapPeerSource returns a PeerSource that yields peers from BootstrapPeers.
func bootstrapPeerSource(bootstrapAddrs []string, logger *zap.Logger) func(context.Context, int) <-chan peer.AddrInfo {
return func(ctx context.Context, num int) <-chan peer.AddrInfo {
out := make(chan peer.AddrInfo, num)
go func() {
defer close(out)
count := 0
for _, s := range bootstrapAddrs {
if count >= num {
return
}
ma, err := multiaddr.NewMultiaddr(s)
if err != nil {
logger.Debug("invalid bootstrap multiaddr", zap.String("addr", s), zap.Error(err))
continue
}
ai, err := peer.AddrInfoFromP2pAddr(ma)
if err != nil {
logger.Debug("failed to parse bootstrap peer", zap.String("addr", s), zap.Error(err))
continue
}
select {
case out <- *ai:
count++
case <-ctx.Done():
return
}
}
}()
return out
}
return func(ctx context.Context, num int) <-chan peer.AddrInfo {
out := make(chan peer.AddrInfo, num)
go func() {
defer close(out)
count := 0
for _, s := range bootstrapAddrs {
if count >= num {
return
}
ma, err := multiaddr.NewMultiaddr(s)
if err != nil {
logger.Debug("invalid bootstrap multiaddr", zap.String("addr", s), zap.Error(err))
continue
}
ai, err := peer.AddrInfoFromP2pAddr(ma)
if err != nil {
logger.Debug("failed to parse bootstrap peer", zap.String("addr", s), zap.Error(err))
continue
}
select {
case out <- *ai:
count++
case <-ctx.Done():
return
}
}
}()
return out
}
}
// hasBootstrapConnections checks if we're connected to any bootstrap peers
@ -250,21 +250,52 @@ func (n *Node) startLibP2P() error {
return fmt.Errorf("failed to load identity: %w", err)
}
// Create LibP2P host with NAT traversal support
// Create LibP2P host with explicit listen addresses
var opts []libp2p.Option
opts = append(opts,
libp2p.Identity(identity),
libp2p.Security(noise.ID, noise.New),
libp2p.DefaultMuxers,
libp2p.EnableNATService(),
libp2p.EnableAutoNATv2(),
libp2p.EnableRelay(),
libp2p.NATPortMap(),
libp2p.EnableAutoRelayWithPeerSource(
bootstrapPeerSource(n.config.Discovery.BootstrapPeers, n.logger.Logger),
),
)
// Add explicit listen addresses from config
if len(n.config.Node.ListenAddresses) > 0 {
listenAddrs := make([]multiaddr.Multiaddr, 0, len(n.config.Node.ListenAddresses))
for _, addr := range n.config.Node.ListenAddresses {
ma, err := multiaddr.NewMultiaddr(addr)
if err != nil {
return fmt.Errorf("invalid listen address %s: %w", addr, err)
}
listenAddrs = append(listenAddrs, ma)
}
opts = append(opts, libp2p.ListenAddrs(listenAddrs...))
n.logger.ComponentInfo(logging.ComponentLibP2P, "Configured listen addresses",
zap.Strings("addrs", n.config.Node.ListenAddresses))
}
// For localhost/development, disable NAT services
// For production, these would be enabled
isLocalhost := len(n.config.Node.ListenAddresses) > 0 &&
(strings.Contains(n.config.Node.ListenAddresses[0], "127.0.0.1") ||
strings.Contains(n.config.Node.ListenAddresses[0], "localhost"))
if isLocalhost {
n.logger.ComponentInfo(logging.ComponentLibP2P, "Localhost detected - disabling NAT services for local development")
// Don't add NAT/AutoRelay options for localhost
} else {
// Production: enable NAT traversal
n.logger.ComponentInfo(logging.ComponentLibP2P, "Production mode - enabling NAT services")
opts = append(opts,
libp2p.EnableNATService(),
libp2p.EnableAutoNATv2(),
libp2p.EnableRelay(),
libp2p.NATPortMap(),
libp2p.EnableAutoRelayWithPeerSource(
bootstrapPeerSource(n.config.Discovery.BootstrapPeers, n.logger.Logger),
),
)
}
h, err := libp2p.New(opts...)
if err != nil {
return err