
vgi-rpc C++¶
C++ implementation of the vgi_rpc framework — Apache Arrow IPC-based RPC for high-performance data services.
Built by Query.Farm
Define RPC methods with typed C++20 handlers using Arrow schemas. The framework provides server dispatch with automatic parameter extraction and result serialization over four transports: stdin/stdout pipes, Unix domain sockets, TCP, and HTTP.
Key Features¶
- Unary RPCs with typed parameter extraction via
get<T>(name) - Producer streams for server-initiated batch data flows
- Exchange streams for bidirectional batch processing
- Client-directed logging at configurable levels
- Introspection via optional
__describe__method - Error handling — exceptions automatically converted to protocol error responses
- Builder pattern — fluent
ServerBuilderAPI for registering methods - Access log — JSONL records per call, with the spec's field-shedding size cap
Transports¶
| Transport | Entry point | Discovery line |
|---|---|---|
| Pipe (stdin/stdout) | Server::run() |
— |
| Unix domain socket | Server::serve_unix(path) |
UNIX:<path> |
| TCP (trusted networks; no auth or TLS) | Server::serve_tcp(host, port) |
TCP:<host>:<port> |
| HTTP | Server::serve_http(HttpConfig) |
PORT:<port> |
Pipe, Unix, and TCP share the same raw Arrow IPC framing and differ only in the socket they read and write. HTTP maps the same protocol onto stateless request/response pairs and carries the optional features below.
Shared memory¶
A side channel, not a transport of its own: it rides alongside a pipe or
socket, which still carries control messages and small batches while a large
batch is written into a POSIX segment and replaced on the wire by a zero-row
pointer batch. Enable it with ServerBuilder::enable_transport_options(),
which answers the __transport_options__ handshake — a worker that stays
silent is read as "no shared memory" and peers stay inline.
Batches below VGI_RPC_SHM_MIN_BATCH_BYTES (128 KiB by default) stay inline,
because the fixed cost of an allocation, a pointer round trip, and the peer's
resolve and free only pays off once the copy it avoids is large enough.
HTTP features¶
All off by default — a server built with HttpConfig{} is byte-identical on
the wire to one built before any of them existed.
| Feature | Configuration |
|---|---|
Capability discovery (GET/OPTIONS {prefix}/health) |
always on |
| Response caps, strict-fail | max_response_bytes, max_externalized_response_bytes |
| External locations (pointer batches) | external_storage_url, externalize_threshold |
| Response codec negotiation (zstd) | compression |
CORS, including Cross-Origin-Resource-Policy |
cors_origin |
| Sticky sessions (AEAD-sealed tokens, TTL, drain) | sticky, sticky_default_ttl, sticky_echo_headers |
Standardized 401s with VGI-Auth-Reason |
reject_all |
| Proxy proof (HMAC-SHA256 proof-of-hop) | proof_mode, proof_origin_id, proof_secrets |
| Token introspection | token_introspection |
Stream state travels as two tokens split by lifetime — a call token minted
once by /init and a cursor re-minted every turn — so a continuation does not
re-serialize the fixed half of the call.
External storage¶
A batch over externalize_threshold is uploaded and replaced on the wire by a
zero-row pointer batch carrying a URL the client re-fetches. external_storage_url
picks the backend by scheme:
| Scheme | Backend | Build |
|---|---|---|
http(s):// |
A service speaking the four-endpoint alloc/PUT/HEAD/GET contract | always |
s3://bucket/prefix |
AWS S3 via aws-sdk-cpp |
-DVGI_RPC_WITH_S3=ON |
gs://bucket/prefix |
Google Cloud Storage via google-cloud-cpp |
-DVGI_RPC_WITH_GCS=ON |
The cloud backends are off by default — both SDKs are long builds, and a deployment that externalises through its own HTTPS service needs neither. Turn them on together with the matching vcpkg manifest features:
cmake --preset default \
-DVCPKG_MANIFEST_FEATURES="s3;gcs" \
-DVGI_RPC_WITH_S3=ON -DVGI_RPC_WITH_GCS=ON
A URI naming a backend the binary was not built with is refused at startup, not on the first payload large enough to externalise.
What a pointer batch carries is always a pre-signed HTTPS URL, never a
bucket path, so the client fetches it holding no cloud credentials and linking
no SDK. signed_url_ttl_seconds bounds how long a leaked pointer stays usable.
Uploaded objects are never deleted — set a lifecycle rule on the bucket.
Three Method Types¶
Unary¶
A single request produces a single response. The client sends parameters, the server returns a result.
Producer¶
The server pushes batches to the client until calling out.finish():
Client ── produce_n(count=3) ──▸ Server
Client ◂── {index: [0]} ── Server
Client ◂── {index: [1]} ── Server
Client ◂── {index: [2]} ── Server
Client ◂── [finish] ── Server
Exchange¶
Lockstep bidirectional streaming — one request, one response, repeat:
Client ── exchange_scale(factor=2.5) ──▸ Server
Client ── {value: [10.0]} ──▸ Server
Client ◂── {value: [25.0]} ── Server
Client ── {value: [4.0]} ──▸ Server
Client ◂── {value: [10.0]} ── Server
Client ── [close] ──▸ Server
Quick Example¶
// © Copyright 2025-2026, Query.Farm LLC - https://query.farm
// SPDX-License-Identifier: Apache-2.0
#include <vgi_rpc/server.h>
#include <vgi_rpc/request.h>
#include <vgi_rpc/result.h>
#include <vgi_rpc/arrow_utils.h>
#include <arrow/array/builder_primitive.h>
#include <arrow/type.h>
int main() {
auto server =
vgi_rpc::ServerBuilder()
.add_unary(
"add",
arrow::schema({
arrow::field("a", arrow::float64()),
arrow::field("b", arrow::float64()),
}),
arrow::schema({arrow::field("result", arrow::float64())}),
[](const vgi_rpc::Request& req, vgi_rpc::CallContext& /*ctx*/) {
double a = req.get<double>("a");
double b = req.get<double>("b");
arrow::DoubleBuilder builder;
VGI_RPC_THROW_NOT_OK(builder.Append(a + b));
auto array = vgi_rpc::unwrap(builder.Finish());
return vgi_rpc::Result::value(
arrow::schema({arrow::field("result", arrow::float64())}), {array});
},
"Add two numbers together.")
.enable_describe("MyServer")
.build();
server->run();
}
Next Steps¶
- Read the Getting Started guide for build instructions and setup
- Browse the Examples for hello world, calculator, and streaming
- Check out the API Reference for all classes and functions
- Learn about the wire protocol and benchmarks on the main vgi-rpc site
- See all language implementations — Python, Go, TypeScript, C++