# Cloud-routed agent (/docs/cloud-routed-agent)



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.

<Callout type="warn">
  **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.
</Callout>

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](/docs/region-pinned-agent) if the same
buyer also locks family per call class and runs a parity suite.
[Multi-tenant SaaS agent](/docs/multi-tenant-saas-agent) if one
runtime serves many customers, each reaching their own cloud
account.
[Regulated-domain agent](/docs/regulated-domain-agent) if the
regulator drives the constraint and the cloud is the substrate
that makes the deployment reachable.

## What you're building [#what-youre-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 [#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.

```typescript
// 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 [#iam-federated-credentials]

The credential resolver is per-request — STS for AWS, managed
identity for Azure, workload identity for Google. The runtime
doesn't hold a long-lived key; the cloud's identity layer hands
out a short-lived token that the transport adapter refreshes.

```typescript
// 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",
  credentials:     await fromContainerMetadata()(),
  defaultModelId:  "anthropic.claude-3-5-sonnet-20241022-v2:0",
});
```

The audit row records the role ARN (or the managed-identity
principal, or the workload-identity binding) — not the
short-lived token. A regulator asking "what identity made this
call" reads one column; the credential itself is never
persisted.

## What today ships [#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]

Roadmap pieces, in expected order:

* **`@pleach/transport-bedrock`.** A first-party Bedrock
  transport with STS credential resolution, region pinning, and
  Bedrock-specific cost-event emission. Until it lands, hosts
  implement `AgentProvider` against the Bedrock Runtime API
  directly.
* **`@pleach/transport-azure-openai`.** A first-party Azure
  OpenAI transport with managed-identity credential resolution,
  deployment-name routing, and Azure-specific cost-event
  emission.
* **`@pleach/transport-vertex`.** A first-party Vertex AI
  transport with workload-identity credential resolution,
  region pinning, and Vertex-specific cost-event emission.
* **Cloud-attestation helpers.** Per-cloud signed manifests
  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-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 [#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.

```sql
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 [#where-to-go-next]

<Cards>
  <Card title="Providers" href="/docs/providers" description="The AgentProvider contract — the extension point a cloud transport implements." />

  <Card title="Family lock" href="/docs/family-lock" description="The permittedFamilies constraint that scopes which families a session can ever reach." />

  <Card title="Region-pinned agent" href="/docs/region-pinned-agent" description="The sibling shape for direct-API buyers running per-call-class family pins and parity suites." />

  <Card title="Audit ledger" href="/docs/audit-ledger" description="The harness_auditable_calls schema and the columns the cost reconciliation reads." />

  <Card title="Multi-tenant SaaS agent" href="/docs/multi-tenant-saas-agent" description="Per-tenant runtime construction when each tenant reaches its own cloud account." />

  <Card title="Air-gapped deployment" href="/docs/air-gapped-deployment" description="The substrate posture for a perimeter-bound deployment, when the cloud is fully on-prem." />
</Cards>
