Technical docs

Operations Runtime

Overview

Support-facing runtime notes confirmed in current backend code.

Sessions and cookies

  • Main app and realtime server both use `express-session` with `connect-redis`.
  • Relevant env vars:
  • `SESSION_COOKIE_NAME` default: `sessionId`
  • `SESSION_SECRET`
  • `COOKIE_TTL`
  • `COOKIE_DOMAIN`
  • `REDIS_SESSION_HOST`
  • `REDIS_SESSION_PORT`
  • `REDIS_SESSION_PREFIX`
  • Session cookie defaults:
  • `sameSite: lax`
  • `httpOnly: true`
  • `secure: true` in production
  • Important TTL nuance:
  • cookie max-age defaults to `7 days` unless `COOKIE_TTL` overrides it
  • Redis session-store TTL is hard-coded to `24 hours`
  • `/api/init/whoami` also self-heals org selection:
  • if the user has no `currentOrganization`, it selects the first org
  • if the user has no orgs at all, it creates `My Organization` with `2000` credits

Health endpoints

  • Main app health endpoint:
  • `GET /health`
  • returns `status` and `timestamp`
  • in non-production, also returns `env`, `session`, and `uptime`
  • Realtime server health endpoint:
  • `GET /chat/realtime/health`
  • returns `status`, `service: realtime`, and `timestamp`

WebSocket token TTLs

  • User WS tokens use `COOKIE_TTL`, defaulting to `7 days`.
  • Webchat WS tokens use `COOKIE_TTL` when set, otherwise default to `24 hours`.
  • Webchat JWT session tokens use `WEBCHAT_JWT_TTL`, defaulting to `7 days`.

Widget snippet domain selection

  • Generated widget snippets start from `API_DOMAIN`.
  • If org webchat settings set `widgetDomain = proxy` and `API_PROXY_DOMAIN` exists, snippets use the proxy domain instead.
  • Current snippets load `/widget/alloy-webchat-widget.js` and initialize with:
  • `apiKey`
  • `eventId`
  • `eventId` is routing metadata for the widget entry point. The runtime execution graph still starts from the published workflow or employee workflow tied to that event.

Realtime session handoff

  • Pending realtime session configs are stored in Redis under:
  • `${REDIS_PREFIX || 'alloy_dev'}realtime:{sessionId}`
  • TTL is `20 minutes`.
  • Configs are one-time use and are deleted on first successful retrieval.
  • Ally realtime session creation is:
  • `POST /chat/realtime/ally/create-session/:organization_id`
  • Ally realtime runs seed runtime context with:
  • `targetOrganizationId`
  • `userId`

Request and upload limits

  • Main app request-body limits:
  • JSON: `70mb`
  • URL-encoded: `70mb`
  • HTTP server timeouts for both app and realtime:
  • `requestTimeout = 600000`
  • `headersTimeout = 600000`
  • `keepAliveTimeout = 5000`
  • Storage uploads still enforce `10 MB` per file.
  • Internal-chat attachments are limited to:
  • max `5` files
  • max `10 MB` each
  • allowed MIME families: images, PDF, SQLite, plain text, XML

Shutdown and error signaling

  • Both servers use `http-graceful-shutdown`.
  • Shutdown timeout is `450000 ms`.
  • Cleanup closes:
  • `redisClient`
  • `redisQueueClient`
  • Sequelize
  • `uncaughtException` and `unhandledRejection` are reported to Sentry and `telegramError`.
  • Telegram delivery requires:
  • `TELEGRAM_BOT_API_KEY`
  • `TELEGRAM_CHAT_ID`
  • Optional Telegram thread routing:
  • `TELEGRAM_EVENTS_THREAD_ID`
  • `TELEGRAM_ERRORS_THREAD_ID`
  • `TELEGRAM_FEEDBACK_THREAD_ID`
  • `TELEGRAM_WARNINGS_THREAD_ID`

Quick support checks

  • Session issues:
  • verify Redis availability
  • compare cookie TTL vs Redis store TTL
  • verify cookie domain and allowed origins
  • Wrong widget snippet domain:
  • check org webchat widget settings plus `API_DOMAIN` and `API_PROXY_DOMAIN`
  • Realtime connect failures:
  • check the pending Redis key has not expired or been consumed already
  • confirm the workflow/voice models still exist
  • Missing Telegram alerts:
  • verify bot token, chat ID, optional thread IDs, and bot access to the target chat

Start building your AI team