vgi-rpc
Transport-agnostic RPC built on Apache Arrow
Define typed interfaces. No code generation. One protocol across pipes, subprocesses, shared memory, and HTTP.
What is vgi-rpc?
vgi-rpc is an RPC framework where you define services as typed interfaces — the framework automatically derives Apache Arrow schemas from your type annotations and handles all serialization.
There are no .proto files,
no code generation step, and no IDL to learn.
Write a Protocol class in Python (or the equivalent interface in Go, TypeScript, or C++), and you get
a fully typed RPC service.
The same interface works across every transport: in-process pipes, subprocess stdin/stdout, Unix sockets, shared memory (zero-copy, 29 GB/s), and HTTP. Switch transports without changing a line of service code.
vgi-rpc supports unary calls, producer streams (server pushes data), and exchange streams (lockstep bidirectional), with built-in error propagation, logging, introspection, and OpenTelemetry tracing.
from typing import Protocol
class MathService(Protocol):
def add(self, a: float, b: float) -> float: ...
def multiply(self, a: float, b: float) -> float: ...
# Use it over any transport
with connect(MathService, ["python", "worker.py"]) as proxy:
result = proxy.add(a=1.0, b=2.0) # Fully typed! Why vgi-rpc?
Built for developers who want typed RPC without the ceremony.
Zero Code Generation
Define services with native type annotations. No .proto files, no IDL, no compile step. Your Protocol class is the interface.
Transport-Agnostic
Pipe, subprocess, Unix socket, shared memory, HTTP — same service code, just swap the transport. Add custom transports too.
Apache Arrow Native
Columnar serialization with zero-copy potential. Efficient for scalar values and massive DataFrames alike.
Streaming Built-in
Producer streams for server-push data and exchange streams for lockstep bidirectional communication. Headers supported.
Multi-Language
Python (reference), TypeScript, Go, and C++ implementations sharing the same wire protocol. True cross-language RPC.
Runtime Introspection
Built-in __describe__ method returns machine-readable service metadata — method signatures, schemas, and types.
Five Transports, One Interface
Your service code stays the same. Only the transport setup changes.
Pipe
~5 μsIn-process, bidirectional byte stream. Lowest latency — ideal for tests and embedded use.
Testing, demosSubprocess
~50 μsSpawn a worker process, communicate over stdin/stdout. Process isolation with low overhead.
Isolated workers, CLI toolsUnix Socket
~30 μsKernel-mediated local IPC. Long-lived connections with per-client threading support.
Local services, daemonsShared Memory
~5 μsZero-copy Arrow batch transfer via memory-mapped segments. Only pointers cross the pipe.
Large batches, co-locatedHTTP
~500 μsStateless HTTP transport with signed state tokens for streaming. Works with any WSGI server.
Network services, browsersFramework Comparison
How vgi-rpc compares to other RPC frameworks.
| Framework | Serialization | IDL Required | Streaming | Typed Proxies | Transports |
|---|---|---|---|---|---|
| vgi-rpc | Arrow IPC | No (Protocol classes) | Producer + Exchange | Yes (mypy strict) | Pipe, subprocess, Unix socket, shm, HTTP |
| Arrow Flight | Arrow IPC + gRPC | No (fixed API) | get/put/exchange | No (generic client) | gRPC (HTTP/2) |
| gRPC | Protobuf | Yes (.proto) | All 4 patterns | Partial | HTTP/2 |
| Apache Thrift | Thrift binary/compact | Yes (.thrift) | No | No | TCP, HTTP |
| ConnectRPC | Protobuf | Yes (.proto) | All 4 patterns | Yes | HTTP/1.1 + HTTP/2 |
| Cap'n Proto | Cap'n Proto | Yes (.capnp) | Promise pipelining | No | TCP |
| JSON-RPC | JSON | No | No | No | HTTP |
See the detailed comparison for an in-depth analysis of each framework.
Performance
Apache Arrow's columnar format combined with transport flexibility delivers exceptional throughput.
Unary Call Latency (Python)
Lower is better. Measured on Apple M-series, Python 3.12.
Streaming & Throughput
100 MB segment, batch transfer
1K-row Arrow batches
Lockstep bidirectional
Single float64 column
Nested dataclass with lists
Language Implementations
One wire protocol, multiple languages. All implementations interoperate via subprocess transport.
Python
Reference ImplementationThe original implementation with full feature support. Strict mypy typing, all transports, streaming, introspection, shared memory, and OpenTelemetry.
pip install vgi-rpc TypeScript
In DevelopmentTypeScript/Bun implementation with subprocess transport support. Arrow IPC serialization via apache-arrow npm package.
Go
In DevelopmentGo implementation using apache/arrow-go. Subprocess transport with conformance testing against the Python reference.
Capability Matrix
Feature support across all implementations. Updated automatically via CI.
Transports
| Transport | Python | TypeScript | Go | C++ |
|---|---|---|---|---|
| Pipe | ✓ | — | — | ✓ |
| Subprocess | ✓ | ✓ | ✓ | ✓ |
| Unix Socket | ✓ | — | — | — |
| Shared Memory | ✓ | — | — | — |
| HTTP | ✓ | ✓ | ✓ | — |
| Worker Pool | ✓ | — | — | — |
RPC Patterns
| Pattern | Python | TypeScript | Go | C++ |
|---|---|---|---|---|
| Unary | ✓ | ✓ | ✓ | ✓ |
| Unary (void) | ✓ | ✓ | ✓ | ✓ |
| Producer | ✓ | ✓ | ✓ | ✓ |
| Producer + Header | ✓ | ✓ | ✓ | ✓ |
| Exchange | ✓ | ✓ | ✓ | ✓ |
| Exchange + Header | ✓ | ✓ | ✓ | ✓ |
Features
| Feature | Python | TypeScript | Go | C++ |
|---|---|---|---|---|
| Introspection | ✓ | ✓ | ✓ | ✓ |
| Client Logging | ✓ | ✓ | ✓ | ✓ |
| Error Propagation | ✓ | ✓ | ✓ | ✓ |
| Complex Types | ✓ | ✓ | ✓ | ✓ |
| Optional Types | ✓ | ✓ | ✓ | ✓ |
| Dataclass Types | ✓ | ✓ | ✓ | ✓ |
| Annotated Types | ✓ | — | — | ✓ |
| Authentication | ✓ | — | — | — |
| External Storage | ✓ | — | — | — |
| OpenTelemetry | ✓ | — | — | — |
Last updated: February 22, 2026
Get Started
Install in your language and define your first service in minutes.
pip install vgi-rpc bun add vgi-rpc-typescript go get github.com/Query-farm/vgi-rpc-go git clone https://github.com/Query-farm/vgi-rpc-cpp.git from typing import Protocol
from vgi_rpc import connect, run_server
# 1. Define your interface
class Calculator(Protocol):
def add(self, a: float, b: float) -> float: ...
def greet(self, name: str) -> str: ...
# 2. Implement it
class CalculatorImpl:
def add(self, a: float, b: float) -> float:
return a + b
def greet(self, name: str) -> str:
return f"Hello, {name}!"
# 3. Use it (subprocess transport)
with connect(Calculator, ["python", "worker.py"]) as calc:
print(calc.add(a=2.0, b=3.0)) # 5.0
print(calc.greet(name="World")) # Hello, World!