gRPC API Overview
All internal service-to-service communication in the Platform-Kernel uses gRPC / Protocol Buffers. The REST endpoints you call from your module code are translated by the API Gateway into gRPC calls directed at the appropriate core service. Module code never calls gRPC directly — it uses the TypeScript SDK or the REST API.
This reference documents the gRPC contracts so that:
- Platform engineers can understand the internal service boundaries.
- Advanced module authors can call services directly from backend sidecars.
- Infrastructure teams can configure mTLS, deadlines, and circuit breakers with accurate RPC names.
Proto Packages — Catalogue
| Proto package | Domain Scope |
|---|---|
platform.iam.v1 | Identity, Access Management, and Tenants |
platform.billing.v1 | Subscriptions, Billing, and Metering |
platform.data.v1 | Unified Data Layer API |
platform.events.v1 | Asynchronous Event Bus schemas |
platform.module_registry.v1 | Module lifecycle and tenant routing |
platform.integration_hub.v1 | Third-party integrations |
platform.common.v1 | Shared types (e.g. Pagination) |
Proto File Locations
All proto files live under .dev/kernel/proto/:
proto/platform/
├── iam/v1/
│ ├── iam_service.proto # Service definitions (3 services, 10 RPCs)
│ └── iam_messages.proto # Request/Response messages
├── billing/v1/
│ ├── billing_service.proto # Service definitions (1 service, 14 RPCs)
│ └── billing_messages.proto # Request/Response messages + enums
├── data/v1/
│ └── data.proto # DataLayerService (5 RPCs)
├── events/v1/
│ ├── events.proto # Event envelope definition
│ └── schemas/v1/
│ ├── auth.proto # Auth domain event schemas
│ ├── common.proto # Shared event fields
│ ├── module.proto # Module domain event schemas
│ └── money.proto # Money domain event schemas
├── module_registry/v1/
│ └── module_registry_service.proto
├── integration_hub/v1/
│ ├── integration_hub_service.proto
│ └── integration_hub_messages.proto
└── common/v1/
└── pagination.proto # PaginationRequest + PaginationMeta
Naming Convention — Proto Packages
| Element | Pattern | Example |
|---|---|---|
| Package | platform.<domain>.v<N> | platform.iam.v1 |
| Service | PascalCase + Service suffix | IamService |
| RPC | PascalCase verb — CRUD: Create, Retrieve, List, Update, Delete | CreateUser, ListPlans |
| Message | <RpcName>Request / <RpcName>Response | CreateUserRequest |
| Enum | SCREAMING_SNAKE_CASE, prefixed with type name | SUBSCRIPTION_STATUS_ACTIVE |
| Field | snake_case | tenant_id, created_at |
| File | snake_case | iam_service.proto, billing_messages.proto |
CRUD verb convention: The platform uses
Retrieve(notGet) for single- resource fetch andListfor paginated collections, following Google API Design Guide semantics. Action endpoints use imperative names:CancelSubscription,HandleWebhook,CheckLimit.
Go Package Convention
Generated Go code is placed in gen/go/ and follows this import path pattern:
kernel.internal/platform-kernel/gen/go/platform/<domain>/v<N>;<domain>v<N>
| Proto package | Go package identifier |
|---|---|
platform.iam.v1 | iamv1 |
platform.billing.v1 | billingv1 |
platform.data.v1 | datav1 |
platform.events.v1 | eventsv1 |
platform.module_registry.v1 | module_registryv1 |
platform.integration_hub.v1 | integration_hubv1 |
platform.common.v1 | commonv1 |
The go_package option is set identically in every .proto file:
option go_package = "kernel.internal/platform-kernel/gen/go/platform/<domain>/v1;<domain>v1";
Example usage in a Go service:
import (
// Import path uses slashes (must match the structure in gen/go)
"kernel.internal/platform-kernel/gen/go/platform/iam/v1"
)
func handle() {
// Package identifier has no slashes (the suffix declared in go_package)
req := &iamv1.CreateUserRequest{
}
}
buf.yaml — Lint and Breaking Change Policy
Located at .dev/kernel/buf.yaml:
version: v2
modules:
- path: proto
lint:
use:
- STANDARD
breaking:
use:
- WIRE_JSON
except:
- FIELD_SAME_ONEOF
| Configuration | Value | Rationale |
|---|---|---|
lint.use | STANDARD | Enforces buf's full standard lint ruleset: naming conventions, comment requirements, import style |
breaking.use | WIRE_JSON | Stricter than FILE — catches field number reuse, type changes, JSON name changes, and removed fields |
breaking.except | FIELD_SAME_ONEOF | Allows adding new optional fields to existing messages; new consumers see defaults, old consumers ignore unknown fields |
Why
WIRE_JSON: Event Bus schemas (platform.events.v1) are consumed by multiple modules simultaneously. Any wire-breaking change would cause data loss (serialization mismatch).WIRE_JSONprevents this at CI time.
buf.gen.yaml — Code Generation Pipeline
Located at .dev/kernel/buf.gen.yaml:
version: v2
managed:
enabled: true
override:
- file_option: go_package_prefix
value: kernel.internal/platform-kernel/gen/go
plugins:
- remote: buf.build/protocolbuffers/go
out: gen/go
opt: paths=source_relative
- remote: buf.build/grpc/go
out: gen/go
opt: paths=source_relative
- remote: buf.build/bufbuild/es:v1.10.0
out: packages/sdk-codegen/src
opt: target=ts
- remote: buf.build/connectrpc/es:v1.4.0
out: packages/sdk-codegen/src
opt: target=ts
- remote: buf.build/bufbuild/validate-go
out: gen/go
opt: paths=source_relative
| Plugin | Output | Purpose |
|---|---|---|
buf.build/protocolbuffers/go | gen/go/ | Go message structs and enums (*.pb.go) |
buf.build/grpc/go | gen/go/ | Go gRPC server interfaces and client stubs (*_grpc.pb.go) |
buf.build/bufbuild/es:v1.10.0 | packages/sdk-codegen/src/ | TypeScript Protobuf-ES message classes |
buf.build/connectrpc/es:v1.4.0 | packages/sdk-codegen/src/ | TypeScript ConnectRPC client (used by Data Layer frontend SDK) |
buf.build/bufbuild/validate-go | gen/go/ | Go field-level validation for event schemas (*.pb.validate.go) |
Run code generation:
# From .dev/kernel/
buf generate
Gateway → Service Protocol
The API Gateway translates every incoming REST request into a gRPC call:
[Browser / Module] ── REST (HTTPS) ──► [Envoy Gateway]
│
JWT validation + rate limit + OpenAPI validation
│
[Go Gateway Service] ── gRPC (mTLS) ──► [Core Service]
│
Response ◄──────────────────────────────
| Layer | Protocol | Authentication |
|---|---|---|
| Client → Envoy | HTTPS (TLS 1.3) | JWT Bearer token |
| Envoy → Go Gateway | Internal (same pod or service mesh) | — |
| Go Gateway → Core Service | gRPC over mTLS | mTLS client certificate |
| Core Service → Core Service | gRPC over mTLS | mTLS client certificate |
Cross-Cutting Concerns
gRPC Deadlines
| Parameter | Value | Environment variable |
|---|---|---|
| Default deadline | 5 seconds | GRPC_CALL_TIMEOUT_MS=5000 |
| Timeout behaviour | Gateway returns 504 Gateway Timeout | — |
| Propagation | Deadline is propagated from Gateway through all service hops | — |
mTLS
All gRPC connections between services use mutual TLS (mTLS) managed by Istio service mesh. Certificates are issued by HashiCorp Vault and rotated every 90 days.
Circuit Breaker
The Go Gateway Service wraps every gRPC call with sony/gobreaker:
| Parameter | Value |
|---|---|
| Failure threshold | 5 consecutive failures within 30 s → circuit OPEN |
| Reset timeout | 30 seconds in OPEN state, then HALF_OPEN |
| Per-tenant isolation | Separate soft breaker per tenant — one tenant cannot trip the circuit for all |
Error Mapping (gRPC → HTTP)
| gRPC status | HTTP status | RFC 9457 type |
|---|---|---|
INVALID_ARGUMENT | 400 Bad Request | problems/validation-failed |
UNAUTHENTICATED | 401 Unauthorized | problems/unauthorized |
PERMISSION_DENIED | 403 Forbidden | problems/forbidden |
NOT_FOUND | 404 Not Found | problems/not-found |
ALREADY_EXISTS | 409 Conflict | problems/conflict |
RESOURCE_EXHAUSTED | 429 Too Many Requests | problems/rate-limit-exceeded |
INTERNAL | 500 Internal Server Error | problems/internal-error |
UNAVAILABLE | 503 Service Unavailable | problems/service-unavailable |
DEADLINE_EXCEEDED | 504 Gateway Timeout | problems/gateway-timeout |
Common Shared Types
PaginationRequest (platform.common.v1)
message PaginationRequest {
int32 limit = 1; // Max items per page (default: 20, max: 100)
string cursor = 2; // Opaque cursor from previous response
}
PaginationMeta (platform.common.v1)
message PaginationMeta {
string cursor = 1; // Cursor for next page
bool has_more = 2; // Whether more pages exist
}
All paginated List* RPCs use these shared types. The cursor is an
opaque, base64-encoded string — callers must not parse or construct it.
Service Reference
| Service file | Phase |
|---|---|
IAM Service — IamService, AuthService, TenantHierarchyService | Batch 34.2 |
Billing Service — BillingService | Batch 34.3 |
Data Layer Service — DataLayerService | Batch 35.1 |
| Events Service — event schemas | Batch 35.2 |
Module Registry Service — ModuleRegistryService | Batch 35.3 |
Integration Hub Service — IntegrationHubService | Batch 35.4 |