<?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: yuwnloyblog</title>
    <description>The latest articles on DEV Community by yuwnloyblog (@yuwnloyblog).</description>
    <link>https://dev.to/yuwnloyblog</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%2F4031438%2Fa3bed103-4744-452d-acb4-46221da09a0d.jpg</url>
      <title>DEV Community: yuwnloyblog</title>
      <link>https://dev.to/yuwnloyblog</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/yuwnloyblog"/>
    <language>en</language>
    <item>
      <title>Benchmarking Real-Time Messaging Without Mixing Up ACKs, Deliveries, and Fan-Out</title>
      <dc:creator>yuwnloyblog</dc:creator>
      <pubDate>Mon, 20 Jul 2026 10:17:33 +0000</pubDate>
      <link>https://dev.to/yuwnloyblog/benchmarking-real-time-messaging-without-mixing-up-acks-deliveries-and-fan-out-1n6f</link>
      <guid>https://dev.to/yuwnloyblog/benchmarking-real-time-messaging-without-mixing-up-acks-deliveries-and-fan-out-1n6f</guid>
      <description>&lt;p&gt;A messaging benchmark can report an impressive number and still tell you almost nothing.&lt;/p&gt;

&lt;p&gt;“Messages per second” might mean WebSocket frames written by a load generator, publishes accepted by a server, messages committed to storage, or callbacks observed by recipients. In group chat, one accepted publish may produce hundreds or thousands of deliveries. Connection storms and steady-state delivery exercise different parts of a system, yet they are often rolled into one average.&lt;/p&gt;

&lt;p&gt;We ran into this problem while improving performance transparency for &lt;a href="https://github.com/juggleim/im-server?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=juggleim_benchmark" rel="noopener noreferrer"&gt;JuggleIM&lt;/a&gt;, an open-source messaging server written in Go. Before publishing bigger numbers, we wanted a benchmark whose semantics were explicit and whose results could be reproduced.&lt;/p&gt;

&lt;p&gt;The result is a small Go harness for private and group chat that reports connection setup, server acknowledgements, and recipient delivery separately. This article explains the decisions behind it, the mistakes it tries to avoid, and how to run it yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start by defining what a message means
&lt;/h2&gt;

&lt;p&gt;For a real-time messaging system, at least four events matter:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Connection established:&lt;/strong&gt; the client completed the production authentication and WebSocket handshake.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Publish attempted:&lt;/strong&gt; the load generator handed a message to a connected client.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Publish acknowledged:&lt;/strong&gt; the server accepted the publish and returned its protocol-level ACK.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Delivery observed:&lt;/strong&gt; a recipient client decoded the message and invoked its application callback.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These events answer different questions.&lt;/p&gt;

&lt;p&gt;Connection latency tells you how quickly a deployment can admit clients. ACK latency covers client serialization, transport, server processing, and the ACK return path. Delivery latency covers the full sender-to-recipient path. Delivery count reveals fan-out.&lt;/p&gt;

&lt;p&gt;Combining them into one number hides the system behavior we actually want to understand.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two workloads, two different shapes
&lt;/h2&gt;

&lt;p&gt;The harness implements two workloads using JuggleIM's production Protobuf-over-WebSocket protocol.&lt;/p&gt;

&lt;h3&gt;
  
  
  Private chat: a ring
&lt;/h3&gt;

&lt;p&gt;Every connected client is a sender. Clients form a ring:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;client 1 -&amp;gt; client 2
client 2 -&amp;gt; client 3
...
client N -&amp;gt; client 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The configured rate is the aggregate publish rate across the ring. This spreads work across clients without generating a quadratic number of conversations or concentrating all sends on one connection.&lt;/p&gt;

&lt;p&gt;For every measured publish, the harness records:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;publish-to-ACK latency;&lt;/li&gt;
&lt;li&gt;whether the ACK succeeded or failed;&lt;/li&gt;
&lt;li&gt;publish-to-recipient-callback latency;&lt;/li&gt;
&lt;li&gt;the number of recipient callbacks observed.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Group chat: explicit fan-out
&lt;/h3&gt;

