Repository navigation
Conversation
Flock runs `php artisan reverb:start` for a site on a loopback port, and the web server sends the site's WebSocket connections and broadcasts under /app and /apps there, so browsers reach it at wss://<site>.test/app.
Each has its own restart counter, so restarting Octane's workers no longer drops Reverb's connections. `flock reverb restart`, the API and the MCP tool's restart_reverb restart only Reverb.
… app WebSockets over HTTP/2 are matched by ?protocol= together with ?client=, which pusher-js always sends.
The apps use reverb_available to offer the Reverb switch, as octane_available does for worker mode.
2d708b3 to
976dece
Compare
976dece to
21b7b35
Compare
|
Thanks a lot for this, Sinan! We wanted to get Reverb in, so we finished it on your branch:
|
There was a problem hiding this comment.
🟡 Changes recommended
Reverb startup lacks a root-user guard, allowing project-owned PHP to execute with root privileges.
5 open issues
What changed in this PR
Adds per-site Laravel Reverb management to Flock, addressing #50. WebSocket connections and broadcasts use the site's existing web address without a separate public port or certificate.
Changes:
- Adds Reverb configuration, port allocation, process supervision, and Caddy routing.
- Exposes Reverb controls and independent restarts through the CLI, API, MCP, and native apps.
- Adds backend and client tests, plus setup and usage documentation.
| File | Description |
|---|---|
windows/Flock.Tray/ViewModels/SitesViewModel.cs |
Tracks Reverb changes and diagnostics. |
windows/Flock.Tray/Settings/SitesPane.xaml.cs |
Includes the Reverb settings section. |
windows/Flock.Tray/Settings/SitesPane.Reverb.xaml.cs |
Adds Reverb controls, status, and output. |
windows/Flock.Tray/Features/Sites/SitesService.cs |
Adds the Reverb restart operation. |
windows/Flock.Tray.Tests/SitesWorkflowTests.cs |
Tests Reverb diagnostics in site summaries. |
windows/Flock.Kit/Settings.cs |
Adds the Reverb setting to site updates. |
windows/Flock.Kit/Models.cs |
Models Reverb availability and status. |
windows/Flock.Kit/FlockClient.cs |
Adds restart requests and operation timeouts. |
windows/Flock.Kit.Tests/DecodingTests.cs |
Tests Reverb status decoding. |
windows/Flock.Kit.Tests/ClientTests.cs |
Tests Reverb requests and timeouts. |
macos/Tests/FlockKitTests/DecodingTests.swift |
Tests Reverb decoding and compatibility. |
macos/Tests/FlockKitTests/ClientTests.swift |
Tests Reverb API requests. |
macos/Sources/FlockUI/Settings/SitesPane.swift |
Adds Reverb controls and diagnostics. |
macos/Sources/FlockKit/Settings.swift |
Adds the Reverb update field. |
macos/Sources/FlockKit/Models.swift |
Models Reverb status and availability. |
macos/Sources/FlockKit/Client.swift |
Adds the dedicated restart request. |
linux/src/panes/sites.rs |
Adds Reverb controls, diagnostics, and output. |
linux/src/api.rs |
Adds Reverb models, requests, and decoding tests. |
internal/web/web.go |
Routes Reverb traffic before other handlers. |
internal/web/web_test.go |
Tests generated routing and handler order. |
internal/web/serve_test.go |
Tests WebSocket forwarding and routing boundaries. |
internal/reverb/reverb.go |
Defines installation detection, process specs, and environment guidance. |
internal/mcpserver/tools_write.go |
Adds Reverb management actions. |
internal/mcpserver/tools_read.go |
Exposes Reverb state and environment lines. |
internal/mcpserver/server.go |
Extends the daemon interface and server instructions. |
internal/mcpserver/mcpserver_test.go |
Extends the fake daemon for Reverb restarts. |
internal/daemon/reverb.go |
Prepares Reverb processes and handles restarts. |
internal/daemon/reverb_test.go |
Tests startup preparation, conflicts, and PHP release selection. |
internal/daemon/frankenphp.go |
Clarifies independent Reverb restarts in a comment. |
internal/daemon/daemon.go |
Integrates Reverb into reloads, status, and PHP release handling. |
internal/daemon/daemon_test.go |
Tests blocked Reverb status when PHP is missing. |
internal/config/config.go |
Adds Reverb settings and shared port allocation. |
internal/config/config_test.go |
Tests port stability and collision avoidance. |
internal/cli/status.go |
Marks Reverb-enabled sites in listings. |
internal/cli/root.go |
Registers the Reverb command. |
internal/cli/reverb.go |
Adds enable, disable, restart, and status commands. |
internal/cli/reverb_test.go |
Tests status errors when the daemon is stopped. |
internal/api/frankenphpapi.go |
Adds the dedicated Reverb restart endpoint and client method. |
internal/api/configapi.go |
Handles Reverb site updates and installation validation. |
internal/api/api.go |
Adds Reverb status contracts and extends reload timeout. |
internal/api/api_test.go |
Tests enabling, disabling, and installation refusal. |
docs/php.md |
Documents Reverb setup, controls, and routing. |
docs/introduction.md |
Introduces managed Reverb support. |
docs/cli.md |
Lists Reverb commands. |
docs/ai.md |
Documents Reverb capabilities in MCP tools. |
Review effort: Balanced
| return nil, st | ||
| } | ||
|
|
||
| if !reverb.Installed(site.Path) { |
| return errors.New("Laravel Reverb isn't installed in " + site.Path + " — run `php artisan install:broadcasting --reverb` first") | ||
| } | ||
| s.cfg.SetReverb(&site, on) | ||
| s.cfg.Upsert(site) |
| NodeShadowed: nodeVersion.Shadowed, Domains: []api.SiteDomain{}, | ||
| } | ||
| status.Server, status.Worker, status.OctaneAvailable = config.ServerFastCGI, site.Worker, frankenphp.HasOctane(site.Path) | ||
| status.ReverbAvailable = reverb.Installed(site.Path) |
| if site.Reverb && (minor == "" || project.PHPFor(cfg, &site, site.Path).Version == minor) { | ||
| reverbs[config.ReverbPortID(site.Name)] = true |
| return fmt.Errorf("Reverb is off for %s", name) | ||
| } | ||
| d.mu.Lock() | ||
| d.restarts[config.ReverbPortID(name)]++ |
| if (reverb is null) | ||
| { | ||
| section.Children.Add(Ui.Caption(site.ReverbAvailable | ||
| ? $"Flock runs php artisan reverb:start for the site. Browsers reach it at {scheme}://{site.Domain}/app, with no port or certificate of its own." | ||
| : "Install Laravel Reverb in the project (php artisan install:broadcasting --reverb) to run it.", Ui.TertiaryText)); | ||
| return section; | ||
| } | ||
|
|
||
| switch (reverb.State) | ||
| { | ||
| case Status.ReverbState.Running: | ||
| section.Children.Add(ReverbState(Ui.Tone.Success, $"Running at {scheme}://{site.Domain}/app")); | ||
| break; | ||
| case Status.ReverbState.Failed: | ||
| section.Children.Add(ReverbState(Ui.Tone.Critical, reverb.Problem.Length > 0 ? reverb.Problem : "Failed")); | ||
| break; | ||
| default: | ||
| section.Children.Add(Warning(reverb.Problem.Length > 0 ? reverb.Problem : "Reverb can't run.")); | ||
| break; | ||
| } |
There was a problem hiding this comment.
All UI element creation should be done via XAML. We can conditionally modify them, but we shouldn't be adding new children in cs files.
| if (!site.IsNode) | ||
| { | ||
| panel.Children.Add(Reverb(site, row)); | ||
| } |
| private static bool ReverbEqual(Status.SiteReverb? previous, Status.SiteReverb? current) | ||
| { | ||
| return previous is null && current is null || previous is not null && current is not null && | ||
| previous.State == current.State && previous.Port == current.Port && previous.Env.SequenceEqual(current.Env) && | ||
| previous.Problem == current.Problem && previous.Log == current.Log; | ||
| } | ||
|
|
There was a problem hiding this comment.
The formatting can be improved here. Start each new condition on a new line with the && or || at the beginning of the line.
A worktree uses its parent's settings, so `flock reverb on` and `off` refused nothing and reported success.
The daemon doesn't run a project's Reverb server with root's rights, notices Reverb being installed, releases a Reverb server by the PHP binary it was started with, and keeps Reverb's restart counters apart from the sites'.
The site, worktree and proxy details and the pane's dialogs are XAML views bound to view models, instead of controls built in code.


