Chat
4. Deployment

Production Requirements & Configuration

Production Requirements & Configuration

Because Tokomono Chat (tokomono/chat) processes AI turns asynchronously on a background queue and streams live token deltas over WebSockets, a production deployment requires a few services and settings to be active.


1. What Must Be Running in Production?

To run tokomono/chat in production, make sure the following four components are enabled and configured:

Component Why It Is Required What Needs to Run
1. Queue Worker (chat queue) Every user message dispatches Tokomono\Chat\Jobs\ProcessChatTurnJob onto the configured chat queue (config('chat.queue', 'chat')) with a job timeout of 110 seconds. A queue worker (or Laravel Horizon supervisor) listening to the chat queue with --timeout=120.
2. WebSocket Server (Laravel Reverb) Token deltas, tool calls, and completion events are broadcast immediately via ChatStreamEvent (ShouldBroadcastNow) over private-tokomono-chat.{conversationId}. Laravel Reverb (php artisan reverb:start) or a managed Reverb WebSocket cluster, reachable over HTTPS/WSS (port 443).
3. Persistent File Storage User-uploaded attachments (images, PDFs, CSVs) are stored on the disk configured in config('chat.attachments.disk', 'public'). Either a linked local public disk (php artisan storage:link) on single-server setups, or an S3/Cloudflare R2 disk on multi-server/cloud setups.
4. Frontend Echo & Vite Assets <TokomonoChat /> and <TokomonoChatWidget /> use window.Echo to subscribe to private channels and display the live Online / Offline indicator. window.Echo initialized with broadcaster: 'reverb' and frontend assets compiled via npm run build with the VITE_REVERB_* variables present.

2. Production Environment Variables (.env)

Ensure the following environment variables are configured in your production .env:

# 1. Broadcasting & Laravel Reverb
BROADCAST_CONNECTION=reverb

REVERB_APP_ID=your-reverb-app-id
REVERB_APP_KEY=your-reverb-app-key
REVERB_APP_SECRET=your-reverb-app-secret
REVERB_HOST="chat.yourdomain.com"
REVERB_PORT=443
REVERB_SCHEME=https

# Exposed to Vite during `npm run build` so window.Echo connects to Reverb
VITE_REVERB_APP_KEY="${REVERB_APP_KEY}"
VITE_REVERB_HOST="${REVERB_HOST}"
VITE_REVERB_PORT="${REVERB_PORT}"
VITE_REVERB_SCHEME="${REVERB_SCHEME}"

# 2. Queue & Chat Worker Settings
QUEUE_CONNECTION=redis
CHAT_QUEUE=chat
CHAT_CHANNEL_PREFIX=tokomono-chat
CHAT_SLOW_RESPONSE_THRESHOLD=30

# 3. File Attachments Disk ('public' or 's3' / 'r2')
CHAT_ATTACHMENTS_DISK=public

# 4. Disable Debug Diagnostics in Production
APP_DEBUG=false
CHAT_DEBUG=false

# 5. AI Provider Credentials (used by laravel/ai and tokomono/ai-suite)
OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=...
GEMINI_API_KEY=...

Important: Because Vite embeds VITE_REVERB_* environment variables into the compiled JavaScript bundle at build time, you must always run npm run build after setting or changing REVERB_APP_KEY or REVERB_HOST.


3. Configuring window.Echo in Your Frontend

The Vue components (<TokomonoChat /> and <TokomonoChatWidget />) read window.Echo to monitor WebSocket health and authorize private conversation channels (POST /api/chat/{hash}/broadcasting/auth).

In your resources/js/app.ts or resources/js/bootstrap.js, initialize Laravel Echo and attach it to window.Echo:

import { configureEcho, echo } from '@laravel/echo-vue';

configureEcho({
    broadcaster: 'reverb',
});

if (typeof window !== 'undefined') {
    window.Echo = echo();
}

Or when using laravel-echo and pusher-js directly:

import Echo from 'laravel-echo';
import Pusher from 'pusher-js';

window.Pusher = Pusher;

window.Echo = new Echo({
    broadcaster: 'reverb',
    key: import.meta.env.VITE_REVERB_APP_KEY,
    wsHost: import.meta.env.VITE_REVERB_HOST,
    wsPort: import.meta.env.VITE_REVERB_PORT ?? 80,
    wssPort: import.meta.env.VITE_REVERB_PORT ?? 443,
    forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https',
    enabledTransports: ['ws', 'wss'],
});

