Operations

Reverb & Echo

Externa uses Reverb + Laravel Echo (laravel-echo + pusher-js) for admin realtime notifications and presence. Locally, prefer Herd Pro Reverb on :8080 (already running as a shared service). AI chat SSE is unchanged.

What websockets cover

Covered by WebSocketsNot covered (other transport)
Private user notifications (file zip / duplication outcomes)AI chat — browser SSE (POST /ai/chat), not Echo
Presence channel online (green dots on Users list)Activity log flood — Spatie rows stay poll/Inertia; not broadcast per row
Connection status indicator (avatar ConnectionDot)Public CMS / GraphQL / API keys — no realtime fan-out
Auth via /broadcasting/auth (session cookie)Unread count when BROADCAST_CONNECTION is log/null60s poll only then

When realtime is on, the notifications bell does not poll every 60s. Poll is the fallback for the minimal stack only.

Events (notification classes that broadcast)

All four use trait App\Notifications\Concerns\BroadcastsWithDatabase:

  • Always database
  • Plus broadcast when BROADCAST_CONNECTION is not log / null / empty
Classdata.typeEmitted from
FileZipReadyNotificationfile_zip_readyPrepareFilesZipJob
FileZipFailedNotificationfile_zip_failedPrepareFilesZipJob
FileDuplicationCompletedNotificationfile_duplication_completedDuplicateFilesJob
FileDuplicationFailedNotificationfile_duplication_failedDuplicateFilesJob

toBroadcast() wraps the same toArray() payload as the database notification (BroadcastMessage).

Channels

Name in routes/channels.phpWire / Echo nameAuth
App.Models.User.{id}Private App.Models.User.{id}Owner only ((int) $user->id === (int) $id)
onlinePresence presence-online (Echo join('online'))Active User; returns { id, name }

Echo listen points (UI)

LocationBehavior
resources/js/lib/echo.tsensureEcho() — boots Reverb client when Inertia realtime.enabled is true
resources/js/app.tsxEager ensureEcho(true) on boot when enabled
NotificationsBellecho.private('App.Models.User.{id}').notification(...) — bumps unread + NOTIFICATIONS_UPDATED_EVENT
useOnlineUsersecho.join('online').here / .joining / .leaving → Users list green dots
useEchoConnection + ConnectionDotTracks Pusher connection state; rendered on sidebar avatar via UserInfo (nav-user.tsxshowConnectionStatus)

Connection dot tones: connected (green), connecting (amber pulse), disabled (muted), else destructive.

Herd Pro runs a shared Reverb on port 8080. Match credentials from Herd → Services → Reverb (defaults in externa-core/.env.example):

BROADCAST_CONNECTION=reverb

REVERB_APP_ID=1001
REVERB_APP_KEY=laravel-herd
REVERB_APP_SECRET=secret
REVERB_HOST=127.0.0.1
REVERB_PORT=8080
REVERB_SCHEME=http

VITE_REVERB_APP_KEY="${REVERB_APP_KEY}"
VITE_REVERB_HOST="${REVERB_HOST}"
VITE_REVERB_PORT="${REVERB_PORT}"
VITE_REVERB_SCHEME="${REVERB_SCHEME}"
  • Host for Echo must be 127.0.0.1 or localhost (not 0.0.0.0).
  • After changing VITE_REVERB_*, restart npm run dev (or rebuild).
  • Do not also run php artisan reverb:start while Herd Reverb owns :8080 — port conflict.
  • Auth endpoint: /broadcasting/auth (web middleware / session cookie).

Standalone Reverb (no Herd service)

php84 artisan reverb:start

Use your own REVERB_APP_* values on both the app and the Reverb process. composer run dev:full starts reverb:start for that path.

Feature detect

HandleInertiaRequests shares:

realtime: {
  enabled: boolean
  broadcaster: string
}

enabled is false when BROADCAST_CONNECTION is log or null — UI skips Echo; bell keeps the 60s unread poll.

Previous
Horizon