Version: 1.0 Status: Implementation-ready Last updated: 2026-01-21
This document defines the Runtime Provider Interface (RPI): the contract between the webhost.systems control plane and each runtime provider (e.g., Cloudflare Workers/DO, AWS Bedrock AgentCore). It also provides concrete guidance for implementing provider adapters, including deployment, invocation (streaming + non-streaming), telemetry, secrets, timeouts, idempotency, and error normalization.
Normative language: MUST, MUST NOT, SHOULD, MAY.
The RPI exists to ensure:
consistent invocation semantics across runtimes (including sessions),
consistent telemetry/cost attribution,
a single control-plane flow for deploy → activate → invoke → observe,
minimal lock-in to any one provider.
The RPI does not mandate a specific LLM SDK or “agent framework” inside the data plane.
The RPI does not attempt to unify every provider-specific feature (e.g., AgentCore tool ecosystem) into one universal surface in v1.
The RPI does not define the UI. It defines backend behavior and contracts.
Control plane (Supabase + web backend) owns: authentication, authorization, deployments, agent metadata, plan enforcement, telemetry ingestion, aggregation, and billing.
Data plane (Cloudflare/AgentCore) owns: executing user agent code and reporting telemetry events.
A provider adapter is control-plane code that:
deploys user code to a provider,
invokes the currently active deployment on that provider,
maps provider responses/errors into the normalized webhost.systems contract,
ensures the deployed workload is configured to emit telemetry securely.
All sessions are represented externally as an opaque string sessionId. The meaning is provider-specific:
Cloudflare: Durable Object id / instance key
AgentCore: runtime session id
The control plane MUST treat sessionId as opaque and MUST NOT attempt to parse it.
These are spec-level types. Implementation languages may vary, but shapes and semantics MUST be preserved.
RuntimeProvider = "cloudflare" | "agentcore"
A deployment is driven by an artifact reference. v1 supports uploaded bundles as the primary input; repo refs are optional. For some runtimes (notably AgentCore), the control plane may also produce and persist a resolved artifact (e.g., a container image) derived from an uploaded bundle.
ArtifactRef.type = "uploaded_bundle" | "repo_ref" | "agentcore_container"
For uploaded_bundle:
uploadId (opaque)
checksum
sizeBytes
For repo_ref:
githubUrl
ref
optional commitHash
For agentcore_container (resolved build output for AgentCore deployments):
imageUri (container image reference, e.g., registry/ECR URI)
optional imageDigest (immutable digest if available)
optional repository (repository name/identifier if tracked)
optional tag (image tag if used)
optional buildId (build pipeline identifier)
Notes:
agentcore_container artifacts are typically produced by the control plane build pipeline from an uploaded_bundle when deploying runtimeProvider="agentcore".
Clients generally submit uploaded_bundle or repo_ref; agentcore_container is usually created by the platform as a derived/resolved artifact for deployment and repeatability.
DeployInput (MUST include):
userId, agentId, deploymentId, deploymentVersion
runtimeProvider
artifactRef
agentConfig (non-secret config, including protocol version)
env:
plain: non-secret env vars (optional)
secretKeys: list of keys expected to exist in provider secret store
telemetry:
endpointUrl (control-plane telemetry ingestion URL)
deploymentId (for signature and attribution)
telemetryAuth (provider-injected secret reference or signing config; see §7)
DeployOutput (MUST include):
providerRef: runtime-specific reference needed for invocation
status: succeeded | failed
error?: normalized error (see §6)
InvokeInput (MUST include):
userId, agentId, deploymentId
runtimeProvider
providerRef
request:
input: either { messages: [...] } or { prompt: string } (server may normalize)
sessionId?
options? (e.g., maxSteps, temperature)
metadata? (traceId, client metadata)
limitsContext:
plan tier info and remaining budgets (or a handle to check budgets)
timeouts:
overallMs (hard cap enforced by control plane gateway)
provider-specific timeouts (optional)
InvokeOutput (MUST include):
response:
output.text
sessionId? (new/continued)
usage? (tokens/computeMs/toolCalls when available)
traceId
providerDiagnostics? (internal-only; MUST NOT leak to untrusted clients)
error? (if failed; normalized)
If streaming is supported:
Adapter MUST provide a streaming pathway that emits:
meta (traceId/sessionId)
delta chunks (text)
usage (final summary)
done
error (normalized envelope)
If streaming is not supported by the provider:
Adapter MAY emulate streaming by chunking buffered output, but MUST still emit the correct event ordering and ensure consistent final usage.
Each provider adapter MUST implement the following capabilities.
deployResponsibilities:
Validate artifact and agent config compatibility with provider constraints.
Provision or update provider resources to host the deployment.
Inject required environment and telemetry configuration.
Return a providerRef that can be used to invoke.
MUST:
be safe to retry (idempotent by deploymentId; see §5.2)
never store plaintext secrets in control plane storage
sanitize provider errors before returning them
invokeResponsibilities:
Enforce request-level constraints (payload size, session policy, timeouts).
Route to provider runtime for the active deployment.
Normalize output and errors.
Ensure telemetry is emitted (either by runtime workload or by adapter, depending on architecture; see §8).
MUST:
include traceId in provider request if possible
return an opaque sessionId when a new session is created
enforce plan limits (control plane responsibility; adapter must accept a “limits decision” or consult a limits service)
healthcheckResponsibilities:
Provide a lightweight readiness signal for provider integration (credentials/config present, basic API reachability).
MUST:
avoid expensive calls
never require deploying user code
estimateCost (MVP)Responsibilities:
Convert usage into an estimated USD cost for telemetry.
MUST:
label estimates as estimates; do not imply reconciliation
be deterministic given the same usage inputs
The invocation protocol version MUST be declared in the deployment manifest (e.g., protocol: "invoke/v1").
Adapters MUST reject unsupported protocol versions with INVALID_REQUEST.
Deploy operations MUST be idempotent per (deploymentId, provider):
If the same deployment is redeployed due to retry, the adapter MUST return the same providerRef when safe, or MUST produce a semantically equivalent deployment and update the deployment record accordingly.
Adapter SHOULD use provider tagging/labels to locate already-created resources.
Control plane MUST enforce overallMs timeout on invocations.
Adapter MUST also set provider-side timeouts where available to avoid runaway compute.
Recommended defaults (tune per provider):
Cloudflare: overall 30–60s for typical requests; sessionful flows may be longer within provider constraints.
AgentCore: allow longer (minutes to hours) only on entitled tiers; still enforce a sane overallMs for the HTTP gateway and provide async job patterns post-v1 if needed.
Adapter MUST validate:
maximum request JSON size
maximum message count and message length (to avoid abuse)
maximum output size (streaming preferred for large output)
Adapter SHOULD implement basic backpressure handling:
reject with RATE_LIMITED or RUNTIME_ERROR when provider capacity is exceeded,
include retryable=true where appropriate.
Control plane SHOULD apply per-user and per-agent rate limits (even if not in MVP).
Adapters MUST normalize provider errors into the webhost.systems error envelope with these codes:
UNAUTHENTICATED / UNAUTHORIZED (control plane usually handles)
NOT_FOUND (missing deployment/provider resource)
INVALID_REQUEST (bad input shape, unsupported protocol)
CONFLICT (deployment in progress, invalid state)
LIMIT_EXCEEDED (plan gate; control plane)
DEPLOYMENT_FAILED (provider deploy failure)
RUNTIME_ERROR (invoke failure, tool failure, provider errors)
INTERNAL (unexpected)
Rules:
Never return raw provider exceptions directly to clients.
Include provider request ids in internal diagnostics only.
Mark retryable accurately:
transient provider errors: retryable=true
validation/config errors: retryable=false
Plaintext secrets MUST NOT be stored in the control plane database.
The control plane may store:
secret key names (envVarKeys)
secret references (provider-specific identifiers)
rotation metadata (timestamps, last rotated by, etc.)
Adapters MUST ensure secrets are injected into the runtime using provider-native mechanisms:
Cloudflare:
Worker secrets (bound at deploy time via provider API)
AgentCore:
v1 default: inject secrets as AgentCore Runtime environment variables as part of the runtime/deployment configuration (do not store plaintext in the control plane database).
Deployment model note (critical): treat AgentCore deployment as artifact-based (commonly container-based). The control plane should be prepared to deploy an artifact reference (e.g., container image URI) plus runtime configuration (lifecycle, compute, env vars), rather than assuming “inline code” deployment.
SDK/package variability note (critical): AWS may split “control plane” vs “runtime/data plane” APIs into separate SDK clients and/or package names across versions. The adapter MUST be written to tolerate this:
pin versions explicitly, and
isolate AWS calls behind the adapter so any SDK renames/command shape changes are localized.
optional enhancement: use AWS Secrets Manager references (or equivalent) for advanced rotation and centralized secret governance.
The control plane MUST store only secret key names and/or secret references/metadata (never plaintext values).
AgentCore is TypeScript-capable end-to-end. However, the adapter MUST be written to reflect two realities:
Deployment is artifact-based (commonly container-based), not necessarily “inline code”.
AWS SDK/package shapes can vary by version, including potential separation of:
“control plane” (create/update/delete runtime resources) and
“runtime/data plane” (invoke runtime sessions)
into different clients and/or package names.
A typical control-plane adapter will:
Deploy by creating/updating an AgentCore runtime using an artifact reference (e.g., container image URI) plus configuration:
lifecycle configuration (idle/max lifetime),
compute shape (vCPU/memory),
environment variables (including secrets injected at deploy time),
optional capability toggles (memory/code interpreter/browser) as allowed by entitlements,
tags for {userId, agentId, deploymentId, deploymentVersion}.
Invoke by calling the AgentCore runtime invocation API with:
the deployment’s runtime reference (e.g., agentRuntimeArn and/or runtime id),
an opaque runtimeSessionId (mapped 1:1 from sessionId),
a payload containing the normalized invoke/v1 request.
Example (illustrative pseudo-implementation; do not hardcode command names or assume a single SDK package name):
// PSEUDOCODE: pin actual package names and command types in implementation. // Some SDK versions may expose separate clients for control vs runtime invocation. const controlClient = new AgentCoreControlClient({ region, credentials }); const runtimeClient = new AgentCoreRuntimeClient({ region, credentials });
// Deploy/update runtime with a container artifact + env var injection const deployResp = await controlClient.createOrUpdateRuntime({ agentRuntimeName: agent-${agentId}-v${deploymentVersion}, agentRuntimeArtifact: { // Container-based artifact reference (recommended baseline) containerConfiguration: { containerUri: artifact.containerUri, // e.g., ECR image URI }, }, lifeCycleConfiguration: { idleTimeoutInSeconds: providerConfig.idleTimeoutSeconds, maxLifetimeInSeconds: providerConfig.maxLifetimeSeconds, }, compute: { vCpu: providerConfig.vCpu, memoryMb: providerConfig.memoryMb, }, environmentVariables: { TELEMETRY_ENDPOINT_URL: telemetry.endpointUrl, TELEMETRY_DEPLOYMENT_ID: deploymentId, TELEMETRY_SECRET: "<injected-as-secret-value>", // Customer-provided secrets (write-only into provider): OPENAI_API_KEY: "<injected-as-secret-value>", }, // Optional provider features gated by entitlements (only set if enabled): memory: providerConfig.memoryEnabled ? { enabled: true } : undefined, codeInterpreter: providerConfig.codeInterpreterEnabled ? { enabled: true } : undefined, browser: providerConfig.browserEnabled ? { enabled: true } : undefined, tags: [ { key: "userId", value: userId }, { key: "agentId", value: agentId }, { key: "deploymentId", value: deploymentId }, { key: "deploymentVersion", value: String(deploymentVersion) }, ], // IAM role / network config may be required depending on provider requirements: roleArn: providerConfig.roleArn, networkConfiguration: providerConfig.networkConfiguration, });
// Invoke with session mapping (opaque sessionId -> runtimeSessionId) const runtimeSessionId = sessionId ?? sess_${Date.now()}; const payloadBytes = new TextEncoder().encode(JSON.stringify(invokeV1Request));
const invokeResp = await runtimeClient.invokeRuntime({ agentRuntimeArn: deployResp.agentRuntimeArn, runtimeSessionId, payload: payloadBytes, // Optional: qualifier/version selector if the provider supports it qualifier: deployResp.qualifier, });
Adapter requirements (normative):
MUST treat sessionId as opaque and pass it through as the provider session identifier (or return the provider-returned session id).
MUST handle “unknown/expired session” as a normalized RUNTIME_ERROR with retryable=false and a safe message (no provider internals leaked).
MUST NOT store plaintext secrets in the control plane DB.
MUST ensure telemetry secrets are deployment-scoped and injected into the runtime as secrets (env vars are acceptable if injected via provider secret mechanisms; do not persist plaintext).
MUST tag provider resources with {userId, agentId, deploymentId, deploymentVersion} for cleanup and future cost reconciliation.
MUST pin specific AWS SDK versions and keep all AWS SDK usage isolated within the adapter to contain churn from package/command renames.
Rotating a secret MAY require a redeploy depending on provider.
Adapters SHOULD document whether redeploy is required and provide a safe path.
Every invocation MUST result in a telemetry event that is:
attributable to { userId, agentId, deploymentId, runtimeProvider },
integrity-protected against spoofing,
sufficient to enforce limits and compute estimated cost.
Pattern A — In-workload reporting (preferred for Cloudflare DO):
The deployed worker/DO calls POST /v1/telemetry/report after each invocation.
Pattern B — Adapter-side reporting (fallback / interim):
Adapter emits telemetry after receiving provider response.
Use only if provider response includes enough usage data and you can attribute compute reliably.
Cloudflare typically fits Pattern A; AgentCore may fit either depending on SDK capabilities and where you can best capture tool usage and session duration.
Telemetry ingestion MUST reject spoofed events.
MVP approach:
Each deployment has a telemetrySecret injected into the runtime.
Runtime signs telemetry payload:
X-Telemetry-Signature: v1=<HMAC_SHA256(body, telemetrySecret)>
X-Telemetry-Deployment-Id: <deploymentId>
Control plane validates signature using the secret reference for that deployment.
Telemetry event MUST include:
timestamp
requests (typically 1)
llmTokens (provider-reported preferred; estimated allowed)
computeMs (wall time or billed compute time)
errors + errorClass
costUsd (estimated allowed)
traceId (if available)
Provider-specific optional:
Cloudflare: DO ops, Workers AI calls
AgentCore: session duration, tool invocations, browser interactions
Each artifact MUST include agent.config.json (or an equivalent manifest) with:
protocol: "invoke/v1"
runtime: "cloudflare" | "agentcore"
entrypoint
env.requiredKeys and env.optionalKeys
capabilities.streaming, capabilities.tools (booleans)
Adapter MUST validate:
manifest exists and is parseable
manifest runtime matches selected runtime provider
required env keys are declared
The deployed code MUST expose a handler compatible with the selected runtime provider.
The behavioral contract is:
Accept InvokeRequest input (messages/prompt, session id)
Return InvokeResponse output (text, session id, usage if available)
Respect timeouts and limits communicated via env/config where applicable
Emit telemetry (if Pattern A)
Adapters MAY provide thin runtime shims/templates that wrap user code into provider-specific handlers.
A Worker acts as the HTTP entrypoint.
A Durable Object (DO) holds per-session state (conversation history, tool state, caches).
sessionId maps to a DO id.
Adapter SHOULD:
create/update Worker script with bundled code
bind:
DO namespace
secrets (telemetry secret, user-provided secrets like model keys)
non-secret env vars
ensure the Worker/DO is configured with:
TELEMETRY_ENDPOINT_URL
TELEMETRY_DEPLOYMENT_ID
TELEMETRY_SECRET (provider secret)
optional CONTROL_PLANE_BASE_URL for any necessary callbacks
Invocation gateway may call the Worker URL directly (server-to-server).
For sessionful calls:
if sessionId present: route to that DO instance
else: generate a new DO instance id and return it
Ensure telemetry emission:
DO should emit telemetry at the end of each request with signature.
Cloudflare supports streaming responses well. Adapter SHOULD:
support SSE from invocation gateway to client
pass through provider streaming where possible, else stream from buffered output
Adapter MUST account for provider constraints (examples; confirm with current provider limits during implementation):
max CPU time
memory limits
maximum request/response sizes
DO concurrency patterns and potential hot-key issues
Control plane creates and manages AgentCore runtime resources using AWS SDK (TypeScript-supported).
Invocations call AgentCore runtime APIs.
Optional: integrate AgentCore tools SDK (code interpreter, browser tools) for premium tiers.
Adapter MUST:
create/update the AgentCore runtime (or deployment unit) for the agent/deployment
store the provider reference (agentRuntimeArn / runtime id) as providerRef
configure secrets via AWS-native mechanisms (preferred)
configure telemetry:
either in-workload reporting (if your runtime code can call out) OR
adapter-side reporting (if sufficient usage data is returned)
Map sessionId to AgentCore runtime session id.
Ensure traceId is propagated as metadata where supported.
Normalize usage reporting:
tokens, session duration, tool calls (if available)
Normalize tool errors into RUNTIME_ERROR with safe messaging.
Depending on the AgentCore invoke API capabilities:
If streaming supported: forward as SSE.
If not: return buffered responses; optionally emulate streaming.
AgentCore usage SHOULD be restricted to higher tiers by policy:
free/starter/pro: Cloudflare default
enterprise (or higher): AgentCore enabled
Adapter MUST refuse deploy/invoke if the user is not entitled (control plane should prevent routing, but adapter must still be safe).
[ ] Validate runtime entitlement before deploy and invoke.
[ ] Enforce deployment immutability: new deploy = new deployment id/version.
[ ] Ensure deploy is idempotent (retry-safe).
[ ] Never persist plaintext secrets.
[ ] Ensure telemetry integrity (sign events).
[ ] Normalize all errors with correct retryable flag.
[ ] Always include or generate traceId.
[ ] Ensure session ids are opaque and stable.
[ ] Tag/label all provider resources with {userId, agentId, deploymentId} for cleanup and audits.
[ ] Provide a best-effort cleanup/deprovision method for agent deletion.
[ ] Implement exponential backoff on transient provider failures.
[ ] Provide provider diagnostics internally (request ids) without leaking to clients.
[ ] MUST NOT allow telemetry spoofing (unsigned events).
[ ] MUST NOT leak secret values in logs/errors.
[ ] MUST NOT permit cross-tenant access via provider resource naming collisions.
Deploy input validation and manifest parsing
Error mapping table (provider error → normalized code)
Telemetry signature generation/verification
Cost estimation determinism
Cloudflare:
Deploy a minimal agent
Invoke stateless and sessionful
Verify telemetry event accepted and stored
Verify streaming path (if implemented)
AgentCore:
Deploy a minimal agent
Invoke with and without session
Verify usage extraction and telemetry
Verify tier gating (deploy/invoke rejected when not entitled)
Invalid manifest → INVALID_REQUEST
Provider auth failure → DEPLOYMENT_FAILED or RUNTIME_ERROR with retryable=false (depending on operation)
Transient provider outage → RUNTIME_ERROR with retryable=true
Telemetry with bad signature → rejected
Add additional runtime providers (e.g., another cloud or container-based runtime) by implementing the same adapter surface.
Add async jobs for long-running invocations (queue + callback/webhook/polling).
Add public agents via API keys, with per-agent rate limiting and usage attribution.
Add billing reconciliation via provider exports to convert cost from estimated → reconciled.