4. Configuring Queue Workers & Laravel Horizon

By default, tokomono/chat dispatches ProcessChatTurnJob onto the chat queue (CHAT_QUEUE=chat). Because LLM streaming and multi-step tool/workflow calls can take longer than standard jobs, ProcessChatTurnJob sets $timeout = 110. Your queue worker must listen to the chat queue and use a timeout of at least 120 seconds.

If you use Laravel Horizon, add a dedicated supervisor-chat block in config/horizon.php:

'defaults' => [
    'supervisor-chat' => [
        'connection' => 'redis',
        'queue' => ['chat'],
        'balance' => 'auto',
        'autoScalingStrategy' => 'time',
        'maxProcesses' => 3,
        'maxTime' => 0,
        'maxJobs' => 0,
        'memory' => 128,
        'tries' => 1,
        'timeout' => 120,
        'nice' => 0,
    ],
],

'environments' => [
    'production' => [
        'supervisor-chat' => [
            'maxProcesses' => 5,
            'balanceMaxShift' => 1,
            'balanceCooldown' => 3,
        ],
    ],
],

5. Securing the Chat Admin UI (config/chat.php)

By default, config/chat.php protects the standalone Chat Admin UI (/chat) with BlockByDefaultMiddleware::class, which denies all requests unless replaced. To enable the Admin UI in production for authorized administrators, replace the middleware in config/chat.php:

'middleware' => ['web', 'auth', 'can:viewChatAdmin'],

6. Verifying Your Deployment with the Built-In Debug Menu (ChatDebugModal)

tokomono/chat includes a built-in Debug Diagnostics Menu (ChatDebugModal.vue) designed specifically to troubleshoot WebSocket, Reverb, and channel authorization issues during development, staging, or initial production verification.

How to Enable or Disable the Debug Menu

  1. Globally via .env / config/chat.php:
    CHAT_DEBUG=true
    
    (When CHAT_DEBUG is omitted, config/chat.php falls back to APP_DEBUG.)
  2. Per Chat Class (isDebug()): You can also override isDebug(): bool on a specific PHP Chat class (for example, to allow only admins to open the debug menu in production):
    public function isDebug(): bool
    {
        return auth()->user()?->isAdmin() ?? false;
    }
    

Security Note: When config('chat.debug') is false, the diagnostic API endpoints (GET /api/chat/{hash}/debug/status and POST /api/chat/{hash}/debug/ping) automatically return 403 Forbidden. Always set CHAT_DEBUG=false in production once verification is complete.

Opening the Debug Menu

When debug mode is active, an amber debug badge appears in the top-right corner directly above <TokomonoChat /> and <TokomonoChatWidget />. Clicking this badge opens the Tokomono Chat Diagnostics modal and automatically runs a live end-to-end check across three sections:

Section in Debug Menu What It Checks What to Look For
1. Reverb Connectie Status Inspects the client-side window.Echo WebSocket state and queries GET /api/chat/{hash}/debug/status for the server broadcaster configuration. - Status badge: Verbonden (green) or Niet verbonden (offline) (red)
- Socket ID: Active Pusher/Reverb socket ID (e.g. 123456.789012)
- Broadcaster Driver: reverb (host:port) configured on the server
2. Private Kanaal Autorisatie Verifies subscription to private-tokomono-chat.{conversationId} and tests POST /api/chat/{hash}/broadcasting/auth. - Status badge: Verbonden & Geautoriseerd (green), Auth OK (WS Offline) (amber), or Autorisatie Fout (red)
- Kanaal: Full private channel name
- Deelnemer Modus: authenticated_user or guest_token
- Auth Endpoint Check: 200 OK (HMAC geldig) or error message
3. Bericht Sturen & Ontvangen Test Dispatches a real ChatStreamEvent (debug.pong) via POST /api/chat/{hash}/debug/ping and waits up to 4 seconds for it to arrive over the private WebSocket. Includes an Opnieuw testen button. - Verzenden (Server → Reverb): OK if the Laravel backend delivered the event to Reverb's HTTP API, or FOUT if REVERB_HOST/REVERB_PORT is unreachable from PHP.
- Ontvangen (Private WS): Displays round-trip latency in ms (e.g. 18 ms) when received, or TIMEOUT if php artisan reverb:start is not running or blocked by your Nginx proxy.