Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Townhall REST API

townhall is Tinytown’s REST control plane daemon. It exposes the same orchestration services used by the tt CLI over HTTP.

Start the Server

# From a town directory
townhall

# Explicit REST mode
townhall rest

# Override bind/port
townhall rest --bind 127.0.0.1 --port 8080

Defaults come from tinytown.toml:

[townhall]
bind = "127.0.0.1"
rest_port = 8080
request_timeout_ms = 30000

Endpoint Groups

The router is split into public/read/write/management groups:

  • Public: GET /health, GET /ready, GET /metrics, GET /api/scaling
  • Read (town.read): GET /v1/town, GET /v1/status, GET /v1/agents, GET /v1/tasks/pending, GET /v1/backlog, GET /v1/agents/{agent}/inbox
  • Write (town.write): POST /v1/tasks/assign, POST /v1/backlog, POST /v1/backlog/{task_id}/claim, POST /v1/backlog/assign-all, DELETE /v1/backlog/{task_id}, POST /v1/messages/send
  • Agent management (agent.manage): POST /v1/agents, POST /v1/agents/{agent}/kill, POST /v1/agents/{agent}/restart, POST /v1/agents/prune, POST /v1/recover, POST /v1/reclaim

The public probes have distinct purposes:

  • /health: lightweight process liveness with uptime_secs
  • /ready: verifies townhall can still reach Redis, reporting Redis latency, town name, and dispatcher heartbeat state
  • /metrics: Prometheus-style text metrics for agent counts by state, task queue depth, completed tasks, active missions, and Redis latency
  • /api/scaling: JSON scaling signal for autoscalers, including queue depth, in-flight work, desired worker count, and a scaling recommendation

Compatibility aliases /healthz and /readyz remain available for existing deployments.

Scaling Signal API

GET /api/scaling exposes an autoscaler-friendly snapshot:

{
  "town": "mytown",
  "timestamp": "2026-04-04T18:00:00Z",
  "queue_depth": 3,
  "pending_tasks": 2,
  "in_flight_tasks": 1,
  "active_agents": 1,
  "cold_agents": 0,
  "desired_agents": 3,
  "max_agents": 10,
  "scaling_recommendation": "scale_up"
}

scaling_recommendation values:

  • scale_up: pending + in-flight work exceeds current active workers
  • steady: current active workers match demand
  • scale_down: extra workers are running and there is no queued work
  • scale_to_zero: no queued work, no in-flight work, and all active workers have been idle past the configured timeout

The worker idle timeout is configured in tinytown.toml:

[agent]
idle_timeout_secs = 300

When the timeout expires, the worker transitions Idle -> Draining -> Stopped and exits cleanly with status code 0. That gives an external autoscaler a stable signal for both scale-down and full scale-to-zero.

When use_streams = true, the scaling endpoint reports pending_tasks from the consumer-group unread lag, in_flight_tasks from XPENDING, and queue_depth as the sum of unread plus in-flight work. This avoids counting acknowledged stream entries that are retained for audit/history.

Autoscaler Integration

Cloud Run:

  • Set min-instances=0
  • Poll /api/scaling
  • Use desired_agents or queue_depth to drive instance count

KEDA:

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: tinytown-agents
spec:
  scaleTargetRef:
    name: tinytown-agent-worker
  minReplicaCount: 0
  maxReplicaCount: 10
  triggers:
    - type: metrics-api
      metadata:
        url: "http://townhall:8080/api/scaling"
        valueLocation: "queue_depth"
        targetValue: "1"

Custom scaler:

  • Poll /api/scaling on a short interval
  • Start workers when scaling_recommendation is scale_up
  • Remove workers when scaling_recommendation is scale_down or scale_to_zero
  • Prefer desired_agents as the authoritative target replica count

Authentication

townhall supports three auth modes in config:

  • none (default): local/no-auth mode
  • api_key: API key via Authorization: Bearer <key> or X-API-Key
  • oidc: declared in config, not yet implemented in middleware

Generate an API key + Argon2 hash:

tt auth gen-key

Then configure:

[townhall.auth]
mode = "api_key"
api_key_hash = "$argon2id$..."
api_key_scopes = ["town.read", "town.write", "agent.manage"]

Example request:

curl -H "Authorization: Bearer $TOWNHALL_API_KEY" \
  http://127.0.0.1:8080/v1/status

Startup Safety Rules

At startup, townhall fails fast when:

  • Binding to a non-loopback address with auth.mode = "none"
  • TLS is enabled but cert_file/key_file are missing or invalid
  • mTLS is required but ca_file is missing or invalid

OpenAPI Spec

The REST contract is documented in:

  • docs/openapi/townhall-v1.yaml

You can load this file in Swagger UI, Stoplight, or Redoc for interactive exploration.