Skip to content

ConnectRPC Protocol

ConnectRPC is the primary RPC framework used across all PiVirt inter-service communications, uniting the performance of gRPC with browser-friendly JSON/HTTP protocols.

ConnectRPC provides:

  • HTTP/1.1 and HTTP/2 support
  • JSON and Protobuf encoding
  • Browser compatibility (no gRPC-Web proxy needed)
  • Streaming support
  • Idiomatic Go, TypeScript, and Python clients
Feature gRPC ConnectRPC REST
Transport HTTP/2 HTTP/1.1, HTTP/2 HTTP/1.1, HTTP/2
Encoding Protobuf Protobuf, JSON JSON
Browser Support Requires proxy Native Native
Streaming Yes Yes Limited
Code Generation Required Optional Optional
proto/pivirt/vm/v1/vm.proto
syntax = "proto3";
package pivirt.vm.v1;
import "google/protobuf/timestamp.proto";
option go_package = "go.pilab.hu/cloud/virtpb/pilab/pivirtd/v1;pivirtdv1";
service VMService {
// Create a new VM
rpc CreateVM(CreateVMRequest) returns (VM);
// Get VM by ID
rpc GetVM(GetVMRequest) returns (VM);
// List VMs with filters
rpc ListVMs(ListVMsRequest) returns (stream VM);
// Update VM spec
rpc UpdateVM(UpdateVMRequest) returns (VM);
// Delete VM
rpc DeleteVM(DeleteVMRequest) returns (google.protobuf.Empty);
}
import (
"context"
"go.pilab.hu/cloud/virtpb/pilab/pivirtd/v1"
"connectrpc.com/connect"
)
func main() {
client := vmv1connect.NewVMServiceClient(
http.DefaultClient,
"https://agent.pilab.hu:8443",
)
resp, err := client.GetVM(
context.Background(),
connect.NewRequest(&vmv1.GetVMRequest{
Id: "vm-abc123",
}),
)
if err != nil {
log.Fatal(err)
}
fmt.Printf("VM: %s\n", resp.Msg.Name)
}
import { VMServiceClient } from '@pilab/pivirt';
const client = new VMServiceClient('https://agent.pilab.hu:8443');
const response = await client.getVM({
id: 'vm-abc123',
});
console.log(`VM: ${response.name}`);
from pivirt.vm.v1 import VMServiceClient
client = VMServiceClient('https://agent.pilab.hu:8443')
response = client.get_vm(id='vm-abc123')
print(f"VM: {response.name}")
POST /pivirt.vm.v1.VMService/GetVM HTTP/1.1
Host: agent.pilab.hu:8443
Content-Type: application/json
Connect-Protocol-Version: 1
{
"id": "vm-abc123"
}
HTTP/1.1 200 OK
Content-Type: application/json
Connect-Content-Encoding: identity
{
"id": "vm-abc123",
"name": "web-01",
"status": "VM_STATUS_RUNNING",
"spec": {
"vcpus": 2,
"memory_bytes": 4294967296
}
}
POST /pivirt.vm.v1.VMService/ListVMs HTTP/1.1
Host: agent.pilab.hu:8443
Content-Type: application/json
Connect-Protocol-Version: 1
{
"namespace": "production"
}

Response (server-streaming):

HTTP/1.1 200 OK
Content-Type: application/json
Connect-Protocol-Version: 1
{"id":"vm-001","name":"web-01"}
{"id":"vm-002","name":"web-02"}
{"id":"vm-003","name":"web-03"}
import (
"crypto/tls"
"crypto/x509"
"net/http"
)
func createTLSClient() *http.Client {
cert, err := tls.LoadX509KeyPair("client.crt", "client.key")
if err != nil {
log.Fatal(err)
}
caCert, err := os.ReadFile("ca.crt")
if err != nil {
log.Fatal(err)
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{cert},
RootCAs: caCertPool,
}
return &http.Client{
Transport: &http.Transport{
TLSClientConfig: tlsConfig,
},
}
}
client := vmv1connect.NewVMServiceClient(
createTLSClient(),
"https://agent.pilab.hu:8443",
)
import (
"net/http"
"connectrpc.com/connect"
)
type tokenAuth struct {
token string
}
func (t *tokenAuth) AddCredentials(req *http.Request) {
req.Header.Set("Authorization", "Bearer "+t.token)
}
client := vmv1connect.NewVMServiceClient(
http.DefaultClient,
"https://agent.pilab.hu:8443",
connect.WithInterceptors(&tokenAuth{token: "your-jwt-token"}),
)
HTTP/1.1 404 Not Found
Content-Type: application/json
Connect-Protocol-Version: 1
{
"code": "not_found",
"message": "VM not found",
"details": [
{
"type": "pivirt.ErrorDetail",
"value": {
"resource": "virtualmachine",
"id": "vm-abc123"
}
}
]
}
Code HTTP Status Description
canceled 499 Operation canceled
unknown 500 Unknown error
invalid_argument 400 Invalid argument
deadline_exceeded 504 Deadline exceeded
not_found 404 Resource not found
already_exists 409 Resource already exists
permission_denied 403 Permission denied
resource_exhausted 429 Resource exhausted
failed_precondition 400 Failed precondition
aborted 409 Operation aborted
out_of_range 400 Out of range
unimplemented 501 Unimplemented
internal 500 Internal error
unavailable 503 Service unavailable
data_loss 500 Data loss
unauthenticated 401 Unauthenticated
Operation Latency (p99) Throughput
GetVM 5ms 10,000 req/s
ListVMs 50ms 1,000 req/s
CreateVM 100ms 500 req/s
  1. Use HTTP/2 for multiplexing
  2. Enable compression for large payloads
  3. Use streaming for large datasets
  4. Implement caching for read-heavy workloads