Skip to content

Fleet, gateways and cutover ​

Control shows every enrolled project with its enforcement coverage, policy versions, gateways and last contact, and lets you move execution from the Sectigo-hosted gateway to a gateway you run without ever having two executors.

Console: Fleet and gateways at /control/orgs/:oid/fleet.

The fleet view ​

GET /v1/orgs/{oid}/fleet returns one item per project:

FieldMeaning
enrollment_stateEnrolled, or invited for a pending invitation (then only the project ID is shown).
servicesProtected services.
enforcement_coverageShare of services behind an enforcing path.
policy_version, policy_versions[]Policy versions in force.
gateways[]Each gateway with freshness, policy_version_acked, last_contact_at, executor.
executorThe gateway ID that executes for this project, or hosted.
last_contact_atMost recent gateway contact.
unacknowledged_changesPolicy or emergency-deny changes a gateway has not acknowledged yet.
expected_impactnull or { staged_policies[], sampled, changed } for staged policies.

Register a gateway ​

A gateway (customer-hosted, or an adapter in an existing API gateway) authenticates with its own key pair. Only the public key is registered.

http
POST /v1/projects/{project_id}/gateways
content-type: application/json

{ "name": "eu-west VPC gateway", "mode": "customer_hosted", "region": "eu-west-1", "public_jwk": { "kty": "EC", "crv": "P-256", "x": "…", "y": "…" } }

For an adapter use "mode": "adapter" with "adapter": "kong" | "apigee" | "aws_apigw". Requires project.admin. A new gateway is staged: status: "active", executor: false. While no gateway holds the executor flag, the hosted gateway executes.

What exists in this release

The control-plane side (registration, signed heartbeats, policy bundles, acknowledgments, freshness, executor ownership) is implemented. The customer-hosted gateway runtime itself and the Kong, Apigee and AWS API Gateway adapters are not part of this repository yet, and none is certified. There is no API yet through which a customer-hosted gateway claims execution of an action.

Heartbeats and bundles ​

A gateway calls in with a compact JWS signed by its registered key over:

json
{ "gateway_id": "gwy_…", "policy_version_acked": "pol_…@3,pol_…@1", "acks": ["dny_…"], "iat": 1763143200, "jti": "hb-6f1c…" }
http
POST /v1/gateways/{gateway_id}/heartbeat
content-type: application/jose

eyJhbGciOiJFUzI1NiIs…
  • iat must be within ±120 seconds and each jti is accepted once, so a captured heartbeat cannot be replayed to fake an acknowledgment.
  • acks lists emergency-deny IDs the gateway now enforces; each new acknowledgment is audited.
  • The response is { bundle, content }: bundle is a compact JWS signed by Axiom's policy key; content is its decoded claims (issued_at, fresh_until, policy_version, executor, policies[], trust, emergency_denies[]).

GET /v1/gateways/{gateway_id}/bundle returns the current bundle to the gateway (header x-axiom-gateway-auth with the same signed payload; acks ignored) or to a project reader.

Bundles are valid for 300 seconds. Freshness is fresh while the last bundle is valid, stale up to 15 minutes after the last contact, then offline. A partitioned gateway may evaluate operations allowed by a still-current bundle; once it is stale, sensitive writes must stay blocked or pending.

One executor per project ​

At most one gateway executes a project's actions. The flag moves in a single update, so a cutover or rollback can never leave two executors. Every change is audited with the executor before and after.

http
POST /v1/gateways/{gateway_id}/status
content-type: application/json

{ "status": "active", "executor": true }
BodyEffect
{ "status": "active", "executor": true }This gateway becomes the project's only executor (cutover, or rollback to it).
{ "status": "draining" } on the executorExecution moves back to the hosted gateway. Work this gateway already claimed stays with it.
{ "status": "draining", "transfer_to": "gwy_…" }Execution moves to another active gateway in the project.
{ "status": "retired" }Refused while the gateway is the executor or still has claimed actions executing. Retirement is final.

The response includes project_executor. While a customer-hosted gateway holds the flag, no other path can claim the project's actions: every hosted path (REST POST /v1/actions/{id}/resume, the hosted HTTP proxy /p/{project}/http/*, the hosted MCP proxy /p/{project}/mcp and the Axiom MCP server) refuses to claim with 409 invalid_state ("execution for this project is owned by gateway …; resume through it"). An action allowed within a standing grant is still recorded: through the REST API or the Axiom MCP server it stays approved (202) for the executor gateway instead of being dispatched by the hosted gateway; the hosted proxies answer the 409. Embedded-SDK services are not affected by the flag: they execute inside the provider, which claims through POST /v1/authorize.

Cutover checklist ​

  1. Register the new gateway; confirm it reports fresh and acknowledges the current policy_version on the fleet screen.
  2. Check policy parity with the simulator and test traffic.
  3. Restrict the upstream so protected routes are reachable only through the new enforced path.
  4. Make the new gateway executor: { "status": "active", "executor": true }.
  5. Drain the old path: in-flight actions claimed before cutover finish where they were claimed; claims never move.
  6. Verify that an action pending approval from before the cutover still resolves exactly once, and that earlier receipts still verify.
  7. To roll back, make the hosted gateway executor again by draining the new gateway.

Developer preview. Contract version 2026-11-14.