Skip to content
pleach
Operate

Cloud-routed agent

An agent reaching Anthropic, OpenAI, or Google models through AWS Bedrock, Azure OpenAI Service, or Vertex AI — IAM-federated, VPC-bound, region-enforced.

A cloud-routed agent is the shape an enterprise reaches for when direct provider API keys are forbidden by policy and inference has to land inside the customer's VPC. The model family is the same as the direct-API shape — anthropic, openai, google — but the transport is AWS Bedrock, Azure OpenAI Service, or Google Vertex AI. IAM federates the credential; region pinning is enforced by the cloud, not the runtime.

Transport packages not yet published. @pleach/transport-bedrock, @pleach/transport-azure-openai, and @pleach/transport-vertex are marked private in the monorepo and are not on npm yet — the npm install / import lines below will not resolve today. This page documents the planned integration shape; the transport packages publish in a later cut.

This page walks the four pieces a security architect asks about: the transport-vs-family separation, IAM-federated credential resolution, region constraints, and the cost row that ties the cloud invoice back to the runtime.

Related shapes. Region-pinned agent if the same buyer also locks family per call class and runs a parity suite. Multi-tenant SaaS agent if one runtime serves many customers, each reaching their own cloud account. Regulated-domain agent if the regulator drives the constraint and the cloud is the substrate that makes the deployment reachable.

What you're building

A turn loop whose provider boundary is the cloud, not the lab. The shape is the same regardless of which cloud the buyer landed on:

  • Family is the model's identity (anthropic, openai, google); transport is the route to it (bedrock, azure-openai, vertex).
  • The credential is an IAM role, a managed identity, or a workload-identity binding — never a long-lived API key.
  • The endpoint is a VPC endpoint, a PrivateLink, or a private service connect URL. The cloud enforces that the call doesn't leave the perimeter.
  • The cost row carries the cloud, the region, and the model — so the runtime ledger reconciles to the cloud invoice.

Transport vs family

The same family routes through different transports without forcing a different cascade decision. A session asking for anthropic can land on the direct API, Bedrock, or both via failover — the cascade rules don't change.

// lib/runtime.ts
import { SessionRuntime, definePleachPlugin } from "@pleach/core";

export function buildCloudRoutedRuntime(req: AuthedRequest) {
  return new SessionRuntime({
    provider:     bedrockProvider,                 // transport
    storage:      new SupabaseAdapter({ client: supabase }),
    checkpointer: new SupabaseSaver({ client: supabase }),
    plugins:      [definePleachPlugin("cleared-tools", { tools: clearedTools })],
    permittedFamilies: new Set(["anthropic"]),     // family lock
    // Transport ("bedrock") and region ("us-east-1") are pinned at the
    // provider — `bedrockProvider` above IS the transport, constructed
    // against the cleared AWS region — not via a session-config field.
    tenantId:          req.tenantId,
  });
}

Why the separation matters under a contract clause: a buyer who clears anthropic through Bedrock for us-east-1 hasn't cleared the same family through Azure OpenAI for westeurope. The transport pin is the structural piece that keeps the cascade inside the cleared substrate.

IAM-federated credentials

The credential resolver is per-request: an AWS credential provider for Bedrock, managed identity for Azure, Application Default Credentials (including workload identity) for Vertex. The runtime doesn't hold a long-lived key; the cloud's identity layer hands out a short-lived credential that the transport refreshes.

// lib/providers/bedrock.ts
import { createBedrockProvider } from "@pleach/transport-bedrock";
import { fromContainerMetadata } from "@aws-sdk/credential-providers";

export const bedrockProvider = createBedrockProvider({
  region:             "us-east-1",
  credentialProvider: fromContainerMetadata(), // resolved per request, refreshed before expiry
  defaultModelId:     "anthropic.claude-3-5-sonnet-20241022-v2:0",
});

Pass the provider function itself, not a resolved credential. The transport calls it on the first request and again before the cached credentials expire (5 minutes early by default, set with credentialRefreshSkewMs). Azure's managed identity is opt-in (credential: "default"), and Vertex falls back to ambient ADC when credentials is omitted. See each transport page for the per-cloud rules.

The credential itself never reaches the stream. A credential failure surfaces as a generic "<transport>: authentication failed" error event, and the underlying error goes only to the server-side onAuthError hook.

What today ships

Today, @pleach/core ships the family-strict cascade, permittedFamilies, the deterministic replay path, and the audit row that records family, model, and region. Bring-your- own-provider via the AgentProvider contract is the supported extension point: a host running a Bedrock, Azure OpenAI, or Vertex endpoint implements the contract directly and threads the cascade.

For the direct-API path (provider keys held by the runtime), AnthropicSdkProvider and AiSdkProvider ship in core.

Roadmap

Roadmap pieces, in expected order:

  • Publishing the transport packages. @pleach/transport-bedrock (per-request AWS credentials), @pleach/transport-azure-openai (opt-in managed identity, deployment-name routing), and @pleach/transport-vertex (ADC and workload identity) are source-complete but not on npm yet. Until they publish, use them as workspace dependencies, or implement AgentProvider against the cloud API directly.
  • Cloud-attestation helpers. Per-cloud signable manifests (unsigned unless a key store is configured) that route audit rows into the cloud's native security surface — AWS Security Hub, Azure Sentinel, GCP Chronicle — so the cloud's existing compliance review absorbs the runtime's audit ledger.
  • VPC endpoint discovery. A configuration map at v1; a service-discovery integration that resolves PrivateLink endpoints from VPC metadata later.

No dates. Track the upstream package READMEs and CHANGELOG for landing notices.

Region as a hard constraint

Region is enforced by the cloud at the endpoint URL. A request to a us-east-1 Bedrock endpoint can't land on a us-west-2 model — the cloud rejects it before the runtime sees the response. The runtime records the region in the audit row, so an out-of-region attempt is visible in the ledger even when the cloud has already rejected it.

A buyer with EU residency policy pins the runtime to a eu-central-1 transport and rejects any session whose context asks for a different region. The check is a one-line precondition on buildCloudRoutedRuntime; the cloud is the enforcement authority.

Cost reconciliation to the cloud invoice

The audit row carries the cloud, the region, the model, and the token count — the same columns the cloud's billing detail breaks out. Reconciliation is one join, not a parallel cost pipeline.

select
  family,
  model_id,
  region,
  count(*)             as calls,
  sum(input_tokens)    as input_tokens,
  sum(output_tokens)   as output_tokens
from harness_auditable_calls
where transport = 'bedrock'
  and created_at >= date_trunc('month', now())
group by family, model_id, region;

The cloud invoice rolls up the same dimensions. A finance reviewer reading both surfaces sees the same numbers.

Where to go next