Pull the latest image and start a multi-validator shard:
docker compose -f shard.yml pull
docker compose -f shard.yml up -dWait for genesis (~2-3 minutes), then check that all validators have transitioned to Running state:
docker compose -f shard.yml logs 2>&1 | grep "Making a transition to Running state"You should see one line per node (boot, validator1-3, readonly). If the output is empty, genesis hasn't completed yet — wait and re-run the command.
Follow logs:
# All nodes
docker compose -f shard.yml logs -f
# Specific node
docker compose -f shard.yml logs -f validator1
docker compose -f shard.yml logs -f boot
docker compose -f shard.yml logs -f readonlyStop:
docker compose -f shard.yml downStop and wipe all data (fresh restart):
docker compose -f shard.yml down -vRequires a Rust toolchain (nightly pinned in rust-toolchain.toml). Build a local Docker image:
./node/docker-commands.sh build-localThen start with the local image:
F1R3FLY_RUST_IMAGE=f1r3fly-rust:local docker compose -f shard.yml up -dFor local development with instant finalization:
docker compose -f standalone.yml up -d
docker compose -f standalone.yml logs -f
docker compose -f standalone.yml down -v| File | Description |
|---|---|
shard.yml |
Full shard: bootstrap + 3 validators + observer, plus Prometheus and Grafana |
standalone.yml |
Single standalone node for development |
validator4.yml |
Additional validator joining existing shard — see Joining an Existing Network |
observer.yml |
Additional read-only node joining existing shard — see Joining an Existing Network |
monitoring.yml |
Prometheus + Grafana + cAdvisor, for a topology that does not already include them |
shard.yml already defines prometheus and grafana, so do not layer monitoring.yml on top of it — the container names collide. Use monitoring.yml with standalone.yml, or with a shard you assembled yourself.
Config files are minimal overrides (~40 lines each) that merge on top of the node's built-in defaults.conf. There is no need to duplicate the full default configuration -- only specify values you want to change. HOCON's include/fallback semantics handle the rest.
All compose files use 2 shared config files. Per-role behavior is controlled via CLI flags in compose commands.
| Config File | Used By | Purpose |
|---|---|---|
conf/default.conf |
All shard roles | Minimal shard overrides (network, ports, consensus tuning) |
conf/standalone-dev.conf |
Standalone | standalone = true, instant finalization |
CLI flags used per role:
| Flag | Used by |
|---|---|
--ceremony-master-mode |
Bootstrap |
--heartbeat-disabled |
Bootstrap, Observer |
--required-signatures N |
Bootstrap |
--genesis-validator |
Validators 1-3 |
Key settings in default.conf (see Consensus Configuration Guide for detailed semantics):
fault-tolerance-threshold = 0.1(tolerates 1 expelled validator in 3-validator set)synchrony-constraint-threshold = 0(no synchrony gate on proposals — correct for multi-parent DAG)enable-mergeable-channel-gc = trueheartbeat.enabled = true(overridden via--heartbeat-disabledfor bootstrap/observer)
The node also has a logging { } section in defaults.conf for controlling log format and output:
logging {
filter = "info,tonic=error,..." # INFO baseline with dependency and transport noise lowered. RUST_LOG overrides it.
format = "json" # "json" or "pretty"
sink = "stdout" # "stdout", "file", or "both"
file {
rotation = "daily" # "never", "hourly", "daily"
retention = 14 # rotated files to keep; 0 = unlimited
}
}When sink includes "file", logs are written to <data-dir>/logs/node.log — in Docker that is /var/lib/rnode/logs/node.log inside the container.
Every compose file caps its container logs at max-size: 100m, max-file: "3"
— 300 MB per container, after which the oldest file is discarded. Raising
RUST_LOG to debug for a reproduction can burn through that window in
minutes, so copy anything you need out of docker logs before it rotates, or
raise the cap for the run.
The Rust node reads a small set of environment variables. All other configuration is in HOCON config files (see Configuration above). Env vars are used only for secrets and logging overrides.
| Variable | Description |
|---|---|
RUST_LOG |
Overrides logging.filter from config (e.g. info, info,f1r3fly.casper=debug). The compose files pass it to a node only when it is set in the shell or in .env. |
OPENAI_ENABLED |
Enable OpenAI AI services (true/false) |
OPENAI_API_KEY |
OpenAI API key (required when OPENAI_ENABLED=true) |
OLLAMA_ENABLED |
Enable local Ollama AI services (true/false) |
OLLAMA_BASE_URL |
Ollama server URL (default: http://localhost:11434) |
OLLAMA_MODEL |
Ollama model name (default: llama3.2) |
OLLAMA_TIMEOUT_SEC |
Ollama request timeout in seconds (default: 120) |
See .env.example for Docker defaults.
| Node | Protocol | gRPC Ext | gRPC Int | HTTP | Discovery | Admin |
|---|---|---|---|---|---|---|
| Bootstrap | 40400 | 40401 | 40402 | 40403 | 40404 | 40405 |
| Validator1 | 40410 | 40411 | 40412 | 40413 | 40414 | 40415 |
| Validator2 | 40420 | 40421 | 40422 | 40423 | 40424 | 40425 |
| Validator3 | 40430 | 40431 | 40432 | 40433 | 40434 | 40435 |
| Validator4 | 40440 | 40441 | 40442 | 40443 | 40444 | 40445 |
| Observer | 40450 | 40451 | 40452 | 40453 | 40454 | 40455 |
shard.yml starts Prometheus and Grafana itself, so a full shard needs nothing
further. For a topology without them — standalone.yml, or a shard you
assembled yourself — start the monitoring stack separately:
docker compose -f monitoring.yml up -d # Start
docker compose -f monitoring.yml down # Stop| Component | URL | Description |
|---|---|---|
| Prometheus | http://localhost:9090 | Metrics, targets, recording rules |
| Grafana | http://localhost:3000 | Dashboards (admin/admin) |
| cAdvisor | http://localhost:8080 | Container CPU/memory/IO metrics |
Prometheus uses DNS-based service discovery — only running nodes get scraped (no false DOWN targets for standalone or partial shard).
Automated startup SLA check:
./scripts/ci/check-casper-init-sla.sh docker/shard.yml 180Debug bundle on failure:
./scripts/ci/collect-casper-init-artifacts.sh docker/shard.yml /tmp/casper-init-artifactsVerify the shard is working end-to-end using the rust-client smoke test:
# Clone rust-client (if not already present)
git clone https://github.com/F1R3FLY-io/rust-client.git ../rust-client
# Run smoke test against the running shard (default: localhost:40411)
cd ../rust-client
./scripts/smoke_test.sh localhost 40412 40413 40452The smoke test builds the rust-client binary and runs 30+ commands covering:
- Deploy / propose / finalize workflow
- Token transfers with finalization verification
- Node status, blocks, metrics
- PoS queries (epoch info, validator status)
- Block streaming (watch-blocks)
- Load testing with concurrent transfers
Results are logged to logs/smoke_test_*.log with pass/fail counters.
The native token's identity is configured in defaults.conf (or via CLI flags) and baked into the TokenMetadata Rholang contract at genesis. These values are immutable after genesis — they cannot be changed without creating a new network.
| Config Field | CLI Flag | Default | Description |
|---|---|---|---|
native-token-name |
--native-token-name |
F1R3CAP |
Full display name |
native-token-symbol |
--native-token-symbol |
F1R3 |
Ticker symbol |
native-token-decimals |
--native-token-decimals |
8 |
Decimal places (1 token = 10^decimals dust) |
Override per-node via environment variables in the compose files (e.g. NATIVE_TOKEN_NAME=MyToken), or per-validator via VALIDATOR1_NATIVE_TOKEN_NAME=MyToken.
After genesis, the values are queryable:
- API:
GET /api/status→nativeTokenName,nativeTokenSymbol,nativeTokenDecimals - On-chain:
rho:system:tokenMetadatacontract with methodsname,symbol,decimals,all
Joiners verify their config matches the on-chain values at startup. A mismatch causes the node to exit with a clear error.
- Bootstrap Node - Initial balance for network operations
- Validator 1-3 - Funded for transaction fees and operations
- Validator 1 - Bonded with 1000 stake
- Validator 2 - Bonded with 1000 stake
- Validator 3 - Bonded with 1000 stake
Bootstrap and Validator 4 are not in bonds.txt and do not validate.
Rust client: https://github.com/F1R3FLY-io/rust-client
- Private Key:
5f668a7ee96d944a4494cc947e4005e172d7ab3461ee5538f1f2a45a835e9657 - Public Key:
04ffc016579a68050d655d55df4e09f04605164543e257c8e6df10361e6068a5336588e9b355ea859c5ab4285a5ef0efdf62bc28b80320ce99e26bb1607b3ad93d - ETH:
fac7dde9d0fa1df6355bd1382fe75ba0c50e8840 - REV:
1111AtahZeefej4tvVR6ti9TJtv8yxLebT31SCEVDCKMNikBk5r3g
- Private Key:
357cdc4201a5650830e0bc5a03299a30038d9934ba4c7ab73ec164ad82471ff9 - Public Key:
04fa70d7be5eb750e0915c0f6d19e7085d18bb1c22d030feb2a877ca2cd226d04438aa819359c56c720142fbc66e9da03a5ab960a3d8b75363a226b7c800f60420 - ETH:
a77c116ce0ebe1331487638233bb52ba6b277da7 - REV:
111127RX5ZgiAdRaQy4AWy57RdvAAckdELReEBxzvWYVvdnR32PiHA
- Private Key:
2c02138097d019d263c1d5383fcaddb1ba6416a0f4e64e3a617fe3af45b7851d - Public Key:
04837a4cff833e3157e3135d7b40b8e1f33c6e6b5a4342b9fc784230ca4c4f9d356f258debef56ad4984726d6ab3e7709e1632ef079b4bcd653db00b68b2df065f - ETH:
df00c6395a23e9b2b8780de9a93c9522512947c3 - REV:
111129p33f7vaRrpLqK8Nr35Y2aacAjrR5pd6PCzqcdrMuPHzymczH
- Private Key:
b67533f1f99c0ecaedb7d829e430b1c0e605bda10f339f65d5567cb5bd77cbcb - Public Key:
0457febafcc25dd34ca5e5c025cd445f60e5ea6918931a54eb8c3a204f51760248090b0c757c2bdad7b8c4dca757e109f8ef64737d90712724c8216c94b4ae661c - ETH:
ca778c4ecf5c6eb285a86cedd4aaf5167f4eae13 - REV:
1111LAd2PWaHsw84gxarNx99YVK2aZhCThhrPsWTV7cs1BPcvHftP
- Private Key:
5ff3514bf79a7d18e8dd974c699678ba63b7762ce8d78c532346e52f0ad219cd - Public Key:
04d26c6103d7269773b943d7a9c456f9eb227e0d8b1fe30bccee4fca963f4446e3385d99f6386317f2c1ad36b9e6b0d5f97bb0a0041f05781c60a5ebca124a251d - ETH:
0cab9328d6d896e5159a1f70bc377e261ded7414 - REV:
1111La6tHaCtGjRiv4wkffbTAAjGyMsVhzSUNzQxH1jjZH9jtEi3M