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