&lt;p&gt;All clients join one synthetic group. A configurable subset acts as senders while every connected member can receive messages.&lt;/p&gt;

&lt;p&gt;The report does not call recipient callbacks “message throughput.” It reports two rates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ACK throughput:&lt;/strong&gt; accepted group publishes per second;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;delivery throughput:&lt;/strong&gt; callbacks observed across group members per second.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If 100 group members receive one accepted publish, that is one successful ACK and roughly 100 delivery events. Both values are useful. Treating them as interchangeable is not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measure connection establishment separately
&lt;/h2&gt;

&lt;p&gt;Opening thousands of authenticated WebSocket connections exercises token validation, connection state, file descriptors, goroutine scheduling, and network setup. Steady-state messaging exercises routing, acknowledgements, persistence, conversation state, and recipient fan-out.&lt;/p&gt;

&lt;p&gt;The harness therefore completes and records all connection attempts before warm-up begins. It refuses to publish a partial baseline if any configured client fails to connect.&lt;/p&gt;

&lt;p&gt;Connection metrics include attempts, successes, failures, error codes, and P50/P95/P99 latency. They are never mixed into steady-state message latency.&lt;/p&gt;

&lt;h2&gt;
  
  
  Warm up before recording
&lt;/h2&gt;

&lt;p&gt;The first few operations of a process may include lazy initialization, empty caches, new database connections, or one-time allocations. Measuring them together with steady state makes short tests especially noisy.&lt;/p&gt;

&lt;p&gt;Each workload has three phases:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;setup -&amp;gt; connect -&amp;gt; warm up -&amp;gt; measure -&amp;gt; delivery grace
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Warm-up messages use the real protocol and storage behavior but are tagged as &lt;code&gt;warmup&lt;/code&gt; and excluded from the report. Measured messages carry a run ID, phase, sequence number, nanosecond send timestamp, and enough padding to reach the requested payload size.&lt;/p&gt;

&lt;p&gt;After sending stops, a configurable grace period collects deliveries already in flight.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use production semantics, not a convenient mock
&lt;/h2&gt;

&lt;p&gt;The benchmark creates synthetic users through JuggleIM's signed server API, obtains real client tokens, and connects through the same WebSocket protocol used by client SDKs. Group workloads create actual group membership before clients connect.&lt;/p&gt;

&lt;p&gt;Stored and counted text messages are enabled by default. Turning persistence off would produce a different and usually much easier workload, so the setting is included in every report.&lt;/p&gt;

&lt;p&gt;The benchmark application secret is read only from an environment variable. It is not placed in process arguments or written into result files.&lt;/p&gt;

&lt;h2&gt;
  
  
  Percentiles are more useful than one average
&lt;/h2&gt;

&lt;p&gt;An average can look healthy while a meaningful fraction of users experience long delays. The report includes minimum, mean, P50, P95, P99, and maximum latency for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;connection establishment;&lt;/li&gt;
&lt;li&gt;successful publish acknowledgements;&lt;/li&gt;
&lt;li&gt;observed deliveries.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Failures are counted by error code instead of being silently discarded or folded into successful latency.&lt;/p&gt;

&lt;p&gt;The JSON report also records the workload and environment:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;server commit and dirty-tree state;&lt;/li&gt;
&lt;li&gt;client count and group sender count;&lt;/li&gt;
&lt;li&gt;target rate, warm-up, duration, and delivery grace;&lt;/li&gt;
&lt;li&gt;payload size and persistence flags;&lt;/li&gt;
&lt;li&gt;OS, architecture, Go version, CPU model, logical CPU count, and memory;&lt;/li&gt;
&lt;li&gt;database and deployment labels.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without that context, two benchmark files are not meaningfully comparable.&lt;/p&gt;

&lt;h2&gt;
  
  
  A verified smoke run
