Configuration

Every environment variable economy-lab reads, with its meaning and default.

Source src/config.ts#L109-L172 · loadConfigsrc/index.ts#L246-L348 · capabilitiesFromEnvscripts/main.ts#L139-L205 · productionExternals.env.example

How configuration is read

economy-lab reads the environment in exactly one place, at startup. The library modules never touch process.env themselves. Instead the edge calls loadConfig once, gets back a plain Config object, and passes it in. A misconfigured deploy therefore fails at startup, not deep inside a request.

That single read also means defaults live in one file. Every tunable below is parsed by loadConfig with a fallback, so an unset or malformed value lands on the documented default rather than leaving the config half-applied. These are the same defaults the operation and background-worker pages cite.

A handful of variables — the signing secrets, the CREDIT↔USD rates, the payout provider, and the infrastructure URLs — are read outside loadConfig, when the edge wires up the Economy and its ports. Those are grouped separately at the end, because they pick adapters rather than tune policy.

Required secrets

Two secrets have no honest default. In production — NODE_ENV=production — a blank value for either makes loadConfig throw a single CONFIG.INVALID fault listing every missing key at once. Outside production a blank secret is tolerated so a local run needs no setup.

VariableMeaningDefault
WEBHOOK_SECRETHMAC key that verifies inbound billing and settlement webhooks (e.g. from a payment processor).none (required in production)
SIGNING_SECRETKey the signer uses to sign chain checkpoints and entitlement-gated URLs. See integrity.none (required in production)

Payouts

These tune when a payout is too small, too frequent, or too old to proceed. The minimums are counted in CREDIT minor units; see the money model for what a minor unit is.

VariableMeaningDefault
PAYOUT_MIN_EARNED_MINORSmallest payout a user may request, counted only against earned CREDIT — not bought or promo-granted. The floor keeps a disbursement from costing more in rail fees than it pays out.2000000 (≈ $100)
PAYOUT_MIN_INTERVAL_MSMinimum time between a user's payout requests. The shipped value matches the live docs; the legal requirement is 14 days (1209600000).86400000 (24h)
MAX_PAYOUT_ATTEMPTSProvider attempts for one payout before the system gives up and reverses it.5
MAX_PAYOUT_AGE_MSLongest a payout may sit in SUBMITTED before the worker force-fails it as timed out, because the provider never reported back.86400000 (24h)
PAYOUT_FEE_BPSPayout-rail fee in basis points, charged on the USD a creator cashes out. This is the rail's own cut, deducted from the disbursement — not platform revenue.150 (≈ 1.5%)

The payout rate and the per-state payout SLAs also feed the saga; the SLAs live under the worker group below, and the rate under externals.

Worker queues

The background worker retries deferred work, then gives up after a cap so a poison message can't wedge a queue. Each cap below is a number of attempts; the worker dead-letters once attempts reaches it.

VariableMeaningDefault
MAX_OUTBOX_ATTEMPTSDelivery attempts an outbox event gets before the relay dead-letters it (status failed).10
MAX_INBOX_ATTEMPTSApply attempts an inbox event gets before the apply worker dead-letters it (status dead). The inbound mirror of MAX_OUTBOX_ATTEMPTS.10
MAX_SUBSCRIPTION_ATTEMPTSConsecutive retryable renewal failures before the renewal sweep lapses a subscription instead of re-billing forever.10

The payout SLAs are per-state staleness thresholds the reconcile sweep folds over. A step stuck past its threshold is overdue. DEFAULT covers any state not named.

VariableMeaningDefault
SLA_PENDING_MSBudget for a payout in PENDING.30000
SLA_SUBMITTED_MSBudget for a payout in SUBMITTED. Distinct from MAX_PAYOUT_AGE_MS, which force-fails; this only schedules the next settle check.120000
SLA_DEFAULT_MSBudget for any other state.60000

Maturity horizons

Topped-up funds wait before they can be spent or paid out, keyed by funding source. An unlisted source falls back to the default entry. See maturity in the lifecycle for why the holding period exists.

