Skip to main content
Version: Next

HTTP API reference

The control plane exposes three API surfaces from one binary role-dependent service:

SurfaceBase pathAudience
Node API/A single ArkFlow node: local stream lifecycle, configuration, events, metrics.
Hub operator API/api/v1Fleet operators and the web console: jobs, nodes, rollouts, audit.
Hub agent API/api/v1/agent/*Compute nodes (Agents): registration, heartbeats, reports, command pull.

Health, readiness, and liveness probes (configurable paths) plus GET /metrics (Prometheus text format) are served on both node and Hub services.

The Hub operator API is a desired-state API: a successful HTTP mutation means an Intent was durably accepted — it does not mean a node has executed the command or that a stream has converged. Poll the resource or subscribe to events to observe convergence.

Conventions

Resource model

A stream has three independent views:

desired       operator target: state, generation, config_version
observed latest node report: state, generation, config_version
convergence comparison: unknown, pending, applying, in_sync, degraded, blocked

Operation records additionally expose intent_id, attempt_id, generation, retry metadata, failure classification, and the latest observed result.

Optimistic concurrency and idempotency

Canonical lifecycle mutation:

PUT /api/v1/nodes/{node_id}/streams/{stream_id}/desired-state
Authorization: Bearer <operator-token>
Content-Type: application/json
If-Match: "generation-3"
Idempotency-Key: orders-desired-4
X-Correlation-ID: request-123

{"state":"running","config_version":"cfg-17"}

The Hub returns 202 Accepted with Location pointing to the operation and an ETag for the new generation:

{
"operation_id": "intent-4-17",
"intent_id": "intent-4-17",
"node_id": "node-a",
"stream_id": "orders",
"generation": 4,
"desired_state": "running",
"config_version": "cfg-17",
"convergence": "pending"
}
  • If-Match is a compare-and-swap guard: an old generation returns 412 with generation_conflict.
  • Idempotency-Key deduplicates retries for the same principal, resource, and request body; reusing a key with another body returns 409 with idempotency_key_reused.
  • A node being offline does not reject a desired-state write; the Intent is dispatched when the node reconnects.

Pagination

Collection endpoints return:

{"items": [], "page": 1, "page_size": 50, "total": 0}

page_size is bounded to 100. Operation listing supports node_id, resource_id, operation, state, and correlation_id filters. Use intent_id and generation as stable reconciliation references instead of assuming that a command ID represents final success.

Problem envelope

Errors use a stable code, human-readable message, echoed correlation_id, and optional machine-readable details:

{
"code": "generation_conflict",
"message": "Expected generation 3, current generation 4",
"correlation_id": "request-123",
"details": {
"expected_generation": 3,
"current_generation": 4,
"resource": {"node_id": "node-a", "stream_id": "orders"}
}
}

Node API

MethodRoutePurpose
GET/systemStatic system descriptor (node identity, capabilities).
GET/statusRuntime status of the node.
GET/nodesNode view (single-node deployment alias).
GET/nodeNode view (singular alias).
GET/streamsList streams with observed state.
GET/streams/{id}One stream's resource view.
POST/streams/{id}/startStart a stream.
POST/streams/{id}/stopStop a stream.
POST/streams/{id}/restartRestart a stream.
GET/operationsList operations (node_id, resource_id, operation, state, correlation_id filters).
GET/operations/{id}One operation record.
DELETE/operations/{id}Cancel a pending Intent.
GET/eventsQuery the local event log.
GET/configuration (/config)Current configuration.
POST/configuration/validate (/config/validate)Validate a configuration body.
GET/PUT/configuration/draftRead/save a working draft configuration.
GET/configuration/diffDiff draft against the live configuration.
GET/configuration/versions (/config/versions)Configuration version history.
POST/configuration/apply (/config/apply)Apply a configuration version.
POST/configuration/rollback/{id} (/config/rollback/{id})Roll back to a previous configuration version.
GET/componentsRegistered components.
GET/components/{kind}/{name}One component's schema and example.
GET/schemaEngine configuration JSON Schema.
GET/metricsPrometheus metrics.

Hub operator API (/api/v1)

Jobs

Jobs are managed with the same desired-state semantics as streams. All mutations accept a validated job specification and return 202 Accepted with an operation reference.

MethodRoutePurpose
POST/jobsCreate a job: {"spec": ..., "desired_state": "stopped" | "running"}.
POST/jobs/validateDeep-validate a spec against the target node_ids before creating anything.
GET/jobsList jobs.
GET/jobs/{id} (/jobs/{id}/status)Observed job state.
GET/jobs/{id}/detailAssignment and convergence detail.
GET/jobs/{id}/versionsVersion history.
GET/jobs/{id}/planExplain the plan: operator boundaries, partition routes, stateful operators, checkpoint policy.
PUT/jobs/{id}/desired-stateStart/stop with the If-Match / Idempotency-Key contract.
GET/POST/jobs/{id}/checkpointsList recovery artifacts / trigger a barrier checkpoint.
GET/POST/jobs/{id}/savepointsList artifacts / trigger a savepoint.
POST/jobs/{id}/upgradesUpgrade to a new version (job must be stopped and converged).
POST/jobs/{id}/upgrades/{upgrade_id}/rollbackRoll back to a compatible savepoint.
POST/jobs/{id}/actions/{action}One-shot actions, equivalent in lifecycle semantics to stream actions.

Recommended submit flow: validate first, submit with desired_state: "stopped", inspect detail and plan, then switch to running. Recovery artifacts are bound to the job version and state format version; an incompatible artifact is rejected before restore instead of corrupting state.

Nodes and streams

MethodRoutePurpose
GET/systemHub system descriptor.
GET/nodesFleet node registry.
GET/streamsStreams across the fleet.
GET/nodes/{node_id}/streams/{id}Authoritative point-in-time stream view on a node.
PUT/nodes/{node_id}/streams/{id}/desired-stateCanonical lifecycle mutation (see Conventions).
POST/nodes/{node_id}/streams/{id}/{action}Targeted one-shot command (start, stop, ...).
POST/nodes/{node_id}/streams/{id}/actions/restartRestart action; converges only after the Agent reports the matching action_id.
GET/nodes/{node_id}/configurationNode configuration view.
GET/nodes/{node_id}/configuration/versionsNode configuration history.
POST/nodes/{node_id}/configuration/applyApply configuration to one node.
POST/nodes/{node_id}/configuration/rollback/{version}Roll node configuration back.
POST/nodes/{node_id}/drainDrain a node (evacuate assignments before maintenance).
POST/DELETE/nodes/{node_id}/maintenanceEnter/exit maintenance mode.

The legacy un-targeted stream mutation routes remain as adapters that create the same durable Intent pipeline.

Operations, events, audit

MethodRoutePurpose
GET/operationsList operations fleet-wide.
GET/operations/{id}One operation record.
DELETE/operations/{id}Cancel a pending Intent (never an executed side effect).
GET/operations/statusOperational status summary for dashboards.
GET/eventsQuery the fleet event log.
GET/events/streamServer-Sent Events stream of live events.
GET/auditBounded audit history (?resource_id={id}), accepted and rejected mutations.

DELETE /operations/{intent_id} cancels an Intent, not an already executed side effect: pending work is suppressed before dispatch; after dispatch, the Attempt outcome remains visible.

Audit records carry actor, target resource, node, correlation ID, outcome (accepted/rejected), and a stable failure code (node_unavailable, capacity, incompatible_capability, expired). Messages contain scalar operation metadata only — never credentials or job configuration bodies.

Rollouts

MethodRoutePurpose
GET/POST/rolloutsList rollouts / create a rollout plan.
GET/rollouts/{id}Rollout state and per-node progress.
POST/rollouts/{id}/actionsRollout actions: pause, resume, cancel, rollback.

See Reconciliation rollout and recovery for the state machine behind these routes.

Discovery and metrics

MethodRoutePurpose
GET/componentsRegistered components across the fleet image.
GET/components/{kind}/{name}One component's schema and example.
GET/schemaEngine configuration JSON Schema.
GET/metricsPrometheus text exposition.

Beyond readiness, reconciliation, outbox, and fleet-state gauges, command dispatch exposes arkflow_command_duration_bucket{command,le} (enqueue-to- acknowledgement latency with _count/_sum companions) and arkflow_command_total{command,outcome}. Labels come from fixed vocabularies — command types (job_start, job_stop, job_checkpoint, job_savepoint, stream actions) and outcome classes (enqueued, acknowledged, succeeded, failed, timed_out, node_unavailable, capacity, rejected); unknown command names collapse into other, and resource IDs, correlation IDs, and error text never become labels. Counters reset on Hub restart.

Hub agent API (/api/v1/agent/*)

The Agent contract is separate from the operator contract: agents authenticate with node session credentials, pull commands, and push observations.

MethodRoutePurpose
POST/agent/registerRegister a node; establishes a session.
POST/agent/heartbeatHeartbeat with lease renewal.
POST/agent/reportObserved stream/node state; carries boot_id and monotonic report_seq.
POST/agent/job-observationsReport job-level observations from the co-located kernel runtime.
GET/agent/commandsPull pending commands.
POST/agent/commands/{id}/resultReport a command result.

Commands carry generation, Attempt ID, configuration version, and expiry. Command acknowledgement is transport state only — convergence is always derived from reports, never from acks.