&lt;/h2&gt;

&lt;p&gt;We checked the full workflow against the repository's local Docker Compose stack. The server and load generator shared one Apple M2 host, the measurement lasted only ten seconds, and the working tree contained the new harness.&lt;/p&gt;

&lt;p&gt;That makes this a functional smoke baseline, &lt;strong&gt;not a production capacity claim&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The workload used 20 connected clients, stored and counted 256-byte messages, a three-second warm-up, a ten-second measurement, and a target of 50 publishes per second.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Connections&lt;/th&gt;
&lt;th&gt;Successful ACKs&lt;/th&gt;
&lt;th&gt;ACK P95 / P99&lt;/th&gt;
&lt;th&gt;Observed deliveries&lt;/th&gt;
&lt;th&gt;Delivery P95 / P99&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Private ring&lt;/td&gt;
&lt;td&gt;20/20&lt;/td&gt;
&lt;td&gt;499/499&lt;/td&gt;
&lt;td&gt;2.37 / 17.09 ms&lt;/td&gt;
&lt;td&gt;499&lt;/td&gt;
&lt;td&gt;8.47 / 36.75 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;20-member group, 2 senders&lt;/td&gt;
&lt;td&gt;20/20&lt;/td&gt;
&lt;td&gt;499/499&lt;/td&gt;
&lt;td&gt;4.54 / 30.19 ms&lt;/td&gt;
&lt;td&gt;9,492&lt;/td&gt;
&lt;td&gt;35.29 / 115.88 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The group workload makes the distinction visible: 499 accepted publishes created 9,492 observed client deliveries. Saying the system handled either “49.9 messages per second” or “949.2 messages per second” without naming the metric would be misleading.&lt;/p&gt;

&lt;p&gt;The full &lt;a href="https://github.com/juggleim/im-server/blob/master/docs/benchmarks/results/local-smoke-20260720-private.json?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=juggleim_benchmark" rel="noopener noreferrer"&gt;private-chat JSON&lt;/a&gt; and &lt;a href="https://github.com/juggleim/im-server/blob/master/docs/benchmarks/results/local-smoke-20260720-group.json?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=juggleim_benchmark" rel="noopener noreferrer"&gt;group-chat JSON&lt;/a&gt; are public.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run both workloads locally
&lt;/h2&gt;

&lt;p&gt;The one-command runner requires Docker Compose, Go, &lt;code&gt;curl&lt;/code&gt;, &lt;code&gt;jq&lt;/code&gt;, &lt;code&gt;openssl&lt;/code&gt;, and &lt;code&gt;shasum&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/juggleim/im-server.git
&lt;span class="nb"&gt;cd &lt;/span&gt;im-server
scripts/run-benchmark.sh all
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It builds and starts the isolated local stack, creates a benchmark application, registers synthetic users, executes both workloads, and writes JSON reports under &lt;code&gt;benchmark-results/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The defaults are intentionally modest. Override them with environment variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;BENCH_CLIENTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;500 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;BENCH_RATE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1000 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;BENCH_WARMUP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;30s &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;BENCH_DURATION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2m &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;BENCH_PAYLOAD_BYTES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1024 &lt;span class="se"&gt;\&lt;/span&gt;
scripts/run-benchmark.sh all
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The CLI rejects non-loopback targets by default. A separate &lt;code&gt;--allow-non-loopback&lt;/code&gt; flag exists for dedicated benchmark environments, but the runner should never be pointed at shared staging or production infrastructure.&lt;/p&gt;

&lt;p&gt;You can inspect the &lt;a href="https://github.com/juggleim/im-server/blob/master/BENCHMARKS.md?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=juggleim_benchmark" rel="noopener noreferrer"&gt;complete benchmark methodology and CLI reference&lt;/a&gt; before running it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a serious capacity test still needs
&lt;/h2&gt;

&lt;p&gt;A reproducible harness is necessary, but it is not sufficient for a defensible capacity claim.&lt;/p&gt;

