Admin API Reference

Admin API Reference

Manage gateway resources through the JWT-protected REST API. Database and Control Plane modes allow writes; File, Data Plane, and Mesh are read-only.

Authentication

Use an HS256 JWT signed with FERRUM_ADMIN_JWT_SECRET (at least 32 characters). Required claims are iss, sub, iat, nbf, exp, jti, and role (viewer, operator, or admin). The default issuer is ferrum-edge.

The example below uses Node.js built-in crypto and an already-configured gateway secret. It issues a 15-minute admin token scoped to ferrum. If your gateway configures an audience or a different issuer, use those matching values. Namespace-scoped requests use X-Ferrum-Namespace; multi-namespace CPs require a matching ns claim.

bash
# Export FERRUM_ADMIN_JWT_SECRET with the same value used by your gateway.
export ADMIN_TOKEN=$(node <<'NODE'
const { createHmac, randomUUID } = require('node:crypto');
const secret = process.env.FERRUM_ADMIN_JWT_SECRET;
if (!secret || secret.length < 32) throw new Error('Set the gateway JWT secret (32+ characters)');
const now = Math.floor(Date.now() / 1000);
const claims = { iss: 'ferrum-edge', sub: 'website-example', role: 'admin',
  ns: ['ferrum'], iat: now, nbf: now, exp: now + 900, jti: randomUUID() };
const encode = value => Buffer.from(JSON.stringify(value)).toString('base64url');
const body = `${encode({ alg: 'HS256', typ: 'JWT' })}.${encode(claims)}`;
console.log(`${body}.${createHmac('sha256', secret).update(body).digest('base64url')}`);
NODE
)
curl -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "X-Ferrum-Namespace: ferrum" http://localhost:9000/proxies

Authentication and namespace authorization reference

Probes and Metrics

EndpointAccess / purpose
GET /liveUnauthenticated liveness; 200 while the process/admin listener is alive
GET /health, /statusCoarse status + ready without auth; full diagnostics with admin JWT, metrics bearer token, or allowed metrics CIDR
GET /metricsPrometheus text; requires admin JWT, metrics bearer token, or allowed metrics CIDR
GET /admin/metricsAdmin JWT; gateway metrics JSON
GET /metrics/runtimeAdmin JWT; process/host state, traffic windows, pools, DNS, and overload
GET /chargesAdmin JWT; chargeback data

Resource Endpoints

PathOperations
/proxies, /consumers, /upstreamsGET list, POST create; GET/PUT/DELETE on /:id
/pluginsGET built-in plugin types
/plugins/configGET list, POST create; GET/PUT/DELETE on /:id
/consumers/:id/credentials/:typePOST append, PUT replace, DELETE credential type
/api-specsImport, list, replace, and delete OpenAPI-managed resources
/namespacesManage namespace registry and tenant resources
/backup, /restore?confirm=trueGET export; POST replace namespace configuration with explicit confirmation
/batchPOST atomic additive resource graph; not a list of arbitrary HTTP operations
/audit, /cluster, /tlsAudit, CP/DP topology, and TLS lifecycle surfaces; see the full reference

Lists support pagination. For request/response schemas, permissions, and the complete endpoint inventory, use the OpenAPI specification and full Admin API reference.

Create a Proxy

bash
curl -X POST http://localhost:9000/proxies \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "X-Ferrum-Namespace: ferrum" \
  -H "Content-Type: application/json" \
  -d '{
    "listen_path": "/my-api",
    "backend_scheme": "http",
    "backend_host": "localhost",
    "backend_port": 3000,
    "strip_listen_path": true,
    "labels": {"provisioned-by": "website-example"}
  }'

Save the returned id. Plugin associations use plugins: [{"plugin_config_id": "…"}]. Creating a proxy-scoped plugin as below attaches it automatically. Labels require Edge v0.9.5+ and are informational metadata.

Attach API-Key Authentication

bash
# Replace YOUR_PROXY_ID with the returned proxy ID.
curl -X POST http://localhost:9000/plugins/config \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "X-Ferrum-Namespace: ferrum" \
  -H "Content-Type: application/json" \
  -d '{
    "plugin_name": "key_auth",
    "scope": "proxy",
    "proxy_id": "YOUR_PROXY_ID",
    "config": {"key_location": "header:X-API-Key"}
  }'

File-mode configs must declare both the plugin’s proxy_id and its proxy association. Global plugins need no proxy association.

Create a Consumer

bash
curl -X POST http://localhost:9000/consumers \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "X-Ferrum-Namespace: ferrum" \
  -H "Content-Type: application/json" \
  -d '{
    "username": "mobile-app",
    "custom_id": "app-v2",
    "credentials": {
      "keyauth": [{"key": "replace-with-a-random-client-api-key"}]
    }
  }'

Credential map keys are keyauth, basicauth, jwt, hmac_auth, and mtls_auth. Values are rotation arrays. JWT/HMAC entries contain secret, not subject. Ordinary reads redact secrets and omit Basic credentials.

Create an Upstream

bash
curl -X POST http://localhost:9000/upstreams \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "X-Ferrum-Namespace: ferrum" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "api-backend-pool",
    "algorithm": "least_connections",
    "targets": [
      {"host": "api-1.internal", "port": 3000, "weight": 100},
      {"host": "api-2.internal", "port": 3000, "weight": 50}
    ]
  }'

Set a proxy’s upstream_id to the returned ID to use the pool. Active/passive health checks use the schema in the upstream reference.

Batch Create

bash
curl -X POST http://localhost:9000/batch \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "X-Ferrum-Namespace: ferrum" \
  -H "Content-Type: application/json" \
  -d '{
    "proxies": [{
      "id": "batch-api", "listen_path": "/batch-api",
      "backend_scheme": "http", "backend_host": "localhost", "backend_port": 3000
    }],
    "consumers": [{"username": "batch-client"}],
    "plugin_configs": [],
    "upstreams": []
  }'

Batch schema and atomicity · Backup and restore semantics

Error Handling

StatusMeaning
400Malformed input, unknown fields, or validation failure
401Missing or invalid token
403Role/namespace denied, or read-only mode
404Resource does not exist
409Conflicting resource or state
503Unavailable dependency or admission guard; inspect the response before retrying a mutation
json
{"error": "description of the rejected request"}