<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Hibira</title>
    <description>The latest articles on DEV Community by Hibira (@hibira).</description>
    <link>https://dev.to/hibira</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4231947%2F30ee80b6-f70c-46c8-8cb5-f8013f2130d0.jpg</url>
      <title>DEV Community: Hibira</title>
      <link>https://dev.to/hibira</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hibira"/>
    <language>en</language>
    <item>
      <title>Laravel Broadcasting: run Reverb yourself, or use a hosted service?</title>
      <dc:creator>Hibira</dc:creator>
      <pubDate>Sat, 07 Nov 2026 11:32:44 +0000</pubDate>
      <link>https://dev.to/hibira/laravel-broadcasting-run-reverb-yourself-or-use-a-hosted-service-4bj8</link>
      <guid>https://dev.to/hibira/laravel-broadcasting-run-reverb-yourself-or-use-a-hosted-service-4bj8</guid>
      <description>&lt;p&gt;Laravel Broadcasting gives you two good ways to push events to browsers. You can run &lt;a href="https://laravel.com/docs/reverb" rel="noopener noreferrer"&gt;Laravel Reverb&lt;/a&gt;, the WebSocket server the Laravel team released with Laravel 11, or you can point the &lt;code&gt;pusher&lt;/code&gt; driver at a hosted service such as Pusher or Hibira. Both speak the Pusher protocol, and both work with Laravel Echo and pusher-js. Your events, channels and &lt;code&gt;routes/channels.php&lt;/code&gt; don't change.&lt;/p&gt;

&lt;p&gt;So the question isn't which one your code supports. It's who runs the WebSocket server.&lt;/p&gt;

&lt;h2&gt;
  
  
  What running Reverb asks of you
&lt;/h2&gt;

&lt;p&gt;Reverb is a long-running PHP process built on an event loop. Getting it working locally is one command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan &lt;span class="nb"&gt;install&lt;/span&gt;:broadcasting
php artisan reverb:start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Production takes more:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A process manager.&lt;/strong&gt; Reverb has to stay up, so it runs under Supervisor or systemd and restarts on deploys.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TLS and a proxy.&lt;/strong&gt; Browsers need &lt;code&gt;wss://&lt;/code&gt;, so Reverb usually sits behind Nginx or a load balancer that terminates TLS and upgrades the connection.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server limits.&lt;/strong&gt; Each connection is an open file. You'll raise the open-file limit, and past about a thousand connections the Reverb docs recommend the &lt;code&gt;ext-uv&lt;/code&gt; event loop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scaling out.&lt;/strong&gt; One Reverb process handles a lot, but more than one server means turning on Reverb's Redis-based scaling and putting a load balancer in front.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Watching it.&lt;/strong&gt; Reverb reports to Laravel Pulse. You'll still want alerts for when the process dies or connections spike.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this is unusual if you already run your own servers. It's the same work as any other daemon.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a hosted service asks of you
&lt;/h2&gt;