&lt;p&gt;For a publishable performance study, we would additionally:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run the server, database, and load generator on separate named hosts.&lt;/li&gt;
&lt;li&gt;Use a clean server commit and preserve every result file.&lt;/li&gt;
&lt;li&gt;Record container limits, MySQL configuration, storage type, network topology, and TLS status.&lt;/li&gt;
&lt;li&gt;Run multiple trials after a longer warm-up and publish all trials, not only the best one.&lt;/li&gt;
&lt;li&gt;Collect server CPU, memory, network, disk, database, and Go profile data.&lt;/li&gt;
&lt;li&gt;Increase load until errors or latency show the actual saturation point.&lt;/li&gt;
&lt;li&gt;Test private chat and multiple group sizes independently.&lt;/li&gt;
&lt;li&gt;Compare systems only when persistence, acknowledgement, payload, fan-out, and delivery semantics are equivalent.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Large numbers without these controls are demonstrations, not benchmarks.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we learned
&lt;/h2&gt;

&lt;p&gt;The most important part of the harness was not the percentile function or the rate limiter. It was deciding what each counter means.&lt;/p&gt;

&lt;p&gt;For messaging systems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;connections are not messages;&lt;/li&gt;
&lt;li&gt;attempted publishes are not acknowledged publishes;&lt;/li&gt;
&lt;li&gt;acknowledged publishes are not recipient deliveries;&lt;/li&gt;
&lt;li&gt;group deliveries are not sender throughput;&lt;/li&gt;
&lt;li&gt;local smoke results are not production capacity.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once those boundaries are explicit, optimization becomes easier too. A high connection P99, high ACK P99, and high group-delivery P99 point to different parts of the system.&lt;/p&gt;

&lt;p&gt;The harness is open source in the &lt;a href="https://github.com/juggleim/im-server?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=juggleim_benchmark" rel="noopener noreferrer"&gt;JuggleIM repository&lt;/a&gt;. If you work on real-time systems, feedback on the workload model, timing semantics, and missing scenarios is especially welcome.&lt;/p&gt;

</description>
      <category>go</category>
      <category>performance</category>
      <category>websocket</category>
      <category>opensource</category>
    </item>
    <item>
      <title>JuggleIM: An Open-Source, Self-Hosted Messaging Backend Built in Go</title>
      <dc:creator>yuwnloyblog</dc:creator>
      <pubDate>Thu, 16 Jul 2026 06:05:11 +0000</pubDate>
      <link>https://dev.to/yuwnloyblog/juggleim-an-open-source-self-hosted-messaging-backend-built-in-go-43nh</link>
      <guid>https://dev.to/yuwnloyblog/juggleim-an-open-source-self-hosted-messaging-backend-built-in-go-43nh</guid>
      <description>&lt;p&gt;Real-time messaging looks simple until you try to build it.&lt;/p&gt;

&lt;p&gt;A prototype can send a message over a WebSocket in an afternoon. A production messaging system must also handle reconnects, acknowledgements, duplicate messages, offline users, conversation state, unread counts, groups, message history, push notifications, multiple devices, moderation, storage, and operational visibility.&lt;/p&gt;

&lt;p&gt;That is a large amount of infrastructure to build before chat becomes useful to your actual product.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/juggleim/im-server?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=juggleim_oss_launch" rel="noopener noreferrer"&gt;JuggleIM&lt;/a&gt; is an open-source, self-hosted messaging backend designed to provide that foundation. It is written in Go, uses Protobuf over WebSocket for client connections, exposes REST APIs for business services, and includes SDKs for major client platforms.&lt;/p&gt;

&lt;p&gt;At the time of writing, the project has earned more than 3,500 GitHub stars and 360 forks. More importantly, it can be started locally with one Docker Compose command.&lt;/p&gt;

&lt;h2&gt;
  
  
  What can you build with it?
&lt;/h2&gt;

