The Chain 138 RPC access product catalog (core-rpc / alltra-rpc /
thirdweb-rpc, each with VMID + HTTP/WS URL + tier + billing model + use
cases + management features) used to be a hardcoded 50-line Go literal
in api/rest/auth.go. The review flagged this as the biggest source of
'magic constants in source' in the backend: changing a partner URL, a
VMID, or a billing model required a Go recompile, and the internal
192.168.11.x CIDR endpoints were baked into the binary.
This PR moves the catalog to backend/config/rpc_products.yaml and adds
a lazy loader so every call site reads from the YAML on first use.
New files:
backend/config/rpc_products.yaml source of truth
backend/api/rest/rpc_products_config.go loader + fallback defaults
backend/api/rest/rpc_products_config_test.go unit tests
Loader path-resolution order (first hit wins):
1. $RPC_PRODUCTS_PATH (absolute or cwd-relative)
2. $EXPLORER_BACKEND_DIR/config/rpc_products.yaml
3. <cwd>/backend/config/rpc_products.yaml
4. <cwd>/config/rpc_products.yaml
5. compiled-in defaultRPCAccessProducts fallback (logs a WARNING)
Validation on load:
- every product must have a non-empty slug,
- every product must have a non-empty http_url,
- slugs must be unique across the catalog.
A malformed YAML causes a WARNING + fallback to defaults, never a
silent empty product list.
Call-site changes in auth.go:
- 'var rpcAccessProducts []accessProduct' (literal) -> func
rpcAccessProducts() []accessProduct (forwards to the lazy loader).
- Both existing consumers (/api/v1/access/products handler at line
~369 and findAccessProduct() at line ~627) now call the function.
Zero other behavioural changes; the JSON shape of the response is
byte-identical.
Tests added:
- TestLoadRPCAccessProductsFromRepoDefault: confirms the shipped
YAML loads, produces >=3 products, and contains the 3 expected
slugs with non-empty http_url.
- TestLoadRPCAccessProductsRejectsDuplicateSlug.
- TestLoadRPCAccessProductsRejectsMissingHTTPURL.
Verification:
go build ./... clean
go vet ./... clean
go test ./api/rest/ PASS (new + existing)
go mod tidy pulled yaml.v3 from indirect to direct
Advances completion criterion 7 (no magic constants): 'Chain 138
access products / VMIDs / provider URLs live in a YAML that operators
can change without a rebuild; internal CIDRs are no longer required
to be present in source.'
REST API Server
REST API implementation for the ChainID 138 Explorer Platform.
Structure
server.go- Main server setup and route configurationroutes.go- Route handlers and URL parsingauth.go- Wallet auth, user-session auth, RPC product access, subscriptions, and API keysblocks.go- Block-related endpointstransactions.go- Transaction-related endpointsaddresses.go- Address-related endpointssearch.go- Unified search endpointmission_control.go- Mission-control bridge trace and cached liquidity helpersvalidation.go- Input validation utilitiesmiddleware.go- HTTP middleware (logging, compression)errors.go- Error response utilities
API Endpoints
Auth
POST /api/v1/auth/nonce- Create a wallet-signature noncePOST /api/v1/auth/wallet- Authenticate a wallet and receive a track JWTPOST /api/v1/auth/register- Create an access-console user sessionPOST /api/v1/auth/login- Log in to the access console
Blocks
GET /api/v1/blocks- List blocks (paginated)GET /api/v1/blocks/{chain_id}/{number}- Get block by numberGET /api/v1/blocks/{chain_id}/hash/{hash}- Get block by hash
Transactions
GET /api/v1/transactions- List transactions (paginated, filterable)GET /api/v1/transactions/{chain_id}/{hash}- Get transaction by hash
Addresses
GET /api/v1/addresses/{chain_id}/{address}- Get address information
Search
GET /api/v1/search?q={query}- Unified search (auto-detects type: block number, address, or transaction hash)
Health
GET /health- Health check endpoint
Mission control
GET /api/v1/mission-control/stream- SSE stream for bridge/RPC healthGET /api/v1/mission-control/bridge/trace?tx=0x...- Blockscout-backed tx trace with Chain 138 contract labelsGET /api/v1/mission-control/liquidity/token/{address}/pools- 30-second cached proxy to token-aggregation pools
Access and API keys
GET /api/v1/access/me- Current signed-in access user and subscriptionsGET /api/v1/access/products- RPC product catalog for Core, Alltra, and Thirdweb lanesGET /api/v1/access/subscriptions- List product subscriptionsPOST /api/v1/access/subscriptions- Request or activate a product subscriptionGET /api/v1/access/admin/subscriptions- List pending or filtered subscriptions for admin reviewPOST /api/v1/access/admin/subscriptions- Approve, suspend, or revoke a subscription as an adminGET /api/v1/access/api-keys- List issued API keysPOST /api/v1/access/api-keys- Create an API key for a tier, product, scopes, expiry, and optional quota overridePOST /api/v1/access/api-keys/{id}- Revoke an API keyDELETE /api/v1/access/api-keys/{id}- Alternate revoke verbGET /api/v1/access/usage- Per-product usage summaryGET /api/v1/access/audit- Recent validated API-key usage rows for the signed-in userGET /api/v1/access/admin/audit- Admin view of recent validated API-key usage rows, optionally filtered by productPOST /api/v1/access/internal/validate-key- Internal edge validation hook for API-key enforcement and usage loggingGET /api/v1/access/internal/validate-key-auth_request-friendly validator for nginx or similar proxies
Track 4 operator
POST /api/v1/track4/operator/run-script- Run an allowlisted script underOPERATOR_SCRIPTS_ROOT
Features
- Input validation (addresses, hashes, block numbers)
- Pagination support
- Query timeouts for database operations
- CORS headers
- Request logging
- Error handling with consistent error format
- Health checks with database connectivity
- Wallet JWT auth for track endpoints
- Email/password user sessions for the explorer access console
- RPC product catalog, subscription state, API key issuance, revocation, and usage summaries
Running
cd backend/api/rest
go run main.go
Or use the development script:
./scripts/run-dev.sh
Configuration
Set environment variables:
DB_HOST- Database hostDB_PORT- Database portDB_USER- Database userDB_PASSWORD- Database passwordDB_NAME- Database namePORT- API server port (default: 8080)CHAIN_ID- Chain ID (default: 138)RPC_URL- Chain RPC used by Track 1 and mission-control health/SSE dataTOKEN_AGGREGATION_BASE_URL- Upstream token-aggregation base URL for mission-control liquidity proxyBLOCKSCOUT_INTERNAL_URL- Internal Blockscout base URL for bridge trace lookupsEXPLORER_PUBLIC_BASE- Public explorer base URL used in mission-control trace responsesCCIP_RELAY_HEALTH_URL- Optional relay health probe URL, for examplehttp://192.168.11.11:9860/healthzCCIP_RELAY_HEALTH_URLS- Optional comma-separated named relay probes, for examplemainnet=http://192.168.11.11:9860/healthz,bsc=http://192.168.11.11:9861/healthz,avax=http://192.168.11.11:9862/healthzMISSION_CONTROL_CCIP_JSON- Optional JSON snapshot fallback when relay health is provided as a file instead of an HTTP endpointOPERATOR_SCRIPTS_ROOT- Root directory for allowlisted Track 4 scriptsOPERATOR_SCRIPT_ALLOWLIST- Comma-separated list of permitted script names or relative pathsOPERATOR_SCRIPT_TIMEOUT_SEC- Optional Track 4 script timeout in seconds (max 599)JWT_SECRET- Shared secret for wallet and user-session JWT signingACCESS_ADMIN_EMAILS- Comma-separated email allowlist for access-console adminsACCESS_INTERNAL_SECRET- Shared secret used by internal edge validators calling/api/v1/access/internal/validate-key
Auth model
There are now two distinct auth planes:
-
Wallet auth
POST /api/v1/auth/noncePOST /api/v1/auth/wallet- Used for wallet-oriented explorer tracks and operator features.
-
Access-console user auth
POST /api/v1/auth/registerPOST /api/v1/auth/login- Used for
/api/v1/access/*endpoints and the frontend/accessconsole.
RPC access model
The access layer currently models three RPC products:
core-rpc- Provider:
besu-core - VMID:
2101 - Approval required
- Intended for operator-grade and sensitive use
- Provider:
alltra-rpc- Provider:
alltra - VMID:
2102 - Self-service subscription model
- Provider:
thirdweb-rpc- Provider:
thirdweb - VMID:
2103 - Self-service subscription model
- Provider:
The explorer can now:
- register and authenticate users
- publish an RPC product catalog
- create product subscriptions
- issue scoped API keys
- set expiry presets and quota overrides
- rotate keys by minting a replacement and revoking the old one
- review approval-gated subscriptions through an admin surface
- revoke keys
- show usage summaries
- show recent audit activity for users and admins
- validate keys for internal edge enforcement and append usage records
- support nginx
auth_requestintegration through theGET /api/v1/access/internal/validate-keyform
Current limitation:
- the internal validation hook exists, but nginx/Besu/relay still need to call it or replicate its rules to enforce traffic at the edge
- billing collection and invoicing are not yet handled by this package
Operational reference:
explorer-monorepo/deployment/ACCESS_EDGE_ENFORCEMENT_RUNBOOK.mdexplorer-monorepo/deployment/common/nginx-rpc-api-key-gate.conf
Mission-control deployment notes
- Include
explorer-monorepo/deployment/common/nginx-mission-control-sse.confin the same nginx server block that proxies/explorer-api/. - Keep the nginx upstream port aligned with the Go API
PORT. - Verify internal reachability to
BLOCKSCOUT_INTERNAL_URLandTOKEN_AGGREGATION_BASE_URLfrom the API host before enabling the mission-control cards in production.