[HN Gopher] Show HN: BinaryRPC - Lightweight WebSocket-based RPC...
       ___________________________________________________________________
        
       Show HN: BinaryRPC - Lightweight WebSocket-based RPC framework in
       modern C++
        
       Hi HN,  I'm a recent CS graduate. During the past few months I
       wrote BinaryRPC, an open-source RPC framework in modern C++20
       focused on low-latency, binary WebSocket messaging.  Why I built it
       * Wanted first-class session support, pluggable QoS levels and a
       simple middleware chain (global, specific, multi handler) without
       extra JSON/XML parsing. * Easy developer experience  A quick
       feature list * Binary WebSocket frames - minimal overhead * Built-
       in session layer (login / reconnect / heartbeat) * QoS1 / QoS2 with
       automatic ACK & retry * Plugin system - rooms, msgpack, etc. can be
       added in one line * Thread-safe core: RAII + folly  Still early
       (solo project), so any feedback on design, concurrency model or
       missing must-have features would help a lot.  Thanks for reading!
       also see "Chat Server in 5 Minutes with BinaryRPC":
       https://medium.com/@efecanerdem0907/building-a-chat-server-i...
        
       Author : efecan0
       Score  : 60 points
       Date   : 2025-07-12 16:32 UTC (6 hours ago)
        
 (HTM) web link (github.com)
 (TXT) w3m dump (github.com)
        
       | jayd16 wrote:
       | My immediate reaction is why websocket based design and TCP (?)
       | over gRPC with http/3 and UDP and multiplexing and such?
        
         | efecan0 wrote:
         | I started with WebSocket over TCP for practical reasons:
         | 
         | * Works everywhere today (browsers, LB, PaaS) with zero extra
         | setup. * One upgrade -> binary frames; no gRPC/proto toolchain
         | or HTTP/3 infra needed. * Simple reliability: TCP handles
         | ordering; I add optional QoS2 on top. * Lets me focus on
         | session/room/middleware features first; transport is swappable
         | later.
         | 
         | QUIC / gRPC-HTTP/3 is on the roadmap once the higher-level API
         | stabilises.
        
         | inetknght wrote:
         | I'm not the author but off the top of my head:
         | 
         | - gRPC is not a library I would trust with safety or privacy.
         | It's used a lot but isn't a great product. I have personally
         | found several fuckups in gRPC and protobuf code resulting in
         | application crashes or risks of remote code execution. Their
         | release tagging is dogshit, their implementation makes you
         | think the standard library and boost libraries are easy to read
         | and understand, and neither takes SDLC lifecycles seriously
         | since there aren't sanitizer builds nor fuzzing regime nor
         | static analysis running against new commits last time I
         | checked.
         | 
         | - http/3 using UDP sends performance into the crater, generally
         | requiring _every_ packet to reach the CPU in userspace instead
         | of being handled in the kernel or even directly by the network
         | interface hardware
         | 
         | - multiplexing isn't needed by most websocket applications
        
           | efecan0 wrote:
           | Thank you for the extra information!
           | 
           | I am a recent CS graduate and I work on this project alone. I
           | chose WebSocket over TCP because it is small, easy to read,
           | and works everywhere without extra tools. gRPC + HTTP/3 is
           | powerful but adds many libraries and more code to learn.
           | 
           | When real users need QUIC or multiplexing, I can change the
           | transport later. Your feedback helps me a lot.
        
             | reactordev wrote:
             | The point people are beating around the bush at here is
             | that a binary RPC framework has no such need for HTTP
             | handling, even for handshaking, when a more terse protocol
             | of your own design would/could/might? be better.
             | 
             | I totally understand your reasoning behind leaning on
             | websockets. You can test with a data channel in a browser
             | app. But if we are talking low-latency, Superman fast,
             | modern C++, RPC and forgeddaboutit. Look into handling an
             | initial payload with credential negotiation outside of HTTP
             | 1.1.
        
               | efecan0 wrote:
               | You're right: HTTP adds an extra RTT and headers we don't
               | strictly need.
               | 
               | My current roadmap is:
               | 
               | 1. Keep WebSocket as the "zero-config / browser-friendly"
               | default. 2. Add a raw-TCP transport with a single-frame
               | handshake: [auth-token | caps] - ACK - binary stream
               | starts. 3. Later, test a QUIC version for mobile / lossy
               | networks.
               | 
               | So users can choose: * plug-and-play (WebSocket) * ultra-
               | low-latency (raw TCP)
               | 
               | Thanks for the nudge this will go on the transport
               | roadmap.
        
           | tgma wrote:
           | > I have personally found several fuckups in gRPC and
           | protobuf code resulting in application crashes or risks of
           | _remote code execution_.
           | 
           | Would be great if you report such remote code executions to
           | the authors/Google. I am sure they handle CVEs etc. There has
           | been a security audit like https://github.com/grpc/grpc/tree/
           | master/doc/grpc_security_a...
           | 
           | > there aren't sanitizer builds nor fuzzing regime nor static
           | analysis running against new commits last time I checked.
           | 
           | Are you making shit up as you go? I randomly picked a
           | recently merged commit and this is the list of test suites
           | ran on the pull request. As far as I recall, this has been
           | the practice for at least 8 years+ (note the MSAN, ASAN, TSAN
           | etc.)
           | 
           | I can see various fuzzers in the code base so that claim is
           | also unsubstantiated https://github.com/grpc/grpc/tree/f5c26a
           | ec2904fddffb70471cbc...                 Android (Internal CI)
           | Kokoro build finished       Basic Tests C Windows Kokoro
           | build finished       Basic Tests C# Linux Kokoro build
           | finished       Basic Tests C# MacOS Kokoro build finished
           | Basic Tests C# Windows Kokoro build finished       Basic
           | Tests C++ iOS Kokoro build finished       Basic Tests C/C++
           | Linux [Build Only] Kokoro build finished       Basic Tests
           | ObjC Examples Kokoro build finished       Basic Tests ObjC
           | iOS Kokoro build finished       Basic Tests PHP Linux Kokoro
           | build finished       Basic Tests PHP MacOS Kokoro build
           | finished       Basic Tests Python Linux Kokoro build finished
           | Basic Tests Python MacOS Kokoro build finished       Bazel
           | Basic Tests for Python (Local) Kokoro build finished
           | Bazel Basic build for C/C++ Kokoro build finished       Bazel
           | C/C++ Opt MacOS Kokoro build finished       Bazel RBE ASAN
           | C/C++ Kokoro build finished       Bazel RBE Build Tests
           | Kokoro build finished       Bazel RBE Debug C/C++ Kokoro
           | build finished       Bazel RBE MSAN C/C++ Kokoro build
           | finished       Bazel RBE Opt C/C++ Kokoro build finished
           | Bazel RBE TSAN C/C++ Kokoro build finished       Bazel RBE
           | Thready-TSAN C/C++ Kokoro build finished       Bazel RBE
           | UBSAN C/C++ Kokoro build finished       Bazel RBE Windows Opt
           | C/C++ Kokoro build finished       Bloat Diff Kokoro build
           | finished       Bloat Difference Bloat Difference       Clang
           | Tidy (internal CI) Kokoro build finished       Distribution
           | Tests C# Linux Kokoro build finished       Distribution Tests
           | C# MacOS Kokoro build finished       Distribution Tests C#
           | Windows Kokoro build finished       Distribution Tests Linux
           | (standalone subset) Kokoro build finished       Distribution
           | Tests PHP Linux Kokoro build finished       Distribution
           | Tests PHP MacOS Kokoro build finished       Distribution
           | Tests Python Linux Arm64 Kokoro build finished
           | Distribution Tests Ruby MacOS Kokoro build finished
           | Distribution Tests Windows (standalone subset) Kokoro build
           | finished       EasyCLA EasyCLA check passed. You are
           | authorized to contribute.       Grpc Examples Tests CPP
           | Kokoro build finished       Memory Difference Memory
           | Difference       Memory Usage Diff Kokoro build finished
           | Mergeable Mergeable Run has been Completed!       Migration
           | Test MacOS Sonoma Kokoro build finished       ObjC Bazel Test
           | Kokoro build finished       Portability Tests Linux [Build
           | Only] (internal CI) Kokoro build finished       Portability
           | Tests Windows [Build Only] (internal CI) Kokoro build
           | finished       Sanity Checks (internal CI) Kokoro build
           | finished       Tooling Tests Python Linux Kokoro build
           | finished       Windows clang-cl with strict warnings [Build
           | Only] Kokoro build finished
        
             | efecan0 wrote:
             | Interesting discussion. My current goal isn't to replace
             | gRPC but to offer a lighter option for simple real-time
             | apps. I'll keep following the thread; the security links
             | are useful, thanks.
        
         | cherryteastain wrote:
         | gRPC's C++ interfaces have horrible design if you want async
         | behaviour. Tons of unsafe and bad practices like the need to
         | call delete this [1]
         | 
         | [1] https://grpc.io/docs/languages/cpp/callback/
        
         | jeffbee wrote:
         | Ironically this library is much closer to what Google uses
         | internally than grpc is.
        
           | efecan0 wrote:
           | Interesting point, thanks!
        
       | efecan0 wrote:
       | Hi everyone, thanks for checking out BinaryRPC!
       | 
       | I built this project because I needed a simple but fast
       | WebSocket-based RPC layer for my own real-time side projects.
       | Existing options felt heavy or JSON-only, so I wrote something
       | binary-focused and plugin-friendly.
       | 
       | I'd really appreciate any feedback on:
       | 
       | * Overall architecture / design smells * Concurrency model
       | (thread-pool vs async IO) * "Must-have" features before this is
       | production-ready
       | 
       | Design notes and a 5-minute chat-server demo are in this short
       | post: https://medium.com/@efecanerdem0907/building-a-chat-
       | server-i...
       | 
       | Any comments, suggestions or PRs are welcome. Thanks again!
        
       | jeffbee wrote:
       | Breezy claims of "exactly once" are a red flag for me. Aside from
       | that I think this framework looks fairly promising.
        
         | efecan0 wrote:
         | Good catch--let me clarify what QoS 2 in BinaryRPC really does.
         | 
         | It follows the MQTT-style 2-step handshake:
         | 
         | 1. Sender - `PUBLISH(id, data)` 2. Receiver - `PUBREC(id)` //
         | stored as "seen but not completed" 3. Sender - `PUBREL(id)` 4.
         | Receiver - `PUBCOMP(id)` // marks id as done, then passes data
         | to the app layer
         | 
         | While an id is in "seen" state the receiver drops duplicates,
         | so the message is delivered to user code exactly once per
         | session even if the socket retries.
         | 
         | If the client reconnects with the same session-key, the server
         | reloads the in-flight id table, so duplicates are still
         | filtered. If the session is lost (no session-key) we fall back
         | to at-least-once because there is no common store.
         | 
         | So: "exactly once within a persisted session; effectively once"
         | as long as the application is idempotent. I'll update the docs
         | to state this more precisely. Thanks for pointing it out!
        
       | denizdoktur wrote:
       | Lightweight, well-designed, and solves a real need. Impressive.
        
         | efecan0 wrote:
         | Thanks!
        
       | sahinemirhan wrote:
       | Very good
        
       | dailker wrote:
       | nice I loved it dude. I hope you get succesful on this.
        
       | MuffinFlavored wrote:
       | > None, AtLeastOnce, ExactlyOnce with retries, ACKs & two-phase
       | commit, plus pluggable back-off strategies & per-session TTL.
       | 
       | Sounds like RabbitMQ/AMQP/similar over WebSocket?
        
         | efecan0 wrote:
         | It looks similar on the surface, but scope and goals are
         | different:
         | 
         | * BinaryRPC = direct request/response calls with optional QoS
         | (per session). - No exchanges/queues, no routing keys. - One
         | logical stream, messages mapped to handlers.
         | 
         | * RabbitMQ / AMQP = full message-broker with persistent queues,
         | fan-out, topic routing, etc.
         | 
         | So you could say BinaryRPC covers the transport/QoS part of
         | AMQP, but stays lightweight and broker-less. If an app later
         | needs full queueing we can still bridge to AMQP, but the core
         | idea here is "RPC first, minimal deps".
        
       | sarpistan wrote:
       | Good job
        
       | sph87 wrote:
       | Modules my guy. The words "modern" and "C++" don't go together
       | while using headers. Also your most basic implementation requires
       | me to write 200+ LOC and add a dozen headers. Then it's a ton of
       | boiler plate code duplication for every function registered.
       | 
       | Basically what I am saying is - you need to place more
       | abstraction between your code and the end-user API.
       | 
       | Take this line:
       | 
       | std::string sayMessage = payload["message"].template
       | get<std::string>();
       | 
       | Why not make a templated getString<"message"> that pulls from
       | payload? So that would instead just be:
       | 
       | auto sayMessage = payload["message"].as_string() or
       | 
       | auto sayMessage = payload.getString<"message">() or
       | 
       | std::string sayMessage = payload["message"] //We infer type from
       | the assignment!!
       | 
       | It's way cleaner. Way more effective. Way more intuitive.
       | 
       | When working on this kind of stuff end-developer experience
       | should always drive the process. Look at your JSON library. Well
       | known and loved. Imagine if instead of:
       | 
       | message["code"] = "JOIN"; it was instead something like:
       | 
       | message.template set<std::string, std::string>("CODE", "JOIN");
       | 
       | Somehow I don't think the latter would have seen any level of
       | meaningful adoption. It's weird, obtuse and overly complex. You
       | need to hide all that.
        
         | efecan0 wrote:
         | Hi.
         | 
         | Thank you for the detailed feedback--this is exactly the kind
         | of input that helps the project grow.
         | 
         | You're right: developer experience needs to be better. Right
         | now there is too much boiler-plate and not enough abstraction.
         | Your example                   std::string msg =
         | payload["message"];  // type inferred
         | 
         | is the direction I want to take. I'll add a thin wrapper so
         | users can write `payload["key"].as_string()` or even rely on
         | assignment type-inference. Refactoring the basic chat demo to
         | be much shorter is now my next task.
         | 
         | About C++20 modules: I agree they are the future. The single-
         | header client was a quick MVP, but module support is on the
         | roadmap as compiler tooling matures.
         | 
         | If you have more DX ideas or want to discuss API design, please
         | open an issue on GitHub I'd be happy to collaborate.
         | 
         | Thanks again for the valuable feedback!
        
           | const_cast wrote:
           | On the topic of modules: a single-header template
           | implementation is still the most practical and quick way to
           | distribute a library. Module support is currently iffy - I
           | wouldn't use them.
        
             | sph87 wrote:
             | I _love_ modules. Honestly. I advocate usage simply as a
             | forcing function for upstream. Tooling support is iffy
             | because usage is low. Usage is low because tooling is iffy.
             | All of the major players in the build space have reasonably
             | mature levels of support though. So it 's one of those
             | things were compilers have outpaced IDE.
        
       ___________________________________________________________________
       (page generated 2025-07-12 23:00 UTC)