&lt;p&gt;JuggleIM is not a complete social product or customer-support application. It is the messaging infrastructure underneath those products.&lt;/p&gt;

&lt;p&gt;Typical use cases include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Private and group chat inside a mobile or web application&lt;/li&gt;
&lt;li&gt;Customer-service messaging&lt;/li&gt;
&lt;li&gt;Marketplace conversations between buyers and sellers&lt;/li&gt;
&lt;li&gt;Live-stream and community chatrooms&lt;/li&gt;
&lt;li&gt;Internal collaboration tools&lt;/li&gt;
&lt;li&gt;Device-to-cloud messaging for IoT products&lt;/li&gt;
&lt;li&gt;Real-time conversations with AI assistants and bots&lt;/li&gt;
&lt;li&gt;Multi-tenant communication features for SaaS products&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The server provides the messaging layer while your business backend continues to own product-specific concepts such as accounts, permissions, subscriptions, orders, or CRM records.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why not build it from scratch?
&lt;/h2&gt;

&lt;p&gt;Sending bytes is only the beginning. A useful messaging system needs answers to questions such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What happens when a client reconnects after losing its network?&lt;/li&gt;
&lt;li&gt;How do you prevent a retried publication from creating duplicate messages?&lt;/li&gt;
&lt;li&gt;How are messages synchronized across a user's phone, browser, and desktop client?&lt;/li&gt;
&lt;li&gt;How do you maintain unread counts and conversation ordering?&lt;/li&gt;
&lt;li&gt;When should an offline push notification be sent?&lt;/li&gt;
&lt;li&gt;How do group membership, mute settings, blocks, and moderation affect delivery?&lt;/li&gt;
&lt;li&gt;Where do message history and delivery state live?&lt;/li&gt;
&lt;li&gt;How does one deployment isolate multiple applications or tenants?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;JuggleIM already models these concerns. The server includes message IDs and sequence numbers, QoS-aware acknowledgements, duplicate-publication filtering, sendbox and history paths, conversation state, group membership, online presence, push routing, and tenant-scoped credentials.&lt;/p&gt;

&lt;p&gt;This lets a team spend more time on its product experience and less time rebuilding messaging plumbing.&lt;/p&gt;

&lt;h2&gt;
  
  
  A modular Go architecture
&lt;/h2&gt;

&lt;p&gt;The open-source server runs as a single Go process with clear internal module boundaries.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Feb680rq7gd2m9gyxc6q8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Feb680rq7gd2m9gyxc6q8.png" alt="JuggleIM system architecture" width="799" height="377"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are four primary entry points:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Entry point&lt;/th&gt;
&lt;th&gt;Default port&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Server API Gateway&lt;/td&gt;
&lt;td&gt;&lt;code&gt;9001&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;REST APIs used by your trusted business backend&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Navigator&lt;/td&gt;
&lt;td&gt;&lt;code&gt;9002&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Validates client tokens and returns the WebSocket endpoint&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Connect Manager&lt;/td&gt;
&lt;td&gt;&lt;code&gt;9003&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Maintains Protobuf-over-WebSocket client connections&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Admin Gateway&lt;/td&gt;
&lt;td&gt;&lt;code&gt;8090&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Serves the administration console and APIs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Requests from these gateways enter an internal actor and RPC runtime. Domain modules then handle messaging, users, friends, presence, conversations, groups, history, push, file storage, subscriptions, bots, moderation, and RTC room signaling.&lt;/p&gt;

&lt;p&gt;Actor methods are routed by a stable target ID such as a user, group, or conversation. The runtime supports synchronous queries, asynchronous commands, grouped routing, and broadcast. This keeps service boundaries explicit without requiring the operational overhead of a distributed microservice deployment for the community edition.&lt;/p&gt;

