コンテンツにスキップ
日本語

HTTP endpoints

このコンテンツはまだ日本語訳がありません。

The management server exposes a small set of operational endpoints. Most are read-only; the admin ones (/cluster/down, /cluster/leave) are opt-in and off by default.

Security (security audit #8): the read-only endpoints still reveal internal topology — member addresses (host:port), roles, the leader, and the shard map. Treat the management server as sensitive: bind it to an internal interface and gate it with the ipAllowlist (and/or auth) options of managementRoutes(...) rather than exposing it publicly.

Liveness — is this process fundamentally healthy?

GET /health
→ 200 OK
{
"status": "UP",
"checks": [
{ "name": "event-loop", "status": true }
]
}

503 with { "status": "DOWN", ... } if any liveness check fails. See Health checks.

Readiness — should this pod receive traffic?

GET /ready
→ 200 OK
{
"status": "UP",
"clusterReady": true,
"checks": [
{ "name": "database", "status": true }
]
}

clusterReady is false until the local node reaches Up; the readiness checks you register contribute the checks array. 503 if the cluster isn’t ready or any check fails.

Available when a cluster is passed to managementRoutes.

GET /cluster/members
→ 200 OK
{
"members": [
{
"address": "actor-ts://my-app@10.0.0.5:2552",
"status": "up",
"version": 3,
"roles": ["compute"]
},
...
],
"self": "actor-ts://my-app@10.0.0.5:2552"
}

Full membership snapshot. Useful for:

  • Manual cluster-state checks during incidents.
  • External dashboards that visualize the cluster.
  • Tests verifying cluster joining.
GET /cluster/leader
→ 200 OK
{ "leader": "actor-ts://my-app@10.0.0.5:2552", "isSelf": true }

The leader’s address plus isSelf (is the local node the leader). leader is null before a leader is elected. Useful for monitoring leadership churn.

GET /cluster/shards?type=cart
→ 200 OK
{
"typeName": "cart",
"leader": "my-app@10.0.0.5:2552",
"takenAt": 1710000000000,
"regions": [
{
"key": "my-app@10.0.0.5:2552",
"address": "my-app@10.0.0.5:2552",
"path": "/system/cluster/sharding/region-cart",
"proxy": false,
"shards": [0, 1, 2, ..., 33]
},
...
],
"shardHome": [ { "shard": 0, "regionKey": "my-app@10.0.0.5:2552" }, ... ]
}

Shows the shard-to-region allocation for a sharded type. Use for:

  • Verifying even distribution after a rebalance.
  • Diagnosing hot regions (one region with too many shards).
  • Manual rebalance triggers in development.

Opt-in via enableMetricsEndpoint: true.

GET /metrics
→ 200 OK
Content-Type: text/plain; version=0.0.4; charset=utf-8
# HELP actor_messages_processed_total ...
# TYPE actor_messages_processed_total counter
actor_messages_processed_total{class="Worker",path="..."} 12345
...

Prometheus text format. See Prometheus exporter.

Opt-in via enableLeaveEndpoint: true.

POST /cluster/leave
→ 202 Accepted
leaving

The 202 body is the plain-text word leaving (not JSON) — the leave is fire-and-forget.

Triggers graceful cluster-leave. The node transitions through leavingexitingremoved; shards rebalance away; the actor system terminates (configurable).

Use for:

  • Pod retirement before rolling-update.
  • Manual node-out during incidents.

Gate behind authentication — anyone with port access can drain your node.

Opt-in via enableDownEndpoint: true.

POST /cluster/down
{ "address": "actor-ts://my-app@10.0.0.5:2552" }
→ 202 Accepted
{ "downed": "actor-ts://my-app@10.0.0.5:2552" }

Returns 202 with { downed } when the member was downed, or 404 if the address is unknown or already terminal.

Force-downs a remote member by address. Destructive — the target’s actors stop, its shards reallocate elsewhere.

Use for:

  • Split-brain recovery when no downing strategy is configured.
  • Removing stuck unreachable members that refuse to recover.

High-risk endpoint — gate behind auth + audit logs.

import { managementRoutes, path, get, concat, completeJson } from 'actor-ts';
const baseRoutes = managementRoutes(system, cluster).routes;
const customRoutes = concat(
baseRoutes,
path('admin',
get(async () => completeJson(200, { appVersion: '1.2.3' })),
),
);
await system.http(port, { host }).bind(customRoutes);

managementRoutes(system, cluster, options) returns the base routes; combine with your own using concat. Useful for adding app-specific admin endpoints alongside the standard set.

The read endpoints (/health, /ready, /cluster/members, /cluster/leader, /cluster/shards) and /cluster/down return JSON. /metrics returns Prometheus text/plain, and /cluster/leave returns the plain-text body leaving.

Errors are plain-text bodies (the framework’s complete(status, message)), not a structured object — e.g. a missing type query param yields a 400 whose body is missing query param \type“.

Status codes follow HTTP conventions: 200 for reads, 202 Accepted for the admin actions (/cluster/leave, /cluster/down), 4xx for client errors (404 when a /cluster/down address is unknown), and 503 when the cluster or a health check is unavailable.

The privileged endpoints (/cluster/down, /cluster/leave, /cluster/members, /cluster/shards, /metrics) accept anonymous requests by default. That’s only safe when the management port is on a network-isolated bind (a separate Service, a different port, 127.0.0.1-only) or behind a reverse-proxy that does authentication for you.

For production deployments that expose management endpoints to a broader network, attach the built-in middlewares via managementRoutes(system, cluster, { auth, ipAllowlist }):

import {
BearerTokenAuth,
IpAllowlist,
managementRoutes,
} from 'actor-ts';
const { routes } = managementRoutes(system, cluster, {
enableLeaveEndpoint: true,
enableDownEndpoint: true,
enableMetricsEndpoint: true,
// Shared-secret bearer token (rotation supported via multiple entries).
auth: BearerTokenAuth({
tokens: [process.env.MGMT_TOKEN!],
realm: 'my-app-mgmt',
}),
// Network-level fence — only requests from these CIDRs reach
// the application at all.
ipAllowlist: IpAllowlist({
allow: ['10.0.0.0/8', '127.0.0.1/32'],
// Operators behind a trusted reverse-proxy: pass a custom
// extractor that reads x-forwarded-for (the default reads
// the socket peer and ignores forwarded headers).
// getClientIp: (req) => req.headers['x-forwarded-for']?.split(',')[0]?.trim(),
}),
});

Policy split:

  • auth wraps the privileged subset (/cluster/*, /metrics). /health and /ready stay anonymous — Kubernetes liveness/readiness probes can’t easily attach an Authorization header. Set authProtectHealth: true when your deployment guarantees the probes can present credentials.
  • ipAllowlist wraps EVERY endpoint including /health and /ready. Network-level isolation is independent of who’s authenticated.

Failure modes:

  • Missing / wrong Authorization header → 401 Unauthorized with WWW-Authenticate: Bearer realm="...".
  • Client IP outside the allowlist (or unresolvable) → 403 Forbidden.

The middlewares are general-purpose — you can use them outside the management subtree via the withMiddleware(mw, route) builder.