&lt;p&gt;With a hosted Pusher-compatible service you set a few values in &lt;code&gt;.env&lt;/code&gt; and keep using the &lt;code&gt;pusher&lt;/code&gt; driver:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="py"&gt;BROADCAST_CONNECTION&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;pusher&lt;/span&gt;
&lt;span class="py"&gt;PUSHER_APP_ID&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;your-app-id&lt;/span&gt;
&lt;span class="py"&gt;PUSHER_APP_KEY&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;your-app-key&lt;/span&gt;
&lt;span class="py"&gt;PUSHER_APP_SECRET&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;your-app-secret&lt;/span&gt;
&lt;span class="py"&gt;PUSHER_HOST&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;ws.hibira.io&lt;/span&gt;
&lt;span class="py"&gt;PUSHER_PORT&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;443&lt;/span&gt;
&lt;span class="py"&gt;PUSHER_SCHEME&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;https&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F41oerlni76vjcykdw7i8.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F41oerlni76vjcykdw7i8.webp" alt="App ID, key and secret on the app page" width="800" height="406"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You don't operate a WebSocket process or its TLS yourself, but you still need to choose a plan sized for peak connections and deliveries. In exchange, you pay a monthly fee once you outgrow the free tier, your events pass through a third party, and you live with the plan's limits.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmeia9qup7884f8po05ds.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmeia9qup7884f8po05ds.webp" alt="Both paths keep your events and channel routes; only the operations differ" width="800" height="274"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Side by side
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Reverb on your servers&lt;/th&gt;
&lt;th&gt;Hosted service&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cost&lt;/td&gt;
&lt;td&gt;Server time and your time&lt;/td&gt;
&lt;td&gt;Free tier, then a monthly plan&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Setup&lt;/td&gt;
&lt;td&gt;Process manager, TLS, proxy, limits&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.env&lt;/code&gt; values&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scaling&lt;/td&gt;
&lt;td&gt;Redis scaling and a load balancer&lt;/td&gt;
&lt;td&gt;The provider's job&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data path&lt;/td&gt;
&lt;td&gt;Stays on your infrastructure&lt;/td&gt;
&lt;td&gt;Passes through the provider&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Extras&lt;/td&gt;
&lt;td&gt;Pulse cards&lt;/td&gt;
&lt;td&gt;Depends on the provider: dashboards, webhooks, push notifications&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Migration / lock-in&lt;/td&gt;
&lt;td&gt;You operate the infrastructure&lt;/td&gt;
&lt;td&gt;Compatible SDKs reduce migration work; features and limits may differ&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  How to choose
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Run Reverb when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You already operate servers with a process manager and monitoring, and one more daemon is little extra work.&lt;/li&gt;
&lt;li&gt;Events must not leave your infrastructure.&lt;/li&gt;
&lt;li&gt;Your traffic is modest and in one region, close to your servers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Use a hosted service when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You deploy to a platform where long-running processes are awkward (serverless, some PaaS plans).&lt;/li&gt;
&lt;li&gt;Nobody on the team wants to be paged for the WebSocket server.&lt;/li&gt;
&lt;li&gt;Your users are far from your servers. A WebSocket server near them shortens every event, even if your app stays where it is.&lt;/li&gt;
&lt;li&gt;You want what sits around the server: a debug console, usage per app, webhooks, push notifications to phones.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  You can change your mind later
&lt;/h2&gt;

&lt;p&gt;The shared protocol reduces migration work, but it does not make providers identical. Check your Laravel version and Echo configuration, channel types (including encrypted and cache channels), client events, webhooks, usage limits and peak concurrent connections before switching. Hosted removes server operations from your team; choosing an adequate plan is still capacity planning.&lt;/p&gt;

&lt;p&gt;Because both speak the same protocol, moving between them is configuration, not code. Going from Reverb to a hosted service means switching &lt;code&gt;BROADCAST_CONNECTION&lt;/code&gt; to &lt;code&gt;pusher&lt;/code&gt;, setting the &lt;code&gt;PUSHER_*&lt;/code&gt; values, and changing Echo's &lt;code&gt;broadcaster&lt;/code&gt; from &lt;code&gt;'reverb'&lt;/code&gt; to &lt;code&gt;'pusher'&lt;/code&gt; with the same host options. Going the other way is the reverse.&lt;/p&gt;

&lt;p&gt;Many teams start hosted to ship quickly and move to Reverb once traffic is predictable, or start on Reverb and move to hosted when the ops work stops being worth it. Either path is fine.&lt;/p&gt;