VariableMeaningDefault
MATURITY_HORIZON_CARD_MSHold for a card top-up — its chargeback window.604800000 (7d)
MATURITY_HORIZON_CRYPTO_MSHold for a crypto top-up.86400000 (1d)
MATURITY_HORIZON_DEFAULT_MSHold for an unlisted funding source. Falls back to the card horizon — the most conservative shipped value.604800000 (7d, = card)

Fees and the risk gate

PLATFORM_FEE_BPS is the platform's cut on a sale, in basis points, where 10000 is 100%. The velocity variables bound how fast one subject may spend before the risk check steps in; see risk and velocity for the actor side of that gate.

VariableMeaningDefault
PLATFORM_FEE_BPSPlatform's transaction fee in basis points. The example models a ~15.3% marketplace fee.1530
VELOCITY_LIMIT_MINORMost a subject may spend within one window, in CREDIT minor units, before the risk check engages.100000
VELOCITY_WINDOW_MSLength of the rolling spending window. Only a subject's attempts inside the last window count; each ages out once older.3600000 (1h)

Replay window and the maintenance pause

REPLAY_WINDOW_MS bounds how stale a signed request may be before it's rejected as a possible replay; see integrity. The two pause bounds open an optional maintenance window.

VariableMeaningDefault
REPLAY_WINDOW_MSHow long a signed webhook timestamp stays valid. Older ones are rejected.300000 (5 min)
ECONOMY_PAUSE_START_MSEpoch ms the window opens (inclusive).unset (no pause)
ECONOMY_PAUSE_END_MSEpoch ms the window closes (exclusive). This is the resumesAt the decline reports.unset (no pause)

The pause is active only when both bounds parse as integers and start <= now < end. While it holds, end-user discretionary writes are declined with ECONOMY_PAUSED; settlement (actor system) and operator fixes still flow, and reads never reach the gate.

Production externals

These pick adapters rather than tune policy, so the edge reads them when it wires the ports — not loadConfig. In production, productionExternals (scripts/main.ts) fails fast with one message if any rate or the provider URL is missing or malformed, so a prod process never runs on the 1:1 dev rate or an auto-approve payout stub. Outside production these fall back to dev stubs, and a local run needs none of them.

The six rate variables configure the fixed-point CREDIT↔USD conversions for the rates port: usd_minor = floor(credit_minor × rate / 10^scale). The gap between buy and par is the platform spread.

VariableMeaningDefault
CREDIT_BUY_RATE / CREDIT_BUY_SCALEbuy rate: USD a user pays per credit at top-up.none (required in production)
CREDIT_PAR_RATE / CREDIT_PAR_SCALEpar rate: a credit's backing value, used by the solvency check.none (required in production)
PAYOUT_RATE / PAYOUT_SCALEpayout rate: what a creator's earned credits cash out at (equal to par).none (required in production)
PROCESSOR_URLEndpoint of the real payout provider for the processor port. Also honored in dev if set.none (required in production)
PROCESSOR_API_KEYBearer token sent to PROCESSOR_URL.unset

Infrastructure adapters

DATABASE_URL picks the storage backend, and the cache and dispatcher URLs pick their transports. Each is optional: unset means the dependency-free in-memory adapter. These belong to storage and messaging, read by capabilitiesFromEnv in src/index.ts.

VariableMeaningDefault
DATABASE_URLA postgres:// or mysql:// DSN selects that engine; any other scheme throws.unset (in-memory store)
REDIS_URLAdds a Redis read-through cache the store consults before the database.unset (no cache)
SQS_QUEUE_URLDelivers outgoing events via an Amazon SQS queue. Wins if DISPATCHER_URL is also set.unset
DISPATCHER_URLPosts outgoing events over HTTP to your event bus.unset (in-process delivery)

HTTP server and worker loop

The serve and worker entry point reads four more variables directly. They control the HTTP service port, shutdown grace, and the worker's tick cadence and batch size.

VariableMeaningDefault
PORTPort the HTTP API listens on.3000
SHUTDOWN_TIMEOUT_MSHow long a graceful shutdown waits for in-flight work before forcing exit.5000
WORKER_INTERVAL_MSGap between worker sweep ticks.60000
WORKER_BATCHRows a worker sweep processes per tick.100

See also