Fixes #50
Replaces #21, which closed when
developwas removed. Same commits, merged withmain; nothing else changed.This lets Flock run a Laravel app's Reverb server for its site. Browsers reach it at the site's own address,
wss://app.test/app, through the web server, so there's no port to open and no separate certificate.install:broadcasting --reverbwritesVITE_REVERB_*as references to these variables, so the Echo client follows them too.What it does
A site setting, like FrankenPHP:
Site.ReverbandSite.ReverbPortare new fields.UsedPortsnames itreverb-<site>.The process:
php artisan reverb:start --host=127.0.0.1 --port=<port> --hostname=127.0.0.1in the project, with the site's PHP version and the same environment as its pool (dumps and capture included).--hostnamestops Reverb from serving TLS itself: given the site's name, it does so whenever Herd or Valet has a certificate for that name.laravel/reverbis reported asunavailable, with theinstall:broadcasting --reverbhint.The web server: one
handle @reverbblock, placed before the site's other handlers (Xdebug's included), with this matcher:?protocol=covers WebSockets over HTTP/2, which have no Upgrade header; pusher-js always sends it./apps/<id>/events) that Laravel broadcasts through./app, for example, keeps working.curl.cainfo), so broadcasts tohttps://app.testwork out of the box.Restart:
RestartSite(POST /v1/sites/{name}/restart) now restarts a site's FrankenPHP and Reverb processes, using the counter FrankenPHP already had.flock frankenphp restarttherefore also restarts Reverb, and Echo reconnects.API:
PATCH /v1/sites/{name}takes"reverb": true|false.SiteStatus.reverbcarries the state, port,.envlines, problem and log name.CLI:
flock reverbas above.flock sitesmarks the site with+ Reverb.MCP:
update_sitegetsreverb_onandreverb_off.site_infoshowsreverb_stateandreverb_env.reverb:startwhen Flock runs it.Not in this PR
reverbfield, so nothing changes for them. Their site panes could get a Reverb toggle next to FrankenPHP, with the state, the.envlines and a restart button. I'm happy to do that in a follow-up, or leave it to you if you'd rather shape the UI yourself..env: Flock only shows the lines. It doesn't touch the project's files.Testing
reverbSite: missing Reverb, the command, its environment and the restart counter, and a port conflict.PATCHon and off, and the refusal for a project without Reverb.@reverbbefore Xdebug's block and adapts.?protocol=request over HTTP/2 reach it./app/dashboardand/apps/liststill go to PHP.install:broadcasting --reverb, the process started as Flock starts it, behind this Caddyfile:wss://chat.test/app/<key>and subscribed to a channel.https://chat.test/apps/<id>/eventsand reached the WebSocket.main:go build ./...,go vet ./...andgo test ./...pass on Linux.go vetalso passes for darwin and windows.go test -racepasses for the touched packages exceptinternal/web, whereTestServesAShareThroughCaddyreports a race. It does the same onmainwithout this PR (-count=3), so it isn't from Reverb.Docs
docs/php.mdgets a Laravel Reverb section after FrankenPHP: what goes to Reverb, the.envlines, rebuilding the front end, restarting, the log, and the commands.docs/cli.mdlists theflock reverbcommands. Theflock frankenphp restartrow now says that it restarts Reverb too.docs/ai.mdadds Reverb tosite_infoandupdate_site.docs/introduction.mdmentions Reverb next to FrankenPHP.postgres.mddoes for creating a database.