&lt;p&gt;If you choose hosted, the &lt;a href="https://hibira.io/guides/laravel" rel="noopener noreferrer"&gt;Laravel guide&lt;/a&gt; has the full setup for Hibira, and the free plan covers development and small apps.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://hibira.io/blog/laravel-reverb-or-hosted?utm_source=devto&amp;amp;utm_medium=community&amp;amp;utm_campaign=crosspost&amp;amp;utm_content=laravel-reverb-or-hosted" rel="noopener noreferrer"&gt;Hibira blog&lt;/a&gt;. Hibira is a hosted, Pusher-compatible WebSocket service with servers in Hong Kong; the Free plan needs no card.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>php</category>
      <category>websocket</category>
      <category>realtime</category>
    </item>
    <item>
      <title>Realtime in Asia: where your WebSocket server is matters</title>
      <dc:creator>Hibira</dc:creator>
      <pubDate>Sat, 07 Nov 2026 11:32:41 +0000</pubDate>
      <link>https://dev.to/hibira/realtime-in-asia-where-your-websocket-server-is-matters-54m4</link>
      <guid>https://dev.to/hibira/realtime-in-asia-where-your-websocket-server-is-matters-54m4</guid>
      <description>&lt;p&gt;A chat message, a price tick or a "someone is typing" indicator feels instant when it arrives within about a tenth of a second. Most of that budget goes to the network, and most of the network time is set by one thing you choose: where the WebSocket server is.&lt;/p&gt;

&lt;h2&gt;
  
  
  Distance sets the floor
&lt;/h2&gt;

&lt;p&gt;Light in optical fibre covers about 200 km per millisecond, roughly two-thirds of its speed in a vacuum. A round trip to a server 1,000 km away therefore takes at least 10 ms, before any routing, queuing or processing. Real cables don't follow the great circle and real routers add delay, so measured round trips are often two to four times this floor: we measured about 30 ms from Hong Kong to Taipei, against a floor of 8 ms.&lt;/p&gt;

&lt;p&gt;Here is the floor from Hong Kong, where Hibira runs today (AWS ap-east-1), to cities across the region:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;From Hong Kong to&lt;/th&gt;
&lt;th&gt;Great-circle distance&lt;/th&gt;
&lt;th&gt;Minimum round trip&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Taipei&lt;/td&gt;
&lt;td&gt;810 km&lt;/td&gt;
&lt;td&gt;8 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Manila&lt;/td&gt;
&lt;td&gt;1,120 km&lt;/td&gt;
&lt;td&gt;11 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bangkok&lt;/td&gt;
&lt;td&gt;1,730 km&lt;/td&gt;
&lt;td&gt;17 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Seoul&lt;/td&gt;
&lt;td&gt;2,090 km&lt;/td&gt;
&lt;td&gt;21 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Singapore&lt;/td&gt;
&lt;td&gt;2,590 km&lt;/td&gt;
&lt;td&gt;26 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tokyo&lt;/td&gt;
&lt;td&gt;2,880 km&lt;/td&gt;
&lt;td&gt;29 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jakarta&lt;/td&gt;
&lt;td&gt;3,270 km&lt;/td&gt;
&lt;td&gt;33 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ashburn, Virginia (US East)&lt;/td&gt;
&lt;td&gt;13,090 km&lt;/td&gt;
&lt;td&gt;131 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These are physics, not benchmarks: no network can beat them, and yours will be slower. The last row is there for scale. Many US services run their realtime infrastructure in US East; Pusher's default cluster, &lt;code&gt;mt1&lt;/code&gt;, is there too. Pusher does offer clusters in Asia, so if you're on Pusher, check which cluster your app was created in.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl10mvcuy13895liiyyto.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl10mvcuy13895liiyyto.webp" alt="Minimum round trip from Hong Kong, set by the speed of light in fibre" width="800" height="311"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it matters more for realtime than for web pages
&lt;/h2&gt;

