Minimal Axum service that serves wallet feature flags, USD exchange rates, and Ethereum address risk reports over HTTP.
Feature flags are intentionally public to read. There is no write/admin HTTP API — updates are done by replacing the JSON file on disk (hot-reloaded).
Signing config files does not help against a compromised host: anyone who can replace the service binary (or its config) can serve arbitrary responses anyway. Trust is host/deploy integrity, not a signature on the JSON.
Operator/CI --> wallet_config.json --> VPS mount
|
remote-config (load / hot-reload)
|
GET /api/configs/wallet
ExchangeRate-API v6 --> in-memory cache --> GET /api/exchange-rates
Infura (ENS) + Etherscan --> GET /api/risk-checker/{address_or_ens}
| Method | Path | Auth | Response |
|---|---|---|---|
GET |
/api/configs/wallet |
none | { "data": { ...flags } } |
GET |
/api/exchange-rates |
none | { "data": { "conversion_rates": { ... }, "time_next_update_unix": … } } |
GET |
/api/risk-checker/{address_or_ens} |
none | { "data": { "address", "ensName", "balance", … } } |
GET |
/health |
none | { "healthy": true, "service": "RemoteConfig", "version": "…" } |
Example wallet payload:
{
"data": {
"enableTestButtons": false,
"enableKeystoneHardwareWallet": false,
"enableHighSecurity": true,
"enableRemoteNotifications": true,
"enableSwap": true
}
}Payload is opaque JSON: add or change keys by updating the file — no service redeploy required. Only invalid JSON is rejected.
Exchange rates are fetched from ExchangeRate-API (v6, USD base) and cached in memory until time_next_update_unix.
Risk reports resolve an Ethereum address or .eth ENS name (via Infura) and fetch balance / transaction / contract data from Etherscan. Invalid input returns 400; unresolved ENS or missing address data returns 404; upstream rate limits return 429.
CORS is off by default (cors_allowed_origins = []). Set origins in TOML when browser clients need cross-origin access.
wallet_config.json is gitignored (host-managed). Copy the example first:
cp wallet_config.example.json wallet_config.json
cargo run -- --config config/default.toml
# GET http://127.0.0.1:6767/api/configs/wallet
# GET http://127.0.0.1:6767/api/exchange-rates
# GET http://127.0.0.1:6767/api/risk-checker/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045
# GET http://127.0.0.1:6767/healthEvery ~250ms the service re-reads the JSON file:
- Valid update → memory updated
- Invalid JSON → last known good kept (warn log)
Startup fails hard if the initial file is missing or invalid.
| File | Purpose |
|---|---|
config/default.toml |
Local defaults (127.0.0.1:6767) |
config/docker.toml.example |
Container template (0.0.0.0:6767) |
Paths in TOML that are relative are resolved against the TOML file’s directory.
config/docker.toml and wallet_config.json are gitignored — create them on the host (or in CI) and bind-mount them. For containers:
cp config/docker.toml.example config/docker.tomlEnvironment overrides use the REMOTE_CONFIG prefix and __ separator, e.g.:
REMOTE_CONFIG__SERVER__PORT=9090
REMOTE_CONFIG__REMOTE_CONFIGS__WALLET_CONFIGS_FILE=/path/to/flags.json
REMOTE_CONFIG__EXCHANGE_RATE__API_KEY=your-key
REMOTE_CONFIG__RISK_CHECKER__ETHERSCAN_API_KEY=your-etherscan-key
REMOTE_CONFIG__RISK_CHECKER__INFURA_API_KEY=your-infura-keyThe image ships the binary only. Mount host-managed TOML and JSON at runtime (same pattern IAC should use).
cp config/docker.toml.example config/docker.toml
cp wallet_config.example.json wallet_config.json
docker build -t remote-config .
docker run --rm -p 6767:6767 \
-e REMOTE_CONFIG__EXCHANGE_RATE__API_KEY=your-key \
-e REMOTE_CONFIG__RISK_CHECKER__ETHERSCAN_API_KEY=your-etherscan-key \
-e REMOTE_CONFIG__RISK_CHECKER__INFURA_API_KEY=your-infura-key \
-v "$(pwd)/wallet_config.json:/app/wallet_config.json:ro" \
-v "$(pwd)/config/docker.toml:/app/config/docker.toml:ro" \
remote-config- Default command:
--config config/docker.toml(expects that path to be mounted; listens on0.0.0.0:6767) - Mount
wallet_config.jsonat/app/wallet_config.json(path from the TOML) - Mount configs read-only; the image runs as uid
10001 - If host files are mode
0640, run the container as the file owner (IAC pattern: setuser:to the admin UID) so the process can read them - Set
REMOTE_CONFIG__EXCHANGE_RATE__API_KEYand risk-checker keys (or put them in the mounted TOML)
cargo fmt --all
cargo clippy --all-targets --all-features -- -D warnings
cargo test