Skip to Content
ConceptsGateway & Live Config

Gateway & Live Config

The gateway is RantAIClaw’s HTTP control plane. It runs alongside the agent — typically inside the daemon process — on a configurable port (default 3000, default bind 127.0.0.1).

This page describes what’s actually wired into the running router today and what the auth model looks like.

What the gateway is for

  • Pairing and bearer auth — issue tokens to clients (the agent-runner, scripts, external orchestrators)
  • Webhook ingress — receive HTTP webhooks as agent inputs
  • Channel-specific webhook endpoints — WhatsApp Cloud, Linq, Nextcloud Talk
  • Trigger ingress/triggers/{*path} routes inbound HTTP into skills/messages defined in webhook-routes.json
  • /api/v1/* surface — sessions, skills, memory, channels, providers/models, status, doctor
  • Live config mutationGET/PUT /api/v1/config/*, secret presence, MCP servers, Telegram connect, Knowledge Base keys, approval resolution
  • Knowledge Base API/api/v1/kb/* (documents, search, graph, groups) — see Knowledge Base
  • Health and metrics/health, /metrics (Prometheus text)

Live-config endpoints in src/gateway/config_api.rs are mounted — config_api::router() is merged onto the app in src/gateway/mod.rs, and so is the Knowledge Base router (crate::kb::axi::api::router(), when built with the default kb feature). See The mounted live-config surface below.

Bind safety

Defaults to 127.0.0.1 (loopback only). To bind a non-loopback host, you must opt in:

[gateway] host = "0.0.0.0" port = 3000 allow_public_bind = true

Without allow_public_bind = true, the gateway refuses to start with a non-loopback bind. The check is in src/gateway/mod.rs:

🛑 Refusing to bind to 0.0.0.0 — set [gateway].allow_public_bind = true

Tunnel exception: if [tunnel].provider != "none" (e.g. cloudflared, ngrok), the public-bind guard is bypassed because the tunnel is expected to terminate TLS and gate access. Document this clearly in your deployment if you rely on it.

The env var RANTAICLAW_ALLOW_PUBLIC_BIND=1 works as an alternative to the config flag.

Pairing and bearer tokens

There is no rantaiclaw gateway pair CLI subcommand. The gateway command is a leaf with --port and --host only.

Pairing is HTTP-driven:

  1. Start the gateway. A 6-digit pairing code is printed to stdout (CSPRNG-generated, rejection-sampled to avoid modulo bias).
  2. Client submits the code via POST /pair with header X-Pairing-Code: <code>.
  3. Server returns a 256-bit token (64 hex chars) once.
  4. Server stores only the SHA-256 hash of the token in [gateway].paired_tokens in config.toml. The plaintext is never persisted.
PAIR_CODE=123456 curl -X POST http://localhost:3000/pair \ -H "X-Pairing-Code: $PAIR_CODE" # → { "token": "abcd...", "issued_at": "..." }

Subsequent requests use Authorization: Bearer <token>. Auth is flat — there are no scopes. A valid token has full access to authenticated routes.

Tokens cannot be recovered if lost; pair again to issue a new one.

Real HTTP endpoint surface

Routes mounted in src/gateway/mod.rs plus the api_v1 router merged on top:

MethodPathAuth
GET/healthnone
GET/metricsnone (Prometheus text)
POST/pairone-time X-Pairing-Code header
POST/webhookbearer + optional X-Webhook-Secret
GET / POST/whatsappMeta verify-token (GET) / HMAC X-Hub-Signature-256 (POST, when app_secret set)
POST/linqHMAC signing-secret (when set)
POST/nextcloud-talkHMAC (when webhook_secret set)
POST/triggers/{*path}bearer
GET / POST/tasksbearer
GET / PUT / DELETE/tasks/{id}bearer
POST/tasks/{id}/reviewbearer
GET / POST/tasks/{id}/commentsbearer
GET/tasks/{id}/eventsbearer
GET/api/v1/versionnone
GET/api/v1/statusbearer
GET/api/v1/doctorbearer
POST/api/v1/agent/chatbearer
GET/api/v1/sessionsbearer
POST/api/v1/sessions/searchbearer
GET/api/v1/sessions/{id}bearer
PUT/api/v1/sessions/{id}/titlebearer
GET/api/v1/insightsbearer
GET/api/v1/skillsbearer
GET/api/v1/skills/{name}bearer
GET/api/v1/memorybearer
GET/api/v1/memory/statsbearer
GET / PUT/api/v1/personalitybearer
GET/api/v1/channelsbearer
GET/api/v1/providersnone
GET/api/v1/providers/{id}/modelsbearer
POST/api/v1/providers/{id}/models/refreshbearer (fetches live model list)
POST/api/v1/approvals/{id}bearer (resolve a pending approval)
GET/api/v1/configbearer
PUT/api/v1/config/modelbearer
PUT/api/v1/config/autonomybearer
GET / PUT/api/v1/secretsbearer (presence-only; keys never returned)
POST / DELETE/api/v1/config/mcp_servers/{name}bearer
POST / DELETE/api/v1/channels/telegrambearer
GET / PUT/api/v1/config/knowledgebearer (kb feature; presence-only)

Knowledge Base router (src/kb/axi/api.rs, merged when built with the default kb feature). These routes bypass the gateway-wide 64 KiB body cap and 120 s timeout — the subtree sets its own larger upload limit (KB_UPLOAD_MAX_BYTES = 32 MiB) and longer ingest timeout (600 s) so document uploads and per-chunk embeds aren’t rejected or cut off:

MethodPathAuth
POST/api/v1/kb/searchbearer
POST / GET/api/v1/kb/documentsbearer (POST ingests; GET lists)
GET / DELETE/api/v1/kb/documents/{id}bearer
GET/api/v1/kb/documents/{id}/intelligencebearer
POST/api/v1/kb/documents/{id}/re-extractbearer
GET / POST/api/v1/kb/groupsbearer
GET / PUT / DELETE/api/v1/kb/groups/{id}bearer
GET / POST/api/v1/kb/groups/{id}/documentsbearer
DELETE/api/v1/kb/groups/{id}/documents/{doc_id}bearer
GET/api/v1/kb/graphbearer
GET/api/v1/kb/driftbearer
POST/api/v1/kb/re-embedbearer

See Knowledge Base for what these endpoints do. There is no /mcp/servers route (MCP servers are managed via POST/DELETE /api/v1/config/mcp_servers/{name}), and no rantaiclaw gateway subcommand for pairing.

Webhook authentication — three flavors

Different endpoints use different verification strategies:

/webhook (generic prompt entry)

  • Bearer token required (when [gateway].require_pairing = true)
  • Optional X-Webhook-Secret header — server SHA-256-hashes the value and compares against the configured hashed secret with constant_time_eq
  • This is a shared-secret check, not an HMAC over the request body

Channel-specific endpoints (HMAC over body)

  • POST /whatsappX-Hub-Signature-256: sha256=<hex> HMAC-SHA256 over the raw body, verified with mac.verify_slice (constant-time)
  • POST /linq — channel-specific signing secret, HMAC-SHA256
  • POST /nextcloud-talkX-Nextcloud-Talk-Random + X-Nextcloud-Talk-Signature headers, HMAC-SHA256

These header names are hard-coded per channel, not user-configurable. Missing or invalid signatures return 401 with a JSON body {"error": "..."}.

Trigger routes — /triggers/{*path}

The trigger route is fed by <config_dir>/webhook-routes.json (typically written by an agent-runner or external orchestrator), not by config TOML. Schema:

[ { "path": "/deploys", "skill": "deploy-checker", "message": "{repo} deployed: status={status}" }, { "path": "/incidents", "skill": "incident-triage", "message": "Alert: {title}" } ]

There is no HMAC, no template engine — just literal placeholders that get formatted from query params. Auth is bearer-only.

Rate limits and idempotency

Per-client-IP sliding-window rate limits:

  • /pair — 10/min
  • /webhook, /triggers/* — 60/min

Idempotency: X-Idempotency-Key header on /webhook deduplicates retries within the configured TTL (default 300s).

The mounted live-config surface

src/gateway/config_api.rs is wired into the running router — config_api::router() is merged in src/gateway/mod.rs alongside api_v1::router(). The mounted, bearer-authenticated handlers are:

  • GET /api/v1/config — read the effective config (secrets redacted)
  • PUT /api/v1/config/model — hot-swap the active model
  • PUT /api/v1/config/autonomy — set the autonomy level
  • GET/PUT /api/v1/secrets — read/write secret presence only; values are never returned
  • POST/DELETE /api/v1/config/mcp_servers/{name} — add or remove an MCP server entry
  • POST/DELETE /api/v1/channels/telegram — connect or disconnect a Telegram channel from the console
  • GET/PUT /api/v1/config/knowledge — read presence / set the Knowledge Base embedding + OCR keys (only when built with the kb feature)

Note the shapes are GET/PUT/POST/DELETE on concrete paths — not an old PATCH /config/* design. Writes call Config::save(), so they persist to config.toml and survive a restart. The Knowledge Base router (crate::kb::axi::api::router()) is merged the same way and adds the /api/v1/kb/* surface — see Knowledge Base.

What the daemon does with the gateway

When you run rantaiclaw daemon, the supervisor starts:

  • the gateway HTTP server on the configured port
  • each enabled channel’s listen task
  • the heartbeat / scheduler / cron loops

Each component is supervised with exponential backoff restart on crash, so a misbehaving channel won’t take down the gateway and vice versa.

Reading the code

  • src/gateway/mod.rs — server bootstrap, route registration, handlers for /health, /metrics, /pair, /webhook, channel webhooks, triggers
  • src/gateway/api_v1.rs — the /api/v1/* router and bearer-auth middleware
  • src/gateway/task_handlers.rs/tasks/* handlers
  • src/gateway/config_api.rs — mounted live-config surface (config_api::router(), merged in mod.rs)
  • src/kb/axi/api.rs — the /api/v1/kb/* Knowledge Base router (merged when the kb feature is built; larger body/timeout caps)
  • src/security/pairing.rs — pair flow, token generation, hash storage, constant-time compare
  • src/security/pairing_store.rs — on-demand channels pair codes (SHA-256-hashed, 0600, TTL + max-uses)
Last updated on