Configuration
Configuration comes from two places: appsettings.json (standard ASP.NET Core config, overridable by environment variables or user secrets) for most settings, and a .env file for broker connections specifically.
appsettings.json sections
| Section | Key | Purpose |
|---|---|---|
GitHub |
WebhookSecret |
Shared secret used to validate the GitHub webhook HMAC signature |
Broker |
AuthToken |
Bearer token required by all /Broker/* admin endpoints |
Broker |
MonthlyMessageLimit |
Default monthly message limit for every broker connection. Defaults to 2000000, CloudAMQP’s free LavinMQ tier cap. Overridable per connection with BROKER_LIMIT_N in .env (see below). Bound once at startup, so a change needs a restart |
Logger |
Url, Key, Token |
Endpoint and credentials for reporting errors to the Projects Monitor system |
Set these via environment variables (e.g. Broker__AuthToken), dotnet user-secrets in development, or your deployment platform’s secret store — never commit real values.
.env — broker connections
Broker connections are not read from appsettings.json; they come from a .env file next to the running executable, loaded by EnvFileLoader:
BROKER_URL_1=amqps://user:password@host1.example.com/vhost1
BROKER_URL_2=amqps://user:password@host2.example.com/vhost2
BROKER_URL_3=amqps://user:password@host3.example.com/vhost3
BROKER_LIMIT_2=1500000
BROKER_ACTIVE=1
BROKER_URL_N— one entry per broker connection (any number of them, numbered from 1). The numbering is the order in which connections are tried when one runs out of capacity.BROKER_LIMIT_N— optional monthly message limit forBROKER_URL_Nonly, overridingBroker:MonthlyMessageLimit. Must be a non-negative integer; anything else is ignored.0takes that connection out of rotation without removing it.BROKER_ACTIVE— 1-based index of the connection to use on startup.- The active connection switches on its own when it reaches its monthly limit (see below). It can also be changed manually at runtime via
POST /Broker/switch/{index}, or the whole list (including limits) can be re-read from disk viaPOST /Broker/reload— see API Reference.
Monthly message limits and automatic rotation
Every broker connection has a monthly message limit: BROKER_LIMIT_N when set, otherwise Broker:MonthlyMessageLimit. GET /broker/usage/current reports the effective limit next to each broker’s usage.
Before publishing a message the handler reserves one unit of that limit against the active connection in the SQLite usage store. The reservation is a single atomic increment-if-below-limit, so concurrent requests can never push a connection past its limit. If the publish then fails, the reservation is handed back.
When the active connection is at its limit, the handler walks the connection list in BROKER_URL_N order starting after the active one, wrapping around to the beginning, and switches to the first connection with room. The switch is logged as a warning naming both connections; no operator action is needed. If every connection is at its limit, the payload is not published and POST /GitHub responds 503 Service Unavailable (logged as an error and reported to Projects Monitor).
Usage is counted per calendar month (UTC), so exhausted connections come back into rotation automatically on the 1st. The active connection does not move back by itself at month start; it simply keeps being used until it fills up again.
Two things to keep in mind:
DELETE /broker/usage/current/{broker}resets the counter routing decisions are based on, so it re-opens that connection with a full month of capacity. Only use it when the broker-side count really was reset.- Lowering
BROKER_LIMIT_Nbelow the current count (thenPOST /Broker/reload) stops that connection immediately.
This is how multiple free-tier broker connections are rotated to stay under CloudAMQP’s per-broker message quota — see Architecture, ADR-0002 and ADR-0003.