Skip to main content

High-performance bidirectional RPC over TCP with MessagePack framing — Go, Python, Rust interop

Project description

Callwire v2

A high-performance, bidirectional RPC framework for Go, Python, and Rust over TCP with MessagePack framing. Both sides of any connection can act as a client and server simultaneously.

[!IMPORTANT] Language-Agnostic Design: Callwire is designed around a simple, clean, and fully-specified wire protocol. You can implement clients and servers in any language by following the SPEC.md.

Features

  • Multi-Language: First-class support for Go, Python, and Rust.
  • Bidirectional: Connection-symmetric. Clients can serve endpoints and servers can invoke client functions over the same socket.
  • TLS & mTLS: Secure transport with server authentication and optional Mutual TLS (mTLS).
  • Service Discovery: Built-in lightweight registry server and auto-refreshing client DiscoverPool.
  • Dynamic Reconnections: Auto-reconnect with exponential backoff on connection drops.
  • Batch API: Concurrent RPC multiplexing on a single connection.

Quick Start

Go

import "github.com/emaad/callwire"

// 1. Export local function
callwire.Export("add", func(a, b int) int { return a + b })

// 2. Call remote function using client
client, _ := callwire.Connect("localhost:9090")
addFunc := callwire.RefWithClient[int](client, "add")
result, _ := addFunc(10, 20) // 30

Python

from callwire import export, serve, Client

# 1. Export local function
@export
def add(a, b):
    return a + b

# 2. Call remote function
client = Client()
client.connect("localhost", 9090)
result = client.call("add", [10, 20]) # 30

Rust

use callwire::{Client, register_unary, serve_on};

// 1. Export local function
register_unary("add", |(a, b): (i64, i64)| -> Result<i64, String> {
    Ok(a + b)
});

// 2. Call remote function
let client = Client::connect("127.0.0.1:9090").await.unwrap();
let result: i64 = client.import("add", &(10i64, 20i64)).await.unwrap(); // 30

TLS & Mutual TLS (mTLS)

Callwire v2 supports standard TLS and client certificate verification (mTLS).

Go TLS Server & Client

// Server
cfg := callwire.TLSConfig{CertPem: cert, KeyPem: key}
callwire.ServeWithTLS("0.0.0.0:9090", cfg)

// Client
clientCfg := callwire.TLSConfig{CAPem: caCert}
client, _ := callwire.ConnectWithReconnectTLS("localhost:9090", clientCfg)

Python TLS Client

client = Client()
client.connect("localhost", 9090, tls={
    "cafile": "ca.pem",
    "certfile": "client.pem", # for mTLS
    "keyfile": "client.key"    # for mTLS
})

Rust TLS Client

let client_cfg = callwire::TlsConfig {
    cert_pem: vec![],
    key_pem: vec![],
    ca_pem: Some(ca_pem), // Server CA cert
};
let client = client_cfg.connect("127.0.0.1:9090").await.unwrap();

Service Discovery

Callwire features a built-in Service Discovery registry (itself powered by Callwire RPC).

Go Registry & Worker Setup

// 1. Start the registry server
callwire.ServeRegistry("127.0.0.1:29090")

// 2. Start a worker and register it
callwire.Export("say_hello", func(name string) string { return "Hello " + name })
go callwire.Serve("127.0.0.1:29091")
callwire.RegisterWith("127.0.0.1:29090", "hello-service", "127.0.0.1:29091")

// 3. Resolve and call using DiscoverPool
pool, _ := callwire.NewDiscoverPool("127.0.0.1:29090", "hello-service")
sayHello := callwire.DiscoverRef[string](pool, "say_hello")
reply, _ := sayHello("World") // "Hello World"

Python DiscoverPool

from callwire import DiscoverPool

pool = DiscoverPool("127.0.0.1:29090", "hello-service")
client = pool.get()
result = client.call("say_hello", ["World"])

Rust DiscoverPool

use callwire::DiscoverPool;

let pool = DiscoverPool::new("127.0.0.1:29090", "hello-service").await.unwrap();
let client = pool.get().unwrap();
let res: String = client.import("say_hello", &("World".to_string(),)).await.unwrap();

Configuration

Env Var Default Description
CALLWIRE_HOST localhost Default hostname for auto-serving & clients
CALLWIRE_PORT 9090 Default port for auto-serving & clients
CALLWIRE_AUTO 1 Set to 0 to disable automatic server launching on Export

Developer Testing

Run the full cross-language integration and unit suites:

# Go tests
cd go/callwire && go test -v ./...

# Python tests
cd python && .venv/bin/python3 -m unittest discover -s . -p "test_*.py"

# Rust tests
cd rust && cargo test -- --nocapture

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

callwire-2.0.0.tar.gz (9.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

callwire-2.0.0-py3-none-any.whl (11.8 kB view details)

Uploaded Python 3

File details

Details for the file callwire-2.0.0.tar.gz.

File metadata

  • Download URL: callwire-2.0.0.tar.gz
  • Upload date:
  • Size: 9.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.2

File hashes

Hashes for callwire-2.0.0.tar.gz
Algorithm Hash digest
SHA256 38aa261a7dfcbe12fb3de1a3722625c9fd8723d823a565917469701324140936
MD5 b15f6d9412c3a0efdbd911014c023be0
BLAKE2b-256 496f2aaf4a0d1358be1efcf4f649f190fffe00edaf6b6607f9a3a5865ecb77be

See more details on using hashes here.

File details

Details for the file callwire-2.0.0-py3-none-any.whl.

File metadata

  • Download URL: callwire-2.0.0-py3-none-any.whl
  • Upload date:
  • Size: 11.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.2

File hashes

Hashes for callwire-2.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 4c01fc699597ff2ce60bcfe719a36844ef626e9c2ee8aa2961ca2abc2cb45b8d
MD5 34e676b6e02a35862c75469e8f232537
BLAKE2b-256 257cd47ae54a95e4aeccfd81f95fe7cf6ab057cd8b6a9278796b3283ce38036c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page