|
1 | 1 | # Ironfish |
2 | 2 |
|
3 | | -<div align="left"> |
| 3 | +Ironfish is a **fault-tolerant, distributed chess analysis engine** built with Rust. It leverages **Stockfish**, **Docker**, and **Gossip protocols** to create a resilient, scalable, and self-healing cluster for chess position analysis. |
4 | 4 |
|
5 | | -[]() |
6 | | -[](https://www.rust-lang.org) |
7 | | -[](LICENSE) |
8 | | -[]() |
9 | | -[]() |
| 5 | +## Key Features |
10 | 6 |
|
11 | | -</div> |
| 7 | +- **Distributed Analysis:** Distributes chess analysis workload across a cluster of nodes. |
| 8 | +- **Fault Tolerance:** Built-in leader election (Hybrid Raft/Bully) and self-healing capabilities. If a node dies, the cluster recovers automatically. |
| 9 | +- **Auto-Discovery:** Nodes discover each other via Static config, UDP Multicast, or DNS (Cloud/Kubernetes). |
| 10 | +- **Load Balancing:** CPU-aware and Queue-aware load balancing strategies. |
| 11 | +- **Secure API:** Token-based authentication (`X-Admin-Key` for management, Bearer tokens for usage). |
| 12 | +- **Multi-Protocol Support:** Single port (8080) exposes REST, gRPC, and GraphQL APIs. |
| 13 | +- **Observability:** Built-in metrics (`/v1/metrics`) tracking CPU, Memory, and Engine usage. |
12 | 14 |
|
13 | | -<div align="center"> |
14 | | - <img src="assets/logo.png" alt="Ironfish Logo" width="256" /> |
15 | | -</div> |
| 15 | +## Architecture |
16 | 16 |
|
17 | | -A distributed, fault-tolerant chess position analysis system using Stockfish, built entirely in Rust. |
18 | | - |
19 | | -## Features |
20 | | - |
21 | | -- **Multi-Protocol API** - REST, gRPC, and GraphQL on separate ports |
22 | | -- **HA Clustering** - Automatic node discovery and gossip-based token replication |
23 | | -- **Token Authentication** - Secure API access with cluster-wide token synchronization |
24 | | -- **Stockfish Integration** - Engine pool for concurrent position analysis |
25 | | -- **Admin CLI** - Cluster management and token administration |
| 17 | +Ironfish operates as a cluster of identical nodes. Each node runs: |
| 18 | +1. **API Layer:** Axum (REST/GraphQL) + Tonic (gRPC) multiplexed on port 8080. |
| 19 | +2. **Cluster Service:** Handles membership, gossip, and failure detection. |
| 20 | +3. **Stockfish Pool:** Manages a pool of Stockfish engine processes for analysis. |
26 | 21 |
|
27 | 22 | ## Quick Start |
28 | 23 |
|
29 | | -### Single Node |
30 | | - |
31 | | -```bash |
32 | | -# Build |
33 | | -cargo build --release |
34 | | - |
35 | | -# Run (requires Stockfish installed) |
36 | | -STOCKFISH_PATH=/usr/local/bin/stockfish ./target/release/ironfish-server |
37 | | -``` |
| 24 | +### Prerequisites |
| 25 | +* Docker & Docker Compose |
| 26 | +* (Optional) Rust toolchain for local development |
| 27 | + |
| 28 | +### Running a Cluster |
| 29 | + |
| 30 | +1. **Start the cluster:** |
| 31 | + ```bash |
| 32 | + docker compose up -d |
| 33 | + ``` |
| 34 | + This spins up 3 nodes (`node1`, `node2`, `node3`). |
| 35 | + |
| 36 | +2. **Create an Admin Token:** |
| 37 | + ```bash |
| 38 | + # Use the default admin key "cluster-admin-secret" defined in docker-compose.yml |
| 39 | + curl -X POST http://localhost:8080/_admin/tokens \ |
| 40 | + -H "X-Admin-Key: cluster-admin-secret" \ |
| 41 | + -H "Content-Type: application/json" \ |
| 42 | + -d '{"name": "my-token"}' |
| 43 | + ``` |
| 44 | + *Response:* `{"token": "iff_..."}` |
| 45 | + |
| 46 | +3. **Analyze a Position:** |
| 47 | + ```bash |
| 48 | + curl -X POST http://localhost:8080/v1/analyze \ |
| 49 | + -H "Authorization: Bearer <YOUR_TOKEN>" \ |
| 50 | + -H "Content-Type: application/json" \ |
| 51 | + -d '{ |
| 52 | + "fen": "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", |
| 53 | + "depth": 15 |
| 54 | + }' |
| 55 | + ``` |
| 56 | + |
| 57 | +### API Endpoints |
| 58 | + |
| 59 | +| Method | Path | Description | Auth | |
| 60 | +| :--- | :--- | :--- | :--- | |
| 61 | +| `GET` | `/v1/health` | Node health status | None | |
| 62 | +| `GET` | `/v1/metrics` | System & Cluster metrics | Bearer | |
| 63 | +| `POST` | `/v1/analyze` | Analyze FEN position | Bearer | |
| 64 | +| `POST` | `/graphql` | GraphQL Endpoint | Bearer | |
| 65 | +| `POST` | `/_admin/tokens` | Create API Token | Admin Key | |
| 66 | +| `DELETE` | `/_admin/tokens/:id` | Revoke API Token | Admin Key | |
38 | 67 |
|
39 | | -### Docker Cluster (Example 3 nodes) |
| 68 | +## Development |
40 | 69 |
|
| 70 | +### Pre-commit Hooks |
| 71 | +The project includes a pre-commit hook to ensure code quality. |
41 | 72 | ```bash |
42 | | -# Start cluster |
43 | | -docker compose up -d |
44 | | - |
45 | | -# Check health |
46 | | -curl http://localhost:8080/v1/health |
47 | | -curl http://localhost:8082/v1/health |
48 | | -curl http://localhost:8084/v1/health |
49 | | - |
50 | | -# Create API token |
51 | | -curl -X POST -H "X-Admin-Key: cluster-admin-secret" \ |
52 | | - -H "Content-Type: application/json" -d '{}' \ |
53 | | - http://localhost:8080/_admin/tokens |
54 | | - |
55 | | -# Analyze position |
56 | | -curl -X POST -H "Authorization: Bearer <token>" \ |
57 | | - -H "Content-Type: application/json" \ |
58 | | - -d '{"fen": "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1", "depth": 15}' \ |
59 | | - http://localhost:8080/v1/analyze |
| 73 | +cp pre-commit .git/hooks/pre-commit |
| 74 | +chmod +x .git/hooks/pre-commit |
60 | 75 | ``` |
61 | 76 |
|
62 | | -## API Endpoints |
63 | | - |
64 | | -### REST API (Port 8080) |
65 | | - |
66 | | -| Method | Endpoint | Description | |
67 | | -|--------|----------|-------------| |
68 | | -| POST | `/v1/analyze` | Analyze chess position | |
69 | | -| POST | `/v1/bestmove` | Get best move only | |
70 | | -| GET | `/v1/health` | Health check (public) | |
71 | | -| GET | `/v1/metrics` | Engine metrics | |
72 | | - |
73 | | -### Admin API (Requires X-Admin-Key header) |
74 | | - |
75 | | -| Method | Endpoint | Description | |
76 | | -|--------|----------|-------------| |
77 | | -| GET | `/_admin/tokens` | List all tokens | |
78 | | -| POST | `/_admin/tokens` | Create new token | |
79 | | -| DELETE | `/_admin/tokens/:id` | Revoke token | |
80 | | -| GET | `/_admin/cluster/status` | Cluster membership | |
81 | | - |
82 | | -### gRPC API (Port 8081) |
83 | | - |
| 77 | +### Running Tests |
84 | 78 | ```bash |
85 | | -# List services |
86 | | -grpcurl -plaintext localhost:8081 list |
87 | | - |
88 | | -# Analyze position |
89 | | -grpcurl -plaintext -H "authorization: Bearer <token>" \ |
90 | | - -d '{"fen": "startpos", "depth": 10}' \ |
91 | | - localhost:8081 chess.ChessAnalysis/Analyze |
92 | | -``` |
93 | | - |
94 | | -### GraphQL API (Port 8080/graphql) |
95 | | - |
96 | | -```graphql |
97 | | -query { |
98 | | - analyze(fen: "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1", depth: 15) { |
99 | | - bestMove { from to } |
100 | | - evaluation { scoreType value } |
101 | | - } |
102 | | -} |
| 79 | +cargo test --workspace |
103 | 80 | ``` |
104 | | - |
105 | | -## Configuration |
106 | | - |
107 | | -### Environment Variables |
108 | | - |
109 | | -| Variable | Default | Description | |
110 | | -|----------|---------|-------------| |
111 | | -| `IRONFISH_NODE_ID` | auto | Node identifier | |
112 | | -| `IRONFISH_BIND_ADDRESS` | 0.0.0.0:8080 | Listen address | |
113 | | -| `IRONFISH_CLUSTER_PEERS` | - | Comma-separated peer list | |
114 | | -| `IRONFISH_TOKEN_SECRET` | dev-secret | Shared HMAC secret for tokens | |
115 | | -| `IRONFISH_ADMIN_KEY` | - | Admin API authentication key | |
116 | | -| `STOCKFISH_PATH` | /usr/local/bin/stockfish | Path to Stockfish binary | |
117 | | - |
118 | | -### Config File (config/default.toml) |
119 | | - |
120 | | -```toml |
121 | | -[node] |
122 | | -id = "auto" |
123 | | -bind_address = "0.0.0.0:8080" |
124 | | -data_dir = "/var/lib/ironfish" |
125 | | - |
126 | | -[stockfish] |
127 | | -binary_path = "/usr/bin/stockfish" |
128 | | -pool_size = 4 |
129 | | -default_depth = 20 |
130 | | - |
131 | | -[cluster] |
132 | | -enabled = true |
133 | | -heartbeat_interval_ms = 1000 |
134 | | -gossip_interval_ms = 5000 |
135 | | - |
136 | | -[discovery] |
137 | | -static_peers = ["node2:8080", "node3:8080"] |
138 | | -multicast_enabled = true |
139 | | - |
140 | | -[auth] |
141 | | -enabled = true |
142 | | -token_ttl_days = 365 |
143 | | -``` |
144 | | - |
145 | | -## Project Structure |
146 | | - |
147 | | -``` |
148 | | -ironfish/ |
149 | | -├── Cargo.toml # Workspace root |
150 | | -├── Dockerfile # Multi-stage build |
151 | | -├── docker-compose.yml # 3-node cluster setup |
152 | | -├── crates/ |
153 | | -│ ├── ironfish-core/ # Core types, traits, errors |
154 | | -│ ├── ironfish-stockfish/ # Stockfish engine pool |
155 | | -│ ├── ironfish-auth/ # Token management & middleware |
156 | | -│ ├── ironfish-cluster/ # Clustering & gossip |
157 | | -│ │ ├── src/ |
158 | | -│ │ │ ├── discovery/ # Peer discovery (static, multicast) |
159 | | -│ │ │ ├── consensus/ # Raft + Bully algorithms |
160 | | -│ │ │ ├── gossip.rs # Token replication |
161 | | -│ │ │ └── network.rs # TCP transport |
162 | | -│ ├── ironfish-api/ # REST, gRPC, GraphQL |
163 | | -│ │ ├── proto/ # Protobuf definitions |
164 | | -│ │ └── src/ |
165 | | -│ │ ├── rest/ # Axum handlers |
166 | | -│ │ ├── grpc/ # Tonic service |
167 | | -│ │ └── graphql/ # async-graphql schema |
168 | | -│ ├── ironfish-cli/ # Admin CLI tool |
169 | | -│ └── ironfish-server/ # Main binary |
170 | | -└── config/ |
171 | | - └── default.toml # Default configuration |
172 | | -``` |
173 | | - |
174 | | -## Development |
175 | | - |
176 | | -```bash |
177 | | -# Run tests |
178 | | -cargo test --all |
179 | | - |
180 | | -# Run with logging |
181 | | -RUST_LOG=info cargo run -p ironfish-server |
182 | | - |
183 | | -# Format code |
184 | | -cargo fmt --all |
185 | | - |
186 | | -# Lint |
187 | | -cargo clippy --all -- -D warnings |
188 | | -``` |
189 | | - |
190 | | -## Cluster Features |
191 | | - |
192 | | -### Token Replication |
193 | | - |
194 | | -Tokens created on any node are automatically replicated to all cluster nodes via gossip protocol. Token revocations are also propagated cluster-wide. |
195 | | - |
196 | | -### Auto-Discovery |
197 | | - |
198 | | -Nodes discover each other using: |
199 | | -1. **Static peers** - Hostname:port list (supports DNS resolution) |
200 | | -2. **Multicast** - UDP discovery on local network (239.255.42.98:7878) |
201 | | - |
202 | | -### Shared Authentication |
203 | | - |
204 | | -All nodes must use the same `IRONFISH_TOKEN_SECRET` to validate tokens created by other nodes. |
| 81 | +Note: Docker integration tests run sequentially to avoid port conflicts. |
205 | 82 |
|
206 | 83 | ## License |
207 | | - |
208 | | -MIT |
| 84 | +MIT |
0 commit comments