tinytown 0.10.0

A simple, fast multi-agent orchestration system using Redis for message passing
Documentation
# 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

```bash
# 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`:

```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:

```json
{
  "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`:

```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:

```yaml
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:

```bash
tt auth gen-key
```

Then configure:

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

Example request:

```bash
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.