This document outlines what's planned for upcoming releases. Items are ordered by developer impact and implementation complexity within each milestone.
- Kotlin DSL configuration (
Echo.create { ... }) - Public, private, and presence channels (Pusher Protocol v7)
StateFlow<ConnectionState>—Disconnected,Connecting,Connected,ReconnectingSharedFlow<EchoError>— typed, sealed error hierarchy- Automatic reconnection with exponential backoff and jitter
- Protocol-level ping/pong keep-alive
- Suspending
Authenticatorinterface with pre-flight auth - Client whispers on private channels
- Auto-resubscribe on reconnect
- Pluggable
EchoEngineandEchoSerializerinterfaces - KDoc on all public API members
Target: works with Laravel Reverb, Soketi, and Sockudo.
Today, when a private or presence channel auth fails, Echo emits a global EchoError.Auth but leaves the channel stuck in Subscribing. The Pusher protocol sends pusher_internal:subscription_error for this case.
Plan:
- Add
ChannelState.Failed(error: EchoError)to theChannelStatesealed class. - Handle
pusher_internal:subscription_errorinEventRouter— transition the affected channel toFailedand emit a per-channel error. - Expose a
channel.error: StateFlow<EchoError?>property onEchoChannel.
Impact: Apps can show per-channel error UI instead of relying solely on the global error stream.
Today, Echo has a single Reconnecting state that retries indefinitely. Two distinct terminal-ish states are missing:
Suspended— entered after prolonged failure (configurablesuspendAfterMs, default 2 min). Retries slow down (e.g., every 30s). Apps show a "degraded connection" UI.Failed— entered on unrecoverable protocol error codes (Pusher codes 4001–4009: invalid key, app disabled, etc.). No further retries. Requires explicitecho.connect()to restart.
Plan:
- Extend
ConnectionStatesealed class withSuspendedandFailed(error: EchoError). - Update
ExponentialBackoffReconnectionManagerto escalate state after threshold. - Parse Pusher close codes in
KtorEchoConnectionand emitFailedfor fatal codes. - Add
suspendAfterMsconfig to the reconnection DSL block.
Impact: Stops infinite retry loops on bad credentials. Lets apps surface meaningful network-degraded states.
When a presence member changes their payload (e.g., updates their display name or status) without leaving and re-joining, Reverb ≥1.4 / Soketi / Sockudo fires pusher_internal:member_updated. Echo currently ignores this frame.
Plan:
- Add
pusher_internal:member_updatedtoPusherFramesealed class. - Update
PresenceChannelImplto emit anupdating {}callback and update the member in the internal members map. - Expose
channel.updating { member -> }onPresenceChannel.
Impact: Presence channels reflect live member metadata without members needing to leave/join.
Today, auth token renewal requires the channel to be re-subscribed. For long-lived connections, tokens (JWTs) may expire while the channel is active.
Plan:
- Accept a
tokenExpiryMshint in theauth { }DSL block. - Launch a proactive refresh coroutine that calls the
Authenticatorbefore expiry and silently re-sendspusher:subscribefor affected channels without tearing down the WebSocket. - Emit
EchoError.Authon failure without disconnecting.
Impact: Zero-downtime token rotation for long-lived sessions (chat, live dashboards).
Target: Soketi and Sockudo (both expose the Pusher HTTP Events API).
Note: Laravel Reverb does not expose the Pusher HTTP Events API directly — use Laravel's Broadcasting backend instead.
Add a lightweight REST client that wraps the Pusher HTTP Events API for fire-and-forget event publishing without an active WebSocket connection.
Plan:
- Expose
echo.rest()returning anEchoRestClientinterface. suspend fun EchoRestClient.trigger(channel: String, event: String, data: Any): Result<Unit>suspend fun EchoRestClient.triggerBatch(events: List<EchoEvent>): Result<Unit>- Authentication via HMAC-SHA256 request signing (same as Pusher REST spec).
- Useful from background services, WorkManager tasks, or one-shot publishes.
Impact: Publish events without holding an open WebSocket — essential for background workers and server-adjacent Android code.
These features are Sockudo-exclusive protocol extensions and are off by default. Reverb and Soketi silently ignore the extra subscribe frame fields, so enabling them on a non-Sockudo server is harmless.
Sockudo supports subscribing with filter expressions evaluated server-side. Only messages whose tags match the filter are delivered — reducing bandwidth without any client-side filtering logic.
Plan:
- Add a
Filtersealed class DSL:Filter.eq("symbol", "BTC") Filter.and(Filter.eq("exchange", "NYSE"), Filter.gt("volume", "1000")) Filter.or(Filter.eq("symbol", "BTC"), Filter.eq("symbol", "ETH"))
- Serialize the filter to JSON and attach it to the
pusher:subscribeframe. - Extend
echo.channel("name") { filter = Filter.eq("priority", "high") }in the channel builder. - Guard behind a feature flag in
EchoConfig:features { tagFiltering = true }.
Impact: Subscribe to one shared broadcast channel and receive only messages relevant to the client — ideal for market data feeds, IoT dashboards, and notification streams.
Sockudo can transmit only the diff between consecutive messages (Fossil or XDelta3 algorithms), saving 30–95% bandwidth on high-frequency update channels.
Plan:
- Extend
PusherFramewithDeltaCacheSyncandDeltaframe types. - Add delta negotiation to the subscribe frame (
"delta": {"enabled": true, "algorithms": ["fossil"]}). - Integrate a Fossil/XDelta3 decompression library (evaluation needed — JNI wrapper vs. pure-Kotlin port).
- Maintain a per-channel, per-conflation-key delta state cache.
- Expose
channel.enableDelta(algorithm = DeltaAlgorithm.FOSSIL)opt-in in the channel DSL. - Guard behind
features { deltaCompression = true }.
Impact: Dramatically reduces data usage for apps streaming high-frequency updates (live prices, sensor readings, game state). Only activates on Sockudo; ignored on Reverb/Soketi.
| Feature | Notes |
|---|---|
| Channel Occupancy | Would require server-side support. Sockudo exposes Prometheus metrics but no occupancy WebSocket events. |
| E2E Channel Encryption | AES-256 client-side encrypt/decrypt before publish. Server-agnostic. Requires key distribution strategy. |
| Presence History | Requires server-side storage. Not supported by any Pusher-compatible server today. |
| MQTT / SSE transports | Pusher protocol is WebSocket-only. Out of scope. |
| Push Notifications | Platform-level feature (FCM/APNs). Out of scope for this SDK. |
If you'd like to work on a roadmap item, open an issue to discuss the approach before submitting a PR. See CONTRIBUTING.md for guidelines.