&lt;p&gt;You can read the full &lt;a href="https://github.com/juggleim/im-server/blob/master/docs/architecture.md?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=juggleim_oss_launch" rel="noopener noreferrer"&gt;English architecture guide&lt;/a&gt; or its &lt;a href="https://github.com/juggleim/im-server/blob/master/docs/architecture_zh.md?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=juggleim_oss_launch" rel="noopener noreferrer"&gt;Chinese version&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Storage without unnecessary complexity
&lt;/h2&gt;

&lt;p&gt;The default deployment requires MySQL 8. It stores application configuration and the core domain data needed by the messaging system.&lt;/p&gt;

&lt;p&gt;MongoDB is optional. When configured as the message storage engine, it provides alternative collections for message, history, and push workloads. A local LevelDB-backed KV store is also available for internal timestamp-ordered data.&lt;/p&gt;

&lt;p&gt;Attachments can integrate with S3-compatible storage, MinIO, Alibaba Cloud OSS, or Qiniu. Offline notifications can be delivered through APNs, FCM, and supported Android vendor channels.&lt;/p&gt;

&lt;p&gt;The result is a small starting topology that can grow with the product instead of requiring a long list of infrastructure dependencies on day one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start it locally
&lt;/h2&gt;

&lt;p&gt;The fastest way to try JuggleIM is Docker Compose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/juggleim/im-server.git
&lt;span class="nb"&gt;cd &lt;/span&gt;im-server
docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the containers become healthy, open the admin console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://127.0.0.1:8090
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The development credentials are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;username: admin
password: 123456
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These credentials are intended only for local development and must be changed before a production deployment.&lt;/p&gt;

&lt;p&gt;Create the first tenant from the admin API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; http://127.0.0.1:8090/admingateway/apps/create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s1"&gt;'{"app_key":"my-app","app_name":"My App"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response contains an &lt;code&gt;app_key&lt;/code&gt; and &lt;code&gt;app_secret&lt;/code&gt;. The secret belongs only on your trusted business backend; it must never be embedded in a client application.&lt;/p&gt;

&lt;p&gt;From there, the business backend can use the server API on port &lt;code&gt;9001&lt;/code&gt;, while client SDKs discover and connect to the WebSocket service on port &lt;code&gt;9003&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The complete setup instructions are available in the &lt;a href="https://www.juggle.im/docs/guide/deploy/quickdeploy/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=juggleim_oss_launch" rel="noopener noreferrer"&gt;deployment guide&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  More than one client platform
&lt;/h2&gt;

&lt;p&gt;A messaging backend is only useful if clients can integrate with it. The JuggleIM organization provides SDKs and demo applications across the ecosystem, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Android&lt;/li&gt;
&lt;li&gt;iOS&lt;/li&gt;
&lt;li&gt;Web&lt;/li&gt;
&lt;li&gt;React Native&lt;/li&gt;
&lt;li&gt;Flutter&lt;/li&gt;
&lt;li&gt;HarmonyOS&lt;/li&gt;
&lt;li&gt;Desktop applications&lt;/li&gt;
&lt;li&gt;Go and Java server-side SDKs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are also demo business services and web clients that show how authentication, users, friends, groups, and application-specific workflows can sit above the core IM server.&lt;/p&gt;

&lt;p&gt;Browse the complete ecosystem from the &lt;a href="https://github.com/juggleim?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=juggleim_oss_launch" rel="noopener noreferrer"&gt;JuggleIM GitHub organization&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Multi-tenancy is part of the design
&lt;/h2&gt;

&lt;p&gt;JuggleIM carries an &lt;code&gt;app_key&lt;/code&gt; through API requests, internal RPC calls, storage operations, and message delivery. This allows one deployment to host multiple isolated applications.&lt;/p&gt;

&lt;p&gt;That is useful for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SaaS products that provision a separate messaging space per customer&lt;/li&gt;
&lt;li&gt;Teams running staging and production applications on shared infrastructure&lt;/li&gt;
&lt;li&gt;Platforms operating several brands or regional applications&lt;/li&gt;
&lt;li&gt;Developers building a reusable communication service for multiple products&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tenant isolation is not something added as an afterthought; it is part of the request and routing context.&lt;/p&gt;

