/operation/admin.llms.txt
Admin API
eRPC ships a live control plane at POST /admin — completely separate from your consumer endpoints. Without touching config files or restarting the server, you can inspect the running topology, instantly pull a misbehaving upstream out of rotation, and manage API keys stored in your database connector.
What you get:
- Runtime introspection of every project, network, and upstream
- One-call cordon/uncordon to remove an upstream from traffic without a deploy
- API key provisioning backed by any database connector
erpc validateCLI to catch config errors before they reach production
Quick taste
Illustrative, not a tuned production config — secret-token auth on the admin endpoint:
admin: auth: strategies: # secret token — pass as x-erpc-secret-token header on every admin request - type: secret secret: value: "your-admin-secret"Agent reference
Copy one of these prompts into your AI agent session (Claude Code, Cursor, …) — each one points the agent at this page's machine-readable reference so it can do the work correctly:
Prompt Example #1: secure and enable the admin endpoint
Enable the eRPC admin control plane in my eRPC config with secret-token auth and locked-down CORS so only my internal dashboard can reach it. Explain what happens if admin.auth is missing vs the entire admin block being absent. Read the full reference first: https://docs.erpc.cloud/operation/admin.llms.txt
Prompt Example #2: cordon a degraded upstream without a redeploy
One of my upstreams is returning elevated errors right now. Show me the exact admin API curl commands to cordon it immediately for all methods, verify the cordon took effect, and restore it when the vendor recovers. My eRPC admin endpoint is at https://erpc.example.com/admin. Reference: https://docs.erpc.cloud/operation/admin.llms.txt
Prompt Example #3: add erpc validate to my CI pipeline
I want to run erpc validate in CI to catch config errors before they reach production. Show me the correct command, how to parse the JSON output as a deployment blocker, and how to skip live endpoint checks in local dev. Work with my existing eRPC config. Reference: https://docs.erpc.cloud/operation/admin.llms.txt
Prompt Example #4: provision and revoke consumer API keys at runtime
My project uses database-backed auth and I need to add, list, and revoke consumer API keys via the admin API without touching config files. Show me the correct erpc_addApiKey / erpc_listApiKeys / erpc_deleteApiKey calls and explain the connectorId vs admin auth difference. Work with my existing eRPC config. Reference: https://docs.erpc.cloud/operation/admin.llms.txt
Admin API — full agent referenceExpand for every option, default, and edge case — or copy this entire section into your AI assistant.
How it works
Any POST or OPTIONS request whose path has exactly one segment equal to "admin" is routed to the admin endpoint by parseUrlPath. The handler first applies admin-specific CORS from admin.cors — OPTIONS preflights return 204 immediately, before any auth evaluation. For non-OPTIONS requests an AuthPayload is built from HTTP headers, then AdminAuthenticate is called against adminAuthRegistry. On success, AdminHandleRequest switch-dispatches on the JSON-RPC method name. Unrecognized methods produce ErrEndpointUnsupported.
Two distinct failure modes exist for misconfigured admin. If the entire admin: block is absent, every request returns "admin is not enabled for this project" (HTTP 401) and OPTIONS also fails with 401 (the CORS block is skipped). If admin: is present but admin.auth: is omitted, requests return "admin auth not configured". Both are HTTP 401 but require different fixes.
adminAuthRegistry is built at startup inside NewERPC using auth.NewAuthRegistry — the same factory as project consumer auth. All strategy types are supported: secret, jwt, siwe, network, database. Most deployments use a single type: secret strategy.
The API key management methods (erpc_addApiKey etc.) are authenticated by the admin auth registry but operate on connectors belonging to the project's consumer auth. The connectorId parameter must match a database strategy connector on the named project's consumer auth config, not on the admin config.
CORS for the admin endpoint defaults to allowedOrigins: ["*"] with allowCredentials: false because the endpoint is gated by secret tokens. The full default set — allowedMethods: ["GET","POST","OPTIONS"], allowedHeaders: ["content-type","authorization","x-erpc-secret-token"], maxAge: 3600 — is auto-synthesised at startup when no admin.cors block is present.
Config schema
All fields are under the top-level admin: key.
| Field | Type | Default | Behavior / footguns |
|---|---|---|---|
admin | *AdminConfig | nil (absent) | When nil, every POST /admin returns 401 "admin is not enabled for this project". OPTIONS preflight also returns 401 — the CORS block is skipped. Source: erpc/http_server.go:L586-L610 (opens in a new tab) |
admin.auth | *AuthConfig | nil | When nil, adminAuthRegistry is nil and every request returns "admin auth not configured". Source: erpc/admin.go:L26-L30 (opens in a new tab) |
admin.auth.strategies | []*AuthStrategyConfig | [] | Supports secret, jwt, siwe, network, database. Most deployments use type: secret. |
admin.cors | *CORSConfig | auto-synthesised: {allowedOrigins: ["*"], allowCredentials: false} | Admin-specific CORS; project-level CORS is never consulted for /admin. Intentionally defaults to * because the endpoint is token-gated. Source: common/defaults.go:L775-L784 (opens in a new tab) |
admin.cors.allowedOrigins | []string | ["*"] | Override in production, e.g. ["https://dashboard.example.com"]. |
admin.cors.allowedMethods | []string | ["GET", "POST", "OPTIONS"] | Set by CORSConfig.SetDefaults. Source: common/defaults.go:L2828-L2829 (opens in a new tab) |
admin.cors.allowedHeaders | []string | ["content-type", "authorization", "x-erpc-secret-token"] | The third entry is the standard header for eRPC secret-token strategies. Source: common/defaults.go:L2831-L2836 (opens in a new tab) |
admin.cors.exposedHeaders | []string | nil | Never set by defaults. When nil, Access-Control-Expose-Headers is omitted. Set explicitly if browser clients need to read custom eRPC response headers. |
admin.cors.allowCredentials | *bool | false (pointer) | Must stay false when allowedOrigins: ["*"] per CORS spec. Source: common/defaults.go:L2838-L2840 (opens in a new tab) |
admin.cors.maxAge | int | 3600 | Preflight cache duration in seconds. Access-Control-Max-Age is emitted when > 0. Source: common/defaults.go:L2841-L2843 (opens in a new tab) |
admin.denyMethods | []string (wildcard) | nil | Blocks matching admin JSON-RPC methods before auth or upstream work. Wildcards (*, ?) and boolean operators (|, &, !) are supported. A blocked method returns JSON-RPC error -32601 ("method not supported"). Evaluated before allowMethods. |
admin.allowMethods | []string (wildcard) | nil | When set, only matching methods are served; all others are blocked. Also re-admits a method blocked by denyMethods. Example: allowMethods: ["erpc_list*"] restricts the admin endpoint to read-only list methods. |
Worked examples
1. Secret-token admin endpoint (minimal production setup). Lock down the control plane with a static shared secret; override allowedOrigins to match your internal dashboard origin:
admin: auth: strategies: - type: secret secret: value: "changeme-use-env-var" cors: allowedOrigins: - "https://ops.example.com"2. Cordon a degraded upstream without a restart. When a provider is returning elevated errors but hasn't fully failed, you can remove it from routing immediately:
# Cordon for all methods
curl -X POST https://erpc.example.com/admin \
-H "x-erpc-secret-token: changeme-use-env-var" \
-H "content-type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"erpc_cordonUpstream",
"params":[{"projectId":"myProject","upstream":"alchemy-mainnet","reason":"elevated 5xx"}]}'
# Verify
curl ... -d '{"jsonrpc":"2.0","id":2,"method":"erpc_listCordoned",
"params":[{"projectId":"myProject"}]}'
# Restore when provider recovers
curl ... -d '{"jsonrpc":"2.0","id":3,"method":"erpc_uncordonUpstream",
"params":[{"projectId":"myProject","upstream":"alchemy-mainnet"}]}'3. Validate config in CI. Run erpc validate before deploying to catch chain-ID mismatches, orphan rate-limit budgets, and missing failsafe policies:
erpc validate --config ./erpc.yaml --format json
# exits 0 = clean, 1 = errors
# skip live checks for local dev nodes:
ERPC_IGNORE_LOCAL_ENDPOINT_VALIDATION=true erpc validate --config ./erpc.yaml4. Provision an API key via admin (requires a database auth connector on the project). Add, list, and revoke consumer API keys without any code deploys:
# Add
curl ... -d '{"jsonrpc":"2.0","id":1,"method":"erpc_addApiKey","params":[{
"projectId":"myProject","connectorId":"my-dynamodb","apiKey":"sk_abc123",
"userId":"user-42","rateLimitBudget":"standard"}]}'
# List (paginated, default limit 50)
curl ... -d '{"jsonrpc":"2.0","id":2,"method":"erpc_listApiKeys","params":[{
"projectId":"myProject","connectorId":"my-dynamodb"}]}'
# Delete
curl ... -d '{"jsonrpc":"2.0","id":3,"method":"erpc_deleteApiKey","params":[{
"projectId":"myProject","connectorId":"my-dynamodb","apiKey":"sk_abc123"}]}'Request/response behavior
Transport
- URL:
POST /admin(single path segment; no project/network/chain segments). - Envelope: JSON-RPC 2.0 —
{"jsonrpc":"2.0","id":<id>,"method":"<name>","params":[...]}. - Auth: evaluated per-request before dispatch. Failure → HTTP 401 JSON-RPC error.
- Batch: a JSON array body fans out to per-method goroutines, each independently authenticated.
- OPTIONS preflight: returns 204 before auth is evaluated; uses
admin.corsexclusively.
erpc_taxonomy
Params: none.
Response:
{
"projects": [
{
"id": "myProject",
"networks": [
{
"id": "evm:1",
"alias": "ethereum",
"upstreams": [{"id": "alchemy-mainnet", "vendor": "alchemy"}],
"providers": []
}
]
}
]
}Lists all loaded projects, their networks (with aliases if configured), and per-network upstreams with vendor name. vendor is empty string when no vendor is detected. The providers field is present in the response struct but is always an empty array in the current implementation — only upstreams is populated. Source: erpc/admin.go:L474-L541 (opens in a new tab)
erpc_config
Params: none.
Response: full *common.Config struct as JSON. Sensitive secret.value fields are replaced with "REDACTED" by SecretStrategyConfig.MarshalJSON. Source: erpc/admin.go:L456-L471 (opens in a new tab)
erpc_project
Params: ["<projectId>"]
Response:
{
"config": {},
"health": {
"upstreams": [],
"initialization": {}
}
}config is the current *ProjectConfig including lazy-loaded networks. health.upstreams is the live list from UpstreamsRegistry.GetUpstreamsHealth() with metrics and cordon state. health.initialization reflects the network initializer status. Returns ErrInvalidRequest if param is missing or not a string. Source: erpc/admin.go:L544-L588 (opens in a new tab)
erpc_addApiKey
Params: [{"projectId": string, "connectorId": string, "apiKey": string, "userId": string, "rateLimitBudget"?: string, "enabled"?: bool}]
enabled defaults to true when omitted. connectorId must match a database strategy connector in the project's consumer auth config (not admin auth). Source: erpc/admin.go:L102-L192 (opens in a new tab)
Response:
{"success": true, "apiKey": "<key>", "userId": "<userId>"}erpc_listApiKeys
Params: [{"projectId": string, "connectorId": string, "limit"?: number, "paginationToken"?: string}]
limit defaults to 50. paginationToken is the opaque nextToken from a previous response. Source: erpc/admin.go:L195-L291 (opens in a new tab)
Response:
{
"apiKeys": [
{
"key": "abc123",
"userId": "user1",
"rateLimitBudget": "standard",
"enabled": true,
"createdAt": "<time.Now()>",
"updatedAt": "<time.Now()>"
}
],
"nextToken": "<opaque>",
"hasMore": true,
"totalReturned": 50
}Footgun: createdAt/updatedAt are always time.Now() — actual creation timestamps are not stored.
erpc_updateApiKey
Params: [{"projectId": string, "connectorId": string, "apiKey": string, "updates": object}]
Applies patch semantics: null values in updates delete fields from the stored blob; non-null values overwrite. No schema enforcement — any JSON field can be added. Source: erpc/admin.go:L294-L381 (opens in a new tab)
Response:
{"success": true, "apiKey": "<key>", "updated": {/* mirror of the updates object passed in */}}erpc_deleteApiKey
Params: [{"projectId": string, "connectorId": string, "apiKey": string}]
Fetches the current record to retrieve userId (range key for deletion), then calls connector.Delete. Source: erpc/admin.go:L384-L453 (opens in a new tab)
Response:
{"success": true, "apiKey": "<key>", "userId": "<userId>"}erpc_cordonUpstream
Params: [{"projectId": string, "upstream": string, "method"?: string, "reason"?: string}]
method defaults to "*" (all methods). reason defaults to "admin: manual cordon". Cordon state persists in-memory until explicitly uncordoned and does not survive process restart. Source: erpc/admin.go:L658-L687 (opens in a new tab)
Response:
{"projectId": "...", "upstream": "...", "method": "*", "cordoned": true, "reason": "admin: manual cordon"}erpc_uncordonUpstream
Params: [{"projectId": string, "upstream": string, "method"?: string, "reason"?: string}]
method defaults to "*". reason defaults to "admin: manual uncordon" (different string from erpc_cordonUpstream). Source: erpc/admin.go:L658-L687 (opens in a new tab)
Response:
{"projectId": "...", "upstream": "...", "method": "*", "cordoned": false, "reason": "admin: manual uncordon"}erpc_listCordoned
Params: [{"projectId": string}]
Footgun: only reports upstreams cordoned for "*" (whole-upstream). Method-scoped cordons (e.g. cordon only for eth_getLogs) are invisible to this method. Use the erpc_upstream_cordoned metric with the reason label to track method-scoped cordons. Source: erpc/admin.go:L692-L729 (opens in a new tab)
Response:
{
"projectId": "myProject",
"cordoned": [
{"upstream": "alchemy-mainnet", "reason": "admin: manual cordon"}
]
}erpc validate CLI
erpc validate --config <path> [--format json|md]Default format: json. Exit code 0 = clean; exit code 1 = errors present or config load failed. Logs are suppressed during validation. Set ERPC_IGNORE_LOCAL_ENDPOINT_VALIDATION=true to skip live checks for local/private endpoints. An endpoint is considered local when its hostname is localhost, 127.0.0.1, ::1, matches *.cluster.local, or resolves to a private-range IP address. Source: erpc/config_analyzer.go:L28-L59 (opens in a new tab)
Static checks — orphan rate-limit budgets, budgets used without rateLimitAutoTune, networks missing failsafe policies, upstreams with empty endpoints, invalid metrics.histogramBuckets.
Live upstream checks — up to 50 concurrent goroutines per project, each with a 5s timeout and 3 retries. For every upstream: fetches eth_chainId (mismatch vs config → error), genesis block hash (skipped for evm.nodeType: full or maxAvailableRecentBlocks > 0), and a stable historical block hash (min(finalized) - 64, falling back to min(latest) - 1024). Within each project/chain group, majority voting flags upstreams that disagree with ≥2-of-≥3 consensus as errors; smaller samples produce warnings.
ValidationReport JSON schema:
{
"errors": [],
"warnings": [],
"notices": [],
"resources": {
"totals": {
"projectsTotal": 2,
"networksTotal": 5,
"upstreamsTotal": 12,
"rateLimitBudgetsTotal": 3
},
"tree": {
"projects": [
{
"id": "myProject",
"networks": [
{
"id": "evm:1",
"alias": "ethereum",
"upstreams": [{"id": "alchemy-mainnet"}]
}
]
}
],
"rateLimiters": {
"budgets": [{"id": "standard", "rulesCount": 2}]
}
}
}
}Source: cmd/erpc/main.go:L97-L142 (opens in a new tab), erpc/config_analyzer.go:L76-L123 (opens in a new tab)
Best practices
- Always set a real
admin.auth.strategiesentry — running without admin auth means the endpoint is unauthenticated. - Override
admin.cors.allowedOriginsto a specific origin (e.g. your internal dashboard URL) when the admin endpoint is reachable from a browser; the default"*"is safe only when the endpoint is not browser-accessible. - Use method-scoped cordons (
"method": "eth_getLogs") to surgically remove an upstream from expensive calls while keeping it available for others — but remembererpc_listCordonedwill not show them; monitor viaerpc_upstream_cordonedmetric. - Cordon state does not survive process restart. For permanent upstream removal, update config and redeploy. Cordons are a break-glass tool for transient degradations.
- Run
erpc validatein CI with--format jsonand parse the exit code. Treat anyerrorsentry as a deployment blocker. UseERPC_IGNORE_LOCAL_ENDPOINT_VALIDATION=truein local dev to skip live checks that will fail against localhost URLs. - Do not store the admin secret in the config file directly — inject it via an environment variable reference or a secrets manager so it stays out of version control.
Edge cases & gotchas
- Two distinct "admin not available" errors.
admin:absent →"admin is not enabled for this project".admin:present butadmin.auth:absent →"admin auth not configured". Both return HTTP 401 but require different fixes. Source:erpc/http_server.go:L586-L610(opens in a new tab),erpc/admin.go:L26-L30(opens in a new tab) admin:absent + OPTIONS = 401 not 204. The CORS block requiress.adminCfg != nil. A browser-based admin dashboard won't work until anadmin:block is present in config, even if only to enable preflight.erpc_listCordonedhides method-scoped cordons. OnlyCordonedReason("*") == trueupstreams appear. Track method-scoped cordons via theerpc_upstream_cordonedmetric with thereasonlabel.- API key methods target the project's consumer auth connector, not admin auth. The
connectorIdmust exist in the project's consumerauth.strategies[].database.connectorconfig. Source:erpc/admin.go:L82-L100(opens in a new tab) erpc_listApiKeystimestamps are alwaystime.Now().createdAt/updatedAtdo not reflect actual creation or modification time — timestamps are not stored in the connector. Source:erpc/admin.go:L257-L259(opens in a new tab)erpc_updateApiKeyuses patch semantics.nullinupdatesdeletes the field; non-null overwrites. No schema validation; any JSON field name is accepted. Source:erpc/admin.go:L351-L357(opens in a new tab)- Cordon state does not survive process restart. It persists in-memory across window rotations only. Source:
erpc/admin.go:L641-L654(opens in a new tab) - Config validator genesis check is skipped for full nodes.
evm.nodeType: fullorevm.maxAvailableRecentBlocks > 0suppresses genesis hash fetching since full nodes may not retain block 0. Source:erpc/config_analyzer.go:L400-L403(opens in a new tab) - Config validator groups by resolved chain ID. If
eth_chainIdfails, the upstream is grouped under"unknown". Two"unknown"upstreams skip cross-comparison to avoid false positives from different chains. The config-declared chain ID is used as a fallback grouping key when the live fetch fails. Source:erpc/config_analyzer.go:L449-L489(opens in a new tab) - Config validator uses
finalized - 64for historical hash comparison. Falls back tolatest - 1024when finalized is unsupported. This avoids reorg false-positives. Source:erpc/config_analyzer.go:L589-L631(opens in a new tab) - Block heatmap
categorylabel uses the full JSON-RPC method name. High-cardinality method sets significantly increase the number of time-series written to your metrics store. eth_blockNumberalways emits bucket label"TIP"andfinality="Realtime". No block number extraction occurs for this method; it is special-cased as a direct TIP/Realtime emit inrecordEvmBlockRangeHeatmap. Source:erpc/block_heatmap.go:L37-L40(opens in a new tab)- CORS
exposedHeadersis never set by defaults. If browser clients need to read custom eRPC response headers,admin.cors.exposedHeadersmust be set explicitly. - Batched admin requests fan out to per-method goroutines. Each goroutine independently authenticates. A failed auth on one method in a batch does not block the others. Source:
erpc/http_server.go:L404-L427(opens in a new tab) - Block heatmap bucket start is always size-aligned.
start = (blockNumber / size) * size(integer floor-division). Two requests for blocks N and N+1 that straddle a size boundary land in different buckets. Source:erpc/block_heatmap.go:L91-L178(opens in a new tab) - Block heatmap tip-unknown fallback. When
tip ≤ 0,ComputeBlockHeatmapBucketreturnssize = 0; the caller falls back totelemetry.EvmBlockRangeBucketSize(default100000) and formats absolute bucket labels. Source:erpc/block_heatmap.go:L63-L72(opens in a new tab)
Block heatmap algorithm
ComputeBlockHeatmapBucket(blockNumber, tip, blockRef) produces the bucket and size label values for erpc_network_evm_block_range_requested_total. Key details:
selectDynamicBucketSize — distance from tip → bucket size:
| Distance from tip | Bucket size |
|---|---|
| ≤ 1,000 | 100 |
| ≤ 5,000 | 1,000 |
| ≤ 20,000 | 5,000 |
| ≤ 50,000 | 10,000 |
| ≤ 100,000 | 30,000 |
| ≤ 300,000 | 50,000 |
| ≤ 1,000,000 | 100,000 |
| ≤ 10,000,000 | 1,000,000 |
| ≤ 30,000,000 | 10,000,000 |
| ≤ 100,000,000 | 50,000,000 |
| > 100,000,000 | 50,000,000 |
Source: erpc/block_heatmap.go:L91-L178 (opens in a new tab)
Label selection (first match wins):
blockRef == "latest"→"LATEST"blockRef == "finalized"→"FINALIZED"blockRef == "safe"→"SAFE"blockRef == "pending"→"PENDING"|blockNumber - tip| ≤ 4→"TIP"(±4 block tolerance)blockNumber > tip + 4→"FUTURE"- Near-tip zone (
tip - start ≤ 5,000,000):- If
end == tip:"L<size>"(e.g."L100k") - Otherwise:
"L<roundedStart>-L<roundedEnd>"; ifroundedEnd == 0the-L0suffix is omitted
- If
- Beyond 5M blocks from tip: absolute
"<start><unit>-<end><unit>"using k/m/b suffixes (size < 1M → thousands, < 1B → millions, else billions)
Observability
| Metric | Type | Labels | When it fires |
|---|---|---|---|
erpc_network_evm_block_range_requested_total | counter | project, network, vendor, upstream, category, user, finality, bucket, size | Every successfully-forwarded EVM request. bucket = label like "TIP", "L100k", "119m-120m". size = numeric bucket size as string. Source: telemetry/metrics.go:L724-L728 (opens in a new tab) |
erpc_upstream_cordoned | gauge | project, vendor, network, upstream, category, reason | Set to 1 on cordon, 0 on uncordon. Source: telemetry/metrics.go:L164-L168 (opens in a new tab) |
erpc_upstream_cordon_duration_seconds | histogram | project, network, upstream | Recorded on uncordon: time spent in cordoned state. Buckets 1–86400 seconds. Source: telemetry/metrics.go:L299 (opens in a new tab) |
No dedicated trace spans for admin methods. The HTTP server's standard OTel server span covers the entire request. The component=admin logger is used for all admin requests. The config analyzer uses a silent zerolog.New(io.Discard) logger during live upstream checks so that ephemeral upstream probes do not pollute CLI output. Source: erpc/config_analyzer.go:L282 (opens in a new tab)
Source code entry points
erpc/admin.go:L38-L64(opens in a new tab) —AdminHandleRequest: switch-dispatch on method name for all 10 admin methodserpc/http_server.go:L835-L838(opens in a new tab) —parseUrlPath: admin endpoint detection (single segment"admin")erpc/http_server.go:L295-L610(opens in a new tab) — main handler: CORS, auth, dispatch, "admin not enabled" error patherpc/erpc.go:L83-L88(opens in a new tab) —NewERPC:adminAuthRegistryconstruction fromcfg.Admin.Autherpc/config_analyzer.go:L76-L728(opens in a new tab) —GenerateValidationReport,ValidationReport/ValidationResourcestypes, static + live check phaseserpc/block_heatmap.go:L1-L200(opens in a new tab) —recordEvmBlockRangeHeatmap,ComputeBlockHeatmapBucket,selectDynamicBucketSize, label formattingcommon/defaults.go:L775-L784(opens in a new tab) —AdminConfig.SetDefaults: auto-synthesised CORS with full default valuescmd/erpc/main.go:L97-L142(opens in a new tab) —validatesubcommand: config load,GenerateValidationReport, render, exit codeerpc/block_heatmap_test.go(opens in a new tab) — bucket label tests with real Arbitrum block numbers, TIP tolerance, no-L0 guaranteeerpc/projects.go:L189-L190(opens in a new tab) —recordEvmBlockRangeHeatmapcall site inproject.Forward(post-successful-forward)data/connector.go(opens in a new tab) —ConnectorMainIndex = "idx_main"constant used by API key management methods as the index key
Related pages
- Auth strategies — the same strategy types used for
admin.auth.strategies. - Rate limiters — rate-limit budgets validated by
erpc validate. - Failsafe policies — networks without failsafe policies trigger a notice in
erpc validate. - Deployment — where to wire the
ERPC_IGNORE_LOCAL_ENDPOINT_VALIDATIONenv var. - Observability — dashboards and alerting around
erpc_upstream_cordoned.