- Clarify that the dev Anvil defaults to Base Sepolia but can be overridden
with FORK_URL (confirmed from containers/anvil-entrypoint.sh)
- Add "Network Contexts" section distinguishing three distinct Anvil usages:
1. Dev stack Anvil (docker-compose): Base Sepolia by default
2. red-team.sh: requires FORK_URL=mainnet because it uses Base mainnet
periphery addresses (V3_FACTORY, SwapRouter02, NPM)
3. FitnessEvaluator.t.sol: independent mainnet fork via BASE_RPC_URL,
unrelated to the docker-compose stack
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
6.5 KiB
ENVIRONMENT.md — Local Dev Stack
How to start, stop, and verify the harb development environment.
Stack Overview
Docker Compose services (in startup order):
| Service | Purpose | Port | Health Check |
|---|---|---|---|
| anvil | Local Ethereum fork (Base Sepolia by default; override with FORK_URL) |
8545 | JSON-RPC response |
| postgres | Ponder database | 5432 | pg_isready |
| bootstrap | Deploys contracts to anvil | — | One-shot, exits 0 |
| ponder | On-chain indexer + GraphQL API | 42069 | HTTP /ready or GraphQL |
| landing | Landing page (Vue 3 + Vite) | 5174 | HTTP response |
| webapp | Staking app (Vue 3) | 5173 | HTTP response |
| txn-bot | Automated recenter/tx bot | — | Process alive |
| caddy | Reverse proxy / TLS | 80/443 | — |
| otterscan | Block explorer | 5100 | — |
Network Contexts
Two distinct Anvil contexts are in use; they target different networks.
Dev stack Anvil (docker-compose)
The anvil service in docker-compose.yml runs containers/anvil-entrypoint.sh, which forks:
${FORK_URL:-https://sepolia.base.org}
Default: Base Sepolia. Uniswap V3 contracts are re-deployed fresh by the bootstrap service at start-up, so the fork target only affects block history and chain-id context. The bootstrap deploys all protocol contracts (Kraiken, Stake, LiquidityManager, OptimizerProxy) at addresses written to tmp/containers/contracts.env.
To fork Base mainnet instead (required for red-team / backtesting — see below):
FORK_URL=https://mainnet.base.org docker compose up -d
Backtesting / red-team Anvil (scripts/harb-evaluator/red-team.sh)
red-team.sh boots the docker-compose stack and then calls protocol operations using Base mainnet addresses for the Uniswap V3 periphery (V3_FACTORY, SwapRouter02, NonfungiblePositionManager). These addresses are only valid on a mainnet fork. Before running red-team.sh, set:
export FORK_URL=https://mainnet.base.org
FitnessEvaluator (onchain/test/FitnessEvaluator.t.sol)
FitnessEvaluator.t.sol is completely independent of the dev Anvil. It forks Base mainnet once per forge test run using the BASE_RPC_URL environment variable and has no relationship to the docker-compose stack:
BASE_RPC_URL=https://mainnet.base.org \
FITNESS_MANIFEST_DIR=/tmp/manifest \
forge test --match-contract FitnessEvaluator -vv
Quick Start
cd /home/debian/harb
# Start everything
docker compose up -d
# Wait for bootstrap (deploys contracts, ~60-90s)
docker compose logs -f bootstrap
# Check all healthy
docker compose ps
Verify Stack Health
# Anvil (local chain)
curl -s http://localhost:8545 -X POST -H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' | jq .result
# Ponder (indexer + GraphQL)
curl -s http://localhost:42069/graphql -X POST \
-H 'Content-Type: application/json' \
-d '{"query":"{ stats { id } }"}' | jq .
# Landing page
curl -sf http://localhost:5174 | head -5
# Staking app
curl -sf http://localhost:5173 | head -5
Container Network
Services communicate on harb-network Docker bridge.
Internal hostnames match service names (e.g., ponder:42069).
Landing page container IP (for Playwright testing): check with
docker inspect landing --format '{{.NetworkSettings.Networks.harb_harb-network.IPAddress}}'
Common URLs (for testing/review)
- Landing:
http://172.18.0.6:5174(container IP) orhttp://localhost:5174 - Staking app:
http://localhost:5173/app/ - Ponder GraphQL:
http://localhost:42069/graphql - Anvil RPC:
http://localhost:8545
Resource Notes
- 8GB VPS — running full stack uses ~4-5GB RAM
- npm install inside containers can OOM with all services running
- Landing container takes ~2min to restart (npm install + vite startup)
- 4GB swap is essential for CI + stack concurrency
Staking App Passwords
For testing login: lobsterDao, test123, lobster-x010syqe?412!
(defined in web-app/src/views/LoginView.vue)
Webapp Environment Variables
| Variable | Default | Set in docker-compose | Purpose |
|---|---|---|---|
VITE_ENABLE_LOCAL_SWAP |
false (unset) |
true |
Show inline ETH→$KRK swap widget on Get KRK page instead of the Uniswap link. Enable for local dev; leave unset for production builds. |
VITE_KRAIKEN_ADDRESS |
from deployments-local.json |
via contracts.env + entrypoint |
Override KRK token address. |
VITE_STAKE_ADDRESS |
from deployments-local.json |
via contracts.env + entrypoint |
Override Stake contract address. |
VITE_DEFAULT_CHAIN_ID |
auto-detected (31337 on localhost) | — | Force the default chain. |
Contract Addresses
After bootstrap, addresses are written to /home/debian/harb/tmp/containers/contracts.env with the following variable names (no VITE_ prefix):
LIQUIDITY_MANAGER=0x...
KRAIKEN=0x...
STAKE=0x...
The entrypoint scripts read this file and re-export the addresses with VITE_ prefixes for Vite builds:
containers/landing-entrypoint.shexportsVITE_KRAIKEN_ADDRESSandVITE_STAKE_ADDRESScontainers/webapp-entrypoint.shexportsVITE_KRAIKEN_ADDRESSandVITE_STAKE_ADDRESS
E2E Test Environment Variables
The Playwright test setup (tests/setup/stack.ts) reads stack coordinates from env vars, falling back to onchain/deployments-local.json when they are absent.
| Variable | Purpose |
|---|---|
STACK_RPC_URL |
RPC endpoint (default: http://localhost:8081/api/rpc) |
STACK_WEBAPP_URL |
Web app base URL (default: http://localhost:8081) |
STACK_GRAPHQL_URL |
GraphQL endpoint (default: http://localhost:8081/api/graphql) |
STACK_KRAIKEN_ADDRESS |
Kraiken contract address (overrides deployments-local.json) |
STACK_STAKE_ADDRESS |
Stake contract address (overrides deployments-local.json) |
STACK_LM_ADDRESS |
LiquidityManager contract address (overrides deployments-local.json) |
STACK_OPTIMIZER_PROXY_ADDRESS |
OptimizerProxy address (optional; enables optimizer integration tests) |
When all three of STACK_KRAIKEN_ADDRESS, STACK_STAKE_ADDRESS, and STACK_LM_ADDRESS are set, the deployments file is not read at all, which allows tests to run in containerised environments that have no local checkout.
Playwright Testing
# Chromium path
/home/debian/.cache/ms-playwright/chromium-1209/chrome-linux64/chrome
# Run against landing (block fonts for speed)
NODE_PATH=$(npm root -g) node test-script.cjs
See tmp/user-test-r4.cjs for the most recent test script pattern.