&lt;h2&gt;
  
  
  A transparent community-edition boundary
&lt;/h2&gt;

&lt;p&gt;The open-source repository implements a modular single-node server. Its internal package is named &lt;code&gt;gmicro.Cluster&lt;/code&gt;, but the community implementation routes work to the current node.&lt;/p&gt;

&lt;p&gt;Multi-node discovery, cross-node routing, failover, horizontal scaling, and commercial support belong to the professional offering. We prefer to make this boundary explicit rather than imply that the community repository provides behavior it does not contain.&lt;/p&gt;

&lt;p&gt;For many product teams, a self-hosted single-node deployment is an effective way to develop, validate, and operate an initial messaging workload. Teams that later need a distributed topology can evaluate the professional edition against their measured requirements.&lt;/p&gt;

&lt;h2&gt;
  
  
  Security starts with clear boundaries
&lt;/h2&gt;

&lt;p&gt;JuggleIM uses tenant-scoped credentials and token-based client authentication. In a production environment, HTTP and WebSocket traffic should be protected with HTTPS and WSS through a trusted TLS termination layer.&lt;/p&gt;

&lt;p&gt;Operators should also restrict the admin console, diagnostics endpoint, database ports, log uploads, and storage credentials. Default development credentials should never remain enabled in production.&lt;/p&gt;

&lt;p&gt;The project does not claim automatic end-to-end encryption between chat participants. If your product requires application-layer content encryption, evaluate it separately as part of the client and key-management design.&lt;/p&gt;

&lt;p&gt;Clear security claims are more valuable than broad promises.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the project is going
&lt;/h2&gt;

&lt;p&gt;The project is actively improving its developer experience and technical transparency. Current community work includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reproducible performance benchmarks&lt;/li&gt;
&lt;li&gt;Broader CI and automated security checks&lt;/li&gt;
&lt;li&gt;More complete configuration references&lt;/li&gt;
&lt;li&gt;End-to-end API examples&lt;/li&gt;
&lt;li&gt;Docker Compose troubleshooting documentation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These tasks are tracked publicly in &lt;a href="https://github.com/juggleim/im-server/issues?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=juggleim_oss_launch" rel="noopener noreferrer"&gt;GitHub Issues&lt;/a&gt;, including issues labeled &lt;a href="https://github.com/juggleim/im-server/labels/good%20first%20issue?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=juggleim_oss_launch" rel="noopener noreferrer"&gt;&lt;code&gt;good first issue&lt;/code&gt;&lt;/a&gt; for new contributors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it, inspect it, and tell us what is missing
&lt;/h2&gt;

&lt;p&gt;If you are adding messaging to a product, JuggleIM gives you a practical starting point that you can run, inspect, and self-host.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/juggleim/im-server?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=juggleim_oss_launch" rel="noopener noreferrer"&gt;Star or fork JuggleIM on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.juggle.im/docs/guide/intro/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=juggleim_oss_launch" rel="noopener noreferrer"&gt;Read the documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.juggle.im/docs/guide/deploy/quickdeploy/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=juggleim_oss_launch" rel="noopener noreferrer"&gt;Follow the quick deployment guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/juggleim/im-server/blob/master/docs/architecture.md?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=juggleim_oss_launch" rel="noopener noreferrer"&gt;Inspect the architecture&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/juggleim/im-server/discussions?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=juggleim_oss_launch" rel="noopener noreferrer"&gt;Ask a question or share an idea&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the project solves a problem for you, a GitHub star helps other developers discover it. If it does not yet fit your use case, open a discussion and describe what you are building. Concrete feedback is how open-source infrastructure becomes more useful.&lt;/p&gt;

</description>
      <category>go</category>
      <category>opensource</category>
      <category>websocket</category>
      <category>selfhosted</category>
    </item>
  </channel>
</rss>
