SDKs · one descriptor contract

First-class clients in six languages

Every SDK is generated from the same proto descriptor and held to one cross-language conformance contract: identical metadata headers, credential handling, authz-cache semantics, and refresh single-flight behavior. udb sdk generate regenerates all six from the embedded descriptor set.

Recommended path

Workflow helpers first, raw RPCs still available

import os
from udb_client.project import UdbProject

with UdbProject.connect(
    target="127.0.0.1:50051",
    tenant_id="acme",
    project_id="billing",
    bearer_token=os.environ["UDB_TOKEN"],
) as udb:
    pdf = open("invoice.pdf", "rb").read()
    udb.storage.upload_file("invoice.pdf", pdf, {"content_type": "application/pdf"})
    udb.authz.allow_role("reader", "invoice", "data.select")
    rows = udb.data.table("invoice").select(where={"status": "open"})
import (
    "os"
    udb "github.com/fahara02/udb/sdk/go/udbclient"
)

project, _ := udb.Connect(ctx, udb.Config{
    Target:    "127.0.0.1:50051",
    TenantID:  "acme",
    ProjectID: "billing",
    Credentials: udb.Credentials{Bearer: os.Getenv("UDB_TOKEN")},
})
defer project.Close()
pdf, _ := os.ReadFile("invoice.pdf")
_, _ = project.Storage.UploadFile(ctx, "invoice.pdf", pdf, udb.WithContentType("application/pdf"))
invoices := project.Entity("invoice", udb.Key("invoice_id"))
rows, _ := invoices.Select(ctx, map[string]any{"status": "open"})
import { readFile } from "node:fs/promises";
import { UdbProject } from "@udb_plus/sdk";

const udb = await UdbProject.connect({
  target: "127.0.0.1:50051",
  tenantId: "acme", projectId: "billing",
  credentials: { bearerToken: process.env.UDB_TOKEN },
});
const pdf = await readFile("invoice.pdf");
await udb.storage.uploadFile("invoice.pdf", pdf, { contentType: "application/pdf" });
await udb.authz.allowRole("reader", { resource: "invoice", action: "data.select" });
use Fahara02\UdbLaravel\UdbProject;

$udb = UdbProject::connect([
    'target' => '127.0.0.1:50051',
    'tenantId' => 'acme',
    'projectId' => 'billing',
    'credentials' => ['bearerToken' => getenv('UDB_TOKEN')],
]);
$pdf = file_get_contents('invoice.pdf');
$udb->storage()->uploadFile('invoice.pdf', $pdf, ['contentType' => 'application/pdf']);
$udb->authz()->allowRole('reader', [
    'resource' => 'invoice', 'action' => 'data.select',
]);
Responsibility map

The SDK removes ceremony, not broker authority

The simple client is a thin workflow layer over served RPCs. It should make normal app code short, but it must not become a second protocol or move broker invariants into client conventions.

Application

Express intent

Upload a file, upsert/select an entity, grant a role, subscribe to events. App code supplies business inputs, not internal table rows or protobuf plumbing.

SDK

Translate safely

Own workflow helpers, byte transfer, token/session handling, typed errors, read-fence helpers, generated entity APIs, and exact sequence tests.

Broker

Enforce truth

Own tenant/project authority, RLS, object placement, lifecycle state, idempotency, read-after-write guarantees, and fail-closed security decisions.

Descriptor

Generate shape

Descriptors and manifests drive SDK surfaces, operation kind, retry boundaries, entity metadata, native service recipes, and benchmark fixtures.

Advanced deployments: add authTarget/auth_target when the native control-plane listener is separate from the data-plane target. Add explicit scopes, purpose, deadlines, and raw generated RPC calls when building admin tools, benchmarks, or integrations that need exact protocol access.

Common recipes

What app code should reach for first

The simple-client plan changes the docs priority: lead with workflow helpers for common product code, then show the raw generated clients as an escape hatch. These helpers are thin wrappers over served UDB RPCs and each has sequence tests so the SDK cannot hide proof reads or extra authority.

WorkflowUse this surfaceWhat the SDK doesWhat stays broker-owned
Upload bytesstorage.uploadFile / upload_fileRegister upload, PUT to broker-minted URL, finalize.Bucket choice, object key, quota, lifecycle state.
Download filestorage.downloadFilePrefer one presigned URL call; stream bytes only when requested or needed.Access decision, file metadata, object-store authorization.
CRUD recordsdata.table("invoice") / Entity(...)Pack filters and records into the DataBroker request shape, decode native records.RLS, tenant/project binding, backend routing, query execution.
Grant a roleauthz.allowRole / allow_roleEmit one CreatePolicyRule call, no hidden list/get probe.Caller authority, policy validity, audit, effective scopes.
Read after writemetadata.afterWrite(receipt)Convert the write receipt into one read fence for the next read only.Durable visibility, wait semantics, replica safety.
Eventsevents.subscribe(topic).ready()Open the stream and expose an explicit readiness boundary before publish.CDC envelope order, outbox delivery, broker-side matching.
Advanced/admin workgenerated, Raw, service stubsExpose exact RPC access without forcing every app through raw request bodies.Protocol contract, method security, lifecycle rules.
download.ts
// Presigned URL path: one GetDownloadUrl RPC.
const link = await udb.storage.downloadFile(fileId, { expiresInMinutes: 15 });

// Fallback path: one DownloadFile stream, reassembled by the SDK.
const bytes = await udb.storage.downloadFile(fileId, { stream: true });
read-your-write.ts
const write = await udb.data.upsert({
  message_type: "Invoice",
  record_json: Buffer.from(JSON.stringify(invoice)),
  return_record: true,
});
const receipt = udb.metadata.receiptFromResponse(write);
const call = receipt ? udb.metadata.afterWrite(receipt) : undefined;

const rows = await udb.data.table("Invoice").select({
  where: { invoice_id: invoice.invoice_id },
  call,
});
Release status

Generated surface vs package maturity

All six SDKs are generated from the same descriptor and participate in conformance. Publishing maturity differs by ecosystem, so the docs separate generated parity from package availability.

LanguageGenerated surfaceInstall pathPrimary facade
GoDataBroker + native servicesgo get github.com/fahara02/udb/sdk/go@v0.3.6udbclient.Connect
PythonDataBroker + native servicespip install udb-client==0.3.6UdbProject.connect
TypeScriptDataBroker + native servicesnpm i @udb_plus/sdk@0.3.6UdbProject.connect
PHPDataBroker + native servicescomposer require fahara02/udb-laravel:^0.3.6UdbProject::connect
C#Generated client surfacedotnet add package Udb.Client --version 0.3.6generated client + metadata helpers
JavaGenerated client surfacebuild from checkout until Maven Central publishing landsgenerated client + Spring-oriented helpers
One contract, enforced

Conformance, not coincidence

🧾

Identical metadata

The same x-tenant-id, x-purpose, x-correlation-id, scopes, and project headers across all six languages — bearer as authorization, API key as x-api-key.

🔄

Refresh single-flight

Concurrent token refreshes collapse to one RefreshToken RPC, and the refreshed credential is hot-swapped into every outbound channel — verified per language.

CI-gated parity

A cross-language conformance runner asserts metadata, credential, authz-cache TTL, and policy-bundle-signature behavior; missing tooling or a failing suite is a release failure.

Generate, scaffold, ship

udb scaffold emits a starter project; udb sdk generate regenerates all six clients from the descriptor; udb proto export hands you the full proto tree.