&lt;p&gt;A web page pays the round trip a few times while it loads. A realtime feature pays it on every interaction:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Typing indicators and cursors&lt;/strong&gt; send client events many times a second; a slow round trip makes them lag behind the user.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chat&lt;/strong&gt; goes from one user to your server, to the WebSocket service, and out to everyone else. Every leg adds its distance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Presence&lt;/strong&gt; (who's online) updates when members join and leave; slow updates show people as online after they've left.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reconnects&lt;/strong&gt; after a phone switches networks cost a TLS handshake plus subscriptions, several round trips in a row.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Count every leg
&lt;/h2&gt;

&lt;p&gt;When your backend publishes an event, it travels:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;from the user's action to your backend,&lt;/li&gt;
&lt;li&gt;from your backend to the WebSocket service's HTTP API,&lt;/li&gt;
&lt;li&gt;from the WebSocket service to every subscribed client.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Legs 1 and 3 depend on where your users are. Leg 2 depends on where your backend is. A backend in Tokyo publishing through a WebSocket service in Virginia crosses the Pacific twice for an event between two users in Taipei. Put the WebSocket service near your users, and if you can, put your backend near it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1vfzhdn6xffmtyz6ka1l.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1vfzhdn6xffmtyz6ka1l.webp" alt="Every event pays three legs; legs ① and ③ depend on where your users are" width="799" height="144"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Measure it yourself
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What this test measures:&lt;/strong&gt; &lt;code&gt;pusher:ping&lt;/code&gt;/&lt;code&gt;pusher:pong&lt;/code&gt; measures an already-established WebSocket round trip to the realtime endpoint, not publish-to-subscriber latency or connection setup time. It excludes your backend, event fan-out and browser rendering. Compare multiple runs from representative networks (including mobile), and track the median and p95; DNS, TLS, routing and congestion can change the result.&lt;/p&gt;

&lt;p&gt;Tables are a starting point; your users' networks are what count. The Pusher protocol has a ping message that the server answers immediately, so you can time the round trip without publishing anything. Save this as &lt;code&gt;ping.mjs&lt;/code&gt; and run it with Node.js 22 or newer (which has &lt;code&gt;WebSocket&lt;/code&gt; built in):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// node ping.mjs YOUR_APP_KEY [host]&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ws.hibira.io&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ws&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WebSocket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`wss://&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;host&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/app/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;?protocol=7&amp;amp;client=js&amp;amp;version=8.4.0`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;times&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;sent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ping&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;sent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pusher:ping&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pusher:pong&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;times&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;sent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pusher:connection_established&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pusher:pong&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;times&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ping&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;250&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;times&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`best &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;times&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt; ms, median &lt;/span&gt;&lt;span class="p"&gt;${((&lt;/span&gt;&lt;span class="nx"&gt;times&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;times&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt; ms`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it from where your users are: a cloud server in each country you serve, or a colleague's laptop on a home connection. The median is the number to compare; the best shows how close the route gets to the floor. Mobile networks add their own delay on top, so test on a phone hotspot too.&lt;/p&gt;

&lt;p&gt;For a view from real users, time how long pusher-js takes from &lt;code&gt;connecting&lt;/code&gt; to &lt;code&gt;connected&lt;/code&gt; and send it to your analytics with the user's country.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we're doing about it
&lt;/h2&gt;

&lt;p&gt;Hibira runs in Hong Kong today because it sits near the middle of East and Southeast Asia. Tokyo is the next region we're preparing, then Singapore. Apps will stay pinned to one region, so you'll pick the one closest to most of your users.&lt;/p&gt;

&lt;p&gt;If you'd like to see the numbers for your own users before you switch, the &lt;a href="https://hibira.io/pricing" rel="noopener noreferrer"&gt;free plan&lt;/a&gt; is enough to run the script above against your own app.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://hibira.io/blog/realtime-latency-in-asia?utm_source=devto&amp;amp;utm_medium=community&amp;amp;utm_campaign=crosspost&amp;amp;utm_content=realtime-latency-in-asia" rel="noopener noreferrer"&gt;Hibira blog&lt;/a&gt;. Hibira is a hosted, Pusher-compatible WebSocket service with servers in Hong Kong; the Free plan needs no card.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>websocket</category>
      <category>realtime</category>
      <category>performance</category>
      <category>networking</category>
    </item>
    <item>
      <title>Add realtime WebSockets with Claude Code in ten minutes</title>
      <dc:creator>Hibira</dc:creator>
      <pubDate>Sat, 07 Nov 2026 11:22:16 +0000</pubDate>
      <link>https://dev.to/hibira/add-realtime-websockets-with-claude-code-in-ten-minutes-3cod</link>
      <guid>https://dev.to/hibira/add-realtime-websockets-with-claude-code-in-ten-minutes-3cod</guid>
      <description>&lt;p&gt;Adding realtime to an app is mostly plumbing: create credentials, put them in the right env file, configure a client library, authorize private channels, and then check that an event actually arrives. That's exactly the kind of work an AI coding agent does well, as long as it can reach the service. The Hibira MCP server gives it that reach.&lt;/p&gt;

&lt;p&gt;This post uses Claude Code. Cursor, VS Code, Claude and ChatGPT work the same way; the &lt;a href="https://hibira.io/mcp-server" rel="noopener noreferrer"&gt;MCP server page&lt;/a&gt; has the setup for each.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the agent can do
&lt;/h2&gt;

&lt;p&gt;Through the &lt;a href="https://modelcontextprotocol.io" rel="noopener noreferrer"&gt;Model Context Protocol&lt;/a&gt;, Hibira offers nine tools. The agent can read your plan and apps, create and update apps, read usage, publish a test event, watch the debug console, and fetch the integration guide for your framework with your app's ID and key filled in. It can't delete apps or rotate secrets, and it never sees an existing app's secret.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: add the server
&lt;/h2&gt;

&lt;p&gt;You need a Hibira account; the &lt;a href="https://hibira.io/signup" rel="noopener noreferrer"&gt;free plan&lt;/a&gt; is enough. In your project folder, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add &lt;span class="nt"&gt;--transport&lt;/span&gt; http hibira https://hibira.io/mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first time Claude Code uses the server, it opens a Hibira page in your browser. Sign in, choose &lt;strong&gt;read only&lt;/strong&gt; or &lt;strong&gt;read and write&lt;/strong&gt;, and approve. If it doesn't open on its own, run &lt;code&gt;/mcp&lt;/code&gt; in Claude Code and choose to authenticate the hibira server. Setting up a new app needs write access.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmsyo2az0cxjo7go64btf.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmsyo2az0cxjo7go64btf.webp" alt="The approval page: choose read only or read and write" width="800" height="1105"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: ask for what you want
&lt;/h2&gt;

&lt;p&gt;Be specific about your stack and where you'll run it. For a Laravel app with a Vite front end:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Add Hibira realtime to this Laravel app. Create an app called "shop-dev" that allows &lt;a href="http://localhost:5173" rel="noopener noreferrer"&gt;http://localhost:5173&lt;/a&gt;, put the keys in .env, set up Laravel Echo, and broadcast OrderShipped on a private orders channel.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What happens next is the agent's own plan, but it usually goes like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;get_account&lt;/strong&gt; to see the plan and the WebSocket host.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;create_app&lt;/strong&gt; with the name and allowed origin. The secret comes back once, and the tool tells the agent to write it straight into your git-ignored &lt;code&gt;.env&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;get_integration_guide&lt;/strong&gt; for &lt;code&gt;laravel&lt;/code&gt;, which returns the Echo configuration, the &lt;code&gt;.env&lt;/code&gt; keys and the steps for private channels, with your values filled in.&lt;/li&gt;
&lt;li&gt;Edits to &lt;code&gt;.env&lt;/code&gt;, &lt;code&gt;config/broadcasting.php&lt;/code&gt;, &lt;code&gt;resources/js/echo.js&lt;/code&gt; and &lt;code&gt;routes/channels.php&lt;/code&gt;, and a new broadcast event.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Review the diff as you would any other change. The edits are ordinary Laravel; nothing in your code depends on the agent afterwards.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsra2guywhk1ep5p2rm50.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsra2guywhk1ep5p2rm50.webp" alt="What the agent usually does with one request" width="800" height="494"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: prove an event arrives
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Publishing is not the same as delivery.&lt;/strong&gt; Seeing an event in &lt;code&gt;watch_debug_events&lt;/code&gt; confirms that Hibira observed the publish or subscription, but it does not prove that a specific browser received and handled the event. Verify the client-side handler with a real subscriber and, for important state, confirm it again through your application's API.&lt;/p&gt;

&lt;p&gt;This is where the debug console earns its keep. Ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Open the app in the browser, then send a test OrderShipped event to private-orders.1 and tell me whether a subscriber received it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The agent calls &lt;strong&gt;send_test_event&lt;/strong&gt;, which publishes like your server would, and &lt;strong&gt;watch_debug_events&lt;/strong&gt;, which listens to the app's debug console for up to 30 seconds and reports connections, subscriptions and messages. If nothing was subscribed, it can see that too, which usually points at the auth endpoint or the allowed origins.&lt;/p&gt;

&lt;p&gt;On the Free plan the debug console shows connections, subscriptions and published messages; paid plans add client events, webhooks and errors.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frrqi0k9ynbgk19urnasj.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frrqi0k9ynbgk19urnasj.webp" alt="The debug console: connections, subscriptions and every published event, live" width="800" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Day-to-day questions
&lt;/h2&gt;

&lt;p&gt;Once connected, the server is useful beyond setup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"How close are my apps to their daily message limit?" uses &lt;strong&gt;get_usage&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;"Why are browsers on staging.example.com refused?" uses &lt;strong&gt;get_app&lt;/strong&gt; to read the allowed origins, and &lt;strong&gt;update_app&lt;/strong&gt; to fix them if you agree.&lt;/li&gt;
&lt;li&gt;"Turn on client events for shop-dev so typing indicators work" is one &lt;strong&gt;update_app&lt;/strong&gt; call.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Keeping it safe
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Understand what the agent can see.&lt;/strong&gt; &lt;code&gt;create_app&lt;/code&gt; returns a &lt;em&gt;new&lt;/em&gt; app secret once to the connected agent so that it can write your local configuration; existing secrets are not returned by read tools. That one-time exposure is still access to a credential: use a trusted agent, review its changes, keep secrets out of browser bundles and commits, give the minimum necessary scope, and revoke an agent token when it is no longer needed.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Read only when you can.&lt;/strong&gt; Grant read and write while you set things up, and read only for everyday questions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tokens for agents without a browser.&lt;/strong&gt; CI jobs and remote agents can use a personal access token instead of signing in. Make one on the AI agents page, limit it to one app, and give it an expiry date:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fx3mc2yreqlgjjo84b3uh.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fx3mc2yreqlgjjo84b3uh.webp" alt="The AI agents page lists every connected agent and token; revoke any of them at once" width="800" height="172"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add &lt;span class="nt"&gt;--transport&lt;/span&gt; http hibira https://hibira.io/mcp &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer hkb_pat_..."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Revoke at any time.&lt;/strong&gt; The AI agents page lists every connected agent and token; revoking one takes effect immediately.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test events are real messages.&lt;/strong&gt; They count toward your plan's daily limit like any other publish, and the server allows a few per second per account.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;Sign up for the &lt;a href="https://hibira.io/signup" rel="noopener noreferrer"&gt;free plan&lt;/a&gt;, add the server, and ask your agent to wire up the framework you use. If it gets stuck, the &lt;a href="https://hibira.io/guides" rel="noopener noreferrer"&gt;integration guides&lt;/a&gt; show the same setup by hand.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://hibira.io/blog/websockets-with-claude-code-mcp?utm_source=devto&amp;amp;utm_medium=community&amp;amp;utm_campaign=crosspost&amp;amp;utm_content=websockets-with-claude-code-mcp" rel="noopener noreferrer"&gt;Hibira blog&lt;/a&gt;. Hibira is a hosted, Pusher-compatible WebSocket service with servers in Hong Kong; the Free plan needs no card.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>websocket</category>
      <category>claude</category>
      <category>mcp</category>
      <category>realtime</category>
    </item>
  </channel>
</rss>
