Central WebSocket hub for multiple Laravel projects.
A single self-hosted Laravel Reverb instance with database-backed, runtime-manageable application credentials.
Resonance replaces a shared, unmaintained soketi + soketi-manager stack with one dedicated Laravel Reverb server that provides real-time WebSocket connectivity to any number of independent Laravel projects.
Each consumer project is issued its own Pusher-protocol credential triplet (app_id / key / secret). Those credentials live in a database and can be created, rotated, disabled, and deleted at runtime — no redeploy, no config-file edits, no SSH.
Because Reverb speaks the Pusher wire protocol v7 (exactly as soketi did), consumer projects migrate with env-var-level changes only. Their application code and broadcast events are untouched.
- soketi is effectively unmaintained — no Node 20+ support, stalled releases — forcing a migration.
- One server for all projects — current and future — instead of per-project infrastructure.
- Runtime credential management — app creation-to-usable in under two minutes via the admin UI.
- Minimal-risk cutover — per-project, zero-downtime, instant rollback by reverting env vars.
[Project A] --HTTP broadcast (signed with key A)--> \
[Project B] --HTTP broadcast (signed with key B)--> [nginx TLS] --> [Reverb :8080] <--reads--> [websocket_apps DB]
[Browsers ] --WSS (app key in URL)----------------> / ^
|
[Admin UI in Resonance] --------+ (CRUD)
Resonance is a small, single-purpose Laravel application. Its only responsibilities are:
- Running the long-lived
reverb:startprocess (a single-threaded ReactPHP event loop). - Owning the
websocket_appstable. - Providing a custom
DatabaseApplicationProviderthat Reverb resolves apps from. - Serving the admin management UI/API.
It hosts no business logic from any consumer project.
Reverb's event loop is single-threaded. A blocking database query inside the per-connection lookup stalls every connected client for the duration of that query. The provider therefore serves all hot-path lookups from an in-process cache (an in-memory array keyed by key and app_id), refreshed lazily with a short TTL. This is a correctness requirement, not an optimisation.
Consequence to be aware of: a newly created or newly disabled app takes up to one TTL window (default 30 s) to become connectable / revoked. Sub-second revocation is a future enhancement, not part of v1.
- Database-driven apps — the
Applicationoption set (origins, ping interval, activity timeout, message size, connection cap, client-event policy) is stored per row. - Encrypted secrets at rest — via Laravel's
encryptedcast (secrets must be retrievable, as Reverb signs with them). - Runtime CRUD — create, rotate, enable/disable, soft-delete apps without a deploy.
- Instant-ish revocation — disabling an app removes it from the provider within one TTL window.
- Admin panel (Filament) — app management, one-time credential display, audit log, and a "restart Reverb now" action for immediate propagation.
- Visibility — Laravel Pulse with Reverb's built-in cards.
- Drop-in for consumers — Pusher protocol v7 means Echo + pusher-js clients connect unchanged.
| Layer | Choice |
|---|---|
| Framework | Laravel 12.x, PHP 8.2+ |
| Realtime | Laravel Reverb (exact minor pinned) |
| Database | PostgreSQL |
| Admin UI | Filament |
| Monitoring | Laravel Pulse |
| Deploy | Docker / container; nginx (or ingress) terminates WSS on 443 → Reverb :8080; reverb:start under Supervisor |
Status: Resonance is in active development (Phase 1). The commands below describe the target workflow; some pieces are still being built. See the roadmap.
# 1. Clone
git clone git@github.com:Akaal-Creatives/resonance.git
cd resonance
# 2. Install dependencies
composer install
pnpm install
# 3. Configure
cp .env.example .env
php artisan key:generate
# set DB_* to your PostgreSQL instance
# 4. Migrate
php artisan migrate
# 5. Create your first WebSocket app (prints credentials once)
php artisan res:app:create "My Project"
# 6. Start the Reverb server
php artisan reverb:start| Command | Purpose |
|---|---|
php artisan res:app:create {name} |
Create an app; generates and prints app_id / key / secret once. |
php artisan res:app:disable {key} |
Disable (revoke without deleting). |
php artisan res:app:list |
List apps. |
php artisan reverb:start |
Start the WebSocket server (production: under Supervisor). |
php artisan reverb:restart |
Graceful restart — disconnects clients; Echo/pusher-js auto-reconnect. |
Because both soketi and Reverb speak Pusher protocol v7, migrating a project is an env-var change only:
# Backend — broadcasting connection points at Resonance
PUSHER_APP_ID=<from res:app:create>
PUSHER_APP_KEY=<from res:app:create>
PUSHER_APP_SECRET=<from res:app:create>
PUSHER_HOST=ws.yourdomain.tld
PUSHER_PORT=443
PUSHER_SCHEME=httpsFrontend Echo config updates its host and key the same way. Application code and broadcast events do not change. Rollback is reverting these variables.
Keep your existing
pusherdriver + pusher-js during cutover — do not bundle a driver or client-library migration into the switch.
| Phase | Scope |
|---|---|
| 0 | Decisions & prerequisites — Reverb version pin, soketi inventory, performance baseline. |
| 1 | Core — Resonance app skeleton, websocket_apps schema, DatabaseApplicationProvider, in-process cache, CLI. (current) |
| 2 | Management layer — Filament admin: CRUD, rotation, audit log, restart action. |
| 3 | Per-project migration off soketi — one project at a time, 48 h soak between each. |
| 4 | Operational hardening — Supervisor, nginx TLS, OS limits, monitoring, backups. |
| 5 | Decommission soketi. |
Full detail lives in the internal requirements document (.tmp/resonance-requirements.md, not tracked in the repository).
Resonance's database holds every consumer project's realtime credentials. Its APP_KEY and database are jointly a full-compromise target. Accordingly:
- Secrets are encrypted at rest and excluded from logs.
- Secrets are displayed in the UI only at creation / rotation time.
- Admin access is restricted to named users; there is no self-registration.
- Every credential-affecting action is written to an audit log.
Found a vulnerability? Please do not open a public issue — email the maintainers directly.
See CONTRIBUTING.md. In short: conventional commits, branch from main, Pint + PHPStan clean, tests green — and never introduce blocking I/O on the Reverb event-loop path.
Licensed under the GNU Affero General Public License v3.0 (AGPL-3.0). See LICENSE.