voryx/thruway
Thruway is a PHP implementation of the WAMP v2 protocol, providing a router and client built on ReactPHP. It enables WebSocket-based pub/sub and RPC for real-time PHP applications, with authentication and flexible transport support.
Architecture fit. Thruway is a dual-purpose package: it ships both a WAMP client and a WAMP router (broker/dealer) on top of ReactPHP and Ratchet. That makes it architecturally orthogonal to a conventional Laravel request/response lifecycle. Pub/sub and RPC-over-WebSocket are the two WAMP primitives it exposes, and both require a long-lived PHP process holding an event loop. In a Laravel codebase this means a supervised daemon (php artisan command under Supervisor/systemd), not an FPM request path. The package therefore fits only if the product genuinely needs WAMP semantics — brokered pub/sub plus routed RPC with a shared URI namespace — and does not fit as a general "real-time notifications" solution.
The strategic problem is overlap. Laravel already ships Reverb (first-party, Pusher protocol), and the Echo client, laravel-websockets lineage, Soketi, and Centrifugo all solve the same user-facing problem with first-party or actively maintained tooling. Thruway's differentiating value is RPC-over-WebSocket with caller/callee routing and progressive/cancellable results — capabilities Echo does not model. If the roadmap doesn't require that, the architecture fit is poor regardless of code quality.
Integration feasibility. Feasible but bespoke. There is no Laravel service provider, no config publishing, no facade — integration is a hand-written provider binding a ReactPHP loop and a Thruway client as singletons, plus a Supervisor-managed router process. Authentication requires bridging Laravel's guard into WAMP's ticket or CRAC challenge-response flow, and channel-level authorization (tenant scoping on URIs) must be authored from scratch. None of this is hard, but all of it is ours to own.
Technical risk. The dominant risk is lifecycle, not correctness. Last release was 2022-02-18 — over three years stale — with no signals of active stewardship. Its dependency chain (Ratchet, ReactPHP major versions) has moved; PHP 8.2/8.3 compatibility and ReactPHP v1.x pinning need verification before any commitment. Treat this as "adopt-and-vendor," not "adopt-and-track." Secondary risks: single-process router with no clustering story, no message durability (WAMP pub/sub is fire-and-forget), and an unfamiliar protocol surface (realms, sessions, roles, URI namespacing) that raises the debugging cost of every incident.
Key questions.
Stack fit. Thruway runs beside Laravel, not inside it. The correct topology is: a dedicated Thruway router process (broker + dealer) as an internal service, Laravel application code as a WAMP client publisher for outbound events, and a separate worker process subscribing to inbound topics and dispatching into Laravel's queue. Web/API requests should enqueue a job or push to the router via a short-lived client — never hold a ReactPHP loop open inside an FPM worker. Browsers connect directly to the router over WebSocket using AutobahnJS, which means the router becomes an internet-facing surface and inherits TLS termination, origin policy, and rate limiting concerns that Reverb would otherwise absorb.
Migration path. Greenfield-only for new real-time features; do not attempt to retrofit existing broadcasting. Introduce a RealtimePublisher interface in the domain layer with Thruway as one adapter and a Laravel broadcast event as the other. This keeps the escape hatch cheap and lets a pilot prove value before any commitment. If Thruway fails the pilot, deleting one adapter is a day's work; deleting it after three features have shipped is a quarter's.
Compatibility. Verify three things before writing code: PHP version support against our matrix; ReactPHP/Ratchet version resolution against anything else in composer.lock that touches the event loop (queue workers, Octane, HTTP clients); and whether the package's autoloading and type declarations survive --prefer-lowest in CI. Laravel's own dependencies don't pin ReactPHP, so conflicts are unlikely but not impossible via transitive packages. Also confirm the router's session serialization behaves under our tenant model.
Sequencing.
RealtimePublisher interface, feature-flagged off.Maintenance. This is the deciding factor. A 2022-vintage package with no active maintainer means we are effectively self-supporting from day one: security patches to the transitive tree, PHP-version bumps, ReactPHP upgrades. Budget a standing fraction of an engineer — realistically 10–20% of one FTE — plus the cognitive cost of the bus factor. The honest framing for stakeholders: we are not adopting a library, we are adopting a small internal service and its upstream dependencies. Mitigate by vendoring or forking to a first-party namespace so a future upgrade path isn't blocked by an unresponsive Packagist entry.
Support. No vendor, no SLA, no commercial backing. Internal support requires at least two engineers fluent in WAMP and ReactPHP — not one. Documentation is sparse and community-sized; expect to write our own runbook covering router restart, realm configuration, and client reconnection semantics.
Scaling. The single-process router is the ceiling. Vertical scaling buys time; horizontal scaling requires realm federation or a different router implementation (Crossbar) — which raises the question of why Thruway's router is in the picture at all. If we only need the client, that's a much smaller commitment: Thruway-as-client against an external router is a defensible architecture; Thruway-as-router-and-client is a much larger operational bet. Size the connection and message-throughput envelope against expected concurrency before the pilot, and note that WebSocket connections are long-lived, so load balancers need sticky sessions or a connection-aware routing tier.
Failure modes.
max_time recycling.Ramp-up. Plan two to three weeks before a mid-level Laravel engineer is productive: WAMP's realm/session/role/URI model and ReactPHP's promise-and-loop style are both unfamiliar idioms, and the failure modes above only become legible with exposure. Pair this with a written decision record stating why WAMP was chosen over Reverb, so the choice survives team turnover.
Recommendation. Default to Laravel Reverb for broadcasting needs and treat Thruway as a narrow, justified exception: adopt only if a validated requirement depends on WAMP RPC routing or interop with an existing WAMP peer, and only with a named owner, a vendored fork, and a bounded pilot. Given a 672-star, three-years-stale package with no maintainer and no Laravel integration layer, the burden of proof sits with the use case, not with the alternative.
How can I help you explore Laravel packages today?