Matrix logo

API Server

The HTTP API server exposes REST endpoints and JSON-RPC on a single listener. It is the primary transport for agent integration when the daemon runs as a service.

Source file: pkg/api/server.go

The HTTP API server exposes REST endpoints and JSON-RPC on a single listener. It is the primary transport for agent integration when the daemon runs as a service.


Design decisions

Single listener, dual protocol

REST and JSON-RPC share the same HTTP server. REST endpoints are at /v1/*; JSON-RPC is at /rpc. This reduces port usage and simplifies deployment.

Structured JSON everywhere

Every response is JSON. Success:

{ "ok": true, "data": { ... } }

Failure:

{ "ok": false, "error": { "code": "...", "message": "...", "retry": false } }

HTTP status codes are secondary: 200 for success, 400 for malformed requests, 401 for auth failures, 422 for semantic failures (compile error, test failure, revert). The envelope is the primary contract.

Bearer token auth

When auth_token is configured, every request except GET /healthz and GET / requires Authorization: Bearer <token>. The comparison uses subtle.ConstantTimeCompare to prevent timing attacks.

When auth is disabled, the server logs a warning: "no auth_token set — all callers on this address can compile/deploy/sign; bind to loopback or set server.auth_token".

Route table

MethodPathHandler
GET/healthzHealth check
GET/Service info
POST/rpcJSON-RPC 2.0
POST/v1/compileCompile
POST/v1/testTest
POST/v1/simulateSimulate
POST/v1/deployDeploy
POST/v1/callCall
GET/v1/chainsList chains
POST/v1/chainsRegister chain
POST/v1/chains/useSet active chain
GET/v1/artifacts/{name}Get artifact
GET/v1/registry/deploymentsLookup deployment

Request decoding

All POST handlers use a shared decode helper:

func decode(w http.ResponseWriter, r *http.Request, v any) bool
  • Closes body via defer
  • Returns false on JSON decode error, writing a 400 envelope
  • Ignores EOF (empty body is valid for some endpoints)

Forge version probing

The server probes forge --version at startup and caches the result. This is included in the health check response so agents know the forge version available.


Server lifecycle

srv := api.New(eng, logger)
err := srv.ListenAndServe(cfg.APIAddr)  // blocks
// ... later ...
err := srv.Shutdown(ctx)  // graceful with 10s timeout

Modifying the API server

What to changeWhere
Add endpointpkg/api/server.go — register handler + method
Change authpkg/api/server.goauthMiddleware
Add middlewarepkg/api/server.go — wrap mux in ListenAndServe
Change response formatpkg/types/types.goEnvelope[T]