https://github.com/lambertse/ibridger
An IPC messaging and RPC framework that bridges processes across language boundaries. Built for modern apps where a C++ daemon, a Node.js server, and a UI frontend all need to talk — reliably and fast.
https://github.com/lambertse/ibridger
ipc rpc-framework
Last synced: about 2 months ago
JSON representation
An IPC messaging and RPC framework that bridges processes across language boundaries. Built for modern apps where a C++ daemon, a Node.js server, and a UI frontend all need to talk — reliably and fast.
- Host: GitHub
- URL: https://github.com/lambertse/ibridger
- Owner: lambertse
- Created: 2026-03-30T09:07:57.000Z (2 months ago)
- Default Branch: master
- Last Pushed: 2026-03-31T09:43:46.000Z (2 months ago)
- Last Synced: 2026-03-31T10:27:07.242Z (2 months ago)
- Topics: ipc, rpc-framework
- Language: C++
- Homepage:
- Size: 188 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Roadmap: docs/ROADMAP.md
Awesome Lists containing this project
README
# iBridger
[](https://github.com/lambertse/ibridger/actions/workflows/ci.yml)
[](https://www.npmjs.com/package/@lambertse/ibridger)
[](LICENSE)
IPC/RPC framework that bridges processes across language boundaries. A C++ server and a JavaScript client talk to each other — or any future language — using the same wire protocol: **Unix domain sockets + Protocol Buffers**, no shared memory, no native bindings.
```
┌──────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ C++ SDK │ │ JS/TS SDK │ │ Go, Python, … │
│ sdk/cpp/ │ │ sdk/js/ │ │ (your SDK) │
└────────┬─────────┘ └────────┬─────────┘ └───────┬─────────┘
│ │ │
│ Protobuf Envelope over framed IPC │
▼ ▼ ▼
┌──────────────────────────────────────────────────────────────┐
│ Unix Domain Socket / Named Pipe (Windows) │
└──────────────────────────────────────────────────────────────┘
```
## Quick Start
### JavaScript / TypeScript server + client
```bash
npm install @lambertse/ibridger
```
```typescript
// server.ts
import { IBridgerServer, typedMethod } from '@lambertse/ibridger';
import { ibridger } from '@lambertse/ibridger';
const server = new IBridgerServer({ endpoint: '/tmp/my.sock' });
server.register('EchoService', {
Echo: typedMethod(
ibridger.examples.EchoRequest,
ibridger.examples.EchoResponse,
async (req) => ({ message: req.message.toUpperCase() }),
),
});
await server.start();
```
```typescript
// client.ts
import { IBridgerClient } from '@lambertse/ibridger';
import { ibridger } from '@lambertse/ibridger';
const client = new IBridgerClient({ endpoint: '/tmp/my.sock' });
await client.connect();
const resp = await client.call(
'EchoService', 'Echo',
{ message: 'hello' },
ibridger.examples.EchoRequest,
ibridger.examples.EchoResponse,
);
console.log(resp.message); // "HELLO"
client.disconnect();
```
### Pure C++ server + client
**Server** — define a service by subclassing `ServiceBase`, then wire it up with `ServerBuilder`:
```cpp
// my_service.h
#include "ibridger/sdk/service_base.h"
#include "my_service.pb.h" // your protobuf-generated types
class GreetService : public ibridger::sdk::ServiceBase {
public:
GreetService() : ServiceBase("GreetService") {
register_method(
"Hello",
[](const GreetRequest& req) {
GreetResponse resp;
resp.set_message("Hello, " + req.name() + "!");
return resp;
});
}
};
```
```cpp
// server_main.cpp
#include "ibridger/sdk/server_builder.h"
#include "my_service.h"
int main() {
auto server = ibridger::sdk::ServerBuilder()
.set_endpoint("/tmp/my.sock")
.add_service(std::make_shared())
.build();
server->start();
::pause(); // wait for SIGINT
server->stop();
}
```
**Client** — connect with `ClientStub` and call with typed proto messages. Pass a `ReconnectConfig` to auto-reconnect if the server restarts:
```cpp
#include "ibridger/sdk/client_stub.h"
#include "my_service.pb.h"
int main() {
ibridger::rpc::ClientConfig cfg;
cfg.endpoint = "/tmp/my.sock";
// Optional: reconnect automatically if the server restarts.
ibridger::rpc::ReconnectConfig rc;
rc.base_delay = std::chrono::milliseconds(200);
rc.max_delay = std::chrono::milliseconds(10'000);
rc.on_reconnect = [] { std::cout << "reconnected\n"; };
cfg.reconnect = rc;
// Optional: react to unexpected disconnection.
cfg.on_disconnect = [] { std::cerr << "server lost\n"; };
ibridger::sdk::ClientStub stub(cfg);
stub.connect();
GreetRequest req;
req.set_name("world");
auto [resp, err] = stub.call(
"GreetService", "Hello", req);
if (!err) std::cout << resp.message() << "\n"; // "Hello, world!"
}
```
**CMakeLists.txt** for a consumer project:
```cmake
find_package(ibridger CONFIG REQUIRED)
target_link_libraries(my_app PRIVATE ibridger::sdk::cpp)
```
### C++ server + JavaScript client
```bash
# Build C++
cmake -B build -DIBRIDGER_BUILD_EXAMPLES=ON && cmake --build build
# Terminal 1 — C++ echo server
./build/sdk/cpp/echo_server /tmp/ibridger.sock
# Terminal 2 — JS client (from sdk/js/)
npx ts-node examples/echo-client.ts /tmp/ibridger.sock
```
### Health check (built-in Ping, any server)
```typescript
const pong = await client.ping();
console.log(pong.serverId, Number(pong.timestampMs));
```
## Resilient connections
Both SDKs detect server disconnection immediately and expose hooks to react to it.
### JavaScript / TypeScript
```typescript
// Notified when the server dies — isConnected becomes false instantly.
client.onDisconnect = () => console.log('server lost');
// Auto-reconnect: call() blocks with exponential backoff until the server recovers.
const client = new IBridgerClient(
{ endpoint: '/tmp/my.sock' },
{ baseDelayMs: 200, maxDelayMs: 10_000, maxAttempts: Infinity,
onReconnect: () => console.log('reconnected') },
);
```
### C++
```cpp
cfg.on_disconnect = [] { std::cerr << "server lost\n"; };
ibridger::rpc::ReconnectConfig rc;
rc.base_delay = std::chrono::milliseconds(200);
rc.max_delay = std::chrono::milliseconds(10'000);
rc.max_attempts = -1; // -1 = unlimited
rc.on_reconnect = [] { std::cout << "reconnected\n"; };
cfg.reconnect = rc;
```
When `reconnect` is set, `call()` / `client.call()` blocks transparently while the server is down — the caller sees no error as long as the server recovers within the backoff budget. `on_disconnect` / `onDisconnect` fires regardless of whether auto-reconnect is enabled.
> **Retry safety:** only the `send()` path is retried (server never received the request). If `recv()` fails the call returns an error — the server may have already processed it.
## Architecture
Four-layer stack (bottom-up):
| Layer | Responsibility | C++ | JavaScript/TypeScript |
|---|---|---|---|
| **Transport** | Platform IPC | `UnixSocketTransport` | `UnixSocketConnection` |
| **Protocol** | Framing + serialization | `FramedConnection`, `EnvelopeCodec` | `FramedConnection`, `EnvelopeCodec` |
| **RPC** | Dispatch + correlation | `Server`, `Client` | `IBridgerServer`, `IBridgerClient` |
| **SDK** | Ergonomic public API | `ServerBuilder`, `ClientStub` | same as RPC layer |
Wire format: `[4-byte big-endian length][protobuf Envelope]`, max 16 MB per frame. Documented in [`docs/WIRE_PROTOCOL.md`](docs/WIRE_PROTOCOL.md).
## Benchmarks
### Build and run
```bash
cmake -B build -DIBRIDGER_BUILD_BENCHMARKS=ON
cmake --build build --target ibridger_benchmarks
./build/core/benchmarks/ibridger_benchmarks
```
Output JSON for CI storage:
```bash
./build/core/benchmarks/ibridger_benchmarks \
--benchmark_format=json --benchmark_out=benchmark_results.json
```
### Representative results (Apple M-series, macOS, Unix socket, Debug build)
chmark | Payload | Wall time/op | Throughput |
|---|---|---|---|
| PingLatency | — | ~16 μs | ~133 k calls/s |
| EchoLatency | 64 B | ~15 μs | ~130 k calls/s |
| EchoLatency | 1 KB | ~17 μs | ~122 k calls/s |
| EchoLatency | 64 KB | ~140 μs | ~14 k calls/s |
| EchoLatency | 256 KB | ~456 μs | ~4.3 k calls/s |
| SequentialThroughput | 64 B | ~16 μs | ~130 k calls/s |
| ConcurrentThroughput | 64 B, 1 thread | ~16 μs | ~128 k calls/s |
| ConcurrentThroughput | 64 B, 4 threads | ~33 μs | ~60 k calls/s (agg.) |
| ConnectionSetup | — | ~12 μs | ~90 k conn/s |
> Numbers from a Debug build; a Release build (`-DCMAKE_BUILD_TYPE=Release`) will be significantly faster. Run on your own hardware for accurate figures.
## Building from source
### C++ (CMake 3.20+, C++17)
```bash
cmake -B build -DIBRIDGER_BUILD_TESTS=ON -DIBRIDGER_BUILD_EXAMPLES=ON
cmake --build build
cd build && ctest --output-on-failure # all C++ tests
cd build && ctest -R # single test
```
On macOS, `brew install protobuf` is recommended so CMake uses the system library instead of compiling protobuf from source.
### JavaScript SDK (Node.js 18+)
```bash
cd sdk/js
npm install
npm test # all JS tests
npx jest --testPathPattern= # specific test
npm run build # compile TypeScript → dist/
```
### Integration tests (C++ server ↔ JS client)
```bash
cd tests/integration && npm install && npm test
```
Requires the C++ build to be complete (`build/sdk/cpp/echo_server`).
## Project layout
```
proto/ibridger/ Wire protocol .proto definitions
core/ C++ transport / protocol / RPC core (ibridger::core)
sdk/cpp/ C++ SDK wrapper — ServerBuilder, ClientStub
sdk/js/ TypeScript SDK — pure Node.js, no native bindings
tests/integration/ Cross-language integration tests (C++ ↔ JS)
docs/ Wire protocol spec, roadmap, adding-a-language guide
```
## Implementing a new language SDK
Any language that can open a Unix domain socket and encode/decode protobuf messages can implement an SDK — the C++ core is not required. See [`docs/adding-a-language.md`](docs/adding-a-language.md) for the four-component checklist and a Go SDK outline.
## Documentation
| Document | Description |
|---|---|
| [`docs/WIRE_PROTOCOL.md`](docs/WIRE_PROTOCOL.md) | Authoritative wire format spec |
| [`docs/adding-a-language.md`](docs/adding-a-language.md) | Guide for new language SDKs |
| [`docs/ROADMAP.md`](docs/ROADMAP.md) | 25-phase implementation plan |
| [npm package README](sdk/js/README.md) | JS/TS SDK API reference |
## License
MIT