seeknal gateway¶
HTTP gateway server that exposes seeknal ask as an API with WebSocket, SSE, REST, and optional Telegram bot and Temporal durable execution.
Synopsis¶
Description¶
The gateway server wraps the seeknal ask agent behind HTTP endpoints so web clients, mobile apps, and bots can interact with your data. It supports multiple transport protocols:
- WebSocket — Streaming, bidirectional chat
- SSE (Server-Sent Events) — One-way streaming for web UIs
- REST — Standard request/response for one-shot questions
- Telegram — Optional bot integration via
--telegram - Temporal — Optional durable execution for long-running agent tasks via
--temporal
Prerequisites¶
The Ask gateway and Telegram channel are included in the default seeknal installation.
For Temporal support:
Commands¶
seeknal gateway start¶
Start the full gateway server (API + optional worker).
| Flag | Type | Default | Description |
|---|---|---|---|
--project |
PATH | Auto-detected | Project path |
--port |
INT | 8000 |
Port to listen on |
--host |
TEXT | 0.0.0.0 |
Host to bind to |
--telegram |
FLAG | False | Enable Telegram bot channel |
--temporal |
FLAG | False | Enable Temporal durable execution |
--no-worker |
FLAG | False | Gateway-only mode (no local Temporal worker) |
--max-activities |
INT | 15 |
Max concurrent Temporal activities per worker |
--redis |
TEXT | None | Redis URL for multi-replica mode |
--callback-url |
TEXT | None | Base URL for worker event callbacks |
--callback-auth-token |
TEXT | None | Shared secret for callback POST auth |
--worker-project-path |
TEXT | None | Project path on the remote worker (split topology) |
--token-config |
PATH | SEEKNAL_TOKEN_CONFIG |
JSON/YAML API token registry for tenant-scoped worker routing |
seeknal gateway backend¶
Start a cloud-only gateway (no local project or worker). Useful when the gateway runs on a separate machine from the data.
Supports --token-config so /temporal/start, /events/{session_id}, /sessions, and worker callbacks derive tenant identity from bearer tokens instead of caller-provided tenant headers.
seeknal gateway worker¶
Start a standalone worker that processes Ask agent runs claimed from the gateway. This is the worker behind seeknal ask requests in any split topology — when users talk about "the ask worker", this is the command. It supports two transports:
temporal— connects to a Temporal server and polls a task queue. Provides durable execution, automatic retries, and per-activity isolation. Concurrency is governed by--max-activities.http— long-polls the gateway's/internal/worker/work-streamendpoint via HTTPS. No Temporal SDK required. Concurrency is governed by--max-concurrency.
Use --transport auto (default) to pick automatically: if a token is configured and the gateway reports HTTP routing, the worker uses HTTP; otherwise Temporal. Force one with --transport temporal or --transport http.
For secure multi-tenant deployments, start the worker with --gateway-url and --api-token. The worker fetches /internal/worker/config, then uses the token-derived tenant queue, callback bearer token, and optional Temporal address/namespace. Existing local workers can still use --tenant or TEMPORAL_TASK_QUEUE when token mode is not configured.
| Flag | Type | Default | Description |
|---|---|---|---|
--project |
PATH | Auto-detected | Project path |
--transport |
TEXT | auto |
auto, temporal, or http |
--max-activities |
INT | 15 |
Temporal only — max concurrent activities per worker. Env: TEMPORAL_MAX_CONCURRENT_ACTIVITIES. |
--max-concurrency |
INT | 1 |
HTTP only — max concurrent agent runs per worker process. Env: SEEKNAL_WORKER_CONCURRENCY. Default 1 preserves the legacy sequential behavior. |
--shutdown-timeout |
FLOAT | 60.0 |
HTTP only — seconds to wait for in-flight tasks to drain on SIGINT before cancelling. Env: SEEKNAL_WORKER_SHUTDOWN_TIMEOUT. |
--callback-url |
TEXT | None | Gateway URL for event callbacks (Temporal mode) |
--callback-auth-token |
TEXT | None | Shared secret for callback auth (legacy compatibility mode) |
--tenant |
TEXT | None | Tenant ID — maps to task queue seeknal-ask-{tenant} |
--gateway-url |
TEXT | SEEKNAL_GATEWAY_URL |
Gateway URL for token-derived runtime config |
--api-token |
TEXT | SEEKNAL_API_TOKEN |
Worker API token used for token-derived routing |
Worker concurrency — sizing guide¶
In tap-in mode (queries push down to PostgreSQL / Iceberg / StarRocks; the worker mostly orchestrates), one agent run is dominated by network I/O — LLM API latency plus DB query latency. This is the best case for in-process concurrency.
| Anthropic tier | RPM ceiling | Recommended --max-concurrency per worker |
|---|---|---|
| Tier 1 (50 RPM) | 50 | 3-5 |
| Tier 2 (1000 RPM) | 1,000 | 10-15 ← typical production |
| Tier 3 (2000 RPM) | 2,000 | 20-25 |
Rules of thumb (take the minimum):
- Memory:
N × per-agent-MB + ~500MB base< container limit. Tap-in mode ≈ 50-120 MB/agent. Full pipeline mode ≈ 150-250 MB/agent. - LLM rate limit:
N × ~10 calls / 60s< provider RPM tier. - DB connections:
N × attached_databases<pg.max_connections / 2.
Scale horizontally past N=20 in one worker. Run multiple worker containers — each polls the same gateway endpoint, and the broker (HttpWorkerBroker) hands each item to exactly one. The new backpressure rule (semaphore acquired before polling) means saturated workers stop claiming work, so the broker re-distributes fairly across the pool.
Sweet spot for typical production: 3 worker containers × --max-concurrency 10 = 30 total concurrent agents on ~6 GB total RAM, survives one worker dying, supports rolling deploys via the bounded-drain shutdown.
Graceful shutdown (HTTP mode)¶
On SIGINT / SIGTERM:
- The worker stops polling for new work.
- In-flight tasks drain up to
--shutdown-timeoutseconds. - Stragglers are cancelled; cancelled tasks still POST
completeso the broker doesn't leak_inflightentries. - Worker exits cleanly.
Set SEEKNAL_WORKER_SHUTDOWN_TIMEOUT to a value ≥ the longest expected agent run when running behind a rolling deployer (Kubernetes, ECS, etc.).
Examples¶
# Basic local gateway
seeknal gateway start --project ./my-project
# Gateway with Telegram bot
seeknal gateway start --telegram
# Cloud mode: gateway + remote worker
seeknal gateway backend --port 8000
seeknal gateway worker --project ./my-project --callback-url http://gateway:8000
# Multi-tenant token mode: queue/callback config is derived from the token
seeknal gateway backend --token-config ./gateway-tokens.yml --port 8000
seeknal gateway worker --project ./my-project --gateway-url http://gateway:8000 --api-token "$SEEKNAL_API_TOKEN"
# Docker worker image
docker build -f docker/Dockerfile.worker -t seeknal-worker:local .
docker run --rm \
-v "$PWD/my-project:/app/project" \
--env-file "$PWD/my-project/.env" \
-e TEMPORAL_ADDRESS=host.docker.internal:7233 \
seeknal-worker:local --project /app/project
# Docker gateway image
docker build -f docker/Dockerfile.gateway -t seeknal-gateway:local .
docker run --rm \
-p 8000:8000 \
-v "$PWD/my-project:/app/project" \
--env-file "$PWD/my-project/.env" \
-e TEMPORAL_ADDRESS=host.docker.internal:7233 \
seeknal-gateway:local
# Docker worker with token-derived tenant routing
docker run --rm \
-v "$PWD/my-project:/app/project" \
--env-file "$PWD/my-project/.env" \
-e SEEKNAL_GATEWAY_URL=http://host.docker.internal:8000 \
-e SEEKNAL_API_TOKEN="$SEEKNAL_API_TOKEN" \
seeknal-worker:local --project /app/project
# Multi-replica with Redis
seeknal gateway start --redis redis://localhost:6379
# Temporal durable execution
seeknal gateway start --temporal --max-activities 20
# HTTP-only worker with in-process concurrency (default N=1 → sequential)
seeknal gateway worker \
--transport http \
--gateway-url http://gateway:8000 \
--api-token "$SEEKNAL_API_TOKEN" \
--max-concurrency 10
# Same via env vars (precedence: CLI flag > env var > default)
SEEKNAL_WORKER_CONCURRENCY=10 \
SEEKNAL_WORKER_SHUTDOWN_TIMEOUT=120 \
seeknal gateway worker \
--transport http \
--gateway-url http://gateway:8000 \
--api-token "$SEEKNAL_API_TOKEN"
# Multi-worker pool (run each command in its own terminal / systemd unit)
# All three workers long-poll the same gateway; broker distributes items.
for i in 1 2 3; do
SEEKNAL_WORKER_CONCURRENCY=10 \
seeknal gateway worker --transport http \
--gateway-url http://gateway:8000 \
--api-token "$SEEKNAL_API_TOKEN" &
done
# Docker: HTTP worker with concurrency
docker run --rm \
-v "$PWD/my-project:/app/project" \
--env-file "$PWD/my-project/.env" \
-e SEEKNAL_GATEWAY_URL=http://host.docker.internal:8000 \
-e SEEKNAL_API_TOKEN="$SEEKNAL_API_TOKEN" \
-e SEEKNAL_WORKER_TRANSPORT=http \
-e SEEKNAL_WORKER_CONCURRENCY=10 \
-e SEEKNAL_WORKER_SHUTDOWN_TIMEOUT=120 \
seeknal-worker:local --project /app/project
# Docker Compose: 3 worker replicas × N=10 (set via .env)
# In .env:
# SEEKNAL_WORKER_CONCURRENCY=10
# SEEKNAL_WORKER_SHUTDOWN_TIMEOUT=120
docker compose -f deploy/docker-compose.worker.yml up --scale seeknal-worker=3
Multi-tenant token registry¶
When a token registry is configured, tenant identity is authenticated instead of trusted from X-Tenant-ID or ?tenant=. The gateway derives the Temporal task queue, callback token, and tenant-scoped session/event access from the bearer token. Request body overrides such as task_queue, project_path, push_url, and api_key are rejected in token mode.
Example gateway-tokens.yml:
tokens:
- token: <random-worker-or-client-token>
tenant_id: acme
task_queue: seeknal-ask-acme
callback_token: <random-callback-token>
callback_url: https://gateway.example.com
temporal_address: temporal.example.com:7233
temporal_namespace: default
project_path: /srv/seeknal/acme
Do not commit token files with real secrets; provision them from your secret manager or deployment environment. Use Authorization: Bearer <token> for HTTP clients. Browser SSE/WebSocket clients that cannot set headers may pass a short-lived api_token or access_token query parameter. If no token registry is configured, Seeknal keeps the previous single-tenant/local behavior.
Architecture¶
Client (browser/app/bot)
│
▼
┌──────────────┐
│ Gateway │ ← seeknal gateway start
│ (Starlette) │
├──────────────┤
│ WebSocket │ /ws/{session_id}
│ SSE │ /events/{session_id}
│ REST │ /ask
│ Cancel │ /sessions/{session_id}/cancel
│ Telegram │ (webhook)
└──────┬───────┘
│
▼
┌──────────────┐
│ Ask Agent │ Same agent as `seeknal ask chat`
│ (per-session)│
└──────────────┘
Runtime behavior¶
- Runs for the same tenant/session are serialized to protect conversation history and DuckDB state.
POST /sessions/{session_id}/cancelrequests cancellation of the active run. WebSocket clients can also send{"type":"cancel"}while a run is streaming.- Tool result events include
elapsed_mswhen timing is available;doneevents include total turnelapsed_ms. - When
--redisis configured, SSE fan-out and session locks use Redis so multi-replica gateway deployments preserve per-session serialization.
Environment Variables¶
| Variable | Description |
|---|---|
GOOGLE_API_KEY |
Google Gemini API key |
TELEGRAM_BOT_TOKEN |
Telegram bot token (with --telegram) |
TEMPORAL_ADDRESS |
Temporal server address (with --temporal) |
TEMPORAL_NAMESPACE |
Temporal namespace |
TEMPORAL_MAX_CONCURRENT_ACTIVITIES |
Max concurrent activities |
CALLBACK_AUTH_TOKEN |
Worker callback auth secret (legacy compatibility mode) |
WORKER_PROJECT_PATH |
Remote worker project path |
SEEKNAL_TOKEN_CONFIG |
JSON/YAML API token registry path |
SEEKNAL_API_TOKENS |
Inline JSON token registry for tests/small deployments |
SEEKNAL_GATEWAY_URL |
Gateway URL used by seeknal gateway worker bootstrap |
SEEKNAL_API_TOKEN |
Worker/client API token used for token-derived routing |
SEEKNAL_WORKER_TRANSPORT |
Worker transport: auto (default), temporal, or http |
SEEKNAL_WORKER_CONCURRENCY |
HTTP worker — max concurrent agent runs per process (default 1) |
SEEKNAL_WORKER_SHUTDOWN_TIMEOUT |
HTTP worker — seconds to drain in-flight tasks on shutdown (default 60) |
Docker images¶
docker/Dockerfile.gateway builds a container that runs seeknal gateway
start, and docker/Dockerfile.worker builds a container that runs seeknal
gateway worker.
Mount a Seeknal project at /app/project, pass project secrets through an
environment file or secret manager, and configure Temporal either directly with
TEMPORAL_ADDRESS/TEMPORAL_TASK_QUEUE or indirectly with
SEEKNAL_GATEWAY_URL/SEEKNAL_API_TOKEN in token-routed deployments.
docker build -f docker/Dockerfile.gateway -t seeknal-gateway:local .
docker build -f docker/Dockerfile.worker -t seeknal-worker:local .
docker compose -f deploy/docker-compose.worker.yml up --build
The compose file is intended for local or on-prem workers running near the data.
Scale it with docker compose -f deploy/docker-compose.worker.yml up --scale
seeknal-worker=3 when the project database and DuckDB state are safe for that
deployment pattern.
See Also¶
- seeknal ask — Interactive CLI agent
- seeknal report-server — Host published reports