Environment Variables
Consolidated reference for all environment controls. The primary config file on macOS and Linux is /etc/edgecoder/edgecoder.env. See scripts/macos/payload/etc/edgecoder/edgecoder.env.example for a fully-annotated template.
Runtime mode
NODE_ENV (default development): production turns on the production-only checks listed under Startup validation.
| Variable | Values | Description |
|---|---|---|
EDGE_RUNTIME_MODE | worker | coordinator | ide-provider | control-plane | inference | all-in-one | Selects which service the daemon runs as. Required. |
HOST | 127.0.0.1 | Listen address for all HTTP services; set 0.0.0.0 in containers/Fly or for LAN multi-machine setups. |
Worker identity and enrollment
| Variable | Example | Description |
|---|---|---|
AGENT_ID | mac-worker-001 | Unique node identifier. Must match the enrolled node ID in the portal. |
AGENT_OS | macos | ubuntu | debian | windows | ios | OS label reported to coordinator. |
AGENT_MODE | swarm-only | ide-enabled | Whether this agent also serves IDE inference requests. |
AGENT_CLIENT_TYPE | edgecoder-native | Runtime flavor label. Keep default unless using a custom runtime. |
AGENT_REGISTRATION_TOKEN | (from Nodes page) | Required for EDGE_RUNTIME_MODE=worker. Generated by node enrollment in the portal. |
Coordinator configuration
| Variable | Example | Description |
|---|---|---|
COORDINATOR_URL | https://coordinator.edgecoder.io | URL of the coordinator this agent connects to. |
COORDINATOR_REGISTRATION_TOKEN | (from Nodes page) | Required for EDGE_RUNTIME_MODE=coordinator. |
COORDINATOR_PUBLIC_URL | https://coordinator.edgecoder.io | Coordinator's publicly-reachable URL (used by coordinator itself). |
COORDINATOR_DISCOVERY_URL | https://control.edgecoder.io/network/coordinators | Control-plane endpoint for coordinator autodiscovery. |
COORDINATOR_DISCOVERY_URLS | https://edgecoder-seed-eu.fly.dev | Portal: comma-separated coordinator URLs, tried in order. The first healthy one (/health/runtime) is cached for 60 s. Takes precedence over COORDINATOR_DISCOVERY_URL (#35). |
COORDINATOR_BOOTSTRAP_URLS | https://coordinator.edgecoder.io | Comma-separated fallback list. |
COORDINATOR_HTTP_TIMEOUT_MS | 15000 | HTTP timeout for coordinator API calls (ms). |
COORDINATOR_POST_RETRIES | 2 | Retry count for coordinator POST requests on transient failure. |
Economy, payouts and receipts (coordinator)
| Variable | Default | Description |
|---|---|---|
RATE_ORACLE_ENABLED | true | Enable the BTC/USD rate oracle (median of sources, daily clamp). Set false to freeze class reference pricing. |
RATE_RECALIBRATION_MS | 86400000 | How often the rate oracle recalibrates its per-class reference rates (ms; min 60000). |
MARKET_DEFAULT_BID_PREMIUM_BPS | 1000 | Default order-book bid premium over the oracle reference, in basis points. |
NETWORK_FEE_BPS | 500 | Network fee skimmed into the fee pool on each settled trade, in basis points. |
HOST_HEARTBEAT_MS | 60000 | Interval at which a host reports its uptime/services heartbeat (ms; min 1000). |
FEE_POOL_DISTRIBUTION_MS | 86400000 | Interval for distributing the network fee pool to hosts by uptime × services (ms; min 60000). |
PAYOUT_AUTO_INTERVAL_MS | 21600000 | Interval for the LNURL auto-payout sweep (ms; min 60000). |
RECEIPT_EPOCH_MS | 3600000 | Length of a signed-work-receipt Merkle epoch before it is sealed and anchored (ms). |
RECEIPT_REPLICATION_MS | 60000 | How often sealed receipt epochs are replicated to peers (ms). |
Model provider
| Variable | Values / Example | Description |
|---|---|---|
LOCAL_MODEL_PROVIDER | edgecoder-local | ollama-local | Inference backend. Use ollama-local when Ollama is installed. |
OLLAMA_MODEL | qwen3.5:9b | Model tag to use/pull when provider is ollama-local. |
OLLAMA_AUTO_INSTALL | true | false | Auto-pull the Ollama model on startup if not present. |
OLLAMA_HOST | http://127.0.0.1:11434 | Ollama daemon URL. Blank = default local host. |
Mesh auth and security
| Variable | Description |
|---|---|
MESH_AUTH_TOKEN | Shared mesh auth secret. Auto-provisioned by coordinator on successful registration if left blank. |
COORDINATOR_MESH_TOKEN | Alias for MESH_AUTH_TOKEN. Keep aligned if both are set. |
MESH_PEER_ALLOWLIST | #52: comma-separated peerId:fingerprint@https://peer-url entries for the peer coordinators this coordinator trusts. @url pins the peer's endpoint, and gossip never changes it. In production every entry must have @url, or the coordinator refuses to start. fingerprint is the SHA-256 (hex) of the peer's SPKI public key. With the mesh token, a peer must also be on this list and sign its registration with that key. Results it forwards must be signed too. Required in production: without it, token-only peer registration and forwarded results are rejected. In dev an empty list keeps the old token-only behaviour. |
COORDINATOR_PRIVATE_KEY_PEM | Ed25519 private key (PKCS#8 PEM) for this coordinator's mesh identity. Set it on every coordinator that peers, since a generated key changes on each restart and would drop off its peers' allowlists. Fingerprint for MESH_PEER_ALLOWLIST: node -e 'const c=require("crypto");const k=c.createPublicKey(process.env.COORDINATOR_PRIVATE_KEY_PEM);console.log(c.createHash("sha256").update(k.export({type:"spki",format:"der"})).digest("hex"))' |
INFERENCE_AUTH_TOKEN | Auth token for the inference service API. |
ADMIN_API_TOKEN | Admin token for control-plane operator APIs. |
PORTAL_SERVICE_TOKEN | Internal service token for portal ↔ coordinator/control-plane calls. |
ENABLE_DEBUG_ROUTES | Set 1 to register coordinator /debug/* routes (dev/e2e only; never registered in production). |
PORTAL_ALLOW_OPEN_INTERNAL | Set 1 (non-production only) to leave portal /internal/* open when PORTAL_SERVICE_TOKEN is unset; otherwise those routes return 503. |
REQUIRE_SIGNED_AGENT_REQUESTS | Default true, always true in production. Agent /heartbeat, /pull and /result must carry signed request headers (x-agent-id, x-signature, …). Set false only for local dev with legacy unsigned agents. |
Result verification (#50, #106)
| Variable | Default | Description |
|---|---|---|
RESULT_VERIFICATION_RATE | 0 | Base fraction of subtasks re-executed for verification. 0 = verification off (the default). |
RESULT_VERIFICATION_ABANDON_PENALTY | 0.25 | Reputation lost for claiming a verification replica and never reporting it. |
RESULT_VERIFICATION_REPLICA_CLAIM_TIMEOUT_MS | 300000 | A claimed replica with no report after this long is reassigned to an agent other than the abandoner(s). |
RESULT_VERIFICATION_MAX_REASSIGNMENTS | 1 | How many times one sampled subtask's stalled replica is reassigned (0–10). After that the subtask waits for the overall timeout. |
RESULT_VERIFICATION_INITIAL_REPUTATION | 0.5 | Reputation (0–1) of an agent id the coordinator has never seen. Below 1 so a caught agent can't regain full trust by re-registering; lower reputation means its results are re-executed more often. |
RESULT_VERIFICATION_MAX_EXECUTIONS | 3 | Max executions of a sampled subtask (original + replicas), 2–3. Values above 3 are capped. At most this minus one replicas run per sampled subtask; whether winning replicas are paid is set by RESULT_VERIFICATION_MINT_REPLICA_REWARDS. |
RESULT_VERIFICATION_MINT_REPLICA_REWARDS | false | Pay winning verification replicas from newly minted compute credits (true/1/yes to enable). Off by default: only the original execution is paid, so verification doesn't add to the credit supply. Enabling minting requires Cody's approval. The coordinator logs a startup warning whenever RESULT_VERIFICATION_RATE is above 0, saying whether replicas are paid. |
Portal and passkeys
| Variable | Example | Description |
|---|---|---|
PORTAL_PUBLIC_URL | https://portal.edgecoder.io | Public-facing portal URL (used in email links, OAuth redirect). |
PORTAL_DATABASE_URL | postgres://... | Portal PostgreSQL connection string. |
PASSKEY_RP_ID | portal.edgecoder.io | WebAuthn relying party ID (must match origin domain). |
PASSKEY_RP_NAME | EdgeCoder Portal | Display name for WebAuthn credential creation. |
PASSKEY_ORIGIN | https://portal.edgecoder.io | Expected WebAuthn origin. |
PASSKEY_CHALLENGE_TTL_MS | 120000 | Passkey challenge expiry (ms). |
PORTAL_TRUST_FLY_CLIENT_IP | unset | When true and Fly's runtime FLY_APP_NAME is set, the rate-limit client IP is read from the Fly-Client-IP header that the Fly edge proxy sets (Fly request headers). Otherwise it is the socket address, and X-Forwarded-* is never trusted (trustProxy: false). Keep unset until #31. Until it is set, all portal traffic on Fly shares the proxy's rate-limit bucket. PORTAL_TRUST_PROXY_HOPS is gone, because Fastify 5.12 ignores hop counts. |
OAUTH_STATE_SECRET | (32+ random chars) | HMAC key for the browser-bound OAuth state cookie (__Host-oauth_state, #88). Required in production (at least 32 characters; the portal refuses to start without it). Set the same value on every instance. Outside production, unset means a random key per process. |
Key provider and rotation
| Variable | Values | Description |
|---|---|---|
KEY_PROVIDER | env | hsm | tpm | secure-enclave | Signing key backend. Default: env. |
HSM_PKCS11_LIB | /usr/lib/libpkcs11.so | Path to PKCS#11 shared library (required when KEY_PROVIDER=hsm). |
HSM_SLOT_ID | 0 | HSM slot identifier. |
HSM_PIN | (secret) | HSM user PIN. |
KEY_ROTATION_DAYS | 90 | Maximum days between key rotations. |
Bluetooth Local (BLE proxy — Mac IDE mode)
Used by the IDE provider server (EDGE_RUNTIME_MODE=ide-provider) to bridge the Mac-side IntelligentRouter to a nearby iPhone over Bluetooth.
| Variable | Default | Description |
|---|---|---|
BT_PROXY_PORT | 11435 | Port the Swift edgecoder-ble-proxy companion listens on. |
BT_PROXY_HOST | 127.0.0.1 | Host for the BLE proxy HTTP API. Always loopback in production. |
BT_PROXY_URL | http://127.0.0.1:11435 | Full URL the IntelligentRouter calls for bluetooth-local inference. Overrides BT_PROXY_HOST + BT_PROXY_PORT. |
BT_STATUS_URL | (auto-set) | Status endpoint checked by the router to decide if BT is available. Set automatically from BT_PROXY_HOST:BT_PROXY_PORT. |
The edgecoder-ble-proxy binary must be installed first:
npm run build:ble-proxy
# Installs to /opt/edgecoder/bin/edgecoder-ble-proxyTask and performance tuning
| Variable | Default | Description |
|---|---|---|
MAX_CONCURRENT_TASKS | 1 | Parallel task limit per agent. Increase gradually only on machines with headroom. |
PEER_OFFER_COOLDOWN_MS | 20000 | Cooldown between peer-direct offers while idle (ms). Lower = more aggressive. |
DOCKER_PROBE_TIMEOUT_MS | 3000 | Timeout for the docker info availability probe; a hung daemon counts as unavailable. Result cached 30 s. |
Inference service (port 4302)
The inference service checks these values at startup against inferenceConfigSchema (src/inference/config.ts). A missing or invalid value stops the service, and the error lists every problem at once. Empty values count as unset (#15).
| Variable | Default | Rule |
|---|---|---|
INFERENCE_AUTH_TOKEN | — | Required. Shared token for /model/* writes and coordinator calls. At least 32 characters in production. Trimmed, so callers must send the trimmed value. |
INFERENCE_REQUIRE_SIGNED_COORDINATOR_REQUESTS | false | true/false (also 1/0, yes/no; case-insensitive). When true, at least one trusted key must be configured. |
INFERENCE_MAX_SIGNATURE_SKEW_MS | 120000 | Integer, 1000–3600000. |
INFERENCE_NONCE_TTL_MS | 300000 | Integer, 1000–86400000. |
INFERENCE_COORDINATOR_PEER_ID | empty | Must be set together with INFERENCE_COORDINATOR_PUBLIC_KEY_PEM (both or neither). |
INFERENCE_COORDINATOR_PUBLIC_KEY_PEM | empty | The trusted coordinator's public key. |
INFERENCE_TRUSTED_COORDINATOR_KEYS_JSON | empty | A JSON object of non-empty peerId → non-empty public key PEM. Anything else fails startup. |
OLLAMA_HOST | http://127.0.0.1:11434 | An http(s) URL. |
OLLAMA_MODEL | qwen3.5:9b | The model that is active at startup. |
OLLAMA_COORDINATOR_MODEL | qwen3.5:9b (decompose and reflect); falls back to OLLAMA_MODEL for chat | The model used for coordinator prompts. |
IDE provider server (port 4304)
| Variable | Default | Description |
|---|---|---|
IDE_PROVIDER_HOST | 127.0.0.1 | Bind address. A non-loopback value requires IDE_PROVIDER_TOKEN. |
IDE_PROVIDER_TOKEN | (from ~/.edgecoder/ide-token) | Bearer token required on all routes except /health. If unset, the token file is created on first start (0600). |
IDE_ALLOWED_ORIGINS | (Tauri origins) | Extra browser origins allowed; any other web Origin gets 403. |
IDE_SHELL_ALLOW | npm,npx,node,python3,… | Comma-separated binaries run_shell may run. |
IDE_SHELL_AUTO_APPROVE | unset | 1 skips the approval prompt for allowlisted shell commands (local opt-in). |
iOS power telemetry (reported by iOS agents)
| Variable | Description |
|---|---|
IOS_ON_EXTERNAL_POWER | true/false — device is charging |
IOS_BATTERY_LEVEL_PCT | Battery percentage (0–100) |
IOS_LOW_POWER_MODE | true/false — iOS Low Power Mode active |
IOS_BATTERY_PULL_MIN_INTERVAL_MS | Throttle between assignment attempts on battery |
IOS_BATTERY_TASK_STOP_LEVEL_PCT | Minimum battery level before stopping assignment |
Bitcoin anchoring
| Variable | Default | Description |
|---|---|---|
BITCOIN_ANCHOR_PROVIDER | mock | Anchoring provider: anchor-proxy, bitcoind, or mock (dev only). blockstream is rejected because it can't broadcast (#102). |
BITCOIN_ANCHOR_CONFIRMATIONS | 6 | Confirmations before an anchor counts as anchored. |
BITCOIN_ANCHOR_DAILY_BUDGET_SATS | 200000 | Daily anchoring budget: refuse a broadcast when (broadcasts in 24h + 1) × BITCOIN_ANCHOR_MAX_FEE_SATS exceeds it. |
BITCOIN_ANCHOR_MAX_FEE_SATS | 20000 | Absolute fee cap per anchor tx. |
BITCOIN_ANCHOR_MAINNET | unset | Set to 1 to allow broadcasting on BITCOIN_NETWORK=bitcoin (opt-in). |
BITCOIN_NETWORK | signet | Must match across all coordinators in the mesh. |
ANCHOR_PROXY_URL | — | URL of the anchor proxy service (when provider is anchor-proxy). |
ANCHOR_PROXY_TOKEN | — | Bearer token for anchor proxy auth. |
BITCOIND_RPC_URL | — | Direct bitcoind RPC URL (when provider is bitcoind). |
BITCOIND_RPC_USER | — | RPC username. |
BITCOIND_RPC_PASSWORD | — | RPC password. |
BITCOIND_WALLET_NAME | — | Wallet name on the Bitcoin node. |
ANCHOR_CONFIRMATION_POLL_MS | 60000 | Poll interval for pending anchors (ms). |
STATS_ANCHOR_MIN_CONFIRMATIONS | 1 | Block confirmations before promoting anchor status. |
ANCHOR_PROXY_PORT | 4310 | Listen port for the anchor proxy service. |
MAX_BROADCASTS_PER_MINUTE | 10 | Rate limit for OP_RETURN broadcasts (anchor proxy). |
Wallet and economy
| Variable | Description |
|---|---|
WALLET_DEFAULT_NETWORK | Default chain/network for wallet operations. |
WALLET_SECRET_PEPPER | Server-side pepper for wallet key derivation. |
COORDINATOR_FEE_BPS | Coordinator fee in basis points. |
COORDINATOR_FEE_ACCOUNT | Fee recipient account address. |
PAYMENT_INTENT_TTL_MS | Payment intent expiry (ms). |
PAYMENT_INTENT_EXPIRY_GRACE_MS | Default 120000. Extra time after the TTL before an unpaid intent is expired; reconcile always asks the Lightning node first. |
LIGHTNING_PROVIDER | lnd or cln. Required in production (coordinator refuses to boot otherwise, unless ECONOMY_PAYMENTS_ENABLED=false). Defaults to mock outside production. |
LND_REST_URL / LND_MACAROON_HEX | LND REST endpoint and invoice macaroon (required when LIGHTNING_PROVIDER=lnd). |
CLN_REST_URL / CLN_API_TOKEN | Core Lightning REST endpoint and token (required when LIGHTNING_PROVIDER=cln). |
ALLOW_MOCK_LIGHTNING | Set to 1 to allow the auto-settling mock provider in local dev. Never allowed in production; not needed when NODE_ENV=test. |
PAYMENT_WEBHOOK_SECRET | Required in production. HMAC-SHA256 key for POST /economy/payments/webhook; senders must send x-payment-signature: sha256=<hex hmac of raw body> plus x-mesh-token (see API endpoints → Payment webhook signature). The webhook is only a hint — settlement is always confirmed with the Lightning node. |
BLE_SYNC_MAX_CREDITS_PER_TX | Default 100. Max credits per offline BLE transaction accepted by /credits/ble-sync. |
BLE_SYNC_ALLOW_V1 | Set 1 (non-production only, one release) to accept legacy v1 BLE signatures that do not cover credits. |
ECONOMY_PAYMENTS_ENABLED | Default true. Set 0, false, off or no (case-insensitive) to run without a Lightning node: no provider is created, and /economy/payments/* and POST /robot/tasks return 503. |
Network and infrastructure
| Variable | Description |
|---|---|
NETWORK_MODE | public_mesh or enterprise_overlay — mesh network policy mode. |
CONTROL_PLANE_URL | Control-plane service URL for coordinator/worker autodiscovery. |
NODE_BIN | Override Node.js binary path if auto-detection fails (macOS/Linux daemon). |
DOCS_SITE_URL | Documentation site URL (used in portal links). |
GITHUB_REPO_URL | Repository URL for changelog/release links. |
SUPPORT_EMAIL | Contact address on the portal's privacy and terms pages (default support@edgecoder.io; plain address only) (#22). |
MODEL_DOWNLOAD_DOMAINS | Comma-separated exact hostnames that signed model manifests may download from; replaces the default models.edgecoder.io,huggingface.co (#22). See Self-hosting. |
Startup validation (#15)
Each service reads its environment once at startup through a zod schema. An invalid value (for example abc for an interval, maybe for a flag, or a URL without http(s)://) stops the service, and the error lists every problem at once. Empty values count as unset. A docs coverage test (tests/common/env-docs.test.ts) fails if a schema key is missing from this page.
| Service | Schema | Production-only rules |
|---|---|---|
| inference | src/inference/config.ts | INFERENCE_AUTH_TOKEN at least 32 characters |
| portal | src/portal/config.ts | PORTAL_SERVICE_TOKEN, WALLET_SECRET_PEPPER, RESEND_API_KEY, DOCS_SITE_URL required; CONTROL_PLANE_URL or COORDINATOR_DISCOVERY_URLS; CONTROL_PLANE_ADMIN_TOKEN when CONTROL_PLANE_URL is set; https for PORTAL_PUBLIC_URL, DOCS_SITE_URL and passkey origins |
| coordinator | src/swarm/coordinator-config.ts | unchanged (requireProdEnv) |
| control-plane | src/control-plane/config.ts | ADMIN_API_TOKEN required in every environment |
Portal: other validated settings
| Variable | Type | Default |
|---|---|---|
NODE_ENV | string | development |
PORTAL_EXTERNAL_HTTP_TIMEOUT_MS | integer ≥ 100 | 7000 |
RESEND_FROM_EMAIL | string | unset |
CONTROL_PLANE_ADMIN_TOKEN | string | unset |
PORTAL_SESSION_TTL_MS | integer ms, 1000 to 31 days | 604800000 |
PORTAL_EMAIL_VERIFY_TTL_MS | integer ms, 1000 to 31 days | 86400000 |
WALLET_SEND_MFA_TTL_MS | integer ms, 1000 to 31 days | 600000 |
MOBILE_OAUTH_TOKEN_TTL_MS | integer ms, 1000 to 31 days | 300000 |
OAUTH_MICROSOFT_PROMPT | string | select_account |
PASSKEY_ALLOWED_ORIGINS | string | unset |
COORDINATOR_OPERATIONS_OWNER_EMAILS | string | unset |
SYSTEM_ADMIN_EMAILS | string | unset |
COORDINATOR_ADMIN_EMAILS | string | unset |
IOS_OAUTH_CALLBACK_PREFIX | string | edgecoder://oauth-callback |
OAUTH_GOOGLE_CLIENT_ID | string | unset |
OAUTH_GOOGLE_CLIENT_SECRET | string | unset |
OAUTH_GOOGLE_AUTHORIZE_URL | http(s) URL | https://accounts.google.com/o/oauth2/v2/auth |
OAUTH_GOOGLE_TOKEN_URL | http(s) URL | https://oauth2.googleapis.com/token |
OAUTH_GOOGLE_USERINFO_URL | http(s) URL | https://openidconnect.googleapis.com/v1/userinfo |
OAUTH_MICROSOFT_CLIENT_ID | string | unset |
OAUTH_MICROSOFT_CLIENT_SECRET | string | unset |
OAUTH_MICROSOFT_AUTHORIZE_URL | http(s) URL | https://login.microsoftonline.com/common/oauth2/v2.0/authorize |
OAUTH_MICROSOFT_TOKEN_URL | http(s) URL | https://login.microsoftonline.com/common/oauth2/v2.0/token |
OAUTH_MICROSOFT_USERINFO_URL | http(s) URL | https://graph.microsoft.com/oidc/userinfo |
OAUTH_GITHUB_CLIENT_ID | string | unset |
OAUTH_GITHUB_CLIENT_SECRET | string | unset |
Coordinator: other validated settings
| Variable | Type | Default |
|---|---|---|
ROBOT_COORDINATOR_FEE_BPS | integer ≥ 0 | 200 |
ROBOT_SWEEP_INTERVAL_MS | integer ≥ 1 | 86400000 |
ROBOT_MIN_SWEEP_SATS | integer ≥ 0 | 10000 |
ROBOT_TASK_DEFAULT_TIMEOUT_MS | integer ≥ 1 | 3600000 |
ROBOT_AUTO_SETTLE_DELAY_MS | integer ≥ 1 | 86400000 |
CONTRIBUTION_BURST_CREDITS | integer ≥ 0 | 25 |
MIN_CONTRIBUTION_RATIO | number ≥ 0 | 1 |
ISSUANCE_WINDOW_MS | integer ≥ 1 | 86400000 |
ISSUANCE_RECALC_MS | integer ≥ 1 | 3600000 |
ISSUANCE_BASE_DAILY_POOL_TOKENS | integer ≥ 0 | 10000 |
ISSUANCE_MIN_DAILY_POOL_TOKENS | integer ≥ 0 | 2500 |
ISSUANCE_MAX_DAILY_POOL_TOKENS | integer ≥ 0 | 100000 |
ISSUANCE_LOAD_CURVE_SLOPE | number ≥ 0 | 0.35 |
ISSUANCE_SMOOTHING_ALPHA | number ≥ 0 | 0.35 |
ISSUANCE_COORDINATOR_SHARE | number ≥ 0 | 0.05 |
ISSUANCE_RESERVE_SHARE | number ≥ 0 | 0.05 |
ANCHOR_INTERVAL_MS | integer ≥ 1 | 7200000 |
STATS_LEDGER_SYNC_INTERVAL_MS | integer ≥ 1 | 10000 |
STATS_ANCHOR_INTERVAL_MS | integer ≥ 1 | 600000 |
AGENT_RATE_LIMIT_MAX | integer ≥ 0 | 30 |
AGENT_RATE_LIMIT_WINDOW_MS | integer ≥ 1 | 60000 |
SECURITY_NONCE_TTL_MS | integer ≥ 1 | 300000 |
SECURITY_MAX_SKEW_MS | integer ≥ 1 | 120000 |
TUNNEL_IDLE_TTL_MS | integer ≥ 1 | 90000 |
TUNNEL_MAX_RELAYS_PER_MIN | integer ≥ 0 | 120 |
RELAY_RATE_LIMIT_PER_10S | integer ≥ 0 | 80 |
DIRECT_WORK_OFFERS_PER_10S | integer ≥ 0 | 20 |
AGENT_ORCHESTRATION_ONLINE_WINDOW_MS | integer ≥ 1 | 120000 |
ROBOT_QUEUE_ENABLED | true/false | false |
REQUIRE_VERIFIED_AGENTS | true/false | false |
INFERENCE_URL | http(s) URL | http://127.0.0.1:4302 |
Control plane: other validated settings
| Variable | Type | Default |
|---|---|---|
UI_RETIRE_REDIRECT_URL | string | unset |
ALLOWED_ADMIN_IPS | string | unset |
ALLOWED_UI_IPS | string | unset |
Observability (#14)
| Variable | Default | Description |
|---|---|---|
LOG_LEVEL | info | pino log level for every service. |
METRICS_TOKEN | unset | Bearer token for Prometheus /metrics (inference: /metrics/prometheus). In production, /metrics is not served at all unless this is set. Outside production it is open. |
REQUEST_ID_FORWARD_ORIGINS | unset | Extra comma-separated origins that receive x-request-id on outbound calls. The service URLs (COORDINATOR_URL, CONTROL_PLANE_URL, INFERENCE_URL, PORTAL_SERVICE_URL, COORDINATOR_PUBLIC_URL, COORDINATOR_DISCOVERY_URLS) are included automatically; third-party APIs never get it. |
SENTRY_DSN | unset | Optional error tracking. Unset: off, nothing is loaded. Set: 5xx errors go to Sentry, tagged with service, route and request id, with no request bodies, headers or cookies. Needs @sentry/node in the image; if it's missing, the service logs one warning and runs without it. |
SENTRY_ENVIRONMENT | NODE_ENV | Sentry environment name. |
SENTRY_RELEASE | unset | Sentry release (for example the git SHA). |
Alerting on 5xx without Sentry
Every service exports edgecoder_http_requests_total{service,method,route,status}. Example Prometheus rules:
groups:
- name: edgecoder-http
rules:
- alert: EdgeCoderHigh5xxRate
# more than 5% of requests failing with 5xx for 10 minutes, per service
expr: |
sum by (service) (rate(edgecoder_http_requests_total{status=~"5.."}[5m]))
/ sum by (service) (rate(edgecoder_http_requests_total[5m])) > 0.05
for: 10m
labels: { severity: page }
- alert: EdgeCoder5xxOnRoute
# any single route returning 5xx steadily (catches a broken endpoint on a busy service)
expr: sum by (service, route) (rate(edgecoder_http_requests_total{status=~"5.."}[5m])) > 0.1
for: 15m
labels: { severity: ticket }
- alert: EdgeCoderMetricsMissing
# the scrape itself failing usually means the service is down or METRICS_TOKEN is wrong
expr: up{job=~"edgecoder-.*"} == 0
for: 5m
labels: { severity: page }Scrape /metrics (inference: /metrics/prometheus) with authorization: Bearer $METRICS_TOKEN. route is the route pattern, so these rules stay low-cardinality.
Common profiles
# 1) Standard mesh worker (most common)
EDGE_RUNTIME_MODE=worker
AGENT_ID=my-node-001
AGENT_OS=macos
AGENT_REGISTRATION_TOKEN=<from portal>
COORDINATOR_URL=https://coordinator.edgecoder.io
# 2) Worker + IDE-capable agent
EDGE_RUNTIME_MODE=worker
AGENT_MODE=ide-enabled
# 3) Dedicated coordinator
EDGE_RUNTIME_MODE=coordinator
COORDINATOR_REGISTRATION_TOKEN=<from portal>
DATABASE_URL=<postgres>
# 4) Local Ollama inference
LOCAL_MODEL_PROVIDER=ollama-local
OLLAMA_MODEL=qwen3.5:9b
OLLAMA_AUTO_INSTALL=trueOperational guidance
Production fail-fast (#5). With NODE_ENV=production, services refuse to boot when required secrets are empty (an empty token no longer means "auth off"):
- coordinator:
MESH_AUTH_TOKEN,PORTAL_SERVICE_URL,PORTAL_SERVICE_TOKEN,ADMIN_API_TOKEN,PAYMENT_WEBHOOK_SECRET, andLIGHTNING_PROVIDERunlessECONOMY_PAYMENTS_ENABLED=false - portal:
PORTAL_SERVICE_TOKEN,WALLET_SECRET_PEPPER,RESEND_API_KEY(plus the existing passkey/URL checks) - inference:
INFERENCE_AUTH_TOKEN - control-plane:
ADMIN_API_TOKEN
Run a fly secrets list checklist against this list before any deploy.
- Keep secrets in production secret stores (Fly Secrets, AWS SSM, etc.) rather than static env files.
- On macOS, the env file is
/etc/edgecoder/edgecoder.env(chmod 600, owned by root). - On Linux, same path — the systemd
EnvironmentFile=-/etc/edgecoder/edgecoder.envdirective loads it. - Review and rotate
MESH_AUTH_TOKENperiodically.