Skip to content

feat: add Cloud World Model — x402 multi-cloud simulation API (Solana + Base) - #214

Open
canvascloudai wants to merge 6 commits into
solana-foundation:mainfrom
canvascloudai:main
Open

feat: add Cloud World Model — x402 multi-cloud simulation API (Solana + Base)#214
canvascloudai wants to merge 6 commits into
solana-foundation:mainfrom
canvascloudai:main

Conversation

@canvascloudai

Copy link
Copy Markdown

What is Cloud World Model?

Cloud World Model is a multi-cloud infrastructure simulation platform that lets AI
agents model AWS, GCP, Azure, OCI, and DigitalOcean deployments without provisioning
real cloud resources. The API exposes RL training loops, chaos engineering, multi-cloud
cost comparison, predictive scaling validation, and AI architecture analysis — all
metered via x402 pay-per-call on Solana mainnet USDC and Base mainnet USDC.

No account, no API key, and no prior registration required. Agents pay only for what
they use.

Entry location

providers/cloudworldmodel/cloud-simulation/
├── PAY.md
└── openapi.json

Key fields

Field Value
name cloud-simulation
title Cloud World Model API
category developer-tools
service_url https://www.cloudworldmodel.ai
openapi.path openapi.json

Payment networks

Network CAIP-2 Asset Address
Solana mainnet solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp USDC-SPL EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
Base mainnet eip155:8453 USDC 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913

Facilitator: https://facilitator.payai.network (PayAI, x402 v2, no API key required)

Proof of compliance

@greptile-apps

greptile-apps Bot commented Aug 2, 2026

Copy link
Copy Markdown

Greptile Summary

The PR adds the Cloud World Model provider and its OpenAPI sidecar for x402-metered multi-cloud simulation on Solana and Base.

  • Adds valid registry metadata and agent-facing usage guidance.
  • Documents pricing, payment flow, and spend-aware usage.
  • Defines the provider’s lifecycle, simulation, RL, chaos, optimization, and AI endpoints.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains; all previously reported findings are fixed in the current code.

Important Files Changed

Filename Overview
providers/cloudworldmodel/cloud-simulation/PAY.md Adds provider metadata and usage documentation; the previously reported use-case and category validation defects are corrected.
providers/cloudworldmodel/cloud-simulation/openapi.json Adds the API specification; the previously missing x402 security scheme is now declared and all referenced security names resolve.

Reviews (3): Last reviewed commit: "fix: use devtools category from registry..." | Re-trigger Greptile

Comment on lines +5 to +12
use_case: |
Call this API when an agent needs to train autoscaling RL policies across AWS, GCP,
Azure, OCI, or DigitalOcean without provisioning real cloud resources; when running
chaos scenarios (instance kills, AZ outages, database overloads, network partitions)
and scoring resilience; when comparing equivalent multi-cloud architectures on cost
and latency; or when requesting AI-generated architecture analysis, optimization
recommendations, or bottleneck diagnostics. All calls are pay-per-use — no account
or API key required; the agent pays in USDC on Solana mainnet or Base via x402.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Invalid provider use-case metadata

When CI validates the new provider, this approximately 670-character use_case exceeds the 255-character maximum and starts with Call this API instead of the required Use for or Use when, causing static catalog validation to reject the provider.

Knowledge Base Used: Provider registry (providers/)

@@ -0,0 +1 @@
{"openapi":"3.1.0","info":{"title":"Cloud World Model — Agent API","version":"1.0.0","description":"Agent-facing API surface for Cloud World Model. Includes free lifecycle operations (create/inspect simulations, list scenarios, poll job status) and 40 x402-priced endpoints spanning simulation execution, RL training, chaos engineering, multi-cloud analysis, AI explanations, and infrastructure validation. 24 of those endpoints are directly indexable by x402 crawlers (cold-probeable without a prior resource ID); the remaining 16 are resource-scoped status and results endpoints that require an existing job or simulation ID. Anonymous callers can pay per request with USDC on Base (x402 protocol) — no account or API key required.","contact":{"name":"Cloud World Model API Support","url":"https://www.cloudworldmodel.ai","email":"api@cloudworldmodel.ai"}},"servers":[{"url":"https://www.cloudworldmodel.ai/api","description":"Production"},{"url":"http://localhost:5000/api","description":"Local development"}],"paths":{"/wallet-auth/challenge":{"x-stability":"stable","post":{"tags":["Wallet Auth"],"summary":"Request an EIP-191 sign-in challenge","description":"Issues a one-time nonce for the given EVM wallet address. The caller must\nsign the returned `message` string with `personal_sign` (EIP-191) and\nsubmit the hex signature to `POST /wallet-auth/verify` within 5 minutes.\n\nRate-limited to 10 requests per IP per minute to prevent nonce-farming.\nNo authentication required.\n","operationId":"walletAuthChallenge","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["walletAddress"],"properties":{"walletAddress":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$","description":"The EVM wallet address requesting a sign-in challenge","example":"0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"}}}}}},"responses":{"200":{"description":"Challenge nonce issued","content":{"application/json":{"schema":{"type":"object","required":["nonce","message","expiresAt"],"properties":{"nonce":{"type":"string","description":"The random hex nonce (32 hex chars)","example":"a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6"},"message":{"type":"string","description":"The full message string to sign with personal_sign","example":"Sign in to Cloud World Model\nNonce: a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6\nExpires: 2026-01-01T00:05:00.000Z"},"expiresAt":{"type":"string","format":"date-time","description":"ISO-8601 timestamp when the nonce expires (5 minutes)"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"429":{"description":"Rate limit exceeded — 10 challenges per IP per minute","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/InternalError"}}}},"/wallet-auth/verify":{"x-stability":"stable","post":{"tags":["Wallet Auth"],"summary":"Verify an EIP-191 signature and receive a session token","description":"Verifies the EIP-191 personal_sign signature for the challenge previously\nissued by `POST /wallet-auth/challenge`. On success, returns a 24-hour\nJWT session token.\n\nUse the token as `Authorization: Bearer <token>` on `GET /simulations`\nand `GET /simulations/{simulationId}` to list and retrieve simulations\nclaimed by this wallet address without a traditional API key.\n\nThe nonce is single-use and consumed on successful verification.\nNo authentication required.\n","operationId":"walletAuthVerify","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["walletAddress","signature"],"properties":{"walletAddress":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$","description":"The EVM wallet address that signed the challenge","example":"0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"},"signature":{"type":"string","description":"Hex-encoded EIP-191 signature from personal_sign (0x-prefixed, 65 bytes)","example":"0x1234...ef01b"}}}}}},"responses":{"200":{"description":"Signature verified — session token issued","content":{"application/json":{"schema":{"type":"object","required":["token","walletAddress","expiresIn"],"properties":{"token":{"type":"string","description":"JWT session token (HS256, 24-hour TTL)"},"walletAddress":{"type":"string","description":"The lowercase verified wallet address","example":"0xd8da6bf26964af9d7eed9e03e53415d37aa96045"},"expiresIn":{"type":"integer","description":"Token lifetime in seconds (86400 = 24 hours)","example":86400}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"description":"Invalid or expired signature","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/InternalError"}}}},"/wallet-auth/x402-session":{"x-stability":"stable","post":{"tags":["Wallet Auth"],"summary":"Issue a wallet session JWT via x402 micro-payment","description":"Issues a 24-hour wallet session JWT in exchange for a small USDC\nmicro-payment on Base (x402 protocol). Designed for managed/MPC\nwallets that cannot sign EIP-191 challenges directly.\n\n**Flow:**\n1. Call this endpoint with no `X-PAYMENT` header — the server returns\n `402 Payment Required` with the USDC amount and payment details.\n2. Submit the x402 payment and resend the request with the\n `X-PAYMENT` header populated.\n3. On success the response body includes both a `settlement` block\n (confirmed transaction hash) and a `walletSession` block\n containing the JWT token and its expiry.\n\nThe issuance is idempotent per transaction hash — replaying the same\ntransaction hash returns the same session: the returned token carries\nthe same `jti` (session nonce) as the original issuance. No new\nsession record is created. The token's `iat`/`exp` timestamps differ\nbetween calls (each JWT is freshly signed), but the session identity\nbound by `jti` is stable across all replays of the same hash.\n\n**Token lifetime and persistence:** Tokens are valid for 24 hours and\nreusable across requests until they expire. Token validity **survives\nserver restarts only when the `WALLET_AUTH_JWT_SECRET` environment\nvariable is set to a stable, persistent value**. Without it, an\nephemeral per-process secret is used and all sessions are invalidated\non every redeploy. The server logs a startup warning when this variable\nis absent.\n\n**Authentication:** x402 micro-payment only (no API key required).\n","operationId":"walletAuthX402Session","security":[{"X402Payment":[]}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":false}}}},"responses":{"200":{"description":"Session JWT issued alongside x402 settlement","content":{"application/json":{"schema":{"type":"object","properties":{"settlement":{"type":"object","description":"x402 on-chain settlement details","properties":{"status":{"type":"string","example":"confirmed"},"transactionHash":{"type":"string","example":"0xabc123..."}}},"walletSession":{"type":"object","description":"Issued wallet session JWT (24-hour TTL). Tokens are reusable\nacross requests until expiry. Token validity survives server\nrestarts only when the `WALLET_AUTH_JWT_SECRET` environment\nvariable is set to a stable, persistent value — without it\nsessions are invalidated on every redeploy.\n","properties":{"token":{"type":"string","description":"JWT session token (HS256, 24-hour TTL, reusable until expiry)"},"expiresAt":{"type":"string","format":"date-time","description":"ISO-8601 expiry timestamp"}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"402":{"$ref":"#/components/responses/X402PaymentRequired"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/scenarios":{"x-stability":"stable","get":{"tags":["Discover"],"summary":"List available simulation templates","description":"Returns the list of pre-built infrastructure scenario templates. Scenarios\ndefine a complete simulation configuration (resources, traffic, connections)\nthat can be loaded directly into a new simulation via the browser UI or the\nAPI.\n\n**No authentication required.**\n","operationId":"listScenarios","security":[],"responses":{"200":{"description":"Array of scenario templates","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Unique scenario identifier","example":"aws-web-app"},"name":{"type":"string","example":"AWS Multi-Tier Web Application"},"description":{"type":"string"},"category":{"type":"string","description":"Scenario category (e.g. web, data, ml)","example":"web"},"provider":{"type":"string","enum":["aws","gcp","azure","oci","digitalocean","multi"],"example":"aws"},"resources":{"type":"array","items":{"$ref":"#/components/schemas/Resource"}}}}}}}},"500":{"$ref":"#/components/responses/InternalError"}}}},"/billing/x402/config":{"x-stability":"stable","get":{"tags":["Discover"],"summary":"Discover x402 pricing and payment config","description":"Returns the x402 payment configuration for this server, including the USDC-on-Base\nwallet address, network, asset contract, facilitator URL, and per-call-type price table.\n\nAI agents can use this endpoint to discover whether x402 anonymous pay-per-request is\nenabled, and to obtain the exact USDC amounts required before sending a `PAYMENT-SIGNATURE`\nheader on a metered endpoint.\n\nThis endpoint requires no authentication and is always public.\n","operationId":"getX402Config","security":[],"responses":{"200":{"description":"x402 payment configuration","content":{"application/json":{"schema":{"type":"object","required":["enabled","network","asset","payTo","facilitatorUrl","creditsPerUsdc","callPrices"],"properties":{"enabled":{"type":"boolean","description":"Whether x402 pay-per-request is active on this server.","example":true},"network":{"type":"string","description":"Blockchain network where payments are accepted.","example":"base"},"asset":{"type":"string","description":"USDC contract address on the configured network.","example":"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"},"payTo":{"type":"string","nullable":true,"description":"EVM wallet address that receives payments (null when disabled).","example":"0xYourWalletAddress"},"facilitatorUrl":{"type":"string","description":"Base URL of the x402 facilitator used to verify payments.","example":"https://facilitator.payai.network"},"creditsPerUsdc":{"type":"integer","description":"How many platform credits equal 1 USDC (determines price per credit).","example":1000},"callPrices":{"type":"object","description":"Per-call-type price table.","additionalProperties":{"type":"object","required":["credits","usdcAtomicUnits","usdcDisplay"],"properties":{"credits":{"type":"integer","description":"Number of platform credits consumed per call."},"usdcAtomicUnits":{"type":"string","description":"Payment amount in USDC atomic units (6-decimal integer string)."},"usdcDisplay":{"type":"string","description":"Human-readable USDC cost string, e.g. \"$0.0050\"."}}},"example":{"chaos_run":{"credits":5,"usdcAtomicUnits":"5000","usdcDisplay":"$0.0050"},"rl_step":{"credits":1,"usdcAtomicUnits":"1000","usdcDisplay":"$0.0010"}}},"simulationRetentionDays":{"type":"integer","description":"Number of days an x402-wallet-owned simulation is retained after its\nlast activity (step, update). Every `/step-hybrid` call on a simulation\nresets this rolling window. After the window expires, the simulation and\nall its metrics are permanently deleted by the server's cleanup sweep.\n","example":90},"autoClaimOnFirstStep":{"type":"boolean","description":"When `true`, an unclaimed (demo) simulation is automatically assigned to\nthe paying wallet's ownership on its first x402-authenticated\n`/step-hybrid` call. No explicit claim endpoint is required.\n","example":true}}}}}},"500":{"$ref":"#/components/responses/InternalError"}}}},"/simulations":{"x-stability":"stable","post":{"tags":["Create"],"summary":"Create a cloud simulation","description":"Creates a simulation with cloud resources (compute, network, database, storage).\nThe simulation serves as the environment for RL training.\n\n**Ownership:** If an `Authorization: Bearer <key>` header is provided, the\nnew simulation is immediately assigned to that API key and is fully write-accessible\nwithout any additional steps. Without authentication the simulation is created\nas a \"demo\" simulation (`apiKeyId: null`), subject to a 20-step limit; use\n`POST /simulations/{simulationId}/claim` to adopt it once you have a key.\n","operationId":"createSimulation","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","resources"],"properties":{"name":{"type":"string","description":"Human-readable name for the simulation","example":"Production Web App Autoscaling"},"description":{"type":"string","description":"Optional description of the simulation","example":"Multi-tier web application with load balancer and database"},"resources":{"type":"array","description":"Cloud resources in the simulation","items":{"$ref":"#/components/schemas/Resource"}},"connections":{"type":"array","description":"Network connections between resources","default":[],"items":{"$ref":"#/components/schemas/Connection"}},"traffic":{"type":"number","description":"Initial traffic load (requests per second)","default":1000,"example":5000}}},"examples":{"awsWebApp":{"summary":"AWS multi-tier web application","value":{"name":"AWS Production Web App","description":"Multi-tier web application on AWS with load balancer and RDS","traffic":5000,"resources":[{"id":"r1","name":"ALB","type":"network","provider":"aws","serviceFamily":"elb","region":"us-east-1","config":{"tier":"standard","targetCapacity":10000}},{"id":"r2","name":"App Server","type":"compute","provider":"aws","serviceFamily":"ec2","region":"us-east-1","config":{"instanceType":"t3.large","instances":4,"autoScaling":true,"minInstances":2,"maxInstances":12}},{"id":"r3","name":"Primary DB","type":"database","provider":"aws","serviceFamily":"rds","region":"us-east-1","config":{"instanceType":"db.r5.large","multiAZ":true}},{"id":"r4","name":"Static Assets","type":"storage","provider":"aws","serviceFamily":"s3","region":"us-east-1","config":{"storageGB":500}}]}},"digitalOceanWebApp":{"summary":"DigitalOcean web application (Droplets + Managed PostgreSQL)","value":{"name":"DO Production Web App","description":"Multi-tier web application on DigitalOcean with Droplets, Managed PostgreSQL, Spaces, and Load Balancer","traffic":3000,"resources":[{"id":"r1","name":"DO Load Balancer","type":"network","provider":"digitalocean","serviceFamily":"load_balancer","region":"nyc3","config":{"tier":"standard","targetCapacity":5000}},{"id":"r2","name":"App Droplets","type":"compute","provider":"digitalocean","serviceFamily":"droplets","region":"nyc3","config":{"instanceType":"s-4vcpu-8gb","instances":3,"autoScaling":true,"minInstances":2,"maxInstances":8}},{"id":"r3","name":"Managed PostgreSQL","type":"database","provider":"digitalocean","serviceFamily":"managed_postgresql","region":"nyc3","config":{"instanceType":"db-s-2vcpu-4gb","multiAZ":true}},{"id":"r4","name":"Spaces Object Storage","type":"storage","provider":"digitalocean","serviceFamily":"spaces","region":"nyc3","config":{"storageGB":500}}]}},"digitalOceanTrafficSpike":{"summary":"DigitalOcean traffic spike — triggers DOKS HPA Droplet scale-out","value":{"name":"DO Traffic Spike Autoscaling","description":"Demonstrates DigitalOcean DOKS Horizontal Pod Autoscaler (HPA) scaling\nDroplets during a sudden traffic surge. Starting traffic (80 000 RPS) is\ndeliberately above the capacity of a single s-2vcpu-4gb Droplet to\nguarantee that CPU breaches the 75 % HPA threshold and triggers automatic\nDroplet provisioning within one simulation step (150 s cooldown window).\nRun the simulation and observe DOKS HPA scale-out events in the event log.\n","traffic":80000,"resources":[{"id":"r1","name":"DO Load Balancer","type":"network","provider":"digitalocean","serviceFamily":"load_balancer","region":"nyc3","config":{"tier":"standard","targetCapacity":100000}},{"id":"r2","name":"droplet-01","type":"compute","provider":"digitalocean","serviceFamily":"droplets","region":"nyc3","config":{"instanceType":"s-2vcpu-4gb","instances":1,"autoScaling":true,"minInstances":1,"maxInstances":10}},{"id":"r3","name":"Managed PostgreSQL","type":"database","provider":"digitalocean","serviceFamily":"managed_postgresql","region":"nyc3","config":{"instanceType":"db-s-1vcpu-1gb","multiAZ":false}}]}}}}}},"responses":{"201":{"description":"Simulation created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Simulation"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"500":{"$ref":"#/components/responses/InternalError"}}},"get":{"tags":["Simulations"],"summary":"List simulations for the authenticated caller","description":"Returns simulations owned by the authenticated caller.\n\n**API key callers:** Returns all simulations assigned to the API key.\nAdmin-scoped keys receive all simulations across all keys.\n\n**Wallet session callers:** Returns simulations claimed by the wallet\naddress (where `ownerWallet` matches), paginated via `limit` and `offset`\nquery parameters and sorted by `updatedAt` descending. The response shape\ndiffers — it is an object `{ simulations, total, limit, offset }` rather\nthan a plain array.\n\nRequires `read` scope or a valid wallet session JWT.\n","operationId":"listSimulations","security":[],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":200,"default":50},"description":"Maximum number of simulations to return (wallet session only)"},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0},"description":"Number of simulations to skip (wallet session only)"}],"responses":{"200":{"description":"Simulations list","content":{"application/json":{"schema":{"oneOf":[{"type":"array","description":"Array of simulations (API key callers)","items":{"$ref":"#/components/schemas/Simulation"}},{"type":"object","description":"Paginated response (wallet session callers)","required":["simulations","total","limit","offset"],"properties":{"simulations":{"type":"array","items":{"$ref":"#/components/schemas/Simulation"}},"total":{"type":"integer","description":"Total simulations matching the wallet address"},"limit":{"type":"integer"},"offset":{"type":"integer"}}}]}}}},"401":{"$ref":"#/components/responses/WalletAuthUnauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/simulations/{simulationId}":{"x-stability":"stable","parameters":[{"name":"simulationId","in":"path","required":true,"schema":{"type":"string"},"description":"Simulation UUID","example":"550e8400-e29b-41d4-a716-446655440000"}],"get":{"tags":["Create"],"summary":"Inspect a simulation by ID","description":"Returns the full state of a simulation including its resources,\ncurrent time step, traffic load, and autoscaling history.\nRequires `read` scope and ownership.\n","operationId":"getSimulation","security":[],"responses":{"200":{"description":"Simulation state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Simulation"}}}},"401":{"$ref":"#/components/responses/WalletAuthUnauthorized"},"403":{"description":"Access denied — caller does not own this simulation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/rl/environments":{"x-stability":"stable","post":{"tags":["Create"],"summary":"Create an RL training environment","description":"Creates a reinforcement learning environment for training agents.\nLinks to an existing simulation and configures episode parameters.\n\nThis endpoint works with simulations built on **any supported provider**,\nincluding AWS, GCP, Azure, OCI, and **DigitalOcean**. Training an agent against\na DigitalOcean simulation lets you learn optimal autoscaling strategies for\nDroplet-based workloads, Managed Database failover handling, and\nmulti-datacenter traffic routing — all without incurring real cloud costs.\nThe observation space, action space, and reward function are identical\nregardless of provider.\n\n**Idle TTL:** Authenticated RL environments expire after **2 hours of inactivity**\n(no `step` or `reset` call received). Expired environments and their linked\nsimulations are removed automatically; subsequent requests return `404`. Reset the\nidle timer by calling `POST /rl/environments/{environmentId}/step` or\n`POST /rl/environments/{environmentId}/reset` at least once every 2 hours during\nlong training runs.\n\n**Note:** In-memory deployments (the default) do not persist RL environments\nacross server restarts. Reconnect or recreate the environment after a restart.\n\n**UI Status Viewer:** Once an environment is running you can monitor its episodes,\ncumulative rewards, and health without writing code — open the\n[RL Environment Status viewer](/admin/rl-environments) in the platform UI and enter\nyour API key to see all active environments at a glance.\n","operationId":"createRLEnvironment","x-codeSamples":[{"lang":"curl","label":"curl","source":"curl -X POST https://your-production-domain.com/api/rl/environments \\\n -H \"Authorization: Bearer $API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"simulationId\": \"a638caad-7423-40a3-bb09-f91235d9392d\",\n \"episodeConfig\": {\n \"maxSteps\": 200,\n \"targetTrafficPattern\": \"wave\",\n \"initialTraffic\": 1500,\n \"targetSLA\": { \"maxLatencyP95\": 180, \"maxErrorRate\": 1.0 },\n \"costBudgetPerHour\": 3.50,\n \"enableFailures\": false\n }\n }'\n"},{"lang":"Python","label":"Python","source":"import requests\n\nBASE_URL = \"https://your-production-domain.com/api\"\nAPI_KEY = \"your-api-key\"\n\nresp = requests.post(\n f\"{BASE_URL}/rl/environments\",\n headers={\"Authorization\": f\"Bearer {API_KEY}\"},\n json={\n \"simulationId\": \"a638caad-7423-40a3-bb09-f91235d9392d\",\n \"episodeConfig\": {\n \"maxSteps\": 200,\n \"targetTrafficPattern\": \"wave\",\n \"initialTraffic\": 1500,\n \"targetSLA\": {\"maxLatencyP95\": 180, \"maxErrorRate\": 1.0},\n \"costBudgetPerHour\": 3.50,\n \"enableFailures\": False,\n },\n },\n)\nresp.raise_for_status()\ndata = resp.json()\nenv_id = data[\"environment\"][\"id\"]\nprint(f\"Created RL environment: {env_id}\")\nprint(f\"Initial CPU: {data['observation']['metrics']['cpuUsage']}%\")\n"},{"lang":"Node.js","label":"Node.js","source":"const BASE_URL = \"https://your-production-domain.com/api\";\nconst API_KEY = \"your-api-key\";\n\nconst resp = await fetch(`${BASE_URL}/rl/environments`, {\n method: \"POST\",\n headers: {\n \"Authorization\": `Bearer ${API_KEY}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n simulationId: \"a638caad-7423-40a3-bb09-f91235d9392d\",\n episodeConfig: {\n maxSteps: 200,\n targetTrafficPattern: \"wave\",\n initialTraffic: 1500,\n targetSLA: { maxLatencyP95: 180, maxErrorRate: 1.0 },\n costBudgetPerHour: 3.50,\n enableFailures: false,\n },\n }),\n});\nconst data = await resp.json();\nconst envId = data.environment.id;\nconsole.log(`Created RL environment: ${envId}`);\nconsole.log(`Initial CPU: ${data.observation.metrics.cpuUsage}%`);\n"}],"security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["simulationId","episodeConfig"],"properties":{"simulationId":{"type":"string","format":"uuid","description":"ID of the simulation to train on","example":"a638caad-7423-40a3-bb09-f91235d9392d"},"episodeConfig":{"$ref":"#/components/schemas/EpisodeConfig"},"rewardWeights":{"$ref":"#/components/schemas/RewardWeights"},"webhookUrl":{"type":"string","format":"uri","description":"Optional HTTPS URL to receive webhook notification when episode completes","example":"https://your-app.com/webhooks/rl-episode"},"webhookSecret":{"type":"string","description":"Optional secret for HMAC-SHA256 webhook signature verification","example":"your-secret-key-here"}}},"examples":{"digitalOceanWaveTraffic":{"summary":"DigitalOcean Droplet cluster — wave traffic pattern","description":"Train an agent on a DigitalOcean simulation using a wave traffic pattern.\nThe simulationId must reference an existing simulation that contains\nDigitalOcean resources (Droplets, Managed Database, Load Balancer).\nThe cost budget of $3.50/hr reflects typical pricing for two\ns-2vcpu-4gb Droplets plus a single Managed PostgreSQL basic node in nyc3.\n","value":{"simulationId":"b7e1c2d4-3f8a-4b5e-9c0d-1e2f3a4b5c6d","episodeConfig":{"maxSteps":200,"targetTrafficPattern":"wave","initialTraffic":1500,"targetSLA":{"maxLatencyP95":180,"maxErrorRate":1},"costBudgetPerHour":3.5,"enableFailures":false}}},"digitalOceanBurstTraffic":{"summary":"DigitalOcean Droplet cluster — burst traffic with failure injection","description":"More challenging episode: burst traffic spikes combined with random failure\ninjection (e.g., Managed Database failover). Useful for training a robust\nagent that can handle both scaling pressure and partial outages.\n","value":{"simulationId":"b7e1c2d4-3f8a-4b5e-9c0d-1e2f3a4b5c6d","episodeConfig":{"maxSteps":150,"targetTrafficPattern":"burst","initialTraffic":800,"targetSLA":{"maxLatencyP95":200,"maxErrorRate":2},"costBudgetPerHour":5,"enableFailures":true}}},"digitalOceanAMDNVMe":{"summary":"DigitalOcean AMD NVMe Droplet cluster — sustained ramp traffic","description":"Train an agent on a DigitalOcean simulation using AMD NVMe Droplets\n(s-2vcpu-4gb-amd). The AMD variant uses NVMe-backed local storage and\nAMD EPYC processors, offering lower per-hour cost than equivalent\nIntel Droplets while delivering comparable CPU performance for\ncompute-bound workloads.\n\nThe cost budget of $2.80/hr reflects typical pricing for two\ns-2vcpu-4gb-amd Droplets plus a single Managed PostgreSQL basic node\nin nyc3. Use this example to benchmark agent policies across Intel vs.\nAMD Droplet fleets under a sustained ramp traffic pattern.\n","value":{"simulationId":"c9f2d3e5-4a7b-4c6f-8d1e-2f3a4b5c6d7e","episodeConfig":{"maxSteps":200,"targetTrafficPattern":"ramp","initialTraffic":1000,"targetSLA":{"maxLatencyP95":175,"maxErrorRate":1},"costBudgetPerHour":2.8,"enableFailures":false,"computeType":"s-2vcpu-4gb-amd"}}},"awsSpot":{"summary":"AWS EC2 Spot — cost-optimized batch RL training using Spot Instances","description":"Train an agent on a cost-sensitive AWS workload backed by EC2 Spot Instances.\nThe simulationId must reference an existing simulation containing Spot-eligible\nresources (e.g. t3.xlarge or c5.2xlarge instances). The cost budget of $1.80/hr\nreflects typical Spot pricing for a small fleet of t3.large instances in us-east-1\n(roughly 70% below on-demand list price). Latency SLA is relaxed to 500 ms p95\nto accommodate the occasional Spot interruption and re-scheduling delay. Use this\nexample to benchmark agent policies that prioritise cost efficiency over strict\nlatency guarantees — a common requirement for ML training, data pipelines, and\nother fault-tolerant batch workloads.\n","value":{"simulationId":"d4e5f6a7-1b2c-4d3e-8f9a-0b1c2d3e4f5a","episodeConfig":{"maxSteps":200,"targetTrafficPattern":"ramp","initialTraffic":1000,"targetSLA":{"maxLatencyP95":500,"maxErrorRate":2},"costBudgetPerHour":1.8,"enableFailures":true},"webhookUrl":"https://your-app.example.com/webhooks/rl-episode","webhookSecret":"aws-spot-rl-secret"}},"gcpSpot":{"summary":"GCP Spot VM — cost-optimized batch RL training using preemptible compute","description":"Train an agent on a cost-sensitive GCP workload backed by Spot VMs (formerly\npreemptible). The simulationId must reference an existing simulation containing\nSpot-eligible resources (e.g. n2-standard-4 or c2-standard-8 Spot instances in\nus-central1). The cost budget of $1.60/hr reflects typical Spot pricing for a\nsmall fleet of n2-standard-4 instances (roughly 70% below on-demand list price).\nLatency SLA is relaxed to 500 ms p95 to accommodate the occasional preemption\nand re-scheduling delay. Use this example to benchmark agent policies that\nprioritise cost efficiency over strict latency guarantees — ideal for ML\ntraining, data processing, and other fault-tolerant batch workloads on GCP.\n","value":{"simulationId":"e5f6a7b8-2c3d-4e5f-9a0b-1c2d3e4f5a6b","episodeConfig":{"maxSteps":200,"targetTrafficPattern":"ramp","initialTraffic":1000,"targetSLA":{"maxLatencyP95":500,"maxErrorRate":2},"costBudgetPerHour":1.6,"enableFailures":true},"webhookUrl":"https://your-app.example.com/webhooks/rl-episode","webhookSecret":"gcp-spot-rl-secret"}},"azureSpot":{"summary":"Azure Spot VM — cost-optimized batch RL training using Azure Spot instances","description":"Train an agent on a cost-sensitive Azure workload backed by Azure Spot VMs.\nThe simulationId must reference an existing simulation containing Spot-eligible\nresources (e.g. Standard_D4s_v3 or Standard_F8s_v2 Spot instances in eastus).\nThe cost budget of $1.50/hr reflects typical Spot pricing for a small fleet of\nStandard_D4s_v3 instances (roughly 70% below pay-as-you-go list price). Latency\nSLA is relaxed to 400 ms p95 to accommodate Azure Spot eviction and re-deployment\ndelays. Use this example to benchmark agent policies that prioritise cost\nefficiency over strict latency guarantees — well-suited for batch inference,\ndata pipelines, and other interruption-tolerant workloads on Azure.\n","value":{"simulationId":"f6a7b8c9-3d4e-4f5a-0b1c-2d3e4f5a6b7c","episodeConfig":{"maxSteps":200,"targetTrafficPattern":"ramp","initialTraffic":1000,"targetSLA":{"maxLatencyP95":400,"maxErrorRate":2},"costBudgetPerHour":1.5,"enableFailures":true},"webhookUrl":"https://your-app.example.com/webhooks/rl-episode","webhookSecret":"azure-spot-rl-secret"}},"ociPreemptible":{"summary":"OCI Ampere A1 Preemptible — cost-optimized batch RL training using OCI preemptible compute","description":"Train an agent on a cost-sensitive OCI workload backed by preemptible Ampere A1\ninstances. The simulationId must reference an existing simulation containing\npreemptible-eligible resources (e.g. VM.Standard.A1.Flex instances in\nus-ashburn-1). OCI preemptible instances are priced at roughly 50% below\nstandard on-demand rates, making them the most cost-effective option in OCI for\nfault-tolerant batch workloads. The cost budget of $1.20/hr reflects typical\npreemptible pricing for a small Ampere A1 fleet. Latency SLA is relaxed to\n600 ms p95 to accommodate the occasional preemption and re-scheduling delay.\nUse this example to benchmark agent policies that prioritise cost efficiency\nover strict latency guarantees — ideal for ML training, data pipelines,\ngenomic workloads, and other interruption-tolerant tasks on OCI.\n","value":{"simulationId":"a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d","episodeConfig":{"maxSteps":200,"targetTrafficPattern":"ramp","initialTraffic":1000,"targetSLA":{"maxLatencyP95":600,"maxErrorRate":2},"costBudgetPerHour":1.2,"enableFailures":true},"webhookUrl":"https://your-app.example.com/webhooks/rl-episode","webhookSecret":"oci-preemptible-rl-secret"}}}}}},"responses":{"201":{"description":"RL environment created successfully","content":{"application/json":{"schema":{"type":"object","properties":{"environment":{"$ref":"#/components/schemas/RLEnvironment"},"observation":{"$ref":"#/components/schemas/Observation"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"404":{"description":"Simulation not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/rl/environments/{environmentId}":{"x-stability":"stable","get":{"tags":["Create"],"summary":"Inspect an RL environment","description":"Retrieve the current state and configuration of an RL environment","operationId":"getRLEnvironment","x-codeSamples":[{"lang":"curl","label":"curl","source":"curl https://your-production-domain.com/api/rl/environments/env-aws-001 \\\n -H \"Authorization: Bearer $API_KEY\"\n"},{"lang":"Python","label":"Python","source":"import requests\n\nBASE_URL = \"https://your-production-domain.com/api\"\nAPI_KEY = \"your-api-key\"\nENV_ID = \"env-aws-001\"\n\nresp = requests.get(\n f\"{BASE_URL}/rl/environments/{ENV_ID}\",\n headers={\"Authorization\": f\"Bearer {API_KEY}\"},\n)\nresp.raise_for_status()\nenv = resp.json()\nprint(f\"Environment {env['id']} isActive={env['isActive']} \"\n f\"step={env['currentStep']}/{env['maxSteps']}\")\n"},{"lang":"Node.js","label":"Node.js","source":"const BASE_URL = \"https://your-production-domain.com/api\";\nconst API_KEY = \"your-api-key\";\nconst ENV_ID = \"env-aws-001\";\n\nconst resp = await fetch(`${BASE_URL}/rl/environments/${ENV_ID}`, {\n headers: { \"Authorization\": `Bearer ${API_KEY}` },\n});\nconst env = await resp.json();\nconsole.log(`Environment ${env.id} isActive=${env.isActive} step=${env.currentStep}/${env.maxSteps}`);\n"}],"security":[],"parameters":[{"name":"environmentId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Environment details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RLEnvironment"}}}},"404":{"$ref":"#/components/responses/NotFound"}}},"delete":{"tags":["Create"],"summary":"Delete an RL training environment","description":"Cancel a running RL training episode. This endpoint is idempotent - calling it multiple times\non the same episode will return success without error.\n\n**Cancellation Rules:**\n- Episodes with isActive=true will be cancelled\n- Episodes already cancelled (isActive=false) will return success (idempotent behavior)\n- Cancelled episodes will have isActive set to false and a cancelledAt timestamp\n","operationId":"cancelRLEnvironment","security":[],"parameters":[{"name":"environmentId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Episode cancelled successfully or was already cancelled.\nReturns the same response whether cancelling for the first time or if already cancelled\n(idempotent operation).\n","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"isActive":{"type":"boolean","enum":[false]},"cancelledAt":{"type":"string","format":"date-time"},"message":{"type":"string","description":"Message indicating if episode was just cancelled or already cancelled"}}},"examples":{"newlyCancelled":{"value":{"id":"env_abc123","isActive":false,"cancelledAt":"2024-01-15T10:30:00Z","message":"RL training episode cancelled successfully"}},"alreadyCancelled":{"value":{"id":"env_abc123","isActive":false,"cancelledAt":"2024-01-15T09:45:00Z","message":"Episode already cancelled"}}}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/simulations/{simulationId}/step-hybrid":{"x-stability":"stable","post":{"tags":["Execute"],"summary":"Advance simulation one step (hybrid ML + rules)","description":"Steps the simulation using the Hybrid Prediction Engine, which blends a\ndeterministic rule-based simulation with a simulated ML-based prediction.\nThe engine uses a confidence threshold to decide how much weight to give\neach path, and falls back to pure rules when ML confidence is low.\n\nReturns the updated simulation state, step metrics, events, the hybrid\ndecision record (including ML confidence and blending rationale), and the\nupdated cumulative hybrid result object.\n\nRequires `write` scope and ownership.\n","operationId":"stepSimulationHybrid","x-codeSamples":[{"lang":"curl","label":"curl","source":"curl -X POST https://your-production-domain.com/api/simulations/sim-abc123/step-hybrid \\\n -H \"Authorization: Bearer $API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"config\": {\"mlWeight\": 0.6, \"confidenceThreshold\": 0.5}}'\n"},{"lang":"Python","label":"Python","source":"import requests\n\nBASE_URL = \"https://your-production-domain.com/api\"\nAPI_KEY = \"your-api-key\"\n\nresp = requests.post(\n f\"{BASE_URL}/simulations/sim-abc123/step-hybrid\",\n headers={\"Authorization\": f\"Bearer {API_KEY}\"},\n json={\"config\": {\"mlWeight\": 0.6, \"confidenceThreshold\": 0.5}},\n)\nresp.raise_for_status()\ndata = resp.json()\nprint(\"ML confidence:\", data[\"hybridDecision\"][\"mlPrediction\"][\"overallConfidence\"])\n"},{"lang":"Node.js","label":"Node.js","source":"const BASE_URL = \"https://your-production-domain.com/api\";\nconst API_KEY = \"your-api-key\";\n\nconst resp = await fetch(`${BASE_URL}/simulations/sim-abc123/step-hybrid`, {\n method: \"POST\",\n headers: {\n \"Authorization\": `Bearer ${API_KEY}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ config: { mlWeight: 0.6, confidenceThreshold: 0.5 } }),\n});\nconst data = await resp.json();\nconsole.log(\"ML confidence:\", data.hybridDecision.mlPrediction.overallConfidence);\n"}],"security":[{"x402":[]}],"x-payment-info":{"protocols":["x402"],"price":{"mode":"fixed","currency":"USD","amount":"0.001"}},"x-x402-call-type":"simulation_step_hybrid","x-x402-price-usdc":"$0.0010","parameters":[{"name":"simulationId","in":"path","required":true,"schema":{"type":"string"},"description":"Simulation UUID","example":"sim-abc123"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"steps":{"type":"integer","description":"Number of time steps to advance in a single call (1–500, default 1).\nEach sub-step evaluates traffic patterns at the correct elapsed time,\nso a ramp or wave pattern reaches the same final value whether driven\nas N×1 individual calls or a single batch call of N.\n","minimum":1,"maximum":500,"default":1,"example":5},"config":{"type":"object","description":"Optional hybrid engine configuration overrides","properties":{"mlWeight":{"type":"number","minimum":0,"maximum":1,"description":"Weight given to ML prediction vs rule-based (0 = pure rules, 1 = pure ML)","example":0.6},"confidenceThreshold":{"type":"number","minimum":0,"maximum":1,"description":"Minimum ML confidence to apply blending; below this threshold pure rules are used","example":0.5}}}},"example":{"config":{"mlWeight":0.6,"confidenceThreshold":0.5}}},"examples":{"defaultBlend":{"summary":"Default hybrid blend (60% ML, 40% rules)","value":{"config":{"mlWeight":0.6,"confidenceThreshold":0.5}}},"rulesHeavy":{"summary":"Rules-heavy blend — prefer deterministic path","value":{"config":{"mlWeight":0.2,"confidenceThreshold":0.7}}}}}}},"responses":{"200":{"description":"Hybrid step result","content":{"application/json":{"schema":{"type":"object","properties":{"simulation":{"$ref":"#/components/schemas/Simulation"},"metrics":{"type":"object","description":"Metrics snapshot for this step"},"events":{"type":"array","items":{"type":"object"}},"hybridDecision":{"type":"object","description":"Hybrid decision metadata for this step","properties":{"blendingApplied":{"type":"boolean"},"fallbackUsed":{"type":"boolean"},"mlPrediction":{"type":"object","properties":{"overallConfidence":{"type":"number","example":0.78},"bottlenecks":{"type":"array","items":{"type":"string"}},"eventLikelihoods":{"type":"array","items":{"type":"object"}}}}}},"hybridResult":{"type":"object","description":"Cumulative hybrid result history"},"expiresAt":{"type":"string","format":"date-time","description":"Present only on x402 (wallet-paying) calls. ISO-8601 timestamp when\nownership (and the simulation itself) expires. Reset to now+90 days on\nevery successful step-hybrid call.\n","example":"2026-10-17T22:00:00.000Z"},"ownership":{"type":"object","description":"Present only on x402 (wallet-paying) calls. Describes the wallet\nthat owns this simulation and when that ownership expires.\n","properties":{"type":{"type":"string","description":"Ownership type — always \"wallet\" for x402 callers.","example":"wallet"},"wallet":{"type":"string","description":"Normalized Ethereum wallet address that owns this simulation.","example":"0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"},"expiresAt":{"type":"string","format":"date-time","description":"ISO-8601 timestamp when ownership (and the simulation itself)\nexpires. Reset to now+90 days on every successful step-hybrid call.\n","example":"2026-10-17T22:00:00.000Z"},"retentionDays":{"type":"integer","description":"Rolling retention window in days (always 90).","example":90}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/X402PaymentRequired"},"403":{"description":"Access denied","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"Simulation already owned by another wallet or API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","example":"simulation_owned_by_another_wallet"},"message":{"type":"string","example":"This simulation is owned by a different wallet."}}}}}},"500":{"$ref":"#/components/responses/InternalError"}}}},"/simulations/{simulationId}/inject-traffic":{"x-stability":"stable","post":{"tags":["Simulations"],"summary":"Inject a random traffic spike","description":"Immediately injects a sudden traffic spike into the simulation by multiplying\nthe current traffic load by a random factor (typically 2×–5×). The spike is\napplied to the simulation state and recorded as a warning event.\n\nUseful for testing autoscaling responsiveness without configuring a full\ntraffic pattern. The effect persists until the next step adjusts traffic\nback via active patterns.\n\nRequires `write` scope and ownership.\n","operationId":"injectTraffic","x-codeSamples":[{"lang":"curl","label":"curl","source":"curl -X POST https://your-production-domain.com/api/simulations/sim-abc123/inject-traffic \\\n -H \"Authorization: Bearer $API_KEY\"\n"},{"lang":"Python","label":"Python","source":"import requests\n\nBASE_URL = \"https://your-production-domain.com/api\"\nAPI_KEY = \"your-api-key\"\n\nresp = requests.post(\n f\"{BASE_URL}/simulations/sim-abc123/inject-traffic\",\n headers={\"Authorization\": f\"Bearer {API_KEY}\"},\n)\nresp.raise_for_status()\ndata = resp.json()\nprint(\"Spike event:\", data[\"event\"][\"message\"])\n"},{"lang":"Node.js","label":"Node.js","source":"const BASE_URL = \"https://your-production-domain.com/api\";\nconst API_KEY = \"your-api-key\";\n\nconst resp = await fetch(`${BASE_URL}/simulations/sim-abc123/inject-traffic`, {\n method: \"POST\",\n headers: { \"Authorization\": `Bearer ${API_KEY}` },\n});\nconst data = await resp.json();\nconsole.log(\"Spike event:\", data.event.message);\n"}],"security":[{"x402":[]}],"x-payment-info":{"protocols":["x402"],"price":{"mode":"fixed","currency":"USD","amount":"0.001"}},"x-x402-call-type":"inject_traffic","x-x402-price-usdc":"$0.0010","parameters":[{"name":"simulationId","in":"path","required":true,"schema":{"type":"string"},"description":"Simulation UUID"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":false}}}},"responses":{"200":{"description":"Traffic spike applied","content":{"application/json":{"schema":{"type":"object","properties":{"simulation":{"$ref":"#/components/schemas/Simulation"},"event":{"type":"object","description":"Event recording the traffic spike","properties":{"id":{"type":"string"},"simulationId":{"type":"string"},"severity":{"type":"string","example":"warning"},"message":{"type":"string","example":"Traffic spike injected: 1000 → 4200 RPS"}}}}}}}},"401":{"$ref":"#/components/responses/ApiKeyUnauthorized"},"403":{"description":"Access denied","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/simulations/{simulationId}/inject-failure":{"x-stability":"stable","post":{"tags":["Simulations"],"summary":"Inject a random node failure","description":"Randomly selects a healthy compute node in the simulation and marks it as\nfailed, updating the resource state and recording a warning event.\n\nReturns 400 if no healthy nodes are available to fail.\nFor fine-grained control over failure type, duration, and target, use\n`POST /simulations/{simulationId}/failures` instead.\n\nRequires `write` scope and ownership.\n","operationId":"injectFailure","x-codeSamples":[{"lang":"curl","label":"curl","source":"curl -X POST https://your-production-domain.com/api/simulations/sim-abc123/inject-failure \\\n -H \"Authorization: Bearer $API_KEY\"\n"},{"lang":"Python","label":"Python","source":"import requests\n\nBASE_URL = \"https://your-production-domain.com/api\"\nAPI_KEY = \"your-api-key\"\n\nresp = requests.post(\n f\"{BASE_URL}/simulations/sim-abc123/inject-failure\",\n headers={\"Authorization\": f\"Bearer {API_KEY}\"},\n)\nresp.raise_for_status()\ndata = resp.json()\nprint(\"Failure event:\", data[\"event\"][\"message\"])\n"},{"lang":"Node.js","label":"Node.js","source":"const BASE_URL = \"https://your-production-domain.com/api\";\nconst API_KEY = \"your-api-key\";\n\nconst resp = await fetch(`${BASE_URL}/simulations/sim-abc123/inject-failure`, {\n method: \"POST\",\n headers: { \"Authorization\": `Bearer ${API_KEY}` },\n});\nconst data = await resp.json();\nconsole.log(\"Failure event:\", data.event.message);\n"}],"security":[{"x402":[]}],"x-payment-info":{"protocols":["x402"],"price":{"mode":"fixed","currency":"USD","amount":"0.001"}},"x-x402-call-type":"inject_failure","x-x402-price-usdc":"$0.0010","parameters":[{"name":"simulationId","in":"path","required":true,"schema":{"type":"string"},"description":"Simulation UUID"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":false}}}},"responses":{"200":{"description":"Node failure injected","content":{"application/json":{"schema":{"type":"object","properties":{"simulation":{"$ref":"#/components/schemas/Simulation"},"event":{"type":"object","description":"Event recording the node failure"}}}}}},"400":{"description":"No healthy nodes available to fail","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/ApiKeyUnauthorized"},"403":{"description":"Access denied","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/rl/environments/{environmentId}/step":{"x-stability":"stable","post":{"tags":["Execute"],"summary":"Execute an RL action and advance one step","description":"Executes an agent action, simulates one time step, and returns the next observation,\nreward, and episode completion status. This is the core training loop interaction.\n\n**Idle TTL:** Each successful step call resets the environment's 2-hour idle timer.\nEnvironments that receive no `step` or `reset` calls for 2 hours are automatically\ndeactivated and their linked simulation artifacts removed. Subsequent requests to a\ndeactivated environment return `404`. Call `step` or `reset` at least once every\n2 hours during long training runs to keep the environment alive.\n","operationId":"stepRLEnvironment","x-codeSamples":[{"lang":"curl","label":"curl","source":"curl -X POST https://your-production-domain.com/api/rl/environments/env-aws-001/step \\\n -H \"Authorization: Bearer $API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"action\": {\"type\": \"scale_out\", \"parameters\": {\"instanceCount\": 1}}}'\n"},{"lang":"Python","label":"Python","source":"import requests\n\nBASE_URL = \"https://your-production-domain.com/api\"\nAPI_KEY = \"your-api-key\"\nENV_ID = \"env-aws-001\"\n\nresp = requests.post(\n f\"{BASE_URL}/rl/environments/{ENV_ID}/step\",\n headers={\"Authorization\": f\"Bearer {API_KEY}\"},\n json={\"action\": {\"type\": \"scale_out\", \"parameters\": {\"instanceCount\": 1}}},\n)\nresp.raise_for_status()\ndata = resp.json()\nprint(f\"t={data['t']} reward={data['reward']:.3f} done={data['done']}\")\nprint(f\"CPU: {data['obs']['cpu_util']:.1%} P95: {data['metrics']['latency_p95']} ms \"\n f\"cost: ${data['metrics']['cost_usd_hr']:.2f}/hr\")\n"},{"lang":"Node.js","label":"Node.js","source":"const BASE_URL = \"https://your-production-domain.com/api\";\nconst API_KEY = \"your-api-key\";\nconst ENV_ID = \"env-aws-001\";\n\nconst resp = await fetch(`${BASE_URL}/rl/environments/${ENV_ID}/step`, {\n method: \"POST\",\n headers: {\n \"Authorization\": `Bearer ${API_KEY}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ action: { type: \"scale_out\", parameters: { instanceCount: 1 } } }),\n});\nconst data = await resp.json();\nconsole.log(`t=${data.t} reward=${data.reward.toFixed(3)} done=${data.done}`);\nconsole.log(`CPU: ${(data.obs.cpu_util * 100).toFixed(1)}% P95: ${data.metrics.latency_p95} ms cost: $${data.metrics.cost_usd_hr.toFixed(2)}/hr`);\n"},{"lang":"Python","label":"Python – warm-up/training","source":"\"\"\"\nTwo-phase training loop: fast warm-up followed by fine-grained training.\n\nPhase 1 – Warm-up (300 s ticks)\n Use large tick_seconds to fast-forward through startup noise before the\n agent starts making meaningful autoscaling decisions. Each step advances\n the simulation clock by 5 minutes, so 20 warm-up steps cover ~1.7 hours\n of simulated time in seconds of wall time.\n\nPhase 2 – Training (60 s ticks)\n Switch to 1-minute ticks for precise autoscaling control. The agent now\n observes and reacts to traffic on a per-minute basis, matching the\n granularity of real autoscaling cooldown windows (e.g. AWS default: 300 s).\n\"\"\"\nimport requests\n\nBASE_URL = \"https://your-production-domain.com/api\"\nAPI_KEY = \"your-api-key\"\nENV_ID = \"env-aws-001\"\n\nsession = requests.Session()\nsession.headers.update({\"Authorization\": f\"Bearer {API_KEY}\"})\n\ndef step(action: dict, tick_seconds: int) -> dict:\n resp = session.post(\n f\"{BASE_URL}/rl/environments/{ENV_ID}/step\",\n json={\"action\": action, \"tick_seconds\": tick_seconds},\n )\n resp.raise_for_status()\n return resp.json()\n\nNO_OP = {\"type\": \"no_op\", \"parameters\": {}}\n\nWARMUP_STEPS = 20\nprint(\"=== Warm-up phase (300 s ticks) ===\")\nfor i in range(WARMUP_STEPS):\n data = step(NO_OP, tick_seconds=300)\n print(\n f\" warmup {i+1:2d}/{WARMUP_STEPS} \"\n f\"sim={data['sim_time_human']:>8s} \"\n f\"cpu={data['obs']['cpu_util']:.1%} \"\n f\"instances={data['obs']['instances']}\"\n )\n if data[\"done\"]:\n print(\" Episode ended during warm-up — reset and retry.\")\n break\n\nprint(\"\\n=== Training phase (60 s ticks) ===\")\ndone = False\nwhile not done:\n cpu = data[\"obs\"][\"cpu_util\"]\n if cpu > 0.75:\n action = {\"type\": \"scale_out\", \"parameters\": {\"instanceCount\": 1}}\n elif cpu < 0.30 and data[\"obs\"][\"instances\"] > 1:\n action = {\"type\": \"scale_in\", \"parameters\": {\"instanceCount\": 1}}\n else:\n action = NO_OP\n\n data = step(action, tick_seconds=60)\n done = data[\"done\"]\n print(\n f\" t={data['t']:4d} sim={data['sim_time_human']:>8s} \"\n f\"reward={data['reward']:+.3f} \"\n f\"cpu={data['obs']['cpu_util']:.1%} \"\n f\"p95={data['metrics']['latency_p95']} ms \"\n f\"cost=${data['metrics']['cost_usd_hr']:.2f}/hr \"\n f\"done={done}\"\n )\n"},{"lang":"Node.js","label":"Node.js – warm-up/training","source":"/**\n * Two-phase training loop: fast warm-up followed by fine-grained training.\n *\n * Phase 1 – Warm-up (300 s ticks)\n * Use large tick_seconds to fast-forward through startup noise before the\n * agent starts making meaningful autoscaling decisions. Each step advances\n * the simulation clock by 5 minutes, so 20 warm-up steps cover ~1.7 hours\n * of simulated time in seconds of wall time.\n *\n * Phase 2 – Training (60 s ticks)\n * Switch to 1-minute ticks for precise autoscaling control. The agent now\n * observes and reacts to traffic on a per-minute basis, matching the\n * granularity of real autoscaling cooldown windows (e.g. AWS default: 300 s).\n */\nconst BASE_URL = \"https://your-production-domain.com/api\";\nconst API_KEY = \"your-api-key\";\nconst ENV_ID = \"env-aws-001\";\n\nasync function step(action, tickSeconds) {\n const resp = await fetch(`${BASE_URL}/rl/environments/${ENV_ID}/step`, {\n method: \"POST\",\n headers: {\n \"Authorization\": `Bearer ${API_KEY}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ action, tick_seconds: tickSeconds }),\n });\n if (!resp.ok) throw new Error(`step failed: ${resp.status}`);\n return resp.json();\n}\n\nconst NO_OP = { type: \"no_op\", parameters: {} };\n\n// --- Phase 1: Warm-up (300 s / 5-minute ticks) ---\nconst WARMUP_STEPS = 20;\nconsole.log(\"=== Warm-up phase (300 s ticks) ===\");\nlet data;\nfor (let i = 0; i < WARMUP_STEPS; i++) {\n data = await step(NO_OP, 300);\n console.log(\n ` warmup ${String(i + 1).padStart(2)}/${WARMUP_STEPS}` +\n ` sim=${data.sim_time_human.padStart(8)}` +\n ` cpu=${(data.obs.cpu_util * 100).toFixed(1)}%` +\n ` instances=${data.obs.instances}`\n );\n if (data.done) {\n console.log(\" Episode ended during warm-up — reset and retry.\");\n break;\n }\n}\n\n// --- Phase 2: Training (60 s / 1-minute ticks) ---\nconsole.log(\"\\n=== Training phase (60 s ticks) ===\");\nlet done = false;\nwhile (!done) {\n const cpu = data.obs.cpu_util;\n let action;\n if (cpu > 0.75) {\n action = { type: \"scale_out\", parameters: { instanceCount: 1 } };\n } else if (cpu < 0.30 && data.obs.instances > 1) {\n action = { type: \"scale_in\", parameters: { instanceCount: 1 } };\n } else {\n action = NO_OP;\n }\n\n data = await step(action, 60);\n done = data.done;\n console.log(\n ` t=${String(data.t).padStart(4)} sim=${data.sim_time_human.padStart(8)}` +\n ` reward=${data.reward >= 0 ? \"+\" : \"\"}${data.reward.toFixed(3)}` +\n ` cpu=${(data.obs.cpu_util * 100).toFixed(1)}%` +\n ` p95=${data.metrics.latency_p95} ms` +\n ` cost=$${data.metrics.cost_usd_hr.toFixed(2)}/hr` +\n ` done=${done}`\n );\n}\n"},{"lang":"Python","label":"Python – mid-episode cold instance (warmup_factor)","source":"\"\"\"\nMid-episode warmup_factor safe-access pattern.\n\nobs.warmup_factor is an optional field — it is ABSENT from the observation\nvector until the first cold compute instance exists in the episode. This happens\nin two cases:\n 1. An `add_resource` action provisions a new compute resource mid-episode.\n 2. Canvas autoscaling adds a compute replica in response to high CPU/traffic.\n\nBecause the field appears dynamically, agents building a fixed-width\nobservation vector must default to 1.0 (fully warmed) when the field is absent:\n\n wf = obs.get(\"warmup_factor\", 1.0)\n\nA value < 1.0 means at least one compute instance is still warming up and is\nnot yet at full throughput. Use wf directly as a feature — do not re-derive it\nfrom step counters or metrics.compute[].warmup_steps_remaining.\n\nThe pattern below shows a typical episode loop that handles both the pre-warmup\nphase (field absent) and the post-add_resource warm-up window (field in (0, 1]).\n\"\"\"\nimport requests\n\nBASE_URL = \"https://your-production-domain.com/api\"\nAPI_KEY = \"your-api-key\"\nENV_ID = \"env-aws-001\"\n\nsession = requests.Session()\nsession.headers.update({\"Authorization\": f\"Bearer {API_KEY}\"})\n\ndef step(action: dict) -> dict:\n resp = session.post(\n f\"{BASE_URL}/rl/environments/{ENV_ID}/step\",\n json={\"action\": action},\n )\n resp.raise_for_status()\n return resp.json()\n\ndone = False\nwhile not done:\n obs = step({\"type\": \"no_op\", \"parameters\": {}})[\"obs\"]\n wf = obs.get(\"warmup_factor\", 1.0) # 1.0 = fully warmed / no cold instance\n\n cpu = obs[\"cpu_util\"]\n\n effective_cpu = cpu / wf if wf > 0 else cpu\n\n if effective_cpu > 0.75:\n action = {\"type\": \"scale_out\", \"parameters\": {\"instanceCount\": 1}}\n elif effective_cpu < 0.30 and obs[\"instances\"] > 1:\n action = {\"type\": \"scale_in\", \"parameters\": {\"instanceCount\": 1}}\n else:\n action = {\"type\": \"no_op\", \"parameters\": {}}\n\n data = step(action)\n done = data[\"done\"]\n print(\n f\" t={data['t']:4d} wf={wf:.2f} \"\n f\"cpu={cpu:.1%} effective_cpu={effective_cpu:.1%} \"\n f\"reward={data['reward']:+.3f} done={done}\"\n )\n"},{"lang":"Node.js","label":"Node.js – mid-episode cold instance (warmup_factor)","source":"/**\n * Mid-episode warmup_factor safe-access pattern.\n *\n * obs.warmup_factor is an optional field — it is ABSENT from the observation\n * vector until the first cold compute instance exists in the episode. This\n * happens in two cases:\n * 1. An `add_resource` action provisions a new compute resource mid-episode.\n * 2. Canvas autoscaling adds a compute replica in response to high CPU/traffic.\n *\n * Because the field appears dynamically, agents building a fixed-width\n * observation vector must default to 1.0 (fully warmed) when the field is absent:\n *\n * const wf = data.obs.warmup_factor ?? 1.0;\n *\n * A value < 1.0 means at least one compute instance is still warming up.\n * Use wf directly as a feature — do not re-derive it from\n * metrics.compute[].warmup_steps_remaining.\n */\nconst BASE_URL = \"https://your-production-domain.com/api\";\nconst API_KEY = \"your-api-key\";\nconst ENV_ID = \"env-aws-001\";\n\nasync function step(action) {\n const resp = await fetch(`${BASE_URL}/rl/environments/${ENV_ID}/step`, {\n method: \"POST\",\n headers: {\n \"Authorization\": `Bearer ${API_KEY}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ action }),\n });\n if (!resp.ok) throw new Error(`step failed: ${resp.status}`);\n return resp.json();\n}\n\nlet done = false;\nlet data = await step({ type: \"no_op\", parameters: {} });\nwhile (!done) {\n // --- build observation vector (always safe even if field absent) ---\n const obs = data.obs;\n const wf = obs.warmup_factor ?? 1.0; // 1.0 = fully warmed / no cold instance\n\n const cpu = obs.cpu_util;\n // penalise decisions made while a cold instance is still warming up\n const effectiveCpu = wf > 0 ? cpu / wf : cpu;\n\n let action;\n if (effectiveCpu > 0.75) {\n action = { type: \"scale_out\", parameters: { instanceCount: 1 } };\n } else if (effectiveCpu < 0.30 && obs.instances > 1) {\n action = { type: \"scale_in\", parameters: { instanceCount: 1 } };\n } else {\n action = { type: \"no_op\", parameters: {} };\n }\n\n data = await step(action);\n done = data.done;\n console.log(\n ` t=${String(data.t).padStart(4)} wf=${wf.toFixed(2)}` +\n ` cpu=${(cpu * 100).toFixed(1)}% effectiveCpu=${(effectiveCpu * 100).toFixed(1)}%` +\n ` reward=${data.reward >= 0 ? \"+\" : \"\"}${data.reward.toFixed(3)} done=${done}`\n );\n}\n"}],"security":[{"x402":[]}],"x-payment-info":{"protocols":["x402"],"price":{"mode":"fixed","currency":"USD","amount":"0.001"}},"x-x402-call-type":"rl_step","x-x402-price-usdc":"$0.0010","parameters":[{"name":"environmentId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"ID of the RL environment","example":"env-aws-001"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["action"],"properties":{"action":{"type":"object","required":["type"],"properties":{"type":{"type":"string","enum":["adjust_threshold","scale_out","scale_in","add_resource","remove_resource","no_op","set_recovery_policy"],"description":"Type of action to execute"},"parameters":{"type":"object","additionalProperties":true,"description":"Action-specific parameters (cpuThreshold, instanceCount, resourceId, etc.)"}},"example":{"type":"scale_out","parameters":{"instanceCount":1}}},"tick_seconds":{"type":"integer","minimum":1,"maximum":3600,"description":"Per-step override for the simulation clock advancement. When provided,\nthis value overrides the episode-level `tick_seconds` set in `episodeConfig`\nfor this step only. Useful for agents that want to fast-forward through\nwarm-up phases (e.g. 300 s ticks) and then switch to finer-grained steps\n(e.g. 60 s ticks) for precise autoscaling decisions. The actual value\nused is reflected in `observation.tick_seconds` in the response.\n","example":300}},"example":{"action":{"type":"scale_out","parameters":{"instanceCount":1}}}},"examples":{"doAdjustThreshold":{"summary":"DO: tune Droplet autoscaling thresholds","description":"Lower the CPU scale-out trigger from the default 70 % to 65 % so the\nDigitalOcean autoscaler scales out earlier, reducing latency during wave\ntraffic peaks. Also tightens the throughput threshold to 70 %.\n","value":{"action":{"type":"adjust_threshold","parameters":{"cpuThreshold":65,"throughputThreshold":70,"latencyThreshold":160}}}},"doScaleOut":{"summary":"DO: proactively add a Droplet replica","description":"Force-provision one additional s-2vcpu-4gb Droplet replica ahead of a\npredicted traffic spike. The first observation after this action will\nreflect the ~30 s cold-start latency overhead modelled for DigitalOcean.\n","value":{"action":{"type":"scale_out","parameters":{"instanceCount":1}}}},"doScaleIn":{"summary":"DO: remove an idle Droplet replica","description":"Remove one Droplet replica when traffic has subsided. The 180 s cooldown\nin the DigitalOcean autoscaling profile prevents thrashing between\nscale-in and scale-out actions.\n","value":{"action":{"type":"scale_in","parameters":{"instanceCount":1}}}},"digitaloceanAMDNVMeScaleOut":{"summary":"DO AMD NVMe: add an s-2vcpu-4gb-amd Droplet replica","description":"Force-provision one additional AMD NVMe Droplet (s-2vcpu-4gb-amd,\n$0.038/hr per instance) ahead of a predicted traffic spike. The AMD\nEPYC variant's NVMe-backed local storage delivers higher I/O throughput\nthan the standard Intel Droplet at the same price point. After the ~30 s\ncold-start overhead the cluster moves from 2 to 3 instances, bringing\nCPU utilisation down from ~71 % to ~56 % and P95 latency from ~148 ms\nto ~118 ms. This action triggers the `digitaloceanAMDNVMeStep` response\nshape, where resources are named \"Droplet s-2vcpu-4gb-amd\" to\ndistinguish AMD NVMe Droplets from standard Intel Droplets.\n","value":{"action":{"type":"scale_out","parameters":{"instanceCount":1}}}}}}}},"responses":{"200":{"description":"Step executed successfully","content":{"application/json":{"schema":{"type":"object","required":["t","obs","metrics","reward","reward_components","done","sim_time_human","info"],"properties":{"t":{"type":"integer","description":"Current simulation time step (incremented after each action)","example":15},"obs":{"$ref":"#/components/schemas/RLObs"},"metrics":{"$ref":"#/components/schemas/RLMetrics"},"resources":{"type":"array","description":"Full resource list with per-resource `recoveryPolicy`. Resources that have never had `set_recovery_policy` applied carry the global defaults (criticalCpuThreshold: 80, criticalSteps: 4, warningCpuThreshold: 70, warningSteps: 3). Use this to confirm a `set_recovery_policy` action took effect or to compare healing configurations across resources.\n","items":{"$ref":"#/components/schemas/Resource"}},"reward":{"type":"number","description":"Scalar total reward for this step (weighted sum of reward_components)","example":0.481},"reward_components":{"type":"object","description":"Individual reward sub-scores before weighting","properties":{"performance":{"type":"number","description":"Performance score (0–1, based on latency and errors)","example":0.812},"cost":{"type":"number","description":"Cost efficiency score (0–1, based on budget)","example":0.924},"stability":{"type":"number","description":"Stability score (−1 to 1, penalizes excessive changes)","example":-0.1},"sla":{"type":"number","description":"SLA compliance score (−1 to 0, penalizes violations)","example":0},"connection_pressure":{"type":"number","description":"DB connection-pool saturation penalty. Only present when the simulation contains database resources. 0 when pool pressure ≤ 1.0 (healthy); decreases with slope −1 per unit of pressure from 1.0 to 1.5 (reaching −0.5), then drops with slope −2 per unit above 1.5 — twice as steep — flooring at −1.0 at pressure ≥ 1.75. Added directly to the weighted sum of the other four components so agents are penalised for driving pools into exhaustion even before latency rises. NOTE: This component is absent until the episode first contains a database resource. When a DB is added mid-episode (via add_resource), the penalty is ramped in linearly over 5 steps (starting at 1/5 of its full magnitude on the first step it appears, reaching full strength after 5 steps) so its introduction does not cause a sudden step-to-step reward discontinuity. Agents may still treat the first appearance as a near-zero baseline.\n","example":-0.3},"unmodeled_cost":{"type":"number","description":"Penalty applied when one or more cost dimensions are active but not modeled in the reward function (e.g. egress charges, cross-AZ traffic). Absent when no unmodeled dimensions are detected. Magnitude equals -(count × unmodeled_cost_penalty_per_dimension). Agents should treat this as a persistent blind-spot signal rather than a transient cost spike.\n","example":-0.2}}},"done":{"type":"boolean","description":"Whether the episode is complete","example":false},"sim_time_human":{"type":"string","description":"Human-readable representation of the elapsed simulated time.\nFormat is `Xh Ym` when at least one hour has elapsed,\n`Xm Ys` when at least one minute (but less than one hour)\nhas elapsed, and `Xs` for less than one minute.\nMirrors the value inside `info.sim_time_human` for convenient\ntop-level access without unpacking the info object.\n","example":"15s"},"info":{"type":"object","description":"Additional diagnostic information","additionalProperties":true,"properties":{"stepMetrics":{"type":"object","description":"Raw metrics from this step"},"eventsGenerated":{"type":"integer","description":"Number of events generated this step"},"currentCost":{"type":"number","description":"Current cost per hour"},"sim_time_human":{"type":"string","description":"Human-readable representation of the elapsed simulated time.\nFormat is `Xh Ym` when at least one hour has elapsed,\n`Xm Ys` when at least one minute (but less than one hour)\nhas elapsed, and `Xs` for less than one minute.\n","example":"1h 0m"},"scale_clamped":{"type":"boolean","description":"Present and `true` when a `scale_out` or `scale_in` action was trimmed to honour the effective per-resource instance bounds (`characteristics.maxInstances` / `characteristics.minInstances` vs `autoscalingConfig.maxInstances` / `autoscalingConfig.minInstances`, whichever is tighter). Absent when no clamping occurred.\n","example":true},"requested":{"type":"integer","description":"Present when `scale_clamped` is `true`. The number of instances the action *requested* to add (scale_out) or remove (scale_in) — i.e. the `instanceCount` action parameter.\n","example":10},"actual":{"type":"integer","description":"Present when `scale_clamped` is `true`. The number of instances that were *actually* added (scale_out) or removed (scale_in) after applying the effective bound. `0` when the action was fully blocked (fleet is already at the hard limit).\n","example":2},"limit":{"type":"integer","description":"Present when `scale_clamped` is `true`. The effective bound that triggered clamping (`effectiveMax` for scale_out or `effectiveMin` for scale_in).\n","example":3}}},"tradeoffSummary":{"$ref":"#/components/schemas/RLTradeoffSummary"},"unmodeled_cost_warning":{"type":"array","items":{"type":"string"},"description":"List of cost dimension identifiers that are active this step but not modeled in the reward function. Present (and non-empty) only when at least one unmodeled dimension is detected. Omitted when all detected dimensions are modeled. Known values: `egress` (network egress charges are incurred whenever traffic > 0), `cross_az_traffic` (cross-AZ data transfer charges apply when resources with different availability zones are connected).\n","example":["egress","cross_az_traffic"]},"unmodeled_cost_penalty":{"type":"number","description":"Total reward penalty applied this step due to unmodeled cost dimensions. Equals -(count of active unmodeled dimensions × episodeConfig.unmodeled_cost_penalty_per_dimension). Present only when `unmodeled_cost_warning` is non-empty.\n","example":-0.2}}},"examples":{"awsStepWithDb":{"summary":"AWS — step response after scale-out (EC2 m5.large + RDS, us-east-1)","description":"The agent scaled out by 1 EC2 instance (now 3 × m5.large). CPU dropped\nfrom 69 % to 53 %, P95 latency is well within the 200 ms SLA, and cost\nrose to $0.57/hr. connection_pressure reflects the RDS Multi-AZ\nconnection-pool ratio; 0.42 is healthy (well below pool exhaustion).\n","value":{"t":42,"obs":{"rps":4750,"cpu_util":0.534,"instances":3,"traffic":4750,"currentTime":42},"metrics":{"cost_usd_hr":0.57,"latency_p95":98,"error_rate":0.003,"uptime":0.997,"sla_violations":0,"connection_pressure":0.42},"reward":0.531,"reward_components":{"performance":0.841,"cost":0.91,"stability":-0.1,"sla":0},"done":false,"sim_time_human":"42s","info":{"stepMetrics":{"cpuUsage":53.4,"throughput":4750},"eventsGenerated":1,"currentCost":0.57,"sim_time_human":"42s"}}},"gcpStepWithDb":{"summary":"GCP — step response after no-op (GCE e2-standard-4 + Cloud SQL, us-central1)","description":"The agent issued a no-op while the cluster ran at 2 × e2-standard-4 GCE\ninstances with Cloud Load Balancing and Cloud SQL. CPU is stable at 48 %,\nP95 latency is 91 ms, and cost is $0.44/hr. connection_pressure reflects\nthe Cloud SQL connection-pool ratio; 0.38 indicates plenty of headroom.\n","value":{"t":30,"obs":{"rps":3820,"cpu_util":0.478,"instances":2,"traffic":3820,"currentTime":30},"metrics":{"cost_usd_hr":0.44,"latency_p95":91,"error_rate":0.002,"uptime":0.998,"sla_violations":0,"connection_pressure":0.38},"reward":0.612,"reward_components":{"performance":0.873,"cost":0.951,"stability":0,"sla":0},"done":false,"sim_time_human":"30s","info":{"stepMetrics":{"cpuUsage":47.8,"throughput":3820},"eventsGenerated":0,"currentCost":0.44,"sim_time_human":"30s"}}},"azureStepWithDb":{"summary":"Azure — step response after no-op (Standard_D4s_v3 + Azure SQL, East US)","description":"The agent issued a no-op while the cluster ran at 2 × Standard_D4s_v3\nVMs behind Azure Load Balancer with Azure SQL. CPU is at 55 %, P95\nlatency is 104 ms, and cost is $0.52/hr. connection_pressure reflects\nthe Azure SQL connection-pool ratio; 0.51 is moderate but healthy.\n","value":{"t":28,"obs":{"rps":4300,"cpu_util":0.551,"instances":2,"traffic":4300,"currentTime":28},"metrics":{"cost_usd_hr":0.52,"latency_p95":104,"error_rate":0.003,"uptime":0.997,"sla_violations":0,"connection_pressure":0.51},"reward":0.487,"reward_components":{"performance":0.796,"cost":0.938,"stability":0,"sla":0},"done":false,"sim_time_human":"28s","info":{"stepMetrics":{"cpuUsage":55.1,"throughput":4300},"eventsGenerated":0,"currentCost":0.52,"sim_time_human":"28s"}}},"ociStepWithDb":{"summary":"OCI — step response after scale-in (VM.Standard3.Flex + Autonomous DB, us-ashburn-1)","description":"The agent scaled in by 1 instance (now 2 × VM.Standard3.Flex) after\ntraffic subsided. CPU is low at 34 %, P95 latency is 72 ms, and cost\nis $0.31/hr. connection_pressure reflects the Autonomous Database\nconnection-pool ratio; 0.29 is well within healthy bounds.\n","value":{"t":25,"obs":{"rps":4820,"cpu_util":0.342,"instances":2,"traffic":4820,"currentTime":25},"metrics":{"cost_usd_hr":0.31,"latency_p95":72,"error_rate":0.001,"uptime":0.999,"sla_violations":0,"connection_pressure":0.29},"reward":0.703,"reward_components":{"performance":0.921,"cost":0.985,"stability":-0.1,"sla":0},"done":false,"sim_time_human":"25s","info":{"stepMetrics":{"cpuUsage":34.2,"throughput":4820},"eventsGenerated":1,"currentCost":0.31,"sim_time_human":"25s"}}},"doStepAfterScaleOut":{"summary":"DO: step response after scaling out to 3 Droplet replicas (with Managed PostgreSQL)","description":"The agent scaled out by 1 Droplet (now 3 × s-2vcpu-4gb). CPU dropped\nfrom 72 % to 58 %, P95 latency improved to 104 ms, and cost rose to\n$1.08/hr (within the $3.50/hr budget). Reward is positive because the\nSLA is satisfied and cost efficiency is high. connection_pressure\nreflects the Managed PostgreSQL connection-pool ratio; 0.44 is healthy.\n","value":{"t":15,"obs":{"rps":1620,"cpu_util":0.582,"instances":3,"traffic":1620,"currentTime":15},"metrics":{"cost_usd_hr":1.08,"latency_p95":104,"error_rate":0.003,"uptime":0.997,"sla_violations":0,"connection_pressure":0.44},"reward":0.481,"reward_components":{"performance":0.812,"cost":0.924,"stability":-0.1,"sla":0},"done":false,"sim_time_human":"15s","info":{"stepMetrics":{"cpuUsage":58.2,"throughput":1620},"eventsGenerated":1,"currentCost":1.08,"sim_time_human":"15s"}}},"doStepThresholdTune":{"summary":"DO: step response after tuning CPU/throughput thresholds","description":"The agent lowered the CPU threshold to 65 % without changing instance\ncount. The cluster runs at 2 Droplets. Metrics are stable; the small\nstability penalty (-0.05) reflects the configuration change itself.\nNo database resource in this scenario — connection_pressure is absent.\n","value":{"t":8,"obs":{"rps":1480,"cpu_util":0.614,"instances":2,"traffic":1480,"currentTime":8},"metrics":{"cost_usd_hr":0.72,"latency_p95":118,"error_rate":0.004,"uptime":0.996,"sla_violations":0},"reward":0.392,"reward_components":{"performance":0.743,"cost":0.96,"stability":-0.05,"sla":0},"done":false,"sim_time_human":"15s","info":{"stepMetrics":{"cpuUsage":61.4,"throughput":1480},"eventsGenerated":0,"currentCost":0.72,"sim_time_human":"15s"}}},"digitaloceanAMDNVMeStep":{"summary":"DigitalOcean — step response after scale-out (s-2vcpu-4gb-amd AMD NVMe Droplets, nyc3)","description":"The agent scaled out by 1 AMD NVMe Droplet (now 3 × s-2vcpu-4gb-amd,\n$0.038/hr per instance). CPU dropped from 71 % to 56 %, P95 latency\nimproved to 98 ms, and cost rose to $0.93/hr (within the $3.50/hr budget).\nThe per-instance compute cost is $0.038/hr, so 3 instances total $0.114/hr\nfor compute alone; `metrics.cost_usd_hr` (0.93) is the full simulated stack\ncost including the DO Load Balancer, Managed PostgreSQL, and modelled\noverhead. The AMD EPYC variant's NVMe-backed local storage yields slightly\nlower P95 latency than the Intel s-2vcpu-4gb equivalent at the same traffic\nlevel — 98 ms vs ~104 ms after scale-out. connection_pressure reflects the\nManaged PostgreSQL db-s-2vcpu-4gb connection-pool ratio; 0.41 is healthy.\nResources are named \"Droplet s-2vcpu-4gb-amd\" to distinguish AMD NVMe\nDroplets from standard Intel Droplets.\n","value":{"t":20,"obs":{"rps":1620,"cpu_util":0.561,"instances":3,"traffic":1620,"currentTime":20},"metrics":{"cost_usd_hr":0.93,"latency_p95":98,"error_rate":0.002,"uptime":0.998,"sla_violations":0,"connection_pressure":0.41},"reward":0.514,"reward_components":{"performance":0.834,"cost":0.918,"stability":-0.1,"sla":0},"done":false,"sim_time_human":"20s","info":{"stepMetrics":{"cpuUsage":56.1,"throughput":1620},"eventsGenerated":1,"currentCost":0.93,"sim_time_human":"20s"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/X402PaymentRequired"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/rl/environments/{environmentId}/batch-step":{"x-stability":"stable","post":{"tags":["RL Environments"],"summary":"Execute multiple actions in a single request","description":"Executes up to 30 actions sequentially in a single HTTP round-trip, advancing the\nsimulation by one step per action. Stops early and returns partial results if the\nepisode ends (`done: true`) before all actions are processed.\n\n**Rate limiting:** Each action in the batch counts as one call against the\n5 000 req/hr RL training quota. A batch of 30 actions consumes 30 quota units.\nIf the quota is exhausted mid-batch, the endpoint returns 429 with a `Retry-After`\nheader indicating how many seconds remain until the window resets.\n\n**Idle TTL:** The last successful batch-step call resets the environment's 2-hour\nidle timer, the same as a single `step` call.\n","operationId":"batchStepRLEnvironment","x-codeSamples":[{"lang":"curl","label":"curl","source":"curl -X POST https://your-production-domain.com/api/rl/environments/env-aws-001/batch-step \\\n -H \"Authorization: Bearer $API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"steps\": [\n {\"action\": {\"type\": \"scale_out\", \"parameters\": {\"instanceCount\": 1}}},\n {\"action\": {\"type\": \"no_op\", \"parameters\": {}}},\n {\"action\": {\"type\": \"no_op\", \"parameters\": {}}}\n ]\n }'\n"},{"lang":"Python","label":"Python","source":"import time, requests\n\nBASE_URL = \"https://your-production-domain.com/api\"\nAPI_KEY = \"your-api-key\"\nENV_ID = \"env-aws-001\"\n\nsession = requests.Session()\nsession.headers.update({\"Authorization\": f\"Bearer {API_KEY}\"})\n\ndef batch_step(steps: list[dict], max_retries: int = 5) -> dict:\n for attempt in range(max_retries):\n resp = session.post(\n f\"{BASE_URL}/rl/environments/{ENV_ID}/batch-step\",\n json={\"steps\": steps},\n )\n if resp.status_code == 429:\n retry_after = int(resp.headers.get(\"Retry-After\", 60))\n print(f\"Rate limited — waiting {retry_after}s\")\n time.sleep(retry_after)\n continue\n resp.raise_for_status()\n return resp.json()\n raise RuntimeError(\"Exceeded max retries\")\n\nNO_OP = {\"action\": {\"type\": \"no_op\", \"parameters\": {}}}\nSCALE_OUT = {\"action\": {\"type\": \"scale_out\", \"parameters\": {\"instanceCount\": 1}}}\n\nwarmup = batch_step([{**NO_OP, \"tick_seconds\": 300}] * 30)\nprint(f\"Warm-up complete: {len(warmup['results'])} steps\")\n\ntotal_reward = 0.0\nwhile True:\n result = batch_step([SCALE_OUT] + [NO_OP] * 9)\n for step_result in result[\"results\"]:\n total_reward += step_result[\"reward\"]\n if step_result[\"done\"]:\n print(f\"Episode complete — total reward: {total_reward:.2f}\")\n break\n else:\n continue\n break\n"},{"lang":"Node.js","label":"Node.js","source":"const BASE_URL = \"https://your-production-domain.com/api\";\nconst API_KEY = \"your-api-key\";\nconst ENV_ID = \"env-aws-001\";\n\nasync function batchStep(steps, maxRetries = 5) {\n for (let attempt = 0; attempt < maxRetries; attempt++) {\n const resp = await fetch(`${BASE_URL}/rl/environments/${ENV_ID}/batch-step`, {\n method: \"POST\",\n headers: { \"Authorization\": `Bearer ${API_KEY}`, \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ steps }),\n });\n if (resp.status === 429) {\n const retryAfter = parseInt(resp.headers.get(\"Retry-After\") ?? \"60\", 10);\n console.log(`Rate limited — waiting ${retryAfter}s`);\n await new Promise(r => setTimeout(r, retryAfter * 1000));\n continue;\n }\n if (!resp.ok) throw new Error(`HTTP ${resp.status}`);\n return resp.json();\n }\n throw new Error(\"Exceeded max retries\");\n}\n\nconst NO_OP = { action: { type: \"no_op\", parameters: {} } };\nconst SCALE_OUT = { action: { type: \"scale_out\", parameters: { instanceCount: 1 } } };\n\n// 30-step warm-up at 300 s ticks\nconst warmup = await batchStep(Array(30).fill({ ...NO_OP, tick_seconds: 300 }));\nconsole.log(`Warm-up complete: ${warmup.results.length} steps`);\n\n// Training loop using 10-step batches\nlet totalReward = 0;\nlet done = false;\nwhile (!done) {\n const { results } = await batchStep([SCALE_OUT, ...Array(9).fill(NO_OP)]);\n for (const r of results) {\n totalReward += r.reward;\n if (r.done) { done = true; break; }\n }\n}\nconsole.log(`Episode complete — total reward: ${totalReward.toFixed(2)}`);\n"}],"security":[],"parameters":[{"name":"environmentId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"ID of the RL environment"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["steps"],"properties":{"steps":{"type":"array","minItems":1,"maxItems":30,"description":"Ordered list of step actions to execute (max 30)","items":{"type":"object","required":["action"],"properties":{"action":{"$ref":"#/components/schemas/Action"},"tick_seconds":{"type":"integer","minimum":1,"maximum":3600,"description":"Simulated seconds to advance per step (overrides environment default)"}}}}}},"examples":{"threeStepBatch":{"summary":"Scale out then observe","value":{"steps":[{"action":{"type":"scale_out","parameters":{"instanceCount":1}}},{"action":{"type":"no_op","parameters":{}}},{"action":{"type":"no_op","parameters":{}}}]}},"warmupBatch":{"summary":"30-step warm-up at 300 s ticks","value":{"steps":[{"action":{"type":"no_op","parameters":{}},"tick_seconds":300}]}}}}}},"responses":{"200":{"description":"Batch steps executed successfully","content":{"application/json":{"schema":{"type":"object","required":["results"],"properties":{"results":{"type":"array","description":"Step results in the same order as the request `steps` array.\nMay be shorter than the request array if the episode ended early\n(`done: true` in the last result).\n","items":{"type":"object","required":["t","obs","metrics","reward","reward_components","done","sim_time_human","info"],"properties":{"t":{"type":"integer","description":"Current step index"},"obs":{"$ref":"#/components/schemas/RLObs"},"metrics":{"$ref":"#/components/schemas/RLMetrics"},"resources":{"type":"array","description":"Full resource list with per-resource recoveryPolicy","items":{"$ref":"#/components/schemas/Resource"}},"reward":{"type":"number","description":"Scalar total reward for this step"},"reward_components":{"type":"object","properties":{"performance":{"type":"number"},"cost":{"type":"number"},"stability":{"type":"number"},"sla":{"type":"number"},"connection_pressure":{"type":"number","description":"DB connection-pool saturation penalty. Only present when the simulation contains database resources. 0 when healthy; negative (floor −1.0) when pool is exhausted. When a DB is added mid-episode the penalty is ramped in linearly over 5 steps so it does not cause a sudden reward jump. See step response for full formula.\n"}}},"done":{"type":"boolean","description":"True when the episode has ended"},"sim_time_human":{"type":"string","description":"Human-readable simulation time"},"info":{"type":"object","additionalProperties":true,"description":"Additional episode metadata. When a `scale_out` or `scale_in` action is trimmed to honour per-resource bounds (`characteristics.maxInstances` / `characteristics.minInstances`), the following flat keys are added: `scale_clamped` (boolean, always `true`), `requested` (integer — instances requested), `actual` (integer — instances applied, `0` if fully blocked), `limit` (integer — effective bound). Absent when no clamping occurred.\n"}}}}}},"examples":{"threeStepResult":{"summary":"Three-step batch — scale out then two no-ops","value":{"results":[{"t":1,"obs":{"rps":4900,"cpu_util":0.58,"instances":3,"traffic":4900,"currentTime":60,"tick_seconds":60},"metrics":{"cost_usd_hr":0.41,"latency_p95":112,"error_rate":0.004,"uptime":0.996,"sla_violations":0},"reward":0.734,"reward_components":{"performance":0.812,"cost":0.901,"stability":0,"sla":0},"done":false,"sim_time_human":"1m 0s","info":{"sim_time_human":"1m 0s"},"resources":[]},{"t":2,"obs":{"rps":5100,"cpu_util":0.44,"instances":3,"traffic":5100,"currentTime":120,"tick_seconds":60},"metrics":{"cost_usd_hr":0.41,"latency_p95":89,"error_rate":0.001,"uptime":0.999,"sla_violations":0},"reward":0.819,"reward_components":{"performance":0.889,"cost":0.901,"stability":0,"sla":0},"done":false,"sim_time_human":"2m 0s","info":{"sim_time_human":"2m 0s"},"resources":[]}]}},"awsBatchWithDb":{"summary":"AWS — batch-step (EC2 m5.large + RDS Multi-AZ, us-east-1)","description":"Two-step batch: scale out by 1 EC2 instance, then observe with a\nno-op. Each step's metrics include connection_pressure, which\nreflects the RDS Multi-AZ connection-pool ratio; values near 0.4\nare healthy (well below pool exhaustion).\n","value":{"results":[{"t":1,"obs":{"rps":4750,"cpu_util":0.61,"instances":3,"traffic":4750,"currentTime":60,"tick_seconds":60},"metrics":{"cost_usd_hr":0.57,"latency_p95":108,"error_rate":0.004,"uptime":0.996,"sla_violations":0,"connection_pressure":0.45},"reward":0.531,"reward_components":{"performance":0.812,"cost":0.901,"stability":-0.1,"sla":0},"done":false,"sim_time_human":"1m 0s","info":{"sim_time_human":"1m 0s"},"resources":[]},{"t":2,"obs":{"rps":4820,"cpu_util":0.53,"instances":3,"traffic":4820,"currentTime":120,"tick_seconds":60},"metrics":{"cost_usd_hr":0.57,"latency_p95":98,"error_rate":0.003,"uptime":0.997,"sla_violations":0,"connection_pressure":0.42},"reward":0.612,"reward_components":{"performance":0.841,"cost":0.91,"stability":0,"sla":0},"done":false,"sim_time_human":"2m 0s","info":{"sim_time_human":"2m 0s"},"resources":[]}]}},"gcpBatchWithDb":{"summary":"GCP — batch-step (GCE e2-standard-4 + Cloud SQL, us-central1)","description":"Two-step batch of no-ops while the cluster runs at 2 × e2-standard-4\nwith Cloud Load Balancing and Cloud SQL. Each step's metrics include\nconnection_pressure, which reflects the Cloud SQL connection-pool\nratio; 0.38 indicates plenty of headroom.\n","value":{"results":[{"t":1,"obs":{"rps":3820,"cpu_util":0.48,"instances":2,"traffic":3820,"currentTime":60,"tick_seconds":60},"metrics":{"cost_usd_hr":0.44,"latency_p95":91,"error_rate":0.002,"uptime":0.998,"sla_violations":0,"connection_pressure":0.38},"reward":0.612,"reward_components":{"performance":0.873,"cost":0.951,"stability":0,"sla":0},"done":false,"sim_time_human":"1m 0s","info":{"sim_time_human":"1m 0s"},"resources":[]},{"t":2,"obs":{"rps":3760,"cpu_util":0.46,"instances":2,"traffic":3760,"currentTime":120,"tick_seconds":60},"metrics":{"cost_usd_hr":0.44,"latency_p95":88,"error_rate":0.002,"uptime":0.998,"sla_violations":0,"connection_pressure":0.36},"reward":0.628,"reward_components":{"performance":0.884,"cost":0.951,"stability":0,"sla":0},"done":false,"sim_time_human":"2m 0s","info":{"sim_time_human":"2m 0s"},"resources":[]}]}},"azureBatchWithDb":{"summary":"Azure — batch-step (Standard_D4s_v3 + Azure SQL, East US)","description":"Two-step batch of no-ops while the cluster runs at 2 × Standard_D4s_v3\nVMs behind Azure Load Balancer with Azure SQL. Each step's metrics\ninclude connection_pressure, which reflects the Azure SQL\nconnection-pool ratio; 0.51 is moderate but healthy.\n","value":{"results":[{"t":1,"obs":{"rps":4300,"cpu_util":0.55,"instances":2,"traffic":4300,"currentTime":60,"tick_seconds":60},"metrics":{"cost_usd_hr":0.52,"latency_p95":104,"error_rate":0.003,"uptime":0.997,"sla_violations":0,"connection_pressure":0.51},"reward":0.487,"reward_components":{"performance":0.796,"cost":0.938,"stability":0,"sla":0},"done":false,"sim_time_human":"1m 0s","info":{"sim_time_human":"1m 0s"},"resources":[]},{"t":2,"obs":{"rps":4360,"cpu_util":0.56,"instances":2,"traffic":4360,"currentTime":120,"tick_seconds":60},"metrics":{"cost_usd_hr":0.52,"latency_p95":106,"error_rate":0.003,"uptime":0.997,"sla_violations":0,"connection_pressure":0.49},"reward":0.482,"reward_components":{"performance":0.79,"cost":0.938,"stability":0,"sla":0},"done":false,"sim_time_human":"2m 0s","info":{"sim_time_human":"2m 0s"},"resources":[]}]}},"ociBatchWithDb":{"summary":"OCI — batch-step (VM.Standard3.Flex + Autonomous DB, us-ashburn-1)","description":"Two-step batch: scale in by 1 instance, then observe with a no-op\n(now 2 × VM.Standard3.Flex). Each step's metrics include\nconnection_pressure, which reflects the Autonomous Database\nconnection-pool ratio; 0.29 is well within healthy bounds.\n","value":{"results":[{"t":1,"obs":{"rps":4820,"cpu_util":0.34,"instances":2,"traffic":4820,"currentTime":60,"tick_seconds":60},"metrics":{"cost_usd_hr":0.31,"latency_p95":72,"error_rate":0.001,"uptime":0.999,"sla_violations":0,"connection_pressure":0.31},"reward":0.703,"reward_components":{"performance":0.921,"cost":0.985,"stability":-0.1,"sla":0},"done":false,"sim_time_human":"1m 0s","info":{"sim_time_human":"1m 0s"},"resources":[]},{"t":2,"obs":{"rps":4780,"cpu_util":0.33,"instances":2,"traffic":4780,"currentTime":120,"tick_seconds":60},"metrics":{"cost_usd_hr":0.31,"latency_p95":70,"error_rate":0.001,"uptime":0.999,"sla_violations":0,"connection_pressure":0.29},"reward":0.812,"reward_components":{"performance":0.934,"cost":0.985,"stability":0,"sla":0},"done":false,"sim_time_human":"2m 0s","info":{"sim_time_human":"2m 0s"},"resources":[]}]}},"doBatchWithDb":{"summary":"DO — batch-step (Droplet s-2vcpu-4gb + Managed PostgreSQL)","description":"Two-step batch: scale out by 1 Droplet, then observe with a no-op\n(now 3 × s-2vcpu-4gb). Each step's metrics include\nconnection_pressure, which reflects the Managed PostgreSQL\nconnection-pool ratio; 0.44 is healthy.\n","value":{"results":[{"t":1,"obs":{"rps":1620,"cpu_util":0.58,"instances":3,"traffic":1620,"currentTime":60,"tick_seconds":60},"metrics":{"cost_usd_hr":1.08,"latency_p95":104,"error_rate":0.003,"uptime":0.997,"sla_violations":0,"connection_pressure":0.46},"reward":0.481,"reward_components":{"performance":0.812,"cost":0.924,"stability":-0.1,"sla":0},"done":false,"sim_time_human":"1m 0s","info":{"sim_time_human":"1m 0s"},"resources":[]},{"t":2,"obs":{"rps":1580,"cpu_util":0.55,"instances":3,"traffic":1580,"currentTime":120,"tick_seconds":60},"metrics":{"cost_usd_hr":1.08,"latency_p95":99,"error_rate":0.002,"uptime":0.998,"sla_violations":0,"connection_pressure":0.44},"reward":0.503,"reward_components":{"performance":0.831,"cost":0.924,"stability":0,"sla":0},"done":false,"sim_time_human":"2m 0s","info":{"sim_time_human":"2m 0s"},"resources":[]}]}},"digitaloceanAMDNVMeBatchStep":{"summary":"DO AMD NVMe — batch-step (s-2vcpu-4gb-amd Droplets + Managed PostgreSQL, nyc3)","description":"Two-step batch on a DigitalOcean simulation backed by AMD NVMe Droplets\n(s-2vcpu-4gb-amd, $0.038/hr per instance). Step 1 scales out by 1 Droplet\n(now 3 × s-2vcpu-4gb-amd), bringing CPU down from 71 % to 56 %. The\nper-instance compute cost is $0.038/hr, so 3 instances total $0.114/hr for\ncompute alone; `metrics.cost_usd_hr` (0.93) is the full simulated stack\ncost including the DO Load Balancer, Managed PostgreSQL, and modelled\noverhead. Step 2 is a no-op that confirms the cluster has stabilised. The\nAMD EPYC variant's NVMe-backed local storage yields slightly lower P95\nlatency than the Intel s-2vcpu-4gb equivalent at the same traffic level —\n98 ms vs ~104 ms after scale-out. connection_pressure reflects the Managed\nPostgreSQL db-s-2vcpu-4gb connection-pool ratio; 0.41 is healthy. Resources\nare named \"Droplet s-2vcpu-4gb-amd\" to distinguish AMD NVMe Droplets from\nstandard Intel Droplets.\n","value":{"results":[{"t":1,"obs":{"rps":1620,"cpu_util":0.561,"instances":3,"traffic":1620,"currentTime":60,"tick_seconds":60},"metrics":{"cost_usd_hr":0.93,"latency_p95":98,"error_rate":0.002,"uptime":0.998,"sla_violations":0,"connection_pressure":0.41},"reward":0.514,"reward_components":{"performance":0.834,"cost":0.918,"stability":-0.1,"sla":0},"done":false,"sim_time_human":"1m 0s","info":{"sim_time_human":"1m 0s"},"resources":[{"id":"res-lb-amd-001","name":"DO Load Balancer","type":"network","provider":"digitalocean","instances":1},{"id":"res-droplet-amd-001","name":"Droplet s-2vcpu-4gb-amd","type":"compute","provider":"digitalocean","instances":3},{"id":"res-pg-amd-001","name":"Managed PostgreSQL","type":"database","provider":"digitalocean","instances":1}]},{"t":2,"obs":{"rps":1580,"cpu_util":0.541,"instances":3,"traffic":1580,"currentTime":120,"tick_seconds":60},"metrics":{"cost_usd_hr":0.93,"latency_p95":94,"error_rate":0.002,"uptime":0.998,"sla_violations":0,"connection_pressure":0.4},"reward":0.531,"reward_components":{"performance":0.848,"cost":0.918,"stability":0,"sla":0},"done":false,"sim_time_human":"2m 0s","info":{"sim_time_human":"2m 0s"},"resources":[{"id":"res-lb-amd-001","name":"DO Load Balancer","type":"network","provider":"digitalocean","instances":1},{"id":"res-droplet-amd-001","name":"Droplet s-2vcpu-4gb-amd","type":"compute","provider":"digitalocean","instances":3},{"id":"res-pg-amd-001","name":"Managed PostgreSQL","type":"database","provider":"digitalocean","instances":1}]}]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"description":"RL rate limit exceeded (5 000 req/hr)","headers":{"Retry-After":{"description":"Seconds until the rate-limit window resets","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/rl/environments/{environmentId}/eval-episodes":{"x-stability":"stable","post":{"tags":["RL Environments"],"summary":"Submit an eval-episode job (async by default; ?sync=true for inline)","description":"Replays one or more ordered action sequences through a fresh episode reset and returns\nper-episode cumulative rewards computed with `evalCostOverrides` applied. Use this to\ndetect **reward blind spots**: policies trained without egress or cross-AZ cost pricing\nwill show a significant reward drop (reward_collapse) when those dimensions are priced.\n\nThe endpoint does **not** mutate any stored environment or simulation state — all\nepisode rollouts are in-memory and ephemeral.\n\n**Async mode (default):** Returns `202 Accepted` immediately with a `jobId`. Poll\n`GET /rl/environments/{environmentId}/eval-episodes/{jobId}` until `status` is\n`completed` or `failed`.\n\n**Sync mode (`?sync=true`):** Runs the rollout in-process and returns the full result\ninline with `200 OK`. Suitable for small workloads (≤ 3 episodes, ≤ 20 steps each).\n\n**reward_collapse logic:** `reward_collapse` is `true` when\n`meanEvalReward < trainingTotalReward − collapseThreshold × |trainingTotalReward|`.\nWith the default threshold of 0.20, any policy whose eval reward is more than 20 %\nbelow its training reward is flagged as collapsing.\n","operationId":"evalRLEpisodes","x-codeSamples":[{"lang":"curl","label":"curl","source":"curl -X POST https://your-production-domain.com/api/rl/environments/env-aws-001/eval-episodes \\\n -H \"Authorization: Bearer $API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"actions\": [\n [\n {\"type\": \"no_op\", \"parameters\": {}},\n {\"type\": \"scale_out\", \"parameters\": {\"instanceCount\": 1}},\n {\"type\": \"no_op\", \"parameters\": {}}\n ]\n ],\n \"evalCostOverrides\": {\"egress\": 0.5, \"cross_az_traffic\": 0.3},\n \"collapseThreshold\": 0.20\n }'\n"},{"lang":"Python","label":"Python","source":"import requests\n\nBASE_URL = \"https://your-production-domain.com/api\"\nAPI_KEY = \"your-api-key\"\nENV_ID = \"env-aws-001\"\n\nresp = requests.post(\n f\"{BASE_URL}/rl/environments/{ENV_ID}/eval-episodes\",\n headers={\"Authorization\": f\"Bearer {API_KEY}\"},\n json={\n \"actions\": [\n [{\"type\": \"no_op\", \"parameters\": {}}] * 10\n ],\n \"evalCostOverrides\": {\"egress\": 0.5, \"cross_az_traffic\": 0.3},\n \"collapseThreshold\": 0.20,\n },\n)\nresp.raise_for_status()\ndata = resp.json()\nprint(f\"reward_collapse={data['reward_collapse']} mean_eval={data['meanEvalReward']:.3f} training={data['trainingTotalReward']:.3f}\")\n"}],"security":[],"parameters":[{"name":"environmentId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"ID of the RL environment whose training reward is used as the baseline"},{"name":"sync","in":"query","required":false,"schema":{"type":"boolean","default":false},"description":"When `true`, run the eval synchronously and return the full result inline with `200 OK`. When omitted or `false` (default), create a background job and return `202 Accepted` with a `jobId`.\n"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["actions"],"properties":{"actions":{"type":"array","minItems":1,"maxItems":10,"description":"Array of episodes. Each episode is an ordered array of actions to replay from a fresh reset. All episodes use the same `evalCostOverrides`. Maximum 10 episodes.\n","items":{"type":"array","description":"Ordered sequence of actions for one eval episode","items":{"$ref":"#/components/schemas/Action"}}},"collapseThreshold":{"type":"number","minimum":0,"maximum":1,"default":0.2,"description":"Fractional drop in reward that triggers `reward_collapse`. Default is 0.20 (collapse when mean eval reward is more than 20 % below training total reward).\n"},"evalCostOverrides":{"type":"object","additionalProperties":{"type":"number","minimum":0},"description":"Per-dimension cost override rates in USD per 1 000 RPS per hour. Supported keys: `egress` (any simulation with traffic > 0) and `cross_az_traffic` (simulations with resources in distinct Availability Zones). These rates inflate `costPerHour` before the reward cost score is computed, revealing policies that exploit unpriced cost blind spots.\n","example":{"egress":0.5,"cross_az_traffic":0.3}}}},"examples":{"basicEval":{"summary":"Single eval episode with egress and cross-AZ costs priced","value":{"actions":[[{"type":"no_op","parameters":{}},{"type":"scale_out","parameters":{"instanceCount":1}},{"type":"no_op","parameters":{}}]],"evalCostOverrides":{"egress":0.5,"cross_az_traffic":0.3},"collapseThreshold":0.2}}}}}},"responses":{"200":{"description":"Eval episodes completed synchronously (only returned when `?sync=true` is set).\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RLEvalJobResult"},"examples":{"collapseDetected":{"summary":"Reward collapse detected — policy exploits egress blind spot","value":{"episodes":[{"episodeIndex":0,"stepsExecuted":3,"totalReward":1.82}],"meanEvalReward":1.82,"trainingTotalReward":4.5,"collapseThreshold":0.2,"reward_collapse":true}},"noCollapse":{"summary":"No collapse — policy is robust to unpriced cost dimensions","value":{"episodes":[{"episodeIndex":0,"stepsExecuted":3,"totalReward":4.21}],"meanEvalReward":4.21,"trainingTotalReward":4.5,"collapseThreshold":0.2,"reward_collapse":false}}}}}},"202":{"description":"Eval job accepted. The job is running asynchronously. Poll `GET /rl/environments/{environmentId}/eval-episodes/{jobId}` until `status` is `completed` or `failed`.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RLEvalJobAccepted"},"example":{"jobId":"3fa85f64-5717-4562-b3fc-2c963f66afa6","status":"pending","createdAt":"2026-01-01T00:00:00.000Z"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/ApiKeyUnauthorized"},"403":{"description":"Access denied — caller does not own this RL environment","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"$ref":"#/components/responses/NotFound"},"429":{"description":"RL rate limit exceeded (5 000 req/hr)","headers":{"Retry-After":{"description":"Seconds until the rate-limit window resets","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/rl/environments/{environmentId}/eval-episodes/{jobId}":{"x-stability":"stable","get":{"tags":["RL Environments"],"summary":"Retrieve an eval-episode job result","description":"Polls the status of an async eval job created by\n`POST /rl/environments/{environmentId}/eval-episodes`.\n\n- While the job is `pending` or `running` the endpoint returns `202` with only `{jobId, status, createdAt}`.\n- When the job is `completed` the endpoint returns `200` with the full eval result merged with job metadata.\n- When the job is `failed` the endpoint returns `200` with `{jobId, status, error, createdAt, completedAt}`.\n","operationId":"getRLEvalJob","security":[],"parameters":[{"name":"environmentId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"ID of the RL environment that owns this eval job"},{"name":"jobId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"ID of the eval job returned by the POST endpoint"}],"responses":{"200":{"description":"Job completed (or failed) — inspect `status` to distinguish","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/RLEvalJobCompleted"},{"$ref":"#/components/schemas/RLEvalJobFailed"}]},"examples":{"completed":{"summary":"Job completed successfully","value":{"jobId":"3fa85f64-5717-4562-b3fc-2c963f66afa6","status":"completed","createdAt":"2026-01-01T00:00:00.000Z","completedAt":"2026-01-01T00:00:01.234Z","episodes":[{"episodeIndex":0,"stepsExecuted":3,"totalReward":1.82}],"meanEvalReward":1.82,"trainingTotalReward":4.5,"collapseThreshold":0.2,"reward_collapse":true}},"failed":{"summary":"Job failed","value":{"jobId":"3fa85f64-5717-4562-b3fc-2c963f66afa6","status":"failed","error":"Environment or simulation no longer exists","createdAt":"2026-01-01T00:00:00.000Z","completedAt":"2026-01-01T00:00:00.100Z"}}}}}},"202":{"description":"Job is still pending or running — continue polling","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RLEvalJobAccepted"}}}},"401":{"$ref":"#/components/responses/ApiKeyUnauthorized"},"403":{"description":"Access denied — caller does not own this RL environment or job","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/rl/environments/{environmentId}/reset":{"x-stability":"stable","post":{"tags":["RL Environments"],"summary":"Reset an RL environment to start a new episode","description":"Resets the environment to its initial state, clearing all scaling history and events.\nUse this to start a new training episode after the previous one completes.\n","operationId":"resetRLEnvironment","x-codeSamples":[{"lang":"curl","label":"curl","source":"curl -X POST https://your-production-domain.com/api/rl/environments/env-aws-001/reset \\\n -H \"Authorization: Bearer $API_KEY\"\n"},{"lang":"Python","label":"Python","source":"import requests\n\nBASE_URL = \"https://your-production-domain.com/api\"\nAPI_KEY = \"your-api-key\"\nENV_ID = \"env-aws-001\"\n\nresp = requests.post(\n f\"{BASE_URL}/rl/environments/{ENV_ID}/reset\",\n headers={\"Authorization\": f\"Bearer {API_KEY}\"},\n)\nresp.raise_for_status()\ndata = resp.json()\nprint(f\"Environment reset — step: {data['environment']['currentStep']}\")\nprint(f\"Initial CPU: {data['observation']['metrics']['cpuUsage']}%\")\n"},{"lang":"Node.js","label":"Node.js","source":"const BASE_URL = \"https://your-production-domain.com/api\";\nconst API_KEY = \"your-api-key\";\nconst ENV_ID = \"env-aws-001\";\n\nconst resp = await fetch(`${BASE_URL}/rl/environments/${ENV_ID}/reset`, {\n method: \"POST\",\n headers: { \"Authorization\": `Bearer ${API_KEY}` },\n});\nconst data = await resp.json();\nconsole.log(`Environment reset — step: ${data.environment.currentStep}`);\nconsole.log(`Initial CPU: ${data.observation.metrics.cpuUsage}%`);\n"}],"security":[],"parameters":[{"name":"environmentId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"ID of the RL environment to reset"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"rewardWeights":{"$ref":"#/components/schemas/RewardWeights"}}}}}},"responses":{"200":{"description":"Environment reset successfully","content":{"application/json":{"schema":{"type":"object","properties":{"environment":{"$ref":"#/components/schemas/RLEnvironment"},"observation":{"$ref":"#/components/schemas/Observation"},"info":{"type":"object","description":"Metadata about the initial environment state","properties":{"sim_time_human":{"type":"string","description":"Human-readable simulation time at episode start (always \"0s\")","example":"0s"}}}}},"examples":{"awsReset":{"summary":"AWS — environment reset (EC2 m5.large cluster, us-east-1)","description":"Episode reset on an AWS simulation. Resources return to their initial\nstate: 2 × m5.large EC2 instances behind an ALB with RDS Multi-AZ.\nScaling history and events are cleared. connectionPressure reflects\nthe RDS connection-pool ratio.\n","value":{"environment":{"id":"env-aws-001","simulationId":"sim-aws-001","isActive":true,"currentStep":0,"maxSteps":100},"observation":{"metrics":{"cpuUsage":41,"latencyP50":38,"latencyP95":82,"errorRate":0.2,"throughput":4800,"costPerHour":0.38,"connectionPressure":0.3},"resources":[{"id":"res-alb-001","name":"ALB","type":"network","provider":"aws","instances":1},{"id":"res-ec2-001","name":"EC2 m5.large","type":"compute","provider":"aws","instances":2},{"id":"res-rds-001","name":"RDS db.r5.large","type":"database","provider":"aws","instances":1}],"traffic":4800,"currentTime":0,"autoscalingConfig":{"scaleOutCpuThreshold":70,"scaleInCpuThreshold":30,"maxInstances":12,"minInstances":2},"scalingHistory":[],"recentEvents":[]},"info":{"sim_time_human":"0s"}}},"gcpReset":{"summary":"GCP — environment reset (GCE e2-standard-4 cluster, us-central1)","description":"Episode reset on a GCP simulation. Resources return to their initial\nstate: 2 × e2-standard-4 GCE instances behind Cloud Load Balancing\nwith Cloud SQL db-standard-4. Scaling history and events are cleared.\nconnectionPressure reflects the Cloud SQL connection-pool ratio.\n","value":{"environment":{"id":"env-gcp-001","simulationId":"sim-gcp-001","isActive":true,"currentStep":0,"maxSteps":150},"observation":{"metrics":{"cpuUsage":42,"latencyP50":40,"latencyP95":88,"errorRate":0.2,"throughput":3900,"costPerHour":0.44,"connectionPressure":0.28},"resources":[{"id":"res-lb-001","name":"Cloud Load Balancing","type":"network","provider":"gcp","instances":1},{"id":"res-gce-001","name":"GCE e2-standard-4","type":"compute","provider":"gcp","instances":2},{"id":"res-csql-001","name":"Cloud SQL db-standard-4","type":"database","provider":"gcp","instances":1}],"traffic":3900,"currentTime":0,"autoscalingConfig":{"scaleOutCpuThreshold":68,"scaleInCpuThreshold":30,"maxInstances":10,"minInstances":2},"scalingHistory":[],"recentEvents":[]},"info":{"sim_time_human":"0s"}}},"azureReset":{"summary":"Azure — environment reset (Standard_D4s_v3 VM Scale Set, East US)","description":"Episode reset on an Azure simulation. Resources return to their initial\nstate: 2 × Standard_D4s_v3 VMs behind an Azure Load Balancer\nwith Azure SQL General Purpose 4 vCores. Scaling history is cleared.\nconnectionPressure reflects the Azure SQL connection-pool ratio.\n","value":{"environment":{"id":"env-azure-001","simulationId":"sim-azure-001","isActive":true,"currentStep":0,"maxSteps":150},"observation":{"metrics":{"cpuUsage":45,"latencyP50":44,"latencyP95":96,"errorRate":0.3,"throughput":4400,"costPerHour":0.52,"connectionPressure":0.33},"resources":[{"id":"res-alb-001","name":"Azure Load Balancer","type":"network","provider":"azure","instances":1},{"id":"res-vm-001","name":"Standard_D4s_v3","type":"compute","provider":"azure","instances":2},{"id":"res-sql-001","name":"Azure SQL General Purpose","type":"database","provider":"azure","instances":1}],"traffic":4400,"currentTime":0,"autoscalingConfig":{"scaleOutCpuThreshold":70,"scaleInCpuThreshold":30,"maxInstances":10,"minInstances":2},"scalingHistory":[],"recentEvents":[]},"info":{"sim_time_human":"0s"}}},"ociReset":{"summary":"OCI — environment reset (VM.Standard3.Flex + Autonomous DB, us-ashburn-1)","description":"Episode reset on an OCI simulation. Resources return to their initial\nstate: 2 × VM.Standard3.Flex instances behind OCI Load Balancer\nwith Autonomous Database (2 OCPU). Scaling history is cleared.\nconnectionPressure reflects the Autonomous DB connection-pool ratio.\n","value":{"environment":{"id":"env-oci-001","simulationId":"sim-oci-001","isActive":true,"currentStep":0,"maxSteps":150},"observation":{"metrics":{"cpuUsage":40,"latencyP50":36,"latencyP95":76,"errorRate":0.1,"throughput":4900,"costPerHour":0.31,"connectionPressure":0.22},"resources":[{"id":"res-lb-001","name":"OCI Load Balancer","type":"network","provider":"oci","instances":1},{"id":"res-vm-001","name":"VM.Standard3.Flex","type":"compute","provider":"oci","instances":2},{"id":"res-adb-001","name":"Autonomous Database 2 OCPU","type":"database","provider":"oci","instances":1}],"traffic":4900,"currentTime":0,"autoscalingConfig":{"scaleOutCpuThreshold":65,"scaleInCpuThreshold":28,"maxInstances":10,"minInstances":2},"scalingHistory":[],"recentEvents":[]},"info":{"sim_time_human":"0s"}}},"digitaloceanReset":{"summary":"DigitalOcean — environment reset (s-2vcpu-4gb Droplets, nyc3)","description":"Episode reset on a DigitalOcean simulation. Resources return to their\ninitial state: 2 × s-2vcpu-4gb Droplets behind a DO Load Balancer\nwith Managed PostgreSQL db-s-2vcpu-4gb. Scaling history is cleared.\nconnectionPressure reflects the Managed PostgreSQL connection-pool ratio.\n","value":{"environment":{"id":"env-do-001","simulationId":"sim-do-001","isActive":true,"currentStep":0,"maxSteps":200},"observation":{"metrics":{"cpuUsage":38,"latencyP50":48,"latencyP95":98,"errorRate":0.3,"throughput":1480,"costPerHour":0.72,"connectionPressure":0.4},"resources":[{"id":"res-lb-001","name":"DO Load Balancer","type":"network","provider":"digitalocean","instances":1},{"id":"res-droplet-001","name":"Droplet s-2vcpu-4gb","type":"compute","provider":"digitalocean","instances":2},{"id":"res-pg-001","name":"Managed PostgreSQL","type":"database","provider":"digitalocean","instances":1}],"traffic":1480,"currentTime":0,"autoscalingConfig":{"scaleOutCpuThreshold":65,"scaleInCpuThreshold":35,"maxInstances":8,"minInstances":1},"scalingHistory":[],"recentEvents":[]},"info":{"sim_time_human":"0s"}}},"digitaloceanAMDNVMeReset":{"summary":"DigitalOcean — environment reset (s-2vcpu-4gb-amd AMD NVMe Droplets, nyc3)","description":"Episode reset on a DigitalOcean simulation backed by AMD NVMe Droplets\n(s-2vcpu-4gb-amd) at $0.038/hr per instance. The AMD EPYC variant\nprovides NVMe-backed local storage and lower P95 latency than the\nIntel equivalent under the same traffic load. Resources return to their\ninitial state: 2 × s-2vcpu-4gb-amd Droplets behind a DO Load Balancer\nwith Managed PostgreSQL db-s-2vcpu-4gb. Scaling history is cleared.\nconnectionPressure reflects the Managed PostgreSQL connection-pool ratio.\nUse this alongside the Intel example to compare agent policy performance\nacross Droplet variants with identical topology.\n","value":{"environment":{"id":"env-do-amd-001","simulationId":"c9f2d3e5-4a7b-4c6f-8d1e-2f3a4b5c6d7e","isActive":true,"currentStep":0,"maxSteps":200},"observation":{"metrics":{"cpuUsage":35.5,"latencyP50":44,"latencyP95":92,"errorRate":0.2,"throughput":995,"costPerHour":0.62,"connectionPressure":0.37},"resources":[{"id":"res-lb-amd-001","name":"DO Load Balancer","type":"network","provider":"digitalocean","instances":1},{"id":"res-droplet-amd-001","name":"Droplet s-2vcpu-4gb-amd","type":"compute","provider":"digitalocean","instances":2},{"id":"res-pg-amd-001","name":"Managed PostgreSQL","type":"database","provider":"digitalocean","instances":1}],"traffic":995,"currentTime":0,"autoscalingConfig":{"scaleOutCpuThreshold":65,"scaleInCpuThreshold":35,"maxInstances":8,"minInstances":1},"scalingHistory":[],"recentEvents":[]},"info":{"sim_time_human":"0s"}}}}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/rl/environments/{environmentId}/observation":{"x-stability":"stable","get":{"tags":["RL Environments"],"summary":"Get the current observation without executing an action","description":"Returns the current state observation without advancing the simulation.\nUseful for initial state inspection or debugging.\n","operationId":"getRLObservation","x-codeSamples":[{"lang":"curl","label":"curl","source":"curl https://your-production-domain.com/api/rl/environments/env-aws-001/observation \\\n -H \"Authorization: Bearer $API_KEY\"\n"},{"lang":"Python","label":"Python","source":"import requests\n\nBASE_URL = \"https://your-production-domain.com/api\"\nAPI_KEY = \"your-api-key\"\nENV_ID = \"env-aws-001\"\n\nresp = requests.get(\n f\"{BASE_URL}/rl/environments/{ENV_ID}/observation\",\n headers={\"Authorization\": f\"Bearer {API_KEY}\"},\n)\nresp.raise_for_status()\ndata = resp.json()\nobs, metrics = data[\"obs\"], data[\"metrics\"]\nprint(f\"Step {obs['currentTime']} CPU: {obs['cpu_util']:.1%} \"\n f\"P95: {metrics['latency_p95']} ms cost: ${metrics['cost_usd_hr']:.2f}/hr\")\n"},{"lang":"Node.js","label":"Node.js","source":"const BASE_URL = \"https://your-production-domain.com/api\";\nconst API_KEY = \"your-api-key\";\nconst ENV_ID = \"env-aws-001\";\n\nconst resp = await fetch(`${BASE_URL}/rl/environments/${ENV_ID}/observation`, {\n headers: { \"Authorization\": `Bearer ${API_KEY}` },\n});\nconst { obs, metrics } = await resp.json();\nconsole.log(`Step ${obs.currentTime} CPU: ${(obs.cpu_util * 100).toFixed(1)}% ` +\n `P95: ${metrics.latency_p95} ms cost: $${metrics.cost_usd_hr.toFixed(2)}/hr`);\n"}],"security":[],"parameters":[{"name":"environmentId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"ID of the RL environment"}],"responses":{"200":{"description":"Current observation","content":{"application/json":{"schema":{"type":"object","required":["obs","metrics","resources"],"properties":{"obs":{"$ref":"#/components/schemas/RLObs"},"metrics":{"$ref":"#/components/schemas/RLMetrics"},"resources":{"type":"array","description":"Full resource list with per-resource `recoveryPolicy`. Resources that have never had `set_recovery_policy` applied carry the global defaults (criticalCpuThreshold: 80, criticalSteps: 4, warningCpuThreshold: 70, warningSteps: 3). Use this to confirm a `set_recovery_policy` action took effect or to compare healing configurations across resources.\n","items":{"$ref":"#/components/schemas/Resource"}},"unmodeled_cost_warning":{"type":"array","description":"List of active unmodeled cost dimension keys (e.g. `[\"egress\", \"cross_az_traffic\"]`). Absent when no unmodeled dimensions are detected. Mirrors the same field in the step response so agents can poll current topology exposure without executing an action.\n","items":{"type":"string"},"example":["egress"]},"reward_components":{"type":"object","description":"Partial reward breakdown reflecting the current simulation state without a step. Only `unmodeled_cost` is present here (the other components — performance, cost, stability, sla — require a step action to compute). Absent when no unmodeled dimensions are active.\n","properties":{"unmodeled_cost":{"type":"number","description":"Penalty for active unmodeled cost dimensions at the current topology. Equals -(count × episodeConfig.unmodeled_cost_penalty_per_dimension). Agents should watch this field across observation polls to detect when a topology change (add_resource / remove_resource) introduced or eliminated a cross-AZ or egress charge mid-episode.\n","example":-0.1}}}}},"examples":{"awsObservation":{"summary":"AWS — observation at step 42 (EC2 m5.large, us-east-1)","description":"Mid-episode observation for an AWS simulation. The cluster is running\n3 × m5.large EC2 instances behind an ALB with RDS Multi-AZ. CPU is\nmoderate, P95 latency is within the 200 ms SLA, and cost is on-budget.\nconnection_pressure reflects the RDS connection-pool ratio.\n","value":{"obs":{"rps":4750,"cpu_util":0.534,"instances":3,"traffic":4750,"currentTime":42},"metrics":{"cost_usd_hr":0.57,"latency_p95":98,"error_rate":0.003,"uptime":0.997,"sla_violations":0,"connection_pressure":0.42}}},"gcpObservation":{"summary":"GCP — observation at step 30 (GCE e2-standard-4, us-central1)","description":"Mid-episode observation for a GCP simulation. The cluster is running\n2 × e2-standard-4 GCE instances behind Cloud Load Balancing with\nCloud SQL. CPU is stable and well within the SLA.\nconnection_pressure reflects the Cloud SQL connection-pool ratio.\n","value":{"obs":{"rps":3820,"cpu_util":0.478,"instances":2,"traffic":3820,"currentTime":30},"metrics":{"cost_usd_hr":0.44,"latency_p95":91,"error_rate":0.002,"uptime":0.998,"sla_violations":0,"connection_pressure":0.38}}},"azureObservation":{"summary":"Azure — observation at step 28 (Standard_D4s_v3, East US)","description":"Mid-episode observation for an Azure simulation. The cluster is running\n2 × Standard_D4s_v3 VMs behind Azure Load Balancer with Azure SQL.\nCPU is moderate; the agent has not yet triggered a scale-out.\nconnection_pressure reflects the Azure SQL connection-pool ratio.\n","value":{"obs":{"rps":4300,"cpu_util":0.551,"instances":2,"traffic":4300,"currentTime":28},"metrics":{"cost_usd_hr":0.52,"latency_p95":104,"error_rate":0.003,"uptime":0.997,"sla_violations":0,"connection_pressure":0.51}}},"ociObservation":{"summary":"OCI — observation at step 25 (VM.Standard3.Flex, us-ashburn-1)","description":"Mid-episode observation for an OCI simulation. The cluster is running\n2 × VM.Standard3.Flex instances behind OCI Load Balancer with\nAutonomous Database. CPU is low; the agent may consider scaling in.\nconnection_pressure reflects the Autonomous DB connection-pool ratio.\n","value":{"obs":{"rps":4820,"cpu_util":0.342,"instances":2,"traffic":4820,"currentTime":25},"metrics":{"cost_usd_hr":0.31,"latency_p95":72,"error_rate":0.001,"uptime":0.999,"sla_violations":0,"connection_pressure":0.29}}},"digitaloceanObservation":{"summary":"DigitalOcean — observation at step 15 (s-2vcpu-4gb Droplets, nyc3)","description":"Mid-episode observation for a DigitalOcean simulation. The cluster is\nrunning 2 × s-2vcpu-4gb Droplets behind a DO Load Balancer with\nManaged PostgreSQL. CPU is stable within the target range.\nconnection_pressure reflects the Managed PostgreSQL connection-pool ratio.\n","value":{"obs":{"rps":1480,"cpu_util":0.614,"instances":2,"traffic":1480,"currentTime":15},"metrics":{"cost_usd_hr":0.72,"latency_p95":118,"error_rate":0.004,"uptime":0.996,"sla_violations":0,"connection_pressure":0.63}}},"digitaloceanAMDNVMeObservation":{"summary":"DigitalOcean — observation at step 20 (s-2vcpu-4gb-amd AMD NVMe Droplets, nyc3)","description":"Mid-episode observation for a DigitalOcean simulation backed by AMD NVMe\nDroplets (s-2vcpu-4gb-amd) at $0.038/hr per instance. The cluster is\nrunning 2 × s-2vcpu-4gb-amd Droplets behind a DO Load Balancer with\nManaged PostgreSQL db-s-2vcpu-4gb. CPU is moderate at 58 %; the agent\nmay consider scaling out before the 65 % threshold is crossed. The AMD\nEPYC variant's NVMe-backed storage contributes to slightly lower P95\nlatency than the Intel equivalent under the same traffic load.\nconnection_pressure reflects the Managed PostgreSQL connection-pool\nratio; 0.48 is healthy with headroom remaining.\n","value":{"obs":{"rps":1580,"cpu_util":0.578,"instances":2,"traffic":1580,"currentTime":20},"metrics":{"cost_usd_hr":0.62,"latency_p95":108,"error_rate":0.003,"uptime":0.997,"sla_violations":0,"connection_pressure":0.48}}}}}}},"401":{"$ref":"#/components/responses/ApiKeyUnauthorized"},"402":{"$ref":"#/components/responses/X402PaymentRequired"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/chaos/run":{"x-stability":"stable","post":{"tags":["Execute"],"summary":"Run a chaos engineering test","description":"Execute a chaos engineering test by injecting failures into a simulation.\nCan use pre-built scenarios or custom failure injections.\n\nThis endpoint works with simulations built on **any supported provider**,\nincluding AWS, GCP, Azure, OCI, and **DigitalOcean**. When targeting a\nDigitalOcean-based simulation, failure types such as `kill_instance`\naffect Droplets, `zone_failure` targets DigitalOcean datacenter regions,\nand `database_crash` targets Managed Database clusters.\n\n**Failure naming surfaces** — three zone-related names, three different contexts:\n- `zone_outage` — custom injection type in `customInjections[].failureType` (this endpoint)\n- `az_outage` — failure type in the synchronous `POST /simulations/{id}/failures` API\n- `zone_failure` — pre-built scenario ID used in `scenarioId`\n\n**Multi-fault cascade example** — inject a zone outage and a database slowdown simultaneously\nvia `customInjections` to test correlated failure handling:\n\n```json\n{\n \"simulationId\": \"sim-abc123\",\n \"customInjections\": [\n { \"failureType\": \"zone_outage\", \"targetZone\": \"us-east-1a\", \"duration\": 120 },\n { \"failureType\": \"database_slowdown\", \"targetResourceId\": \"db-primary\", \"intensity\": 80, \"duration\": 120 }\n ]\n}\n```\n\n**Resilience grade caveat** — the `grade` in the result reflects a static vulnerability\nanalysis of your architecture (SPOF detection, redundancy gaps). It may not change between\na single-fault run and a multi-fault run on already-resilient topologies because the grade\nmeasures architectural exposure, not live error rate. To measure severity differences between\nfault combinations, pair `POST /chaos/run` (vulnerability report) with the synchronous\n`POST /simulations/{id}/failures` + step-loop pattern (live metrics).\n","operationId":"runChaosTest","x-codeSamples":[{"lang":"curl","label":"curl","source":"curl -X POST https://your-production-domain.com/api/chaos/run \\\n -H \"Authorization: Bearer $API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"simulationId\": \"a638caad-7423-40a3-bb09-f91235d9392d\",\n \"scenarioId\": \"zone_failure\",\n \"duration\": 300,\n \"webhookUrl\": \"https://your-app.com/webhooks/chaos\",\n \"webhookSecret\": \"your-secret-key-here\"\n }'\n"},{"lang":"Python","label":"Python","source":"import requests\n\nBASE_URL = \"https://your-production-domain.com/api\"\nAPI_KEY = \"your-api-key\"\n\nresp = requests.post(\n f\"{BASE_URL}/chaos/run\",\n headers={\"Authorization\": f\"Bearer {API_KEY}\"},\n json={\n \"simulationId\": \"a638caad-7423-40a3-bb09-f91235d9392d\",\n \"scenarioId\": \"zone_failure\",\n \"duration\": 300,\n \"webhookUrl\": \"https://your-app.com/webhooks/chaos\",\n \"webhookSecret\": \"your-secret-key-here\",\n },\n)\nresp.raise_for_status()\njob = resp.json()[\"job\"]\nprint(f\"Chaos job started: {job['id']} status={job['status']}\")\n"},{"lang":"Node.js","label":"Node.js","source":"const BASE_URL = \"https://your-production-domain.com/api\";\nconst API_KEY = \"your-api-key\";\n\nconst resp = await fetch(`${BASE_URL}/chaos/run`, {\n method: \"POST\",\n headers: {\n \"Authorization\": `Bearer ${API_KEY}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n simulationId: \"a638caad-7423-40a3-bb09-f91235d9392d\",\n scenarioId: \"zone_failure\",\n duration: 300,\n webhookUrl: \"https://your-app.com/webhooks/chaos\",\n webhookSecret: \"your-secret-key-here\",\n }),\n});\nconst { job } = await resp.json();\nconsole.log(`Chaos job started: ${job.id} status=${job.status}`);\n"}],"security":[{"x402":[]}],"x-payment-info":{"protocols":["x402"],"price":{"mode":"fixed","currency":"USD","amount":"0.005"}},"x-x402-call-type":"chaos_run","x-x402-price-usdc":"$0.0050","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["simulationId","duration"],"properties":{"simulationId":{"type":"string","format":"uuid","description":"ID of the base simulation to test","example":"a638caad-7423-40a3-bb09-f91235d9392d"},"scenarioId":{"type":"string","description":"Pre-built scenario ID (optional)","example":"zone_failure","enum":["zone_failure","database_crash","network_partition","cascading_failure","random_instance_failure","database_slowdown","database_overload"]},"customInjections":{"type":"array","description":"Custom failure injections (optional)","items":{"type":"object","required":["type","targetId","injectionTime"],"properties":{"type":{"type":"string","enum":["kill_instance","network_delay","database_slowdown","database_overload","cpu_spike","memory_pressure","zone_outage"],"description":"Type of failure to inject"},"targetId":{"type":"string","description":"ID of the resource to target (or zone ID for zone_outage)"},"injectionTime":{"type":"integer","description":"Simulation step when failure should be injected"},"duration":{"type":"integer","description":"How long the failure lasts (in steps, optional)"},"severity":{"type":"number","description":"Severity multiplier (0.0-1.0, optional)"}}}},"duration":{"type":"integer","description":"Test duration in simulation steps","default":300,"example":300},"webhookUrl":{"type":"string","format":"uri","description":"Optional HTTPS URL to receive webhook notification when job completes","example":"https://your-app.com/webhooks/chaos"},"webhookSecret":{"type":"string","description":"Optional secret for HMAC-SHA256 webhook signature verification","example":"your-secret-key-here"}},"example":{"simulationId":"a638caad-7423-40a3-bb09-f91235d9392d","scenarioId":"zone_failure","duration":300}},"examples":{"prebuiltScenario":{"summary":"Pre-built zone failure scenario (any provider)","value":{"simulationId":"a638caad-7423-40a3-bb09-f91235d9392d","scenarioId":"zone_failure","duration":300,"webhookUrl":"https://your-app.com/webhooks/chaos","webhookSecret":"your-secret-key-here"}},"digitalOceanDropletCrash":{"summary":"DigitalOcean — crash a Droplet with custom injection","value":{"simulationId":"d1234abc-0000-40a3-bb09-d091235d9392","duration":180,"customInjections":[{"type":"kill_instance","targetId":"droplet-web-1","injectionTime":30,"duration":90}],"webhookUrl":"https://your-app.com/webhooks/chaos","webhookSecret":"your-secret-key-here"}},"digitalOceanDatabaseCrash":{"summary":"DigitalOcean — crash a Managed Database cluster","value":{"simulationId":"d1234abc-0000-40a3-bb09-d091235d9392","scenarioId":"database_crash","duration":240,"webhookUrl":"https://your-app.com/webhooks/chaos","webhookSecret":"your-secret-key-here"}}}}}},"responses":{"202":{"description":"Chaos test job accepted and started","content":{"application/json":{"schema":{"type":"object","properties":{"jobId":{"type":"string","format":"uuid","description":"Top-level chaos job ID (mirrors `job.id`) for convenient access.","example":"job-abc123"},"job":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"job-abc123"},"type":{"type":"string","enum":["chaos_test"],"example":"chaos_test"},"status":{"type":"string","enum":["pending","running"],"example":"running"},"simulationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"}}},"message":{"type":"string","example":"Chaos test job started. Use GET /chaos/jobs/{id} to check status."}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/X402PaymentRequired"},"404":{"description":"Simulation not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/chaos/batch":{"x-stability":"stable","post":{"tags":["Execute"],"summary":"Run a batch of chaos tests in parallel","description":"Execute multiple chaos engineering tests in parallel. Each scenario can use either\na pre-built scenario or custom failure injections. Results are aggregated across all tests.\n\n**DigitalOcean compatibility:** All pre-built scenarios (`zone_failure`, `database_crash`,\n`network_partition`, etc.) and custom injection types (`kill_instance`, `zone_outage`,\n`database_slowdown`, etc.) work identically on DigitalOcean simulations.\nUse Droplet-specific resource IDs (e.g. `droplet-web-1`) and DO datacenter region names\n(e.g. `nyc3`, `sfo3`) when targeting a DigitalOcean simulation.\n","operationId":"createBatchChaosTest","x-codeSamples":[{"lang":"curl","label":"curl","source":"curl -X POST https://your-production-domain.com/api/chaos/batch \\\n -H \"Authorization: Bearer $API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"simulationId\": \"a638caad-7423-40a3-bb09-f91235d9392d\",\n \"scenarios\": [\n {\"scenarioId\": \"zone_failure\", \"duration\": 120},\n {\"scenarioId\": \"database_crash\", \"duration\": 90}\n ],\n \"webhookUrl\": \"https://your-app.com/webhooks/chaos\",\n \"webhookSecret\": \"your-secret-key-here\"\n }'\n"},{"lang":"Python","label":"Python","source":"import requests\n\nBASE_URL = \"https://your-production-domain.com/api\"\nAPI_KEY = \"your-api-key\"\n\nresp = requests.post(\n f\"{BASE_URL}/chaos/batch\",\n headers={\"Authorization\": f\"Bearer {API_KEY}\"},\n json={\n \"simulationId\": \"a638caad-7423-40a3-bb09-f91235d9392d\",\n \"scenarios\": [\n {\"scenarioId\": \"zone_failure\", \"duration\": 120},\n {\"scenarioId\": \"database_crash\", \"duration\": 90},\n ],\n \"webhookUrl\": \"https://your-app.com/webhooks/chaos\",\n \"webhookSecret\": \"your-secret-key-here\",\n },\n)\nresp.raise_for_status()\njob = resp.json()[\"job\"]\nprint(f\"Batch job started: {job['id']} totalJobs={job['totalJobs']}\")\n"},{"lang":"Node.js","label":"Node.js","source":"const BASE_URL = \"https://your-production-domain.com/api\";\nconst API_KEY = \"your-api-key\";\n\nconst resp = await fetch(`${BASE_URL}/chaos/batch`, {\n method: \"POST\",\n headers: {\n \"Authorization\": `Bearer ${API_KEY}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n simulationId: \"a638caad-7423-40a3-bb09-f91235d9392d\",\n scenarios: [\n { scenarioId: \"zone_failure\", duration: 120 },\n { scenarioId: \"database_crash\", duration: 90 },\n ],\n webhookUrl: \"https://your-app.com/webhooks/chaos\",\n webhookSecret: \"your-secret-key-here\",\n }),\n});\nconst { job } = await resp.json();\nconsole.log(`Batch job started: ${job.id} totalJobs=${job.totalJobs}`);\n"}],"security":[{"x402":[]}],"x-payment-info":{"protocols":["x402"],"price":{"mode":"fixed","currency":"USD","amount":"0.005"}},"x-x402-call-type":"chaos_batch","x-x402-price-usdc":"$0.0050","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["simulationId","scenarios"],"properties":{"simulationId":{"type":"string","format":"uuid","description":"ID of the base simulation to test","example":"a638caad-7423-40a3-bb09-f91235d9392d"},"scenarios":{"type":"array","description":"Array of chaos test scenarios to execute in parallel","minItems":1,"maxItems":10,"items":{"type":"object","properties":{"scenarioId":{"type":"string","description":"Pre-built scenario ID (optional, mutually exclusive with customInjections)","example":"zone_failure","enum":["zone_failure","database_crash","network_partition","cascading_failure","random_instance_failure","database_slowdown"]},"customInjections":{"type":"array","description":"Custom failure injections (optional, mutually exclusive with scenarioId)","items":{"type":"object","required":["type","targetId","injectionTime"],"properties":{"type":{"type":"string","enum":["kill_instance","network_delay","database_slowdown","database_overload","cpu_spike","memory_pressure","zone_outage"],"description":"Type of failure to inject"},"targetId":{"type":"string","description":"ID of the resource to target (or zone ID for zone_outage)"},"injectionTime":{"type":"integer","description":"Simulation step when failure should be injected"},"duration":{"type":"integer","description":"How long the failure lasts (in steps, optional)"},"severity":{"type":"number","description":"Severity multiplier (0.0-1.0, optional)"}}}},"duration":{"type":"integer","description":"Test duration in simulation steps","minimum":10,"maximum":300,"default":300,"example":120}}}},"webhookUrl":{"type":"string","format":"uri","description":"Optional HTTPS URL to receive webhook notification when batch completes","example":"https://example.com/webhook"},"webhookSecret":{"type":"string","description":"Optional secret for HMAC-SHA256 webhook signature verification","example":"secret123"}},"example":{"simulationId":"a638caad-7423-40a3-bb09-f91235d9392d","scenarios":[{"scenarioId":"zone_failure","duration":120},{"scenarioId":"database_crash","duration":90}]}},"examples":{"generic":{"summary":"Generic batch — zone failure + DB crash + custom kill","value":{"simulationId":"sim_abc123","scenarios":[{"scenarioId":"zone_failure","duration":120},{"scenarioId":"database_crash","duration":90},{"customInjections":[{"type":"kill_instance","targetId":"web-1","injectionTime":30,"duration":60}],"duration":150}],"webhookUrl":"https://example.com/webhook","webhookSecret":"secret123"}},"digitalOcean":{"summary":"DigitalOcean — nyc3 zone failure + sfo3 database crash + Droplet API server kill in parallel","value":{"simulationId":"sim_do_droplets","scenarios":[{"scenarioId":"zone_failure","targetId":"nyc3","duration":120},{"scenarioId":"database_crash","targetId":"sfo3","duration":90},{"customInjections":[{"type":"kill_instance","targetId":"droplet-web-1","injectionTime":10,"duration":60},{"type":"kill_instance","targetId":"droplet-api-1","injectionTime":20,"duration":60}],"duration":90}],"webhookUrl":"https://your-app.example.com/webhooks/chaos","webhookSecret":"do-webhook-secret"}},"ociPreemptible":{"summary":"OCI Preemptible Ampere A1 — cpu_stress + instance_kill testing interruption tolerance of a preemptible Ampere A1 fleet","value":{"simulationId":"sim-oci-preemptible-a1-prod","scenarios":[{"customInjections":[{"type":"cpu_stress","targetId":"vm-a1-flex-1","injectionTime":10,"duration":60},{"type":"cpu_stress","targetId":"vm-a1-flex-2","injectionTime":10,"duration":60}],"duration":90},{"customInjections":[{"type":"kill_instance","targetId":"vm-a1-flex-3","injectionTime":15,"duration":45}],"duration":75}],"webhookUrl":"https://your-app.example.com/webhooks/chaos","webhookSecret":"oci-preemptible-chaos-secret"}}}}}},"responses":{"202":{"description":"Batch chaos test job accepted and started","content":{"application/json":{"schema":{"type":"object","properties":{"job":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"batch_xyz789"},"type":{"type":"string","enum":["batch_chaos_test"],"example":"batch_chaos_test"},"status":{"type":"string","enum":["pending","running"],"example":"running"},"totalJobs":{"type":"integer","description":"Number of child chaos tests in this batch","example":3},"createdAt":{"type":"string","format":"date-time"}}},"message":{"type":"string","example":"Batch chaos test started. Use GET /chaos/batch/{id} to check status."}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/X402PaymentRequired"},"404":{"description":"Simulation not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/analysis/optimize":{"x-stability":"stable","post":{"tags":["Optimize & Predict"],"summary":"Submit an infrastructure optimization job","description":"Analyzes your current architecture and generates 50+ tested variations\nwith ranked recommendations for cost/performance optimization.\n\n**Ownership requirement:** The `simulationId` must refer to a simulation\nthat was created with, or claimed by, the same API key used in this\nrequest. Simulations created via the public browser workspace (i.e.\n`POST /api/simulations` without an `Authorization` header) are unowned\nand will return `403` here until claimed. To associate ownership, either:\n- Create the simulation with a Bearer token from the start:\n `POST /api/simulations` with `Authorization: Bearer <key>`, or\n- Claim an existing unowned simulation before calling this endpoint:\n `POST /api/simulations/{simulationId}/claim`.\n","operationId":"submitOptimization","x-codeSamples":[{"lang":"curl","label":"curl","source":"curl -X POST https://your-production-domain.com/api/analysis/optimize \\\n -H \"Authorization: Bearer $API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"simulationId\": \"sim-abc123\",\n \"goals\": {\n \"primary\": \"minimize_cost\",\n \"constraints\": {\n \"max_cost_per_hour\": 10.0,\n \"min_throughput\": 5000,\n \"max_latency_p95\": 200\n }\n },\n \"testScenario\": {\n \"traffic_pattern\": \"spike\",\n \"duration_steps\": 100,\n \"include_failures\": true\n }\n }'\n"},{"lang":"Python","label":"Python","source":"import requests\n\nBASE_URL = \"https://your-production-domain.com/api\"\nAPI_KEY = \"your-api-key\"\n\nresp = requests.post(\n f\"{BASE_URL}/analysis/optimize\",\n headers={\"Authorization\": f\"Bearer {API_KEY}\"},\n json={\n \"simulationId\": \"sim-abc123\",\n \"goals\": {\n \"primary\": \"minimize_cost\",\n \"constraints\": {\n \"max_cost_per_hour\": 10.0,\n \"min_throughput\": 5000,\n \"max_latency_p95\": 200,\n },\n },\n \"testScenario\": {\n \"traffic_pattern\": \"spike\",\n \"duration_steps\": 100,\n \"include_failures\": True,\n },\n },\n)\nresp.raise_for_status()\njob = resp.json()[\"job\"]\nprint(\"Job ID:\", job[\"id\"], \"Status:\", job[\"status\"])\n"},{"lang":"Node.js","label":"Node.js","source":"const BASE_URL = \"https://your-production-domain.com/api\";\nconst API_KEY = \"your-api-key\";\n\nconst resp = await fetch(`${BASE_URL}/analysis/optimize`, {\n method: \"POST\",\n headers: {\n \"Authorization\": `Bearer ${API_KEY}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n simulationId: \"sim-abc123\",\n goals: {\n primary: \"minimize_cost\",\n constraints: {\n max_cost_per_hour: 10.0,\n min_throughput: 5000,\n max_latency_p95: 200,\n },\n },\n testScenario: {\n traffic_pattern: \"spike\",\n duration_steps: 100,\n include_failures: true,\n },\n }),\n});\nconst { job } = await resp.json();\nconsole.log(\"Job ID:\", job.id, \"Status:\", job.status);\n"}],"security":[{"x402":[]}],"x-payment-info":{"protocols":["x402"],"price":{"mode":"fixed","currency":"USD","amount":"0.005"}},"x-x402-call-type":"optimization_run","x-x402-price-usdc":"$0.0050","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["simulationId","goals"],"properties":{"simulationId":{"type":"string","description":"ID of simulation to optimize"},"goals":{"type":"object","required":["primary"],"properties":{"primary":{"type":"string","enum":["minimize_cost","maximize_performance","balance"],"description":"Primary optimization objective"},"constraints":{"type":"object","properties":{"max_cost_per_hour":{"type":"number","description":"Maximum acceptable cost per hour (USD)"},"min_throughput":{"type":"number","description":"Minimum required throughput (requests/second)"},"max_latency_p95":{"type":"number","description":"Maximum acceptable P95 latency (milliseconds)"}}},"weights":{"type":"object","description":"Custom weights for multi-objective optimization","properties":{"cost":{"type":"number"},"performance":{"type":"number"},"stability":{"type":"number"}}}},"example":{"primary":"minimize_cost","constraints":{"max_cost_per_hour":10,"min_throughput":5000,"max_latency_p95":200}}},"testScenario":{"type":"object","properties":{"traffic_pattern":{"type":"string"},"duration_steps":{"type":"integer","default":100},"include_failures":{"type":"boolean"}}},"webhookUrl":{"type":"string","format":"uri","description":"Optional HTTPS URL to receive webhook notification when job completes","example":"https://your-app.com/webhooks/optimization"},"webhookSecret":{"type":"string","description":"Optional secret for HMAC-SHA256 webhook signature verification","example":"your-secret-key-here"}},"example":{"simulationId":"sim-abc123","goals":{"primary":"minimize_cost","constraints":{"max_cost_per_hour":10,"min_throughput":5000,"max_latency_p95":200}}}}}}},"responses":{"202":{"description":"Optimization job accepted","content":{"application/json":{"schema":{"type":"object","properties":{"job":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string"},"createdAt":{"type":"string"}}},"message":{"type":"string"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/X402PaymentRequired"},"403":{"description":"Simulation not owned by this API key","content":{"application/json":{"schema":{"type":"object","required":["error","reason","remedy"],"properties":{"error":{"type":"string","example":"Simulation not owned by this API key"},"reason":{"type":"string","example":"The simulationId refers to a simulation that was not created with, or claimed by, the API key used in this request. Simulations created via the public browser workspace (without an Authorization header) have no owner and cannot be used here until claimed."},"remedy":{"type":"string","example":"Either create an owned simulation via POST /api/simulations with a write-scoped Bearer token, or claim an existing unowned simulation via POST /api/simulations/{simulationId}/claim before calling this endpoint."}}}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/predictions/validate":{"x-stability":"stable","post":{"tags":["Optimize & Predict"],"summary":"Validate infrastructure against a traffic forecast","description":"Tests whether current infrastructure can handle a predicted traffic pattern.\nReturns validation results with bottlenecks and recommendations.\n\nWhen a `webhookUrl` is provided, the following payload is POSTed to that URL\nwhen the job completes (example shown for the AWS EC2 validation request above):\n\n```json\n{\n \"event\": \"prediction.completed\",\n \"jobId\": \"a1b2c3d4-e5f6-7890-abcd-ef1234567890\",\n \"jobType\": \"prediction_validation\",\n \"status\": \"completed\",\n \"completedAt\": \"2025-11-23T10:35:00Z\",\n \"data\": {\n \"validationResult\": {\n \"passed\": false,\n \"summary\": \"Infrastructure will fail under peak load due to CPU saturation\",\n \"peakMetrics\": {\n \"timestamp\": 60,\n \"traffic\": 12000,\n \"cpuUsage\": 98,\n \"latencyP95\": 820,\n \"errorRate\": 8.4,\n \"costPerHour\": 3.20\n },\n \"bottlenecksDetected\": [\n \"CPU saturation at 98%\",\n \"Error rate exceeds 5%\"\n ],\n \"failurePoints\": [\n { \"timestamp\": 55, \"traffic\": 10500, \"reason\": \"CPU saturation\" }\n ],\n \"recommendations\": [\n \"Scale out to 5 instances before peak\",\n \"Increase CPU threshold to 75%\"\n ]\n }\n }\n}\n```\n\nThe request is signed with HMAC-SHA256; verify the `X-Webhook-Signature` header\nagainst your `webhookSecret` before processing the payload.\n","operationId":"validateTrafficForecast","x-codeSamples":[{"lang":"curl","label":"curl","source":"curl -X POST https://your-production-domain.com/api/predictions/validate \\\n -H \"Authorization: Bearer $API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"simulationId\": \"sim-aws-ec2-prod\",\n \"trafficForecast\": {\n \"name\": \"Black Friday 2025\",\n \"dataPoints\": [\n {\"timestamp\": 0, \"rps\": 2000, \"label\": \"Baseline\"},\n {\"timestamp\": 60, \"rps\": 12000, \"label\": \"Peak\"},\n {\"timestamp\": 100, \"rps\": 2500, \"label\": \"Return to baseline\"}\n ]\n },\n \"testSteps\": 100\n }'\n"},{"lang":"Python","label":"Python","source":"import requests\n\nBASE_URL = \"https://your-production-domain.com/api\"\nAPI_KEY = \"your-api-key\"\n\nresp = requests.post(\n f\"{BASE_URL}/predictions/validate\",\n headers={\"Authorization\": f\"Bearer {API_KEY}\"},\n json={\n \"simulationId\": \"sim-aws-ec2-prod\",\n \"trafficForecast\": {\n \"name\": \"Black Friday 2025\",\n \"dataPoints\": [\n {\"timestamp\": 0, \"rps\": 2000, \"label\": \"Baseline\"},\n {\"timestamp\": 60, \"rps\": 12000, \"label\": \"Peak\"},\n {\"timestamp\": 100, \"rps\": 2500, \"label\": \"Return to baseline\"},\n ],\n },\n \"testSteps\": 100,\n },\n)\nresp.raise_for_status()\njob = resp.json()[\"job\"]\nprint(\"Job ID:\", job[\"id\"], \"Status:\", job[\"status\"])\n"},{"lang":"Node.js","label":"Node.js","source":"const BASE_URL = \"https://your-production-domain.com/api\";\nconst API_KEY = \"your-api-key\";\n\nconst resp = await fetch(`${BASE_URL}/predictions/validate`, {\n method: \"POST\",\n headers: {\n \"Authorization\": `Bearer ${API_KEY}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n simulationId: \"sim-aws-ec2-prod\",\n trafficForecast: {\n name: \"Black Friday 2025\",\n dataPoints: [\n { timestamp: 0, rps: 2000, label: \"Baseline\" },\n { timestamp: 60, rps: 12000, label: \"Peak\" },\n { timestamp: 100, rps: 2500, label: \"Return to baseline\" },\n ],\n },\n testSteps: 100,\n }),\n});\nconst { job } = await resp.json();\nconsole.log(\"Job ID:\", job.id, \"Status:\", job.status);\n"}],"security":[{"x402":[]}],"x-payment-info":{"protocols":["x402"],"price":{"mode":"fixed","currency":"USD","amount":"0.005"}},"x-x402-call-type":"prediction_validate","x-x402-price-usdc":"$0.0050","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["simulationId","trafficForecast"],"properties":{"simulationId":{"type":"string","description":"ID of simulation to test","example":"sim-abc123"},"trafficForecast":{"type":"object","required":["name","dataPoints"],"properties":{"name":{"type":"string","description":"Name of the traffic forecast"},"description":{"type":"string","description":"Optional description"},"dataPoints":{"type":"array","description":"Traffic data points over time","items":{"type":"object","required":["timestamp","rps"],"properties":{"timestamp":{"type":"number"},"rps":{"type":"number"},"label":{"type":"string"}}}},"peakRPS":{"type":"number","description":"Peak requests per second"},"avgRPS":{"type":"number","description":"Average requests per second"}},"example":{"name":"Peak Traffic Test","dataPoints":[{"timestamp":0,"rps":2000,"label":"Baseline"},{"timestamp":60,"rps":12000,"label":"Peak"}]}},"testSteps":{"type":"integer","default":100,"description":"Number of simulation steps to run","example":100},"webhookUrl":{"type":"string","format":"uri","description":"Optional HTTPS URL to receive webhook notification when job completes","example":"https://your-app.com/webhooks/validation"},"webhookSecret":{"type":"string","description":"Optional secret for HMAC-SHA256 webhook signature verification","example":"your-secret-key-here"}},"example":{"simulationId":"sim-abc123","trafficForecast":{"name":"Peak Traffic Test","dataPoints":[{"timestamp":0,"rps":2000,"label":"Baseline"},{"timestamp":60,"rps":12000,"label":"Peak"}]},"testSteps":100}},"examples":{"awsValidation":{"summary":"AWS — validate EC2 m5.xlarge cluster against a Black Friday traffic spike","value":{"simulationId":"sim-aws-ec2-prod","trafficForecast":{"name":"Black Friday 2025 — AWS Production","description":"Predicted 6x traffic spike starting at step 30, peaking at step 60","dataPoints":[{"timestamp":0,"rps":2000,"label":"Baseline"},{"timestamp":30,"rps":6000,"label":"Pre-peak ramp"},{"timestamp":60,"rps":12000,"label":"Peak — Black Friday midnight"},{"timestamp":90,"rps":8000,"label":"Post-peak decline"},{"timestamp":100,"rps":2500,"label":"Return to baseline"}]},"testSteps":100,"webhookUrl":"https://your-app.example.com/webhooks/predictions","webhookSecret":"aws-prediction-secret"}},"gcpValidation":{"summary":"GCP — validate Cloud Run service against a seasonal holiday burst","value":{"simulationId":"sim-gcp-cloudrun-prod","trafficForecast":{"name":"GCP Seasonal Spike — Holiday 2025","description":"Gradual ramp over 70 steps peaking at 4x baseline","dataPoints":[{"timestamp":0,"rps":3000,"label":"Baseline"},{"timestamp":40,"rps":8000,"label":"Holiday ramp"},{"timestamp":70,"rps":12000,"label":"Peak — Holiday noon"},{"timestamp":90,"rps":6000,"label":"Post-holiday wind-down"},{"timestamp":100,"rps":3200,"label":"Return to baseline"}]},"testSteps":100,"webhookUrl":"https://your-app.example.com/webhooks/predictions","webhookSecret":"gcp-prediction-secret"}},"azureValidation":{"summary":"Azure — validate AKS cluster against a sudden product launch spike","value":{"simulationId":"sim-azure-aks-prod","trafficForecast":{"name":"Azure Product Launch Traffic","description":"Sudden 10x spike from launch announcement, sustained for 50 steps","dataPoints":[{"timestamp":0,"rps":1000,"label":"Pre-launch baseline"},{"timestamp":20,"rps":10000,"label":"Launch announcement — spike"},{"timestamp":50,"rps":8000,"label":"Sustained high traffic"},{"timestamp":80,"rps":3000,"label":"Gradual decline"},{"timestamp":100,"rps":1500,"label":"New elevated baseline"}]},"testSteps":100,"webhookUrl":"https://your-app.example.com/webhooks/predictions","webhookSecret":"azure-prediction-secret"}},"ociValidation":{"summary":"OCI — validate Compute + Autonomous Database against a month-end batch processing surge","value":{"simulationId":"sim-oci-compute-prod","trafficForecast":{"name":"OCI Month-End Batch Surge","description":"Recurring month-end reporting job — 3x query load for steps 25 through 80","dataPoints":[{"timestamp":0,"rps":800,"label":"Normal operations"},{"timestamp":25,"rps":2400,"label":"Month-end batch start"},{"timestamp":60,"rps":2600,"label":"Peak batch load"},{"timestamp":80,"rps":1200,"label":"Batch wind-down"},{"timestamp":100,"rps":850,"label":"Return to normal"}]},"testSteps":100,"webhookUrl":"https://your-app.example.com/webhooks/predictions","webhookSecret":"oci-prediction-secret"}},"digitalOceanValidation":{"summary":"DigitalOcean — validate Droplet cluster against an unpredictable viral content spike","value":{"simulationId":"sim-do-droplets-prod","trafficForecast":{"name":"DigitalOcean Viral Traffic Event","description":"Sudden 5x baseline spike within 15 steps from a viral post","dataPoints":[{"timestamp":0,"rps":500,"label":"Normal baseline"},{"timestamp":15,"rps":2500,"label":"Viral spike onset"},{"timestamp":40,"rps":3000,"label":"Peak viral traffic"},{"timestamp":70,"rps":1500,"label":"Declining viral effect"},{"timestamp":100,"rps":700,"label":"New elevated baseline"}]},"testSteps":100,"webhookUrl":"https://your-app.example.com/webhooks/predictions","webhookSecret":"do-prediction-secret"}},"awsSpotValidation":{"summary":"AWS EC2 Spot — validate fault-tolerant batch cluster against a cost-optimized ramp forecast","value":{"simulationId":"sim-aws-spot-batch-prod","trafficForecast":{"name":"AWS Spot Batch Ramp — Cost-Optimized Workload","description":"Gradual ramp to 3x baseline reflecting a nightly batch pipeline; relaxed latency acceptable given Spot pricing savings","dataPoints":[{"timestamp":0,"rps":500,"label":"Idle baseline"},{"timestamp":20,"rps":1000,"label":"Batch job start"},{"timestamp":50,"rps":1500,"label":"Peak batch throughput"},{"timestamp":75,"rps":1000,"label":"Batch wind-down"},{"timestamp":100,"rps":500,"label":"Return to idle"}]},"testSteps":100,"webhookUrl":"https://your-app.example.com/webhooks/predictions","webhookSecret":"aws-spot-prediction-secret"}},"gcpSpotValidation":{"summary":"GCP Spot VM — validate fault-tolerant batch cluster against a cost-optimized ramp forecast","value":{"simulationId":"sim-gcp-spot-batch-prod","trafficForecast":{"name":"GCP Spot Batch Ramp — Cost-Optimized Workload","description":"Gradual ramp to 3x baseline reflecting a nightly batch pipeline on GCP Spot VMs; relaxed latency acceptable given ~70% preemptible pricing savings","dataPoints":[{"timestamp":0,"rps":400,"label":"Idle baseline"},{"timestamp":20,"rps":800,"label":"Batch job start"},{"timestamp":50,"rps":1200,"label":"Peak batch throughput"},{"timestamp":75,"rps":800,"label":"Batch wind-down"},{"timestamp":100,"rps":400,"label":"Return to idle"}]},"testSteps":100,"webhookUrl":"https://your-app.example.com/webhooks/predictions","webhookSecret":"gcp-spot-prediction-secret"}},"azureSpotValidation":{"summary":"Azure Spot VM — validate fault-tolerant batch cluster against a cost-optimized ramp forecast","value":{"simulationId":"sim-azure-spot-batch-prod","trafficForecast":{"name":"Azure Spot Batch Ramp — Cost-Optimized Workload","description":"Gradual ramp to 3x baseline reflecting a nightly batch pipeline on Azure Spot VMs; relaxed latency acceptable given ~70% pay-as-you-go pricing savings","dataPoints":[{"timestamp":0,"rps":300,"label":"Idle baseline"},{"timestamp":20,"rps":600,"label":"Batch job start"},{"timestamp":50,"rps":900,"label":"Peak batch throughput"},{"timestamp":75,"rps":600,"label":"Batch wind-down"},{"timestamp":100,"rps":300,"label":"Return to idle"}]},"testSteps":100,"webhookUrl":"https://your-app.example.com/webhooks/predictions","webhookSecret":"azure-spot-prediction-secret"}},"ociPreemptibleValidation":{"summary":"OCI Ampere A1 Preemptible — validate fault-tolerant batch cluster against a cost-optimized ramp forecast","value":{"simulationId":"sim-oci-preemptible-batch-prod","trafficForecast":{"name":"OCI Preemptible Batch Ramp — Cost-Optimized Workload","description":"Gradual ramp to 3x baseline reflecting a nightly batch pipeline on OCI preemptible Ampere A1 instances; relaxed latency acceptable given ~50% preemptible pricing savings","dataPoints":[{"timestamp":0,"rps":350,"label":"Idle baseline"},{"timestamp":20,"rps":700,"label":"Batch job start"},{"timestamp":50,"rps":1050,"label":"Peak batch throughput"},{"timestamp":75,"rps":700,"label":"Batch wind-down"},{"timestamp":100,"rps":350,"label":"Return to idle"}]},"testSteps":100,"webhookUrl":"https://your-app.example.com/webhooks/predictions","webhookSecret":"oci-preemptible-prediction-secret"}}}}}},"responses":{"202":{"description":"Validation job accepted","content":{"application/json":{"schema":{"type":"object","properties":{"job":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["pending","running"]},"type":{"type":"string","example":"validation"},"createdAt":{"type":"string","format":"date-time"}}},"message":{"type":"string","example":"Validation job started. Poll /predictions/jobs/{id} for status."}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/X402PaymentRequired"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/predictions/optimize-thresholds":{"x-stability":"stable","post":{"tags":["Optimize & Predict"],"summary":"Find optimal autoscaling thresholds","description":"Tests multiple threshold combinations to find the best autoscaling configuration\nfor the predicted traffic pattern.\n\nWhen a `webhookUrl` is provided, the following payload is POSTed to that URL\nwhen the job completes (example shown for the AWS EC2 Auto Scaling request above):\n\n```json\n{\n \"event\": \"prediction.completed\",\n \"jobId\": \"a1b2c3d4-e5f6-7890-abcd-ef1234567890\",\n \"jobType\": \"prediction_optimization\",\n \"status\": \"completed\",\n \"completedAt\": \"2025-11-23T10:35:00Z\",\n \"data\": {\n \"bestThresholds\": {\n \"scaleOutCpuThreshold\": 70,\n \"scaleInCpuThreshold\": 30,\n \"scaleOutThroughputThreshold\": 75,\n \"scaleInThroughputThreshold\": 35,\n \"scaleOutLatencyThreshold\": 120,\n \"cooldownSeconds\": 180,\n \"minInstances\": 3,\n \"maxInstances\": 15\n }\n }\n}\n```\n\nThe request is signed with HMAC-SHA256; verify the `X-Webhook-Signature` header\nagainst your `webhookSecret` before processing the payload.\n","operationId":"optimizeThresholds","x-codeSamples":[{"lang":"curl","label":"curl","source":"curl -X POST https://your-production-domain.com/api/predictions/optimize-thresholds \\\n -H \"Authorization: Bearer $API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"simulationId\": \"sim-aws-ec2-prod\",\n \"trafficForecast\": {\n \"name\": \"Black Friday 2025\",\n \"dataPoints\": [\n {\"timestamp\": 0, \"rps\": 2000, \"label\": \"Baseline\"},\n {\"timestamp\": 60, \"rps\": 12000, \"label\": \"Peak\"},\n {\"timestamp\": 100, \"rps\": 2500, \"label\": \"Return to baseline\"}\n ]\n },\n \"testSteps\": 100\n }'\n"},{"lang":"Python","label":"Python","source":"import requests\n\nBASE_URL = \"https://your-production-domain.com/api\"\nAPI_KEY = \"your-api-key\"\n\nresp = requests.post(\n f\"{BASE_URL}/predictions/optimize-thresholds\",\n headers={\"Authorization\": f\"Bearer {API_KEY}\"},\n json={\n \"simulationId\": \"sim-aws-ec2-prod\",\n \"trafficForecast\": {\n \"name\": \"Black Friday 2025\",\n \"dataPoints\": [\n {\"timestamp\": 0, \"rps\": 2000, \"label\": \"Baseline\"},\n {\"timestamp\": 60, \"rps\": 12000, \"label\": \"Peak\"},\n {\"timestamp\": 100, \"rps\": 2500, \"label\": \"Return to baseline\"},\n ],\n },\n \"testSteps\": 100,\n },\n)\nresp.raise_for_status()\njob = resp.json()[\"job\"]\nprint(\"Job ID:\", job[\"id\"], \"Status:\", job[\"status\"])\n"},{"lang":"Node.js","label":"Node.js","source":"const BASE_URL = \"https://your-production-domain.com/api\";\nconst API_KEY = \"your-api-key\";\n\nconst resp = await fetch(`${BASE_URL}/predictions/optimize-thresholds`, {\n method: \"POST\",\n headers: {\n \"Authorization\": `Bearer ${API_KEY}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n simulationId: \"sim-aws-ec2-prod\",\n trafficForecast: {\n name: \"Black Friday 2025\",\n dataPoints: [\n { timestamp: 0, rps: 2000, label: \"Baseline\" },\n { timestamp: 60, rps: 12000, label: \"Peak\" },\n { timestamp: 100, rps: 2500, label: \"Return to baseline\" },\n ],\n },\n testSteps: 100,\n }),\n});\nconst { job } = await resp.json();\nconsole.log(\"Job ID:\", job.id, \"Status:\", job.status);\n"}],"security":[{"x402":[]}],"x-payment-info":{"protocols":["x402"],"price":{"mode":"fixed","currency":"USD","amount":"0.005"}},"x-x402-call-type":"prediction_optimize_thresholds","x-x402-price-usdc":"$0.0050","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["simulationId","trafficForecast"],"properties":{"simulationId":{"type":"string","description":"ID of simulation to optimize","example":"sim-abc123"},"trafficForecast":{"type":"object","required":["name","dataPoints"],"properties":{"name":{"type":"string","description":"Name of the traffic forecast"},"description":{"type":"string","description":"Optional description"},"dataPoints":{"type":"array","description":"Traffic data points over time","items":{"type":"object","required":["timestamp","rps"],"properties":{"timestamp":{"type":"number"},"rps":{"type":"number"},"label":{"type":"string"}}}},"peakRPS":{"type":"number","description":"Peak requests per second"},"avgRPS":{"type":"number","description":"Average requests per second"}},"example":{"name":"Peak Traffic Test","dataPoints":[{"timestamp":0,"rps":2000,"label":"Baseline"},{"timestamp":60,"rps":12000,"label":"Peak"}]}},"testSteps":{"type":"integer","default":100,"description":"Number of simulation steps to run per test","example":100},"webhookUrl":{"type":"string","format":"uri","description":"Optional HTTPS URL to receive webhook notification when job completes","example":"https://your-app.com/webhooks/threshold-optimization"},"webhookSecret":{"type":"string","description":"Optional secret for HMAC-SHA256 webhook signature verification","example":"your-secret-key-here"}},"example":{"simulationId":"sim-abc123","trafficForecast":{"name":"Peak Traffic Test","dataPoints":[{"timestamp":0,"rps":2000,"label":"Baseline"},{"timestamp":60,"rps":12000,"label":"Peak"}]},"testSteps":100}},"examples":{"awsOptimization":{"summary":"AWS — find optimal EC2 Auto Scaling thresholds for Black Friday traffic","value":{"simulationId":"sim-aws-ec2-prod","trafficForecast":{"name":"Black Friday 2025 — AWS Production","description":"Predicted 6x traffic spike starting at step 30, peaking at step 60","dataPoints":[{"timestamp":0,"rps":2000,"label":"Baseline"},{"timestamp":30,"rps":6000,"label":"Pre-peak ramp"},{"timestamp":60,"rps":12000,"label":"Peak — Black Friday midnight"},{"timestamp":90,"rps":8000,"label":"Post-peak decline"},{"timestamp":100,"rps":2500,"label":"Return to baseline"}]},"testSteps":100,"webhookUrl":"https://your-app.example.com/webhooks/predictions","webhookSecret":"aws-optimization-secret"}},"gcpOptimization":{"summary":"GCP — find optimal Cloud Run thresholds for a seasonal holiday burst","value":{"simulationId":"sim-gcp-cloudrun-prod","trafficForecast":{"name":"GCP Seasonal Spike — Holiday 2025","description":"Gradual ramp over 70 steps peaking at 4x baseline","dataPoints":[{"timestamp":0,"rps":3000,"label":"Baseline"},{"timestamp":40,"rps":8000,"label":"Holiday ramp"},{"timestamp":70,"rps":12000,"label":"Peak — Holiday noon"},{"timestamp":90,"rps":6000,"label":"Post-holiday wind-down"},{"timestamp":100,"rps":3200,"label":"Return to baseline"}]},"testSteps":100,"webhookUrl":"https://your-app.example.com/webhooks/predictions","webhookSecret":"gcp-optimization-secret"}},"azureOptimization":{"summary":"Azure — find optimal AKS HPA thresholds for a product launch spike","value":{"simulationId":"sim-azure-aks-prod","trafficForecast":{"name":"Azure Product Launch Traffic","description":"Sudden 10x spike from launch announcement, sustained for 50 steps","dataPoints":[{"timestamp":0,"rps":1000,"label":"Pre-launch baseline"},{"timestamp":20,"rps":10000,"label":"Launch announcement — spike"},{"timestamp":50,"rps":8000,"label":"Sustained high traffic"},{"timestamp":80,"rps":3000,"label":"Gradual decline"},{"timestamp":100,"rps":1500,"label":"New elevated baseline"}]},"testSteps":100,"webhookUrl":"https://your-app.example.com/webhooks/predictions","webhookSecret":"azure-optimization-secret"}},"ociOptimization":{"summary":"OCI — find optimal autoscaling thresholds for a month-end batch processing surge","value":{"simulationId":"sim-oci-compute-prod","trafficForecast":{"name":"OCI Month-End Batch Surge","description":"Recurring month-end reporting job — 3x query load for steps 25 through 80","dataPoints":[{"timestamp":0,"rps":800,"label":"Normal operations"},{"timestamp":25,"rps":2400,"label":"Month-end batch start"},{"timestamp":60,"rps":2600,"label":"Peak batch load"},{"timestamp":80,"rps":1200,"label":"Batch wind-down"},{"timestamp":100,"rps":850,"label":"Return to normal"}]},"testSteps":100,"webhookUrl":"https://your-app.example.com/webhooks/predictions","webhookSecret":"oci-optimization-secret"}},"digitalOceanOptimization":{"summary":"DigitalOcean — find optimal Droplet autoscaling thresholds for a viral content spike","value":{"simulationId":"sim-do-droplets-prod","trafficForecast":{"name":"DigitalOcean Viral Traffic Event","description":"Sudden 5x baseline spike within 15 steps from a viral post","dataPoints":[{"timestamp":0,"rps":500,"label":"Normal baseline"},{"timestamp":15,"rps":2500,"label":"Viral spike onset"},{"timestamp":40,"rps":3000,"label":"Peak viral traffic"},{"timestamp":70,"rps":1500,"label":"Declining viral effect"},{"timestamp":100,"rps":700,"label":"New elevated baseline"}]},"testSteps":100,"webhookUrl":"https://your-app.example.com/webhooks/predictions","webhookSecret":"do-optimization-secret"}}}}}},"responses":{"202":{"description":"Optimization job accepted","content":{"application/json":{"schema":{"type":"object","properties":{"job":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["pending","running"]},"type":{"type":"string","example":"threshold_optimization"},"createdAt":{"type":"string","format":"date-time"}}},"message":{"type":"string","example":"Threshold optimization job started. Poll /predictions/jobs/{id} for status."}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/X402PaymentRequired"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/multi-cloud/explore":{"x-stability":"stable","post":{"tags":["Optimize & Predict"],"summary":"Explore multi-cloud deployment strategies","description":"Analyze a workload profile and generate optimized multi-cloud deployment strategies.\nThe system evaluates different provider combinations across AWS, GCP, Azure, OCI, and DigitalOcean\nbased on cost, latency, and vendor lock-in considerations.\n\n**DigitalOcean as a candidate provider:** DigitalOcean is a first-class candidate in every\nexploration run. It is particularly well-suited for cost-optimized workloads — Droplets and\nManaged Databases typically produce the lowest monthly spend in the comparison report. Set a\nhigh `cost` weight (e.g. 0.7+) and a moderate budget to see DigitalOcean-primary strategies\nappear at the top of `topStrategies` in the results.\n\n**Request body fields:** The request body has two top-level fields that are independent of\neach other. `workloadProfile` describes the workload being evaluated (compute/database\ninstances, traffic, latency requirements, and primary region). `optimizationWeights` is a\nseparate top-level object — **not** nested inside `workloadProfile` — that controls how\nstrategies are ranked: `cost` (default 0.4), `latency` (default 0.4), and `vendorLockIn`\n(default 0.2). Weights need not sum to 1.0; the engine normalises them during scoring. A\ncost-heavy weight (e.g. `cost: 0.9`) surfaces the cheapest strategies at the top of\n`topStrategies`; a latency-heavy weight (e.g. `latency: 0.9`) surfaces the lowest-latency\nstrategies instead.\n","operationId":"exploreMultiCloudStrategies","x-codeSamples":[{"lang":"curl","label":"curl","source":"curl -X POST https://your-production-domain.com/api/multi-cloud/explore \\\n -H \"Authorization: Bearer $API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"workloadProfile\": {\n \"computeInstances\": 8,\n \"databaseInstances\": 2,\n \"storageGB\": 500,\n \"trafficRPS\": 2500,\n \"latencyRequirementMs\": 100,\n \"primaryRegion\": \"us-east-1\",\n \"secondaryRegions\": [\"eu-west-1\"]\n },\n \"optimizationWeights\": {\n \"cost\": 0.4,\n \"latency\": 0.4,\n \"vendorLockIn\": 0.2\n },\n \"webhookUrl\": \"https://your-app.com/webhooks/multicloud\"\n }'\n"},{"lang":"Python","label":"Python","source":"import requests\n\nBASE_URL = \"https://your-production-domain.com/api\"\nAPI_KEY = \"your-api-key\"\n\npayload = {\n \"workloadProfile\": {\n \"computeInstances\": 8,\n \"databaseInstances\": 2,\n \"storageGB\": 500,\n \"trafficRPS\": 2500,\n \"latencyRequirementMs\": 100,\n \"primaryRegion\": \"us-east-1\",\n \"secondaryRegions\": [\"eu-west-1\"],\n },\n \"optimizationWeights\": {\"cost\": 0.4, \"latency\": 0.4, \"vendorLockIn\": 0.2},\n \"webhookUrl\": \"https://your-app.com/webhooks/multicloud\",\n}\n\nresp = requests.post(\n f\"{BASE_URL}/multi-cloud/explore\",\n json=payload,\n headers={\"Authorization\": f\"Bearer {API_KEY}\"},\n)\nresp.raise_for_status()\ndata = resp.json()\njob = data[\"job\"]\nprint(f\"Job started: id={job['id']} status={job['status']}\")\nprint(f\"Poll status at: GET /api/multi-cloud/jobs/{job['id']}\")\n"},{"lang":"Node.js","label":"Node.js","source":"const BASE_URL = \"https://your-production-domain.com/api\";\nconst API_KEY = \"your-api-key\";\n\nconst payload = {\n workloadProfile: {\n computeInstances: 8,\n databaseInstances: 2,\n storageGB: 500,\n trafficRPS: 2500,\n latencyRequirementMs: 100,\n primaryRegion: \"us-east-1\",\n secondaryRegions: [\"eu-west-1\"],\n },\n optimizationWeights: { cost: 0.4, latency: 0.4, vendorLockIn: 0.2 },\n webhookUrl: \"https://your-app.com/webhooks/multicloud\",\n};\n\nconst resp = await fetch(`${BASE_URL}/multi-cloud/explore`, {\n method: \"POST\",\n headers: {\n \"Authorization\": `Bearer ${API_KEY}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(payload),\n});\nconst data = await resp.json();\nconst { job } = data;\nconsole.log(`Job started: id=${job.id} status=${job.status}`);\nconsole.log(`Poll status at: GET /api/multi-cloud/jobs/${job.id}`);\n"},{"lang":"curl","label":"curl (OCI Preemptible)","source":"curl -X POST https://your-production-domain.com/api/multi-cloud/explore \\\n -H \"Authorization: Bearer $API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"workloadProfile\": {\n \"computeInstances\": 4,\n \"databaseInstances\": 1,\n \"storageGB\": 200,\n \"trafficRPS\": 700,\n \"latencyRequirementMs\": 600,\n \"primaryRegion\": \"us-ashburn-1\"\n },\n \"optimizationWeights\": {\n \"cost\": 0.75,\n \"latency\": 0.15,\n \"vendorLockIn\": 0.10\n },\n \"webhookUrl\": \"https://your-app.example.com/webhooks/multicloud\",\n \"webhookSecret\": \"oci-preemptible-secret\"\n }'\n"},{"lang":"Python","label":"Python (OCI Preemptible)","source":"import requests\n\nBASE_URL = \"https://your-production-domain.com/api\"\nAPI_KEY = \"your-api-key\"\n\npayload = {\n \"workloadProfile\": {\n \"computeInstances\": 4,\n \"databaseInstances\": 1,\n \"storageGB\": 200,\n \"trafficRPS\": 700,\n \"latencyRequirementMs\": 600,\n \"primaryRegion\": \"us-ashburn-1\",\n },\n \"optimizationWeights\": {\"cost\": 0.75, \"latency\": 0.15, \"vendorLockIn\": 0.10},\n \"webhookUrl\": \"https://your-app.example.com/webhooks/multicloud\",\n \"webhookSecret\": \"oci-preemptible-secret\",\n}\n\nresp = requests.post(\n f\"{BASE_URL}/multi-cloud/explore\",\n json=payload,\n headers={\"Authorization\": f\"Bearer {API_KEY}\"},\n)\nresp.raise_for_status()\ndata = resp.json()\njob = data[\"job\"]\nprint(f\"Job started: id={job['id']} status={job['status']}\")\nprint(f\"Poll status at: GET /api/multi-cloud/jobs/{job['id']}\")\n"},{"lang":"Node.js","label":"Node.js (OCI Preemptible)","source":"const BASE_URL = \"https://your-production-domain.com/api\";\nconst API_KEY = \"your-api-key\";\n\nconst payload = {\n workloadProfile: {\n computeInstances: 4,\n databaseInstances: 1,\n storageGB: 200,\n trafficRPS: 700,\n latencyRequirementMs: 600,\n primaryRegion: \"us-ashburn-1\",\n },\n optimizationWeights: { cost: 0.75, latency: 0.15, vendorLockIn: 0.10 },\n webhookUrl: \"https://your-app.example.com/webhooks/multicloud\",\n webhookSecret: \"oci-preemptible-secret\",\n};\n\nconst resp = await fetch(`${BASE_URL}/multi-cloud/explore`, {\n method: \"POST\",\n headers: {\n \"Authorization\": `Bearer ${API_KEY}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(payload),\n});\nconst data = await resp.json();\nconst { job } = data;\nconsole.log(`Job started: id=${job.id} status=${job.status}`);\nconsole.log(`Poll status at: GET /api/multi-cloud/jobs/${job.id}`);\n"}],"security":[{"x402":[]}],"x-payment-info":{"protocols":["x402"],"price":{"mode":"fixed","currency":"USD","amount":"0.005"}},"x-x402-call-type":"multicloud_explore","x-x402-price-usdc":"$0.0050","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["workloadProfile"],"properties":{"workloadProfile":{"type":"object","description":"Workload characteristics to evaluate. Must contain only the documented properties listed below. Unknown keys are rejected with HTTP 400 — place optimizationWeights as a separate top-level field alongside workloadProfile, not inside it.","required":["computeInstances","databaseInstances","storageGB","trafficRPS","latencyRequirementMs","primaryRegion"],"properties":{"computeInstances":{"type":"integer","minimum":1,"description":"Number of compute instances required"},"databaseInstances":{"type":"integer","minimum":1,"description":"Number of database instances required"},"storageGB":{"type":"integer","minimum":1,"description":"Storage capacity in gigabytes"},"trafficRPS":{"type":"integer","minimum":1,"description":"Expected traffic in requests per second"},"latencyRequirementMs":{"type":"integer","minimum":1,"description":"Maximum acceptable latency in milliseconds"},"primaryRegion":{"type":"string","description":"Primary deployment region"},"secondaryRegions":{"type":"array","description":"Optional secondary regions for multi-region deployment","items":{"type":"string"}},"requiresMultiRegion":{"type":"boolean","description":"Whether multi-region deployment is required","default":false},"dataResidencyRequirements":{"type":"array","description":"Data residency constraints (e.g., GDPR regions)","items":{"type":"string"}},"sourceProvider":{"type":"string","enum":["aws","gcp","azure","oci","digitalocean"],"description":"The cloud provider you are currently on"}},"additionalProperties":false,"example":{"computeInstances":8,"databaseInstances":2,"storageGB":500,"trafficRPS":2500,"latencyRequirementMs":100,"primaryRegion":"us-east-1"}},"optimizationWeights":{"type":"object","description":"Weights for optimization objectives. Each field is a number 0–1; a per-field value > 1 returns 400. The engine normalises the weights during scoring so the sum need not equal 1.0 — e.g. {cost: 0.9, latency: 0.3} is valid. Unknown keys return 400.","additionalProperties":false,"properties":{"cost":{"type":"number","minimum":0,"maximum":1,"default":0.4,"description":"Weight for cost optimization","example":0.4},"latency":{"type":"number","minimum":0,"maximum":1,"default":0.4,"description":"Weight for latency optimization","example":0.4},"vendorLockIn":{"type":"number","minimum":0,"maximum":1,"default":0.2,"description":"Weight for minimizing vendor lock-in","example":0.2}}},"maxCostPerHour":{"type":"number","exclusiveMinimum":0,"description":"Optional hard budget ceiling (USD/hr). Strategies whose `totalCostPerHour` exceeds this value are ranked last in the results rather than filtered out, so you can see what is available within and outside your budget in a single response.\n","example":5},"errorBudgetPct":{"type":"number","minimum":0,"maximum":100,"description":"Optional SLO error-budget constraint (%). Strategies whose implied error rate (derived from provider-count redundancy: single-provider ~0.5%, two-provider ~0.15%, three+ ~0.05%) exceeds this threshold are ranked last. For example, setting `0.1` means any single-provider strategy is demoted below multi-provider alternatives.\n","example":0.1},"webhookUrl":{"type":"string","format":"uri","description":"Optional HTTPS URL to receive webhook notification when job completes","example":"https://your-app.com/webhooks/multicloud"},"webhookSecret":{"type":"string","description":"Optional secret for HMAC-SHA256 webhook signature verification","example":"your-secret-key-here"},"modifiers":{"type":"object","description":"Optional pricing modifier overrides for this exploration run. All fields default to off/on-demand. Pass these to apply discount-adjusted cost estimates in the returned strategies and comparison report.","additionalProperties":false,"properties":{"awsCommitment":{"type":"string","enum":["on-demand","1yr","3yr"],"default":"on-demand","description":"AWS Savings Plan commitment tier. `1yr` applies ~40% off EC2 compute costs; `3yr` applies ~60% off. Only affects AWS allocations in the returned strategies."},"azureHybridBenefit":{"type":"boolean","default":false,"description":"When true, applies Azure Hybrid Benefit (~40% off Azure compute and database) for existing Windows Server / SQL Server licence holders."},"spotEligible":{"type":"boolean","default":false,"description":"When true, shows estimated Spot / Preemptible VM pricing alongside on-demand costs for eligible providers (AWS ~70% off, GCP ~80% off, Azure ~75% off, OCI ~50% off). DigitalOcean has no spot offering."},"oracleLicenseHolder":{"type":"boolean","default":false,"description":"When true, notes an OCI BYOL (Bring Your Own Oracle Licence) cost impact in the comparison report (~50% off OCI database costs for Standard Edition or Enterprise Edition licence holders). BYOL is informational — it does not change strategy cost figures."}}}},"example":{"workloadProfile":{"computeInstances":8,"databaseInstances":2,"storageGB":500,"trafficRPS":2500,"latencyRequirementMs":100,"primaryRegion":"us-east-1"},"optimizationWeights":{"cost":0.4,"latency":0.4,"vendorLockIn":0.2}}},"examples":{"balanced":{"summary":"Balanced multi-cloud workload (equal cost, latency, lock-in weights)","value":{"workloadProfile":{"computeInstances":10,"databaseInstances":2,"storageGB":500,"trafficRPS":2500,"latencyRequirementMs":100,"primaryRegion":"us-east-1","secondaryRegions":["eu-west-1"],"dataResidencyRequirements":["us","eu"]},"optimizationWeights":{"cost":0.4,"latency":0.4,"vendorLockIn":0.2},"webhookUrl":"https://your-app.com/webhooks/multicloud"}},"costOptimized":{"summary":"Cost-optimized workload favouring DigitalOcean","value":{"workloadProfile":{"computeInstances":4,"databaseInstances":1,"storageGB":200,"trafficRPS":1000,"latencyRequirementMs":150,"primaryRegion":"us-east-1"},"optimizationWeights":{"cost":0.7,"latency":0.2,"vendorLockIn":0.1},"webhookUrl":"https://your-app.com/webhooks/multicloud"}},"digitalOceanPrimary":{"summary":"DigitalOcean-primary strategy — maximize cost savings with DO Droplets","value":{"workloadProfile":{"computeInstances":3,"databaseInstances":1,"storageGB":100,"trafficRPS":800,"latencyRequirementMs":200,"primaryRegion":"nyc3","dataResidencyRequirements":["us"],"sourceProvider":"digitalocean"},"optimizationWeights":{"cost":0.75,"latency":0.15,"vendorLockIn":0.1},"webhookUrl":"https://your-app.example.com/webhooks/multicloud","webhookSecret":"do-multicloud-secret"}},"digitaloceanAMDNVMe":{"summary":"DigitalOcean AMD NVMe Droplet — cost-optimized I/O-intensive workload targeting s-2vcpu-4gb-amd","value":{"workloadProfile":{"computeInstances":2,"databaseInstances":1,"storageGB":100,"trafficRPS":600,"latencyRequirementMs":180,"primaryRegion":"nyc3","dataResidencyRequirements":["us"],"sourceProvider":"digitalocean"},"optimizationWeights":{"cost":0.8,"latency":0.15,"vendorLockIn":0.05},"webhookUrl":"https://your-app.example.com/webhooks/multicloud","webhookSecret":"do-amd-nvme-secret"}},"awsPrimary":{"summary":"AWS-primary strategy — high-performance enterprise workload on EC2 and RDS Multi-AZ","value":{"workloadProfile":{"computeInstances":20,"databaseInstances":3,"storageGB":2000,"trafficRPS":5000,"latencyRequirementMs":50,"primaryRegion":"us-east-1","secondaryRegions":["us-west-2"],"dataResidencyRequirements":["us"],"sourceProvider":"aws"},"optimizationWeights":{"cost":0.3,"latency":0.5,"vendorLockIn":0.2},"webhookUrl":"https://your-app.example.com/webhooks/multicloud","webhookSecret":"aws-multicloud-secret"}},"awsSpot":{"summary":"AWS EC2 Spot strategy — cost-optimized fault-tolerant batch workload using Spot Instances","value":{"workloadProfile":{"computeInstances":4,"databaseInstances":1,"storageGB":200,"trafficRPS":1000,"latencyRequirementMs":600,"primaryRegion":"us-east-1","sourceProvider":"aws"},"optimizationWeights":{"cost":0.75,"latency":0.15,"vendorLockIn":0.1},"webhookUrl":"https://your-app.example.com/webhooks/multicloud","webhookSecret":"aws-spot-secret"}},"gcpPrimary":{"summary":"GCP-primary strategy — ML analytics workload on Cloud Run and Cloud SQL","value":{"workloadProfile":{"computeInstances":12,"databaseInstances":2,"storageGB":1000,"trafficRPS":3000,"latencyRequirementMs":80,"primaryRegion":"us-central1","secondaryRegions":["europe-west1"],"dataResidencyRequirements":["us","eu"],"sourceProvider":"gcp"},"optimizationWeights":{"cost":0.3,"latency":0.5,"vendorLockIn":0.2},"webhookUrl":"https://your-app.example.com/webhooks/multicloud","webhookSecret":"gcp-multicloud-secret"}},"gcpSpot":{"summary":"GCP Spot VM strategy — cost-optimized batch workload using preemptible compute","value":{"workloadProfile":{"computeInstances":3,"databaseInstances":1,"storageGB":150,"trafficRPS":800,"latencyRequirementMs":500,"primaryRegion":"us-central1","sourceProvider":"gcp"},"optimizationWeights":{"cost":0.75,"latency":0.15,"vendorLockIn":0.1},"webhookUrl":"https://your-app.example.com/webhooks/multicloud","webhookSecret":"gcp-spot-secret"}},"azurePrimary":{"summary":"Azure-primary strategy — compliance-heavy enterprise workload on AKS and Azure Database","value":{"workloadProfile":{"computeInstances":8,"databaseInstances":2,"storageGB":800,"trafficRPS":2000,"latencyRequirementMs":100,"primaryRegion":"eastus","secondaryRegions":["westeurope"],"dataResidencyRequirements":["us","eu"],"sourceProvider":"azure"},"optimizationWeights":{"cost":0.2,"latency":0.4,"vendorLockIn":0.4},"webhookUrl":"https://your-app.example.com/webhooks/multicloud","webhookSecret":"azure-multicloud-secret"}},"azureSpot":{"summary":"Azure Spot VM strategy — cost-optimized fault-tolerant workload using Azure Spot instances","value":{"workloadProfile":{"computeInstances":2,"databaseInstances":1,"storageGB":100,"trafficRPS":600,"latencyRequirementMs":400,"primaryRegion":"eastus","sourceProvider":"azure"},"optimizationWeights":{"cost":0.75,"latency":0.15,"vendorLockIn":0.1},"webhookUrl":"https://your-app.example.com/webhooks/multicloud","webhookSecret":"azure-spot-secret"}},"ociPrimary":{"summary":"OCI-primary strategy — database-intensive workload on OCI Compute and Autonomous Database","value":{"workloadProfile":{"computeInstances":6,"databaseInstances":2,"storageGB":600,"trafficRPS":1500,"latencyRequirementMs":120,"primaryRegion":"us-ashburn-1","dataResidencyRequirements":["us"],"sourceProvider":"oci"},"optimizationWeights":{"cost":0.5,"latency":0.3,"vendorLockIn":0.2},"webhookUrl":"https://your-app.example.com/webhooks/multicloud","webhookSecret":"oci-multicloud-secret"}},"ociSpot":{"summary":"OCI Ampere A1 Spot strategy — cost-optimized fault-tolerant batch workload using OCI preemptible Ampere A1 compute","value":{"workloadProfile":{"computeInstances":3,"databaseInstances":1,"storageGB":200,"trafficRPS":700,"latencyRequirementMs":600,"primaryRegion":"us-ashburn-1","sourceProvider":"oci"},"optimizationWeights":{"cost":0.75,"latency":0.15,"vendorLockIn":0.1},"webhookUrl":"https://your-app.example.com/webhooks/multicloud","webhookSecret":"oci-spot-secret"}}}}}},"responses":{"202":{"description":"Multi-cloud exploration job started","content":{"application/json":{"schema":{"type":"object","properties":{"job":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"job-abc123"},"type":{"type":"string","enum":["multicloud_exploration"],"example":"multicloud_exploration"},"status":{"type":"string","enum":["pending","running"],"example":"running"},"createdAt":{"type":"string","format":"date-time"}}},"message":{"type":"string","example":"Multi-cloud exploration started. Use GET /multi-cloud/jobs/{id} to check status."}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/X402PaymentRequired"}}}},"/multi-cloud/jobs/{jobId}":{"x-stability":"stable","get":{"tags":["Multi-Cloud Strategy"],"summary":"Get multi-cloud exploration job status","description":"Get the current status and progress of a multi-cloud strategy exploration job","operationId":"getMultiCloudJob","x-codeSamples":[{"lang":"curl","label":"curl","source":"curl https://your-production-domain.com/api/multi-cloud/jobs/job_abc123 \\\n -H \"Authorization: Bearer $API_KEY\"\n"},{"lang":"Python","label":"Python","source":"import requests\n\nBASE_URL = \"https://your-production-domain.com/api\"\nAPI_KEY = \"your-api-key\"\nJOB_ID = \"job_abc123\"\n\nresp = requests.get(\n f\"{BASE_URL}/multi-cloud/jobs/{JOB_ID}\",\n headers={\"Authorization\": f\"Bearer {API_KEY}\"},\n)\nresp.raise_for_status()\njob = resp.json()\nprint(f\"Job {JOB_ID} status={job['status']} progress={job.get('progress', 0)}% \"\n f\"strategies={job.get('strategiesGenerated', 0)}\")\n"},{"lang":"Node.js","label":"Node.js","source":"const BASE_URL = \"https://your-production-domain.com/api\";\nconst API_KEY = \"your-api-key\";\nconst JOB_ID = \"job_abc123\";\n\nconst resp = await fetch(`${BASE_URL}/multi-cloud/jobs/${JOB_ID}`, {\n headers: { \"Authorization\": `Bearer ${API_KEY}` },\n});\nconst job = await resp.json();\nconsole.log(`Job ${JOB_ID} status=${job.status} progress=${job.progress ?? 0}% strategies=${job.strategiesGenerated ?? 0}`);\n"}],"security":[],"parameters":[{"name":"jobId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"ID of the multi-cloud job"}],"responses":{"200":{"description":"Multi-cloud job status","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["pending","running","completed","failed"]},"progress":{"type":"number","description":"Completion progress (0-100)","example":75},"strategiesGenerated":{"type":"integer","description":"Number of strategies generated so far","example":12},"workloadProfile":{"$ref":"#/components/schemas/WorkloadProfile"},"optimizationWeights":{"type":"object","properties":{"cost":{"type":"number"},"latency":{"type":"number"},"vendorLockIn":{"type":"number"}}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"completedAt":{"type":"string","format":"date-time"},"error":{"type":"string"},"estimateDisclaimer":{"type":"string","description":"Present only when status is `completed`. Reminder that cost and latency figures are baseline planning estimates; validate with materialize + step under production load."}}}}}},"401":{"$ref":"#/components/responses/ApiKeyUnauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/multi-cloud/jobs/{jobId}/results":{"x-stability":"stable","get":{"tags":["Multi-Cloud Strategy"],"summary":"Get multi-cloud exploration results","description":"Retrieve the **final** results from a multi-cloud exploration job.\n\n**Only returns data once the job has finished.** This endpoint returns\nresults exclusively when the job `status` is `completed`. While the job\nis still `pending` or `running` it returns `400 INVALID_REQUEST`; a\n`failed` job returns `500`.\n\n**Polling pattern:**\n- Poll `GET /multi-cloud/jobs/{jobId}` (or subscribe to `/stream`) until `status` is `completed`, then call this endpoint once for the final ranked strategies and comparison report.\n- To read strategies as they accumulate while the job is still running, use `GET /multi-cloud/jobs/{jobId}/partial-results` instead — that endpoint returns `isComplete: false` until the job reaches a terminal state.\n- For real-time streaming of progress, use the `/stream` endpoint.\n\n**Response field names:**\nThe successful `200` response contains `topStrategies` (the highest-scoring strategies,\nsorted by `metrics.compositeScore` descending) and `allStrategies` (every strategy\nevaluated, also sorted). Do **not** use the field names `variants`, `strategies`, or\n`ranked` — those do not exist in the response.\n","operationId":"getMultiCloudResults","x-codeSamples":[{"lang":"curl","label":"curl","source":"until curl -sf \"https://your-production-domain.com/api/multi-cloud/jobs/job_abc123\" \\\n -H \"Authorization: Bearer $API_KEY\" | grep -q '\"status\":\"completed\"'; do\n sleep 2\ndone\n\ncurl \"https://your-production-domain.com/api/multi-cloud/jobs/job_abc123/results\" \\\n -H \"Authorization: Bearer $API_KEY\"\n\ncurl \"https://your-production-domain.com/api/multi-cloud/jobs/job_abc123/results?providers=digitalocean,aws\" \\\n -H \"Authorization: Bearer $API_KEY\"\n"},{"lang":"Python","label":"Python","source":"import time\nimport requests\n\nBASE_URL = \"https://your-production-domain.com/api\"\nAPI_KEY = \"your-api-key\"\nJOB_ID = \"job_abc123\"\nHEADERS = {\"Authorization\": f\"Bearer {API_KEY}\"}\n\nwhile True:\n status = requests.get(\n f\"{BASE_URL}/multi-cloud/jobs/{JOB_ID}\", headers=HEADERS,\n ).json()\n if status[\"status\"] in (\"completed\", \"failed\", \"cancelled\"):\n break\n time.sleep(2)\n\nresp = requests.get(\n f\"{BASE_URL}/multi-cloud/jobs/{JOB_ID}/results\",\n headers=HEADERS,\n)\nresp.raise_for_status()\ndata = resp.json()\nprint(f\"Job {JOB_ID} status={data['status']} \"\n f\"complete={data['isComplete']} strategies={data['strategiesGenerated']}\")\nfor strategy in data.get(\"topStrategies\", []):\n cost = strategy[\"metrics\"][\"monthlyCost\"]\n latency = strategy[\"metrics\"][\"avgLatencyMs\"]\n print(f\" {strategy['name']} cost=${cost}/mo latency={latency}ms\")\n"},{"lang":"Node.js","label":"Node.js","source":"const BASE_URL = \"https://your-production-domain.com/api\";\nconst API_KEY = \"your-api-key\";\nconst JOB_ID = \"job_abc123\";\nconst HEADERS = { \"Authorization\": `Bearer ${API_KEY}` };\n\n// Poll status until the job is finished. /results returns 400 while\n// the job is still pending or running; use /partial-results to read\n// strategies as they accumulate during a run.\nlet status;\ndo {\n await new Promise(r => setTimeout(r, 2000));\n status = await fetch(`${BASE_URL}/multi-cloud/jobs/${JOB_ID}`, {\n headers: HEADERS,\n }).then(r => r.json());\n} while (![\"completed\", \"failed\", \"cancelled\"].includes(status.status));\n\nconst resp = await fetch(`${BASE_URL}/multi-cloud/jobs/${JOB_ID}/results`, {\n headers: HEADERS,\n});\nconst data = await resp.json();\nconsole.log(`Job ${JOB_ID} status=${data.status} complete=${data.isComplete} strategies=${data.strategiesGenerated}`);\nfor (const strategy of data.topStrategies ?? []) {\n const { monthlyCost, avgLatencyMs } = strategy.metrics;\n console.log(` ${strategy.name} cost=$${monthlyCost}/mo latency=${avgLatencyMs}ms`);\n}\n"}],"security":[],"parameters":[{"name":"jobId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"ID of the multi-cloud job"},{"name":"providers","in":"query","required":false,"schema":{"type":"string","example":"digitalocean,aws"},"description":"Comma-separated list of cloud provider names to filter results by preferred primary provider.\nA strategy is included if any of the specified providers holds ≥ 50 % of the traffic allocation.\nSupported values: `aws`, `gcp`, `azure`, `digitalocean`, `oci`.\nIf omitted, all strategies are returned.\n"}],"responses":{"200":{"description":"Job results (partial or complete based on job status)","content":{"application/json":{"schema":{"type":"object","properties":{"jobId":{"type":"string"},"status":{"type":"string","enum":["pending","running","completed","failed","cancelled"]},"progress":{"type":"number","description":"Job progress percentage (0-100)"},"isComplete":{"type":"boolean","description":"True if job is finished, false if still running or pending"},"strategiesGenerated":{"type":"number","description":"Number of strategies generated so far"},"allStrategies":{"type":"array","items":{"$ref":"#/components/schemas/Strategy"},"description":"All raw strategies generated so far (available during and after generation)"},"topStrategies":{"type":"array","items":{"$ref":"#/components/schemas/Strategy"},"description":"Top 10 optimized strategies (only available when status is completed)"},"comparisonReport":{"type":"string","description":"Markdown comparison report (only available when complete)"},"completedAt":{"type":"string","format":"date-time"},"estimateDisclaimer":{"type":"string","description":"Reminder that cost and latency figures are baseline planning estimates; validate with materialize + step under production load."}}},"examples":{"partialResults":{"value":{"jobId":"job_abc123","status":"running","progress":45,"isComplete":false,"strategiesGenerated":15,"allStrategies":[{"name":"AWS-Dominant Strategy","description":"Primary AWS deployment with GCP failover","allocations":[{"provider":"aws","percentage":70},{"provider":"gcp","percentage":30}],"metrics":{"monthlyCost":12500,"avgLatencyMs":45,"vendorLockInScore":65}},{"name":"DigitalOcean Cost-Optimized","description":"DigitalOcean primary with AWS failover — lowest monthly spend","allocations":[{"provider":"digitalocean","percentage":80},{"provider":"aws","percentage":20}],"metrics":{"monthlyCost":7800,"avgLatencyMs":55,"vendorLockInScore":48}}],"topStrategies":[]}},"completeResults":{"value":{"jobId":"job_abc123","status":"completed","progress":100,"isComplete":true,"strategiesGenerated":42,"allStrategies":[{"name":"AWS-Dominant Strategy","description":"Primary AWS deployment with GCP failover","allocations":[{"provider":"aws","percentage":70},{"provider":"gcp","percentage":30}],"metrics":{"monthlyCost":12500,"avgLatencyMs":45,"vendorLockInScore":65}},{"name":"DigitalOcean Cost-Optimized","description":"DigitalOcean primary with AWS failover — lowest monthly spend","allocations":[{"provider":"digitalocean","percentage":80},{"provider":"aws","percentage":20}],"metrics":{"monthlyCost":7800,"avgLatencyMs":55,"vendorLockInScore":48}}],"topStrategies":[{"name":"Multi-Cloud Balanced","description":"Optimized multi-cloud strategy for cost and performance","allocations":[{"provider":"aws","percentage":40},{"provider":"gcp","percentage":35},{"provider":"azure","percentage":25}],"metrics":{"monthlyCost":11200,"avgLatencyMs":42,"vendorLockInScore":35}},{"name":"DigitalOcean Cost-Optimized","description":"DigitalOcean primary with AWS failover — lowest monthly spend","allocations":[{"provider":"digitalocean","percentage":80},{"provider":"aws","percentage":20}],"metrics":{"monthlyCost":7800,"avgLatencyMs":55,"vendorLockInScore":48}}],"comparisonReport":"# Multi-Cloud Strategy Comparison Report...","completedAt":"2024-01-15T10:30:00Z"}},"digitalOceanWinner":{"summary":"DigitalOcean wins — cost-optimized workload where DO-primary ranks","value":{"jobId":"job_do_xyz789","status":"completed","progress":100,"isComplete":true,"strategiesGenerated":38,"allStrategies":[{"name":"DigitalOcean-Primary Strategy","description":"DigitalOcean Droplets (s-4vcpu-8gb) as primary compute, DigitalOcean Managed Databases (PostgreSQL) for persistence, and Spaces for S3-compatible object storage — lowest total monthly spend in the comparison","allocations":[{"provider":"digitalocean","percentage":80},{"provider":"aws","percentage":20}],"metrics":{"monthlyCost":7800,"avgLatencyMs":55,"vendorLockInScore":48}},{"name":"AWS-Dominant Strategy","description":"Primary AWS deployment with GCP failover","allocations":[{"provider":"aws","percentage":70},{"provider":"gcp","percentage":30}],"metrics":{"monthlyCost":14200,"avgLatencyMs":43,"vendorLockInScore":74}},{"name":"GCP-Primary Strategy","description":"GCP Cloud Run primary with Azure failover","allocations":[{"provider":"gcp","percentage":60},{"provider":"azure","percentage":40}],"metrics":{"monthlyCost":11600,"avgLatencyMs":46,"vendorLockInScore":61}}],"topStrategies":[{"name":"DigitalOcean-Primary Strategy","description":"DigitalOcean Droplets (s-4vcpu-8gb) as primary compute, DigitalOcean Managed Databases (PostgreSQL) for persistence, and Spaces for S3-compatible object storage — lowest total monthly spend in the comparison","allocations":[{"provider":"digitalocean","percentage":80},{"provider":"aws","percentage":20}],"metrics":{"monthlyCost":7800,"avgLatencyMs":55,"vendorLockInScore":48}},{"name":"DigitalOcean + GCP Balanced","description":"DigitalOcean Droplets for primary API tier with DigitalOcean Managed Databases, GCP Cloud Run for burst compute — good cost/latency balance","allocations":[{"provider":"digitalocean","percentage":65},{"provider":"gcp","percentage":35}],"metrics":{"monthlyCost":9400,"avgLatencyMs":50,"vendorLockInScore":41}},{"name":"AWS-Dominant Strategy","description":"Primary AWS deployment with GCP failover","allocations":[{"provider":"aws","percentage":70},{"provider":"gcp","percentage":30}],"metrics":{"monthlyCost":14200,"avgLatencyMs":43,"vendorLockInScore":74}}],"comparisonReport":"# Multi-Cloud Strategy Comparison Report\n\n## Winner: DigitalOcean-Primary Strategy\n\nMonthly cost: **$7,800** — 45% lower than the AWS-dominant baseline ($14,200).\nAverage latency: **55 ms** — comfortably within the 150 ms SLA requirement.\nVendor lock-in score: **48/100** — moderate lock-in, significantly lower than the AWS-only baseline (74/100).\n\n## Key Resources\n\n- **Compute:** DigitalOcean Droplets (s-4vcpu-8gb, nyc3) — predictable hourly pricing with no data-transfer surprises within the region.\n- **Database:** DigitalOcean Managed Databases (PostgreSQL, 2-node HA cluster) — automated failover, daily backups, and connection pooling included.\n- **Object Storage:** DigitalOcean Spaces — S3-compatible API, 250 GB included, CDN edge caching available at no extra charge.\n\n## Trade-offs\n\n| Metric | DO-Primary | AWS-Dominant | DO + GCP |\n|---|---|---|---|\n| Monthly cost | $7,800 | $14,200 | $9,400 |\n| Avg latency | 55 ms | 43 ms | 50 ms |\n| Lock-in score | 48 | 74 | 41 |\n\n## Recommendation\n\nFor cost-sensitive workloads with moderate latency requirements, DigitalOcean Droplets\npaired with DigitalOcean Managed Databases and Spaces deliver the best cost efficiency.\nThe 12 ms latency difference versus the AWS baseline is unlikely to impact end-user\nexperience for the given SLA. Consider the DigitalOcean + GCP Balanced strategy if\nfuture burst capacity beyond current Droplet limits is anticipated.","completedAt":"2024-01-16T09:45:00Z"}},"awsPrimaryWinner":{"summary":"AWS wins — performance-optimized workload where EC2 m5.xlarge + RDS Multi-AZ ranks","value":{"jobId":"job_aws_perf123","status":"completed","progress":100,"isComplete":true,"strategiesGenerated":40,"allStrategies":[{"name":"AWS-Primary Strategy","description":"EC2 m5.xlarge Auto Scaling group across us-east-1a/1b with RDS db.r5.large Multi-AZ and CloudFront CDN — lowest p95 latency in comparison","allocations":[{"provider":"aws","percentage":100}],"metrics":{"monthlyCost":14500,"avgLatencyMs":38,"vendorLockInScore":72}},{"name":"AWS + GCP Balanced","description":"EC2 primary with GCP Cloud Run burst capacity","allocations":[{"provider":"aws","percentage":70},{"provider":"gcp","percentage":30}],"metrics":{"monthlyCost":13200,"avgLatencyMs":41,"vendorLockInScore":58}},{"name":"DigitalOcean Cost-Optimized","description":"DigitalOcean Droplets primary — lowest cost but 17 ms higher latency","allocations":[{"provider":"digitalocean","percentage":80},{"provider":"aws","percentage":20}],"metrics":{"monthlyCost":7800,"avgLatencyMs":55,"vendorLockInScore":48}}],"topStrategies":[{"name":"AWS-Primary Strategy","description":"EC2 m5.xlarge Auto Scaling group across us-east-1a/1b with RDS db.r5.large Multi-AZ and CloudFront CDN — lowest p95 latency in comparison","allocations":[{"provider":"aws","percentage":100}],"metrics":{"monthlyCost":14500,"avgLatencyMs":38,"vendorLockInScore":72}},{"name":"AWS + GCP Balanced","description":"EC2 primary with GCP Cloud Run burst capacity","allocations":[{"provider":"aws","percentage":70},{"provider":"gcp","percentage":30}],"metrics":{"monthlyCost":13200,"avgLatencyMs":41,"vendorLockInScore":58}}],"comparisonReport":"# Multi-Cloud Strategy Comparison Report\n\n## Winner: AWS-Primary Strategy\n\nMonthly cost: **$14,500** — within the $25,000 budget.\nAverage latency: **38 ms** — best in class, comfortably within the 50 ms SLA.\nVendor lock-in score: **72/100** — acceptable given the performance requirements.\n\n## Key Resources\n\n- **Compute:** EC2 m5.xlarge (4 vCPU / 16 GB) Auto Scaling group, 3–12 instances, us-east-1a/1b.\n- **Database:** RDS db.r5.large Multi-AZ PostgreSQL — automatic failover within 60 seconds.\n- **CDN:** CloudFront with edge caching reduces origin load by ~40%.\n\n## Trade-offs\n\n| Metric | AWS-Primary | AWS + GCP | DO Cost-Optimized |\n|---|---|---|---|\n| Monthly cost | $14,500 | $13,200 | $7,800 |\n| Avg latency | 38 ms | 41 ms | 55 ms |\n| Lock-in score | 72 | 58 | 48 |\n\n## Recommendation\n\nFor workloads with a 50 ms SLA, AWS-Primary delivers the best latency. If budget is a constraint, the AWS + GCP Balanced strategy saves $1,300/month with only 3 ms latency degradation.","completedAt":"2024-01-17T08:20:00Z"}},"gcpPrimaryWinner":{"summary":"GCP wins — ML analytics workload where Cloud Run + Cloud SQL ranks","value":{"jobId":"job_gcp_ml456","status":"completed","progress":100,"isComplete":true,"strategiesGenerated":35,"allStrategies":[{"name":"GCP-Primary Strategy","description":"Cloud Run (fully managed, us-central1) with Cloud SQL for PostgreSQL (HA) and Cloud Storage — best autoscaling fit for variable ML inference load","allocations":[{"provider":"gcp","percentage":100}],"metrics":{"monthlyCost":11800,"avgLatencyMs":42,"vendorLockInScore":63}},{"name":"GCP + AWS Hybrid","description":"GCP Cloud Run primary with AWS Lambda for async processing jobs","allocations":[{"provider":"gcp","percentage":65},{"provider":"aws","percentage":35}],"metrics":{"monthlyCost":12400,"avgLatencyMs":44,"vendorLockInScore":51}},{"name":"AWS-Dominant Strategy","description":"EC2 m5.large fleet — higher cost, comparable latency","allocations":[{"provider":"aws","percentage":100}],"metrics":{"monthlyCost":15600,"avgLatencyMs":45,"vendorLockInScore":74}}],"topStrategies":[{"name":"GCP-Primary Strategy","description":"Cloud Run (fully managed, us-central1) with Cloud SQL for PostgreSQL (HA) and Cloud Storage — best autoscaling fit for variable ML inference load","allocations":[{"provider":"gcp","percentage":100}],"metrics":{"monthlyCost":11800,"avgLatencyMs":42,"vendorLockInScore":63}},{"name":"GCP + AWS Hybrid","description":"GCP Cloud Run primary with AWS Lambda for async processing jobs","allocations":[{"provider":"gcp","percentage":65},{"provider":"aws","percentage":35}],"metrics":{"monthlyCost":12400,"avgLatencyMs":44,"vendorLockInScore":51}}],"comparisonReport":"# Multi-Cloud Strategy Comparison Report\n\n## Winner: GCP-Primary Strategy\n\nMonthly cost: **$11,800** — $3,800 below the AWS baseline.\nAverage latency: **42 ms** — within the 80 ms SLA requirement.\nVendor lock-in score: **63/100** — moderate, lower than an AWS-only approach.\n\n## Key Resources\n\n- **Compute:** Cloud Run (fully managed) in us-central1 — scales to zero between inference jobs, eliminating idle compute cost.\n- **Database:** Cloud SQL for PostgreSQL (HA, db-n1-standard-4) — regional failover with 99.95% SLA.\n- **Storage:** Cloud Storage Standard — multi-region bucket with CDN integration for model artefacts.\n\n## Trade-offs\n\n| Metric | GCP-Primary | GCP + AWS | AWS-Dominant |\n|---|---|---|---|\n| Monthly cost | $11,800 | $12,400 | $15,600 |\n| Avg latency | 42 ms | 44 ms | 45 ms |\n| Lock-in score | 63 | 51 | 74 |\n\n## Recommendation\n\nGCP-Primary is optimal for variable ML inference loads. Cloud Run's scale-to-zero behaviour saves up to 35% on compute versus always-on EC2 instances at comparable load.","completedAt":"2024-01-18T11:35:00Z"}},"azurePrimaryWinner":{"summary":"Azure wins — compliance workload where AKS + Azure Database for PostgreSQL ranks","value":{"jobId":"job_azure_comp789","status":"completed","progress":100,"isComplete":true,"strategiesGenerated":38,"allStrategies":[{"name":"Azure-Primary Strategy","description":"AKS (eastus, Standard_D4s_v3 nodes) with Azure Database for PostgreSQL Flexible Server and Azure Front Door — strongest GDPR/HIPAA compliance posture","allocations":[{"provider":"azure","percentage":100}],"metrics":{"monthlyCost":13200,"avgLatencyMs":44,"vendorLockInScore":67}},{"name":"Azure + AWS Hybrid","description":"Azure primary with AWS S3 for object storage overflow","allocations":[{"provider":"azure","percentage":75},{"provider":"aws","percentage":25}],"metrics":{"monthlyCost":14100,"avgLatencyMs":46,"vendorLockInScore":55}},{"name":"GCP-Primary Strategy","description":"GCP Cloud Run with Cloud SQL — lower lock-in, weaker native compliance tooling","allocations":[{"provider":"gcp","percentage":100}],"metrics":{"monthlyCost":11800,"avgLatencyMs":42,"vendorLockInScore":63}}],"topStrategies":[{"name":"Azure-Primary Strategy","description":"AKS (eastus, Standard_D4s_v3 nodes) with Azure Database for PostgreSQL Flexible Server and Azure Front Door — strongest GDPR/HIPAA compliance posture","allocations":[{"provider":"azure","percentage":100}],"metrics":{"monthlyCost":13200,"avgLatencyMs":44,"vendorLockInScore":67}},{"name":"Azure + AWS Hybrid","description":"Azure primary with AWS S3 for object storage overflow","allocations":[{"provider":"azure","percentage":75},{"provider":"aws","percentage":25}],"metrics":{"monthlyCost":14100,"avgLatencyMs":46,"vendorLockInScore":55}}],"comparisonReport":"# Multi-Cloud Strategy Comparison Report\n\n## Winner: Azure-Primary Strategy\n\nMonthly cost: **$13,200** — within the $20,000 budget.\nAverage latency: **44 ms** — within the 100 ms SLA.\nVendor lock-in score: **67/100** — justified by compliance requirements (GDPR, HIPAA, ISO 27001).\n\n## Key Resources\n\n- **Compute:** AKS cluster (Standard_D4s_v3, 3–10 nodes) in eastus with Availability Zones — meets HA requirements for HIPAA.\n- **Database:** Azure Database for PostgreSQL Flexible Server (General Purpose, 4 vCores) with geo-redundant backup.\n- **Networking:** Azure Front Door with WAF — OWASP rule sets satisfy PCI-DSS network security controls.\n\n## Trade-offs\n\n| Metric | Azure-Primary | Azure + AWS | GCP-Primary |\n|---|---|---|---|\n| Monthly cost | $13,200 | $14,100 | $11,800 |\n| Avg latency | 44 ms | 46 ms | 42 ms |\n| Lock-in score | 67 | 55 | 63 |\n\n## Recommendation\n\nFor GDPR/HIPAA/ISO 27001 workloads, Azure-Primary provides the most complete native compliance toolkit. GCP is $1,400/month cheaper but requires third-party tooling to meet the same compliance bar.","completedAt":"2024-01-19T14:00:00Z"}},"ociPrimaryWinner":{"summary":"OCI wins — database-intensive workload where Compute + Autonomous Database ranks","value":{"jobId":"job_oci_db321","status":"completed","progress":100,"isComplete":true,"strategiesGenerated":33,"allStrategies":[{"name":"OCI-Primary Strategy","description":"OCI Compute VM.Standard.E4.Flex (4 OCPU / 64 GB) with Autonomous Database (ATP, 4 OCPU) in us-ashburn-1 — best price-performance for database-heavy workloads","allocations":[{"provider":"oci","percentage":100}],"metrics":{"monthlyCost":9600,"avgLatencyMs":48,"vendorLockInScore":55}},{"name":"OCI + AWS Hybrid","description":"OCI primary database tier with AWS EC2 for the web and API layer","allocations":[{"provider":"oci","percentage":60},{"provider":"aws","percentage":40}],"metrics":{"monthlyCost":11200,"avgLatencyMs":46,"vendorLockInScore":48}},{"name":"AWS-Dominant Strategy","description":"EC2 + RDS — higher cost for equivalent database throughput","allocations":[{"provider":"aws","percentage":100}],"metrics":{"monthlyCost":16400,"avgLatencyMs":42,"vendorLockInScore":74}}],"topStrategies":[{"name":"OCI-Primary Strategy","description":"OCI Compute VM.Standard.E4.Flex (4 OCPU / 64 GB) with Autonomous Database (ATP, 4 OCPU) in us-ashburn-1 — best price-performance for database-heavy workloads","allocations":[{"provider":"oci","percentage":100}],"metrics":{"monthlyCost":9600,"avgLatencyMs":48,"vendorLockInScore":55}},{"name":"OCI + AWS Hybrid","description":"OCI primary database tier with AWS EC2 for the web and API layer","allocations":[{"provider":"oci","percentage":60},{"provider":"aws","percentage":40}],"metrics":{"monthlyCost":11200,"avgLatencyMs":46,"vendorLockInScore":48}}],"comparisonReport":"# Multi-Cloud Strategy Comparison Report\n\n## Winner: OCI-Primary Strategy\n\nMonthly cost: **$9,600** — 41% lower than the AWS-dominant baseline ($16,400).\nAverage latency: **48 ms** — within the 120 ms SLA requirement.\nVendor lock-in score: **55/100** — moderate, offset by significant cost savings.\n\n## Key Resources\n\n- **Compute:** OCI VM.Standard.E4.Flex (4 OCPU / 64 GB RAM) — flexible OCPU allocation reduces idle-time waste.\n- **Database:** Autonomous Database (ATP, 4 OCPU) — self-tuning, automatic patching, and built-in connection pooling eliminate DBA overhead.\n- **Networking:** OCI FastConnect to AWS for the OCI + AWS hybrid variant — sub-5 ms inter-cloud latency.\n\n## Trade-offs\n\n| Metric | OCI-Primary | OCI + AWS | AWS-Dominant |\n|---|---|---|---|\n| Monthly cost | $9,600 | $11,200 | $16,400 |\n| Avg latency | 48 ms | 46 ms | 42 ms |\n| Lock-in score | 55 | 48 | 74 |\n\n## Recommendation\n\nFor database-heavy workloads, OCI Autonomous Database delivers the best cost efficiency. The OCI + AWS Hybrid strategy is worth considering if the web/API tier already has AWS dependencies, adding only $1,600/month for a 2 ms latency improvement.","completedAt":"2024-01-20T16:45:00Z"}},"digitaloceanAMDNVMeMultiCloud":{"summary":"DigitalOcean AMD NVMe Droplets — cost-optimized strategy using s-2vcpu-4gb-amd for maximum price-performance","value":{"jobId":"job_do_amd_nvme_001","status":"completed","progress":100,"isComplete":true,"strategiesGenerated":36,"allStrategies":[{"name":"DigitalOcean AMD NVMe Primary Strategy","description":"DigitalOcean AMD NVMe Droplets (s-2vcpu-4gb-amd, nyc3) as primary compute — NVMe-backed local SSD storage delivers higher I/O throughput than standard Droplets at the same price point, with DigitalOcean Managed Databases (PostgreSQL) and Spaces for object storage","allocations":[{"provider":"digitalocean","percentage":85},{"provider":"aws","percentage":15}],"metrics":{"monthlyCost":6200,"avgLatencyMs":52,"vendorLockInScore":44}},{"name":"DigitalOcean AMD NVMe + GCP Balanced","description":"s-2vcpu-4gb-amd Droplets for primary API tier with GCP Cloud Run for burst compute — good cost/latency balance","allocations":[{"provider":"digitalocean","percentage":70},{"provider":"gcp","percentage":30}],"metrics":{"monthlyCost":8100,"avgLatencyMs":49,"vendorLockInScore":38}},{"name":"AWS-Dominant Strategy","description":"EC2 t3.medium fleet — higher baseline cost, comparable single-request latency","allocations":[{"provider":"aws","percentage":100}],"metrics":{"monthlyCost":13800,"avgLatencyMs":41,"vendorLockInScore":74}}],"topStrategies":[{"name":"DigitalOcean AMD NVMe Primary Strategy","description":"DigitalOcean AMD NVMe Droplets (s-2vcpu-4gb-amd, nyc3) as primary compute — NVMe-backed local SSD storage delivers higher I/O throughput than standard Droplets at the same price point, with DigitalOcean Managed Databases (PostgreSQL) and Spaces for object storage","allocations":[{"provider":"digitalocean","percentage":85},{"provider":"aws","percentage":15}],"metrics":{"monthlyCost":6200,"avgLatencyMs":52,"vendorLockInScore":44}},{"name":"DigitalOcean AMD NVMe + GCP Balanced","description":"s-2vcpu-4gb-amd Droplets for primary API tier with GCP Cloud Run for burst compute — good cost/latency balance","allocations":[{"provider":"digitalocean","percentage":70},{"provider":"gcp","percentage":30}],"metrics":{"monthlyCost":8100,"avgLatencyMs":49,"vendorLockInScore":38}},{"name":"AWS-Dominant Strategy","description":"EC2 t3.medium fleet — higher baseline cost, comparable single-request latency","allocations":[{"provider":"aws","percentage":100}],"metrics":{"monthlyCost":13800,"avgLatencyMs":41,"vendorLockInScore":74}}],"comparisonReport":"# Multi-Cloud Strategy Comparison Report\n\n## Winner: DigitalOcean AMD NVMe Primary Strategy\n\nMonthly cost: **$6,200** — 55% lower than the AWS-dominant baseline ($13,800).\nAverage latency: **52 ms** — within the 150 ms SLA requirement.\nVendor lock-in score: **44/100** — low lock-in, easy to migrate if requirements change.\n\n## Key Resources\n\n- **Compute:** DigitalOcean AMD NVMe Droplets (s-2vcpu-4gb-amd, nyc3) — AMD EPYC processors with NVMe-backed local SSD storage deliver higher disk I/O throughput than standard Intel Droplets at the same hourly rate ($0.036/hr per Droplet). Ideal for workloads with frequent local reads/writes or ephemeral scratch space.\n- **Database:** DigitalOcean Managed Databases (PostgreSQL, 2-node HA cluster, nyc3) — automated failover, daily backups, and PgBouncer connection pooling included at no extra charge.\n- **Object Storage:** DigitalOcean Spaces — S3-compatible API, 250 GB included, optional CDN edge caching.\n\n## AMD NVMe vs Standard Droplets\n\nThe `s-2vcpu-4gb-amd` slug selects the AMD NVMe variant of the standard shared-CPU tier. Compared to the equivalent Intel Droplet (`s-2vcpu-4gb`):\n- Same vCPU count and RAM\n- Same hourly price\n- NVMe local SSD instead of spinning disk — up to 3× higher sequential read throughput\n- AMD EPYC \"Milan\" or \"Rome\" core depending on host availability\n\nChoose the AMD NVMe variant when your workload is I/O-bound (e.g. log processing, local caching, build pipelines) or when you want deterministic low-latency disk access without paying for a dedicated CPU plan.\n\n## Trade-offs\n\n| Metric | DO AMD NVMe Primary | DO AMD NVMe + GCP | AWS-Dominant |\n|---|---|---|---|\n| Monthly cost | $6,200 | $8,100 | $13,800 |\n| Avg latency | 52 ms | 49 ms | 41 ms |\n| Lock-in score | 44 | 38 | 74 |\n\n## Recommendation\n\nFor cost-sensitive workloads with moderate I/O requirements, the DigitalOcean AMD NVMe Primary strategy delivers the best value. The 11 ms latency gap versus the AWS baseline is unlikely to affect end-user experience for the given SLA. If burst capacity beyond current Droplet limits is anticipated, consider the DO AMD NVMe + GCP Balanced strategy, which adds only $1,900/month for a 3 ms latency improvement and lower vendor lock-in.","completedAt":"2024-01-21T10:15:00Z"}}}}}},"400":{"description":"Job not completed yet","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/ApiKeyUnauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/multi-cloud/jobs/{jobId}/partial-results":{"x-stability":"stable","get":{"tags":["Multi-Cloud Strategy"],"summary":"Get partial multi-cloud exploration results while a job is running","description":"Retrieve strategies accumulated so far for a multi-cloud exploration job, even while the job is still running.\n\nThis endpoint is the dedicated channel for polling partial results during job execution.\nThe `/results` endpoint requires the job to be completed; use this endpoint instead when you want\nto start analyzing strategies before the full exploration finishes.\n\n**Polling pattern:**\n```\nwhile true:\n data = GET /api/multi-cloud/jobs/{jobId}/partial-results\n show data.allStrategies to user\n if data.isComplete: break\n sleep(2s)\nfull = GET /api/multi-cloud/jobs/{jobId}/results\n```\n\n**isComplete flag:**\n- `false` — job is still `pending` or `running`; more strategies may arrive\n- `true` — job has reached a terminal state (`completed`, `failed`, or `cancelled`)\n\n**When `isComplete` is true and `status` is `completed`**, fetch the ranked\n`topStrategies` and `comparisonReport` from `GET /api/multi-cloud/jobs/{jobId}/results`.\n","operationId":"getMultiCloudPartialResults","x-codeSamples":[{"lang":"curl","label":"curl","source":"while true; do\n DATA=$(curl -s \"https://your-production-domain.com/api/multi-cloud/jobs/job_abc123/partial-results\" \\\n -H \"Authorization: Bearer $API_KEY\")\n echo \"$DATA\" | jq '{strategies: (.allStrategies | length), isComplete: .isComplete}'\n [ \"$(echo \"$DATA\" | jq -r '.isComplete')\" = \"true\" ] && break\n sleep 2\ndone\n"},{"lang":"Node.js","label":"Node.js","source":"const BASE_URL = \"https://your-production-domain.com/api\";\nconst API_KEY = \"your-api-key\";\nconst JOB_ID = \"job_abc123\";\n\nasync function pollPartialResults(jobId) {\n while (true) {\n const resp = await fetch(`${BASE_URL}/multi-cloud/jobs/${jobId}/partial-results`, {\n headers: { \"Authorization\": `Bearer ${API_KEY}` },\n });\n const data = await resp.json();\n console.log(`strategies so far: ${data.allStrategies?.length ?? 0}, isComplete: ${data.isComplete}`);\n if (data.isComplete) break;\n await new Promise(r => setTimeout(r, 2000));\n }\n // Fetch full ranked results once complete\n const full = await fetch(`${BASE_URL}/multi-cloud/jobs/${jobId}/results`, {\n headers: { \"Authorization\": `Bearer ${API_KEY}` },\n });\n return full.json();\n}\n"}],"security":[],"parameters":[{"name":"jobId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"ID of the multi-cloud job"},{"name":"providers","in":"query","required":false,"schema":{"type":"string","example":"digitalocean,aws"},"description":"Comma-separated list of cloud provider names to filter results by preferred primary provider.\nA strategy is included if any of the specified providers holds ≥ 50 % of the traffic allocation.\nSupported values: `aws`, `gcp`, `azure`, `digitalocean`, `oci`.\nIf omitted, all strategies are returned.\n"}],"responses":{"200":{"description":"Partial or terminal results for the job","content":{"application/json":{"schema":{"type":"object","required":["jobId","status","progress","isComplete","strategiesGenerated","allStrategies"],"properties":{"jobId":{"type":"string"},"status":{"type":"string","enum":["pending","running","completed","failed","cancelled"]},"progress":{"type":"number","description":"Job progress percentage (0-100)"},"isComplete":{"type":"boolean","description":"True if job has reached a terminal state, false if still pending or running"},"strategiesGenerated":{"type":"number","description":"Number of strategies generated so far"},"allStrategies":{"type":"array","items":{"$ref":"#/components/schemas/Strategy"},"description":"All raw strategies generated so far"},"error":{"type":"string","description":"Error message — only present when status is failed"}}},"examples":{"runningPartial":{"summary":"Job still running — 12 strategies accumulated so far","value":{"jobId":"job_abc123","status":"running","progress":40,"isComplete":false,"strategiesGenerated":12,"allStrategies":[{"name":"AWS-Dominant Strategy","description":"Primary AWS deployment","allocations":[{"provider":"aws","percentage":100}],"metrics":{"monthlyCost":14500,"avgLatencyMs":38,"vendorLockInScore":72}}]}},"terminalComplete":{"summary":"Job completed — isComplete is true, fetch /results for ranked output","value":{"jobId":"job_abc123","status":"completed","progress":100,"isComplete":true,"strategiesGenerated":42,"allStrategies":[]}},"digitaloceanAMDNVMePartialMultiCloud":{"summary":"AMD NVMe mid-run — s-2vcpu-4gb-amd strategy visible before job completes","value":{"jobId":"job_do_amd_nvme_partial_001","status":"running","progress":55,"isComplete":false,"strategiesGenerated":20,"allStrategies":[{"name":"DigitalOcean AMD NVMe Primary Strategy","description":"DigitalOcean AMD NVMe Droplets (s-2vcpu-4gb-amd, nyc3) as primary compute — NVMe-backed local SSD storage delivers higher I/O throughput than standard Droplets at the same price point, with DigitalOcean Managed Databases (PostgreSQL) and Spaces for object storage","allocations":[{"provider":"digitalocean","percentage":85},{"provider":"aws","percentage":15}],"metrics":{"monthlyCost":6200,"avgLatencyMs":52,"vendorLockInScore":44}},{"name":"DigitalOcean AMD NVMe + GCP Balanced","description":"s-2vcpu-4gb-amd Droplets for primary API tier with GCP Cloud Run for burst compute — good cost/latency balance","allocations":[{"provider":"digitalocean","percentage":70},{"provider":"gcp","percentage":30}],"metrics":{"monthlyCost":8100,"avgLatencyMs":49,"vendorLockInScore":38}},{"name":"AWS-Dominant Strategy","description":"EC2 t3.medium fleet — higher baseline cost, comparable single-request latency","allocations":[{"provider":"aws","percentage":100}],"metrics":{"monthlyCost":13800,"avgLatencyMs":41,"vendorLockInScore":74}}]}},"digitaloceanAMDNVMeFilteredPartial":{"summary":"AMD NVMe mid-run filtered — ?providers=digitalocean excludes AWS-dominant entries while job is still running","value":{"jobId":"job_do_amd_nvme_partial_001","status":"running","progress":55,"isComplete":false,"strategiesGenerated":14,"allStrategies":[{"name":"DigitalOcean AMD NVMe Primary Strategy","description":"DigitalOcean AMD NVMe Droplets (s-2vcpu-4gb-amd, nyc3) as primary compute — NVMe-backed local SSD storage delivers higher I/O throughput than standard Droplets at the same price point, with DigitalOcean Managed Databases (PostgreSQL) and Spaces for object storage","allocations":[{"provider":"digitalocean","percentage":85},{"provider":"aws","percentage":15}],"metrics":{"monthlyCost":6200,"avgLatencyMs":52,"vendorLockInScore":44}},{"name":"DigitalOcean AMD NVMe + GCP Balanced","description":"s-2vcpu-4gb-amd Droplets for primary API tier with GCP Cloud Run for burst compute — good cost/latency balance","allocations":[{"provider":"digitalocean","percentage":70},{"provider":"gcp","percentage":30}],"metrics":{"monthlyCost":8100,"avgLatencyMs":49,"vendorLockInScore":38}}]}}}}}},"401":{"$ref":"#/components/responses/ApiKeyUnauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/multi-cloud/jobs/{jobId}/stream":{"x-stability":"stable","get":{"summary":"Stream multi-cloud exploration results in real-time","description":"Stream multi-cloud exploration results using Server-Sent Events (SSE).\nThis endpoint provides real-time updates as strategies are generated.\n\n**SSE Event Types:**\n- `init`: Initial job state when connection is established\n- `strategyGenerated`: A new strategy has been generated (sent for each strategy)\n- `progressUpdate`: Progress update (sent periodically)\n- `completed`: Job has finished successfully (final event before connection closes)\n- `failed`: Job encountered an unrecoverable error (final event before connection closes)\n- `cancelled`: Job was cancelled via `DELETE /api/multi-cloud/jobs/{jobId}` (final event before connection closes)\n\n**Connection Behavior:**\n- Connection remains open until job completes, fails, or is cancelled\n- All existing strategies are streamed immediately upon connection\n- New strategies are sent as they're generated\n- Connection automatically closes when job finishes\n\n**Agent Reconnection and Recovery Guide:**\n\nAfter receiving a terminal SSE event (`completed`, `failed`, or `cancelled`) the server\ncloses the connection. Each event requires a different agent response:\n\n- **`completed`**: The job finished successfully. No reconnection is needed. Fetch the\n full ranked results from `GET /api/multi-cloud/jobs/{jobId}/results` to retrieve\n `topStrategies`, `allStrategies`, and the `comparisonReport`. The results endpoint\n also accepts a `?providers=` filter if you only need strategies for specific clouds.\n\n- **`failed`**: The job encountered an unrecoverable error. Inspect the `error` field in\n the event payload for the root cause. Transient errors (e.g. a pricing API timeout)\n are safe to retry — submit a new job via `POST /api/multi-cloud/jobs`. Permanent errors\n (e.g. an invalid scenario ID) should not be retried without fixing the underlying\n input first. Do not attempt to reconnect to the same `jobId`; it will not recover.\n\n- **`cancelled`**: Cancellation is terminal and intentional. No retry is needed or\n recommended. If the cancellation was unintended, submit a new job.\n\n**Handling unexpected connection drops (no terminal event received):**\n\nIf the SSE connection closes without a `completed`, `failed`, or `cancelled` event —\nfor example due to a network interruption, proxy timeout, or server restart — the job\nmay still be running. Use the following fallback strategy:\n\n1. Poll `GET /api/multi-cloud/jobs/{jobId}` to check the current `status` field.\n2. If `status` is `running` or `pending`, reconnect to this stream endpoint. The\n server replays all strategies generated so far on reconnect, so no data is lost.\n3. If `status` is `completed`, `failed`, or `cancelled`, treat it the same as if you\n had received the corresponding terminal SSE event (see above).\n\nAgents should implement an exponential back-off (e.g. 1 s, 2 s, 4 s, cap at 30 s)\nbefore each reconnection attempt to avoid hammering the server during an outage.\n\n**Use Cases:**\n- Start analyzing strategies while generation continues\n- Real-time progress monitoring\n- Faster decision-making with early access to good strategies\n\n**Client Code Sample (JavaScript / Node.js):**\n\n`EventSource` does not support custom headers, so use `fetch` with a\n`ReadableStream` to pass the Bearer token:\n\n```javascript\nasync function streamMultiCloudJob(jobId, apiToken) {\n const response = await fetch(\n `https://your-host/api/multi-cloud/jobs/${jobId}/stream`,\n {\n headers: {\n Authorization: `Bearer ${apiToken}`,\n Accept: 'text/event-stream',\n },\n }\n );\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${await response.text()}`);\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n\n // SSE frames are separated by double newlines\n const frames = buffer.split(/\\n\\n/);\n buffer = frames.pop(); // keep incomplete trailing frame\n\n for (const frame of frames) {\n const eventLine = frame.match(/^event:\\s*(.+)$/m);\n const dataLine = frame.match(/^data:\\s*(.+)$/m);\n if (!dataLine) continue;\n\n const eventType = eventLine ? eventLine[1].trim() : 'message';\n const payload = JSON.parse(dataLine[1]);\n\n switch (eventType) {\n case 'init':\n console.log('Stream opened. Job status:', payload.status);\n break;\n\n case 'progressUpdate':\n console.log(`Progress: ${payload.progress}% — ${payload.message}`);\n break;\n\n case 'strategyGenerated':\n console.log('New strategy:', payload.strategy.name,\n '| cost $' + payload.strategy.metrics.monthlyCost);\n break;\n\n case 'completed':\n console.log('Job complete. Top strategy:',\n payload.topStrategy?.name);\n reader.cancel(); // close the connection\n return payload;\n }\n }\n }\n}\n\n// Usage\nstreamMultiCloudJob('job_aws_perf123', process.env.API_KEY)\n .then(result => console.log('Final result:', result))\n .catch(err => console.error('Stream error:', err));\n```\n\n**Client Code Sample (Python):**\n\nUse `httpx` with streaming to consume the SSE connection.\nInstall with `pip install httpx`.\n\n```python\nimport httpx\nimport json\nimport os\n\n\ndef stream_multi_cloud_job(job_id: str, api_token: str) -> dict:\n \"\"\"Stream a multi-cloud exploration job and return the final payload.\"\"\"\n url = f\"https://your-host/api/multi-cloud/jobs/{job_id}/stream\"\n headers = {\n \"Authorization\": f\"Bearer {api_token}\",\n \"Accept\": \"text/event-stream\",\n }\n\n with httpx.stream(\"GET\", url, headers=headers, timeout=None) as response:\n response.raise_for_status()\n\n buffer = \"\"\n\n for chunk in response.iter_text():\n buffer += chunk\n\n *frames, buffer = buffer.split(\"\\n\\n\")\n\n for frame in frames:\n event_type = \"message\"\n data_str = None\n\n for line in frame.splitlines():\n if line.startswith(\"event:\"):\n event_type = line[len(\"event:\"):].strip()\n elif line.startswith(\"data:\"):\n data_str = line[len(\"data:\"):].strip()\n\n if data_str is None:\n continue\n\n payload = json.loads(data_str)\n\n if event_type == \"init\":\n print(f\"Stream opened. Job status: {payload['status']}\")\n\n elif event_type == \"progressUpdate\":\n print(f\"Progress: {payload['progress']}% — {payload.get('message', '')}\")\n\n elif event_type == \"strategyGenerated\":\n strategy = payload[\"strategy\"]\n cost = strategy[\"metrics\"][\"monthlyCost\"]\n print(f\"New strategy: {strategy['name']} | cost ${cost}\")\n\n elif event_type == \"completed\":\n top = payload.get(\"topStrategy\", {})\n print(f\"Job complete. Top strategy: {top.get('name')}\")\n return payload # connection closes when the context exits\n\n return {}\n\n\nif __name__ == \"__main__\":\n result = stream_multi_cloud_job(\n job_id=\"job_aws_perf123\",\n api_token=os.environ[\"API_KEY\"],\n )\n print(\"Final result:\", result)\n```\n\n**Agent Reconnection Code Sample — with exponential back-off (JavaScript / Node.js):**\n\nThe samples above cover the happy path. The snippet below adds the full\nreconnect loop: detecting a connection drop without a terminal event,\npolling the status endpoint as a fallback, and reconnecting with\nexponential back-off. All three terminal events (`completed`, `failed`,\n`cancelled`) are handled explicitly.\n\n```javascript\nconst BASE_URL = 'https://your-host/api';\n\n// Fetch the current job state without opening an SSE stream.\nasync function pollJobStatus(jobId, apiToken) {\n const res = await fetch(`${BASE_URL}/multi-cloud/jobs/${jobId}`, {\n headers: { Authorization: `Bearer ${apiToken}` },\n });\n if (!res.ok) throw new Error(`Poll failed: HTTP ${res.status}`);\n return res.json(); // { status, progress, ... }\n}\n\nasync function streamWithReconnect(jobId, apiToken) {\n let delay = 1_000; // back-off starts at 1 s\n const MAX_DELAY = 30_000;\n\n while (true) {\n let receivedTerminal = false;\n\n try {\n // ── Open the SSE connection ──────────────────────────────────\n const response = await fetch(\n `${BASE_URL}/multi-cloud/jobs/${jobId}/stream`,\n {\n headers: {\n Authorization: `Bearer ${apiToken}`,\n Accept: 'text/event-stream',\n },\n }\n );\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${await response.text()}`);\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n outer: while (true) {\n const { value, done } = await reader.read();\n if (done) break; // server closed — check receivedTerminal below\n\n buffer += decoder.decode(value, { stream: true });\n const frames = buffer.split(/\\n\\n/);\n buffer = frames.pop(); // keep any incomplete trailing frame\n\n for (const frame of frames) {\n const eventLine = frame.match(/^event:\\s*(.+)$/m);\n const dataLine = frame.match(/^data:\\s*(.+)$/m);\n if (!dataLine) continue;\n\n const eventType = eventLine ? eventLine[1].trim() : 'message';\n const payload = JSON.parse(dataLine[1]);\n\n switch (eventType) {\n case 'init':\n // Successful (re)connect — reset back-off timer.\n console.log('Connected. Job status:', payload.status);\n delay = 1_000;\n break;\n\n case 'progressUpdate':\n console.log(`Progress: ${payload.progress}% — ${payload.message}`);\n break;\n\n case 'strategyGenerated':\n console.log('Strategy:', payload.strategy.name,\n '| $' + payload.strategy.metrics.monthlyCost + '/mo');\n break;\n\n // ── Terminal events ──────────────────────────────────────\n case 'completed':\n console.log('Job complete. Top strategy:', payload.topStrategy?.name);\n receivedTerminal = true;\n reader.cancel();\n return { status: 'completed', payload };\n\n case 'failed':\n console.error('Job failed:', payload.error);\n receivedTerminal = true;\n reader.cancel();\n return { status: 'failed', payload };\n\n case 'cancelled':\n console.warn('Job cancelled.');\n receivedTerminal = true;\n reader.cancel();\n return { status: 'cancelled', payload };\n }\n\n if (receivedTerminal) break outer;\n }\n }\n } catch (err) {\n // Network error, proxy timeout, or server restart.\n console.warn('SSE connection error:', err.message);\n }\n\n // Already handled cleanly — exit the retry loop.\n if (receivedTerminal) break;\n\n // ── Fallback: poll before reconnecting ───────────────────────────\n // The connection dropped without a terminal event. The job may\n // still be running, or it may have finished while we were offline.\n try {\n const job = await pollJobStatus(jobId, apiToken);\n\n if (job.status === 'completed') {\n console.log('Recovered via poll — job already completed.');\n return { status: 'completed', payload: job };\n }\n if (job.status === 'failed') {\n console.error('Recovered via poll — job failed:', job.error);\n return { status: 'failed', payload: job };\n }\n if (job.status === 'cancelled') {\n console.warn('Recovered via poll — job was cancelled.');\n return { status: 'cancelled', payload: job };\n }\n // status is 'running' or 'pending' — reconnect after back-off.\n console.log(`Job still ${job.status}. Reconnecting in ${delay / 1000}s…`);\n } catch (pollErr) {\n console.warn('Poll also failed:', pollErr.message, '— will retry.');\n }\n\n // ── Exponential back-off ─────────────────────────────────────────\n await new Promise(resolve => setTimeout(resolve, delay));\n delay = Math.min(delay * 2, MAX_DELAY);\n }\n}\n\n// Usage\nstreamWithReconnect('job_aws_perf123', process.env.API_KEY)\n .then(({ status, payload }) => console.log('Done:', status, payload))\n .catch(err => console.error('Unrecoverable error:', err));\n```\n\n**Agent Reconnection Code Sample — with exponential back-off (Python):**\n\n```python\nimport httpx\nimport json\nimport os\nimport time\n\n\nBASE_URL = \"https://your-host/api\"\nTERMINAL_STATUSES = {\"completed\", \"failed\", \"cancelled\"}\n\n\ndef poll_job_status(job_id: str, api_token: str) -> dict:\n \"\"\"Fetch the current job state without opening an SSE stream.\"\"\"\n url = f\"{BASE_URL}/multi-cloud/jobs/{job_id}\"\n headers = {\"Authorization\": f\"Bearer {api_token}\"}\n response = httpx.get(url, headers=headers, timeout=10)\n response.raise_for_status()\n return response.json() # {\"status\": ..., \"progress\": ..., ...}\n\n\ndef stream_with_reconnect(job_id: str, api_token: str) -> dict:\n \"\"\"\n Open the SSE stream and reconnect automatically after unexpected drops.\n\n Polls the status endpoint when the connection closes without a terminal\n event, and applies exponential back-off before each reconnection attempt.\n\n Returns {\"status\": <terminal_status>, \"payload\": <event_payload>}.\n \"\"\"\n url = f\"{BASE_URL}/multi-cloud/jobs/{job_id}/stream\"\n headers = {\n \"Authorization\": f\"Bearer {api_token}\",\n \"Accept\": \"text/event-stream\",\n }\n delay = 1.0 # back-off starts at 1 s\n max_delay = 30.0\n\n while True:\n received_terminal = False\n\n try:\n with httpx.stream(\"GET\", url, headers=headers, timeout=None) as response:\n response.raise_for_status()\n delay = 1.0 # reset back-off on successful connect\n buffer = \"\"\n\n for chunk in response.iter_text():\n buffer += chunk\n *frames, buffer = buffer.split(\"\\n\\n\")\n\n for frame in frames:\n event_type = \"message\"\n data_str = None\n\n for line in frame.splitlines():\n if line.startswith(\"event:\"):\n event_type = line[len(\"event:\"):].strip()\n elif line.startswith(\"data:\"):\n data_str = line[len(\"data:\"):].strip()\n\n if data_str is None:\n continue\n\n payload = json.loads(data_str)\n\n if event_type == \"init\":\n print(f\"Connected. Job status: {payload['status']}\")\n\n elif event_type == \"progressUpdate\":\n print(f\"Progress: {payload['progress']}% — {payload.get('message', '')}\")\n\n elif event_type == \"strategyGenerated\":\n s = payload[\"strategy\"]\n print(f\"Strategy: {s['name']} | ${s['metrics']['monthlyCost']}/mo\")\n\n elif event_type == \"completed\":\n top = payload.get(\"topStrategy\", {})\n print(f\"Job complete. Top strategy: {top.get('name')}\")\n received_terminal = True\n return {\"status\": \"completed\", \"payload\": payload}\n\n elif event_type == \"failed\":\n print(f\"Job failed: {payload.get('error')}\")\n received_terminal = True\n return {\"status\": \"failed\", \"payload\": payload}\n\n elif event_type == \"cancelled\":\n print(\"Job cancelled.\")\n received_terminal = True\n return {\"status\": \"cancelled\", \"payload\": payload}\n\n if received_terminal:\n break\n\n except (httpx.HTTPError, httpx.StreamError) as exc:\n print(f\"SSE connection error: {exc}\")\n\n if received_terminal:\n break\n\n try:\n job = poll_job_status(job_id, api_token)\n\n if job[\"status\"] in TERMINAL_STATUSES:\n print(f\"Recovered via poll — job {job['status']}.\")\n return {\"status\": job[\"status\"], \"payload\": job}\n\n print(f\"Job still {job['status']}. Reconnecting in {delay:.0f}s…\")\n\n except httpx.HTTPError as exc:\n print(f\"Poll also failed: {exc} — will retry.\")\n\n time.sleep(delay)\n delay = min(delay * 2, max_delay)\n\n return {}\n\n\nif __name__ == \"__main__\":\n result = stream_with_reconnect(\n job_id=\"job_aws_perf123\",\n api_token=os.environ[\"API_KEY\"],\n )\n print(\"Done:\", result[\"status\"], result.get(\"payload\", {}))\n```\n","operationId":"streamMultiCloudJob","x-codeSamples":[{"lang":"curl","label":"curl","source":"curl -N https://your-production-domain.com/api/multi-cloud/jobs/job_abc123/stream \\\n -H \"Authorization: Bearer $API_KEY\" \\\n -H \"Accept: text/event-stream\"\n"},{"lang":"Python","label":"Python","source":"import httpx\nimport json\n\nBASE_URL = \"https://your-production-domain.com/api\"\nAPI_KEY = \"your-api-key\"\nJOB_ID = \"job_abc123\"\n\nurl = f\"{BASE_URL}/multi-cloud/jobs/{JOB_ID}/stream\"\nheaders = {\"Authorization\": f\"Bearer {API_KEY}\", \"Accept\": \"text/event-stream\"}\n\nwith httpx.stream(\"GET\", url, headers=headers, timeout=None) as response:\n response.raise_for_status()\n buffer = \"\"\n for chunk in response.iter_text():\n buffer += chunk\n *frames, buffer = buffer.split(\"\\n\\n\")\n for frame in frames:\n event_type, data_str = \"message\", None\n for line in frame.splitlines():\n if line.startswith(\"event:\"):\n event_type = line[len(\"event:\"):].strip()\n elif line.startswith(\"data:\"):\n data_str = line[len(\"data:\"):].strip()\n if not data_str:\n continue\n payload = json.loads(data_str)\n if event_type == \"progressUpdate\":\n print(f\"Progress: {payload['progress']}%\")\n elif event_type == \"strategyGenerated\":\n s = payload[\"strategy\"]\n print(f\"Strategy: {s['name']} cost=${s['metrics']['monthlyCost']}/mo\")\n elif event_type == \"completed\":\n top = payload.get(\"topStrategy\", {})\n print(f\"Done. Top strategy: {top.get('name')}\")\n break\n"},{"lang":"Node.js","label":"Node.js","source":"const BASE_URL = \"https://your-production-domain.com/api\";\nconst API_KEY = \"your-api-key\";\nconst JOB_ID = \"job_abc123\";\n\nconst response = await fetch(`${BASE_URL}/multi-cloud/jobs/${JOB_ID}/stream`, {\n headers: { \"Authorization\": `Bearer ${API_KEY}`, \"Accept\": \"text/event-stream\" },\n});\nif (!response.ok) throw new Error(`HTTP ${response.status}`);\n\nconst reader = response.body.getReader();\nconst decoder = new TextDecoder();\nlet buffer = \"\";\n\nwhile (true) {\n const { value, done } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n const frames = buffer.split(/\\n\\n/);\n buffer = frames.pop();\n for (const frame of frames) {\n const eventLine = frame.match(/^event:\\s*(.+)$/m);\n const dataLine = frame.match(/^data:\\s*(.+)$/m);\n if (!dataLine) continue;\n const eventType = eventLine ? eventLine[1].trim() : \"message\";\n const payload = JSON.parse(dataLine[1]);\n if (eventType === \"progressUpdate\") {\n console.log(`Progress: ${payload.progress}%`);\n } else if (eventType === \"strategyGenerated\") {\n const s = payload.strategy;\n console.log(`Strategy: ${s.name} cost=$${s.metrics.monthlyCost}/mo`);\n } else if (eventType === \"completed\") {\n console.log(\"Done. Top strategy:\", payload.topStrategy?.name);\n reader.cancel();\n }\n }\n}\n"}],"tags":["Multi-Cloud Strategy"],"security":[],"parameters":[{"name":"jobId","in":"path","required":true,"schema":{"type":"string"},"description":"Multi-cloud job ID"}],"responses":{"200":{"description":"SSE stream of job updates","content":{"text/event-stream":{"schema":{"type":"string","description":"Server-Sent Events stream"},"examples":{"awsStream":{"summary":"AWS — EC2 m5.xlarge + RDS Multi-AZ performance workload stream","value":"event: init\ndata: {\"jobId\":\"job_aws_perf123\",\"status\":\"running\",\"progress\":0,\"strategiesGenerated\":0}\n\nevent: progressUpdate\ndata: {\"jobId\":\"job_aws_perf123\",\"progress\":20,\"strategiesGenerated\":8,\"message\":\"Evaluating EC2 instance families and RDS configurations\"}\n\nevent: strategyGenerated\ndata: {\"jobId\":\"job_aws_perf123\",\"strategy\":{\"name\":\"AWS-Primary Strategy\",\"description\":\"EC2 m5.xlarge Auto Scaling group across us-east-1a/1b with RDS db.r5.large Multi-AZ and CloudFront CDN — lowest p95 latency in comparison\",\"allocations\":[{\"provider\":\"aws\",\"percentage\":100}],\"metrics\":{\"monthlyCost\":14500,\"avgLatencyMs\":38,\"vendorLockInScore\":72}},\"strategiesGenerated\":9}\n\nevent: strategyGenerated\ndata: {\"jobId\":\"job_aws_perf123\",\"strategy\":{\"name\":\"AWS + GCP Balanced\",\"description\":\"EC2 primary with GCP Cloud Run burst capacity — reduces lock-in by 14 points with only 3 ms latency increase\",\"allocations\":[{\"provider\":\"aws\",\"percentage\":70},{\"provider\":\"gcp\",\"percentage\":30}],\"metrics\":{\"monthlyCost\":13200,\"avgLatencyMs\":41,\"vendorLockInScore\":58}},\"strategiesGenerated\":10}\n\nevent: progressUpdate\ndata: {\"jobId\":\"job_aws_perf123\",\"progress\":65,\"strategiesGenerated\":26,\"message\":\"Ranking strategies by p95 latency against 50 ms SLA\"}\n\nevent: completed\ndata: {\"jobId\":\"job_aws_perf123\",\"status\":\"completed\",\"progress\":100,\"strategiesGenerated\":40,\"topStrategy\":{\"name\":\"AWS-Primary Strategy\",\"metrics\":{\"monthlyCost\":14500,\"avgLatencyMs\":38,\"vendorLockInScore\":72}},\"comparisonReport\":\"# Multi-Cloud Strategy Comparison Report\\n\\n## Winner: AWS-Primary Strategy\\n\\nAverage latency: **38 ms** — best in class, within the 50 ms SLA.\\nMonthly cost: **$14,500** — within the $25,000 budget.\\nVendor lock-in score: **72/100** — acceptable given performance requirements.\",\"completedAt\":\"2024-01-17T08:20:00Z\"}\n"},"gcpStream":{"summary":"GCP — Cloud Run + Cloud SQL ML analytics workload stream","value":"event: init\ndata: {\"jobId\":\"job_gcp_ml456\",\"status\":\"running\",\"progress\":0,\"strategiesGenerated\":0}\n\nevent: progressUpdate\ndata: {\"jobId\":\"job_gcp_ml456\",\"progress\":18,\"strategiesGenerated\":6,\"message\":\"Evaluating Cloud Run autoscaling profiles for variable ML inference load\"}\n\nevent: strategyGenerated\ndata: {\"jobId\":\"job_gcp_ml456\",\"strategy\":{\"name\":\"GCP-Primary Strategy\",\"description\":\"Cloud Run (fully managed, us-central1) with Cloud SQL for PostgreSQL (HA) and Cloud Storage — best autoscaling fit for variable ML inference load; scales to zero between jobs\",\"allocations\":[{\"provider\":\"gcp\",\"percentage\":100}],\"metrics\":{\"monthlyCost\":11800,\"avgLatencyMs\":42,\"vendorLockInScore\":63}},\"strategiesGenerated\":7}\n\nevent: strategyGenerated\ndata: {\"jobId\":\"job_gcp_ml456\",\"strategy\":{\"name\":\"GCP + AWS Hybrid\",\"description\":\"GCP Cloud Run primary with AWS Lambda for async batch processing — reduces cost by 5% versus GCP-only while adding cross-cloud redundancy\",\"allocations\":[{\"provider\":\"gcp\",\"percentage\":65},{\"provider\":\"aws\",\"percentage\":35}],\"metrics\":{\"monthlyCost\":12400,\"avgLatencyMs\":44,\"vendorLockInScore\":51}},\"strategiesGenerated\":8}\n\nevent: progressUpdate\ndata: {\"jobId\":\"job_gcp_ml456\",\"progress\":70,\"strategiesGenerated\":24,\"message\":\"Comparing scale-to-zero savings across Cloud Run, Lambda, and Container Apps\"}\n\nevent: completed\ndata: {\"jobId\":\"job_gcp_ml456\",\"status\":\"completed\",\"progress\":100,\"strategiesGenerated\":35,\"topStrategy\":{\"name\":\"GCP-Primary Strategy\",\"metrics\":{\"monthlyCost\":11800,\"avgLatencyMs\":42,\"vendorLockInScore\":63}},\"comparisonReport\":\"# Multi-Cloud Strategy Comparison Report\\n\\n## Winner: GCP-Primary Strategy\\n\\nMonthly cost: **$11,800** — $3,800 below the AWS baseline.\\nAverage latency: **42 ms** — within the 80 ms SLA.\\nCloud Run scale-to-zero eliminates idle compute cost between ML inference jobs.\",\"completedAt\":\"2024-01-18T11:35:00Z\"}\n"},"azureStream":{"summary":"Azure — AKS + Azure Database for PostgreSQL compliance workload stream","value":"event: init\ndata: {\"jobId\":\"job_azure_comp789\",\"status\":\"running\",\"progress\":0,\"strategiesGenerated\":0}\n\nevent: progressUpdate\ndata: {\"jobId\":\"job_azure_comp789\",\"progress\":22,\"strategiesGenerated\":8,\"message\":\"Evaluating GDPR/HIPAA compliance posture across AKS, GKE, and EKS configurations\"}\n\nevent: strategyGenerated\ndata: {\"jobId\":\"job_azure_comp789\",\"strategy\":{\"name\":\"Azure-Primary Strategy\",\"description\":\"AKS (eastus, Standard_D4s_v3 nodes) with Azure Database for PostgreSQL Flexible Server and Azure Front Door — strongest GDPR/HIPAA compliance posture with native Policy and Defender integration\",\"allocations\":[{\"provider\":\"azure\",\"percentage\":100}],\"metrics\":{\"monthlyCost\":13200,\"avgLatencyMs\":44,\"vendorLockInScore\":67}},\"strategiesGenerated\":9}\n\nevent: strategyGenerated\ndata: {\"jobId\":\"job_azure_comp789\",\"strategy\":{\"name\":\"Azure + AWS Hybrid\",\"description\":\"AKS primary with AWS S3 for object storage overflow — reduces storage cost by 12% while maintaining Azure compliance perimeter for compute and database tiers\",\"allocations\":[{\"provider\":\"azure\",\"percentage\":75},{\"provider\":\"aws\",\"percentage\":25}],\"metrics\":{\"monthlyCost\":14100,\"avgLatencyMs\":46,\"vendorLockInScore\":55}},\"strategiesGenerated\":10}\n\nevent: progressUpdate\ndata: {\"jobId\":\"job_azure_comp789\",\"progress\":60,\"strategiesGenerated\":23,\"message\":\"Scoring compliance coverage for GDPR, HIPAA, ISO 27001, and PCI-DSS controls\"}\n\nevent: completed\ndata: {\"jobId\":\"job_azure_comp789\",\"status\":\"completed\",\"progress\":100,\"strategiesGenerated\":38,\"topStrategy\":{\"name\":\"Azure-Primary Strategy\",\"metrics\":{\"monthlyCost\":13200,\"avgLatencyMs\":44,\"vendorLockInScore\":67}},\"comparisonReport\":\"# Multi-Cloud Strategy Comparison Report\\n\\n## Winner: Azure-Primary Strategy\\n\\nMonthly cost: **$13,200** — within the $20,000 budget.\\nAverage latency: **44 ms** — within the 100 ms SLA.\\nCompliance: native GDPR, HIPAA, and ISO 27001 tooling; no third-party additions required.\",\"completedAt\":\"2024-01-19T14:00:00Z\"}\n"},"ociStream":{"summary":"OCI — Compute + Autonomous Database database-intensive workload stream","value":"event: init\ndata: {\"jobId\":\"job_oci_db321\",\"status\":\"running\",\"progress\":0,\"strategiesGenerated\":0}\n\nevent: progressUpdate\ndata: {\"jobId\":\"job_oci_db321\",\"progress\":25,\"strategiesGenerated\":8,\"message\":\"Benchmarking Autonomous Database ATP throughput against RDS and Cloud SQL at equivalent OCPU counts\"}\n\nevent: strategyGenerated\ndata: {\"jobId\":\"job_oci_db321\",\"strategy\":{\"name\":\"OCI-Primary Strategy\",\"description\":\"OCI Compute VM.Standard.E4.Flex (4 OCPU / 64 GB) with Autonomous Database ATP (4 OCPU) in us-ashburn-1 — best price-performance for OLTP-heavy workloads; Autonomous Database self-tunes indexes and query plans\",\"allocations\":[{\"provider\":\"oci\",\"percentage\":100}],\"metrics\":{\"monthlyCost\":9600,\"avgLatencyMs\":48,\"vendorLockInScore\":55}},\"strategiesGenerated\":9}\n\nevent: strategyGenerated\ndata: {\"jobId\":\"job_oci_db321\",\"strategy\":{\"name\":\"OCI + AWS Hybrid\",\"description\":\"OCI Autonomous Database for primary OLTP workload with AWS S3 and Lambda for analytics offload — keeps database cost advantage while leveraging mature AWS analytics ecosystem\",\"allocations\":[{\"provider\":\"oci\",\"percentage\":70},{\"provider\":\"aws\",\"percentage\":30}],\"metrics\":{\"monthlyCost\":11200,\"avgLatencyMs\":50,\"vendorLockInScore\":44}},\"strategiesGenerated\":10}\n\nevent: progressUpdate\ndata: {\"jobId\":\"job_oci_db321\",\"progress\":72,\"strategiesGenerated\":24,\"message\":\"Calculating total cost of ownership including Autonomous Database OCPU licensing\"}\n\nevent: completed\ndata: {\"jobId\":\"job_oci_db321\",\"status\":\"completed\",\"progress\":100,\"strategiesGenerated\":33,\"topStrategy\":{\"name\":\"OCI-Primary Strategy\",\"metrics\":{\"monthlyCost\":9600,\"avgLatencyMs\":48,\"vendorLockInScore\":55}},\"comparisonReport\":\"# Multi-Cloud Strategy Comparison Report\\n\\n## Winner: OCI-Primary Strategy\\n\\nMonthly cost: **$9,600** — 34% lower than the AWS RDS baseline ($14,500).\\nAverage latency: **48 ms** — within the 75 ms SLA.\\nAutonomous Database eliminates DBA overhead for index tuning, patching, and vacuuming.\",\"completedAt\":\"2024-01-20T09:10:00Z\"}\n"},"digitalOceanStream":{"summary":"DigitalOcean — Droplets + Managed Databases cost-optimized workload stream","value":"event: init\ndata: {\"jobId\":\"job_do_xyz789\",\"status\":\"running\",\"progress\":0,\"strategiesGenerated\":0}\n\nevent: progressUpdate\ndata: {\"jobId\":\"job_do_xyz789\",\"progress\":20,\"strategiesGenerated\":7,\"message\":\"Evaluating DigitalOcean Droplet sizes and Managed Database tiers against workload traffic profile\"}\n\nevent: strategyGenerated\ndata: {\"jobId\":\"job_do_xyz789\",\"strategy\":{\"name\":\"DigitalOcean-Primary Strategy\",\"description\":\"DigitalOcean Droplets (s-4vcpu-8gb, nyc3) as primary compute with Managed Databases (PostgreSQL, 2-node HA) and Spaces for S3-compatible object storage — lowest total monthly spend; predictable flat-rate pricing with no data-transfer surprises within region\",\"allocations\":[{\"provider\":\"digitalocean\",\"percentage\":80},{\"provider\":\"aws\",\"percentage\":20}],\"metrics\":{\"monthlyCost\":7800,\"avgLatencyMs\":55,\"vendorLockInScore\":48}},\"strategiesGenerated\":8}\n\nevent: strategyGenerated\ndata: {\"jobId\":\"job_do_xyz789\",\"strategy\":{\"name\":\"DigitalOcean + GCP Balanced\",\"description\":\"DigitalOcean Droplets for primary API tier with Managed Databases, GCP Cloud Run for burst compute — good cost/latency balance with lower lock-in than DO-only\",\"allocations\":[{\"provider\":\"digitalocean\",\"percentage\":65},{\"provider\":\"gcp\",\"percentage\":35}],\"metrics\":{\"monthlyCost\":9400,\"avgLatencyMs\":50,\"vendorLockInScore\":41}},\"strategiesGenerated\":9}\n\nevent: progressUpdate\ndata: {\"jobId\":\"job_do_xyz789\",\"progress\":68,\"strategiesGenerated\":26,\"message\":\"Ranking strategies by monthly cost; verifying all options satisfy 150 ms SLA\"}\n\nevent: completed\ndata: {\"jobId\":\"job_do_xyz789\",\"status\":\"completed\",\"progress\":100,\"strategiesGenerated\":38,\"topStrategy\":{\"name\":\"DigitalOcean-Primary Strategy\",\"metrics\":{\"monthlyCost\":7800,\"avgLatencyMs\":55,\"vendorLockInScore\":48}},\"comparisonReport\":\"# Multi-Cloud Strategy Comparison Report\\n\\n## Winner: DigitalOcean-Primary Strategy\\n\\nMonthly cost: **$7,800** — 45% lower than the AWS-dominant baseline ($14,200).\\nAverage latency: **55 ms** — within the 150 ms SLA.\\nVendor lock-in score: **48/100** — moderate, significantly lower than the AWS-only baseline (74/100).\",\"completedAt\":\"2024-01-16T09:45:00Z\"}\n"},"digitaloceanAMDNVMeStream":{"summary":"DigitalOcean AMD NVMe Droplets — in-flight stream with s-2vcpu-4gb-amd strategies accumulating","value":"event: init\ndata: {\"jobId\":\"job_do_amd_nvme001\",\"status\":\"running\",\"progress\":0,\"strategiesGenerated\":0}\n\nevent: progressUpdate\ndata: {\"jobId\":\"job_do_amd_nvme001\",\"progress\":22,\"strategiesGenerated\":6,\"message\":\"Evaluating DigitalOcean AMD NVMe Droplet sizes and Managed Database tiers against I/O-intensive workload profile\"}\n\nevent: strategyGenerated\ndata: {\"jobId\":\"job_do_amd_nvme001\",\"strategy\":{\"name\":\"DigitalOcean AMD NVMe Primary Strategy\",\"description\":\"DigitalOcean AMD NVMe Droplets (s-2vcpu-4gb-amd, nyc3) as primary compute — NVMe-backed local SSD delivers higher I/O throughput than standard Droplets at the same price point, with DigitalOcean Managed Databases (PostgreSQL, 2-node HA) and Spaces for object storage\",\"allocations\":[{\"provider\":\"digitalocean\",\"percentage\":100}],\"metrics\":{\"monthlyCost\":6200,\"avgLatencyMs\":52,\"vendorLockInScore\":44}},\"strategiesGenerated\":7}\n\nevent: strategyGenerated\ndata: {\"jobId\":\"job_do_amd_nvme001\",\"strategy\":{\"name\":\"DO AMD NVMe + GCP Balanced\",\"description\":\"s-2vcpu-4gb-amd Droplets for primary API tier with GCP Cloud Run for burst compute — good cost/latency balance with lower lock-in than DO-only\",\"allocations\":[{\"provider\":\"digitalocean\",\"percentage\":65},{\"provider\":\"gcp\",\"percentage\":35}],\"metrics\":{\"monthlyCost\":8100,\"avgLatencyMs\":49,\"vendorLockInScore\":38}},\"strategiesGenerated\":8}\n\nevent: progressUpdate\ndata: {\"jobId\":\"job_do_amd_nvme001\",\"progress\":55,\"strategiesGenerated\":19,\"message\":\"Ranking strategies by monthly cost; verifying all options satisfy 150 ms SLA\"}\n"},"failedStream":{"summary":"Job failure — upstream provider pricing API unavailable mid-run","value":"event: init\ndata: {\"jobId\":\"job_aws_fail001\",\"status\":\"running\",\"progress\":0,\"strategiesGenerated\":0}\n\nevent: progressUpdate\ndata: {\"jobId\":\"job_aws_fail001\",\"progress\":35,\"strategiesGenerated\":14,\"message\":\"Fetching live pricing data for EC2, RDS, and CloudFront\"}\n\nevent: failed\ndata: {\"jobId\":\"job_aws_fail001\",\"status\":\"failed\",\"progress\":35,\"error\":\"PricingFetchError: AWS Pricing API returned 503 after 3 retries — unable to compute accurate cost estimates\",\"failedAt\":\"2024-01-21T10:14:22Z\"}\n"},"cancelledStream":{"summary":"Job cancellation — agent issued DELETE before completion","value":"event: init\ndata: {\"jobId\":\"job_gcp_cancel002\",\"status\":\"running\",\"progress\":0,\"strategiesGenerated\":0}\n\nevent: progressUpdate\ndata: {\"jobId\":\"job_gcp_cancel002\",\"progress\":52,\"strategiesGenerated\":21,\"message\":\"Evaluating GCP Cloud Run burst strategies against 80 ms SLA\"}\n\nevent: cancelled\ndata: {\"jobId\":\"job_gcp_cancel002\",\"status\":\"cancelled\",\"progress\":52,\"strategiesGenerated\":21,\"reason\":\"Cancelled by agent request via DELETE /api/multicloud/jobs/job_gcp_cancel002\",\"cancelledAt\":\"2024-01-21T11:03:45Z\"}\n"},"digitaloceanAMDNVMeFailedStream":{"summary":"DigitalOcean AMD NVMe — job failure mid-run (pricing API unavailable)","value":"event: init\ndata: {\"jobId\":\"job_do_amd_fail003\",\"status\":\"running\",\"progress\":0,\"strategiesGenerated\":0}\n\nevent: progressUpdate\ndata: {\"jobId\":\"job_do_amd_fail003\",\"progress\":41,\"strategiesGenerated\":16,\"message\":\"Fetching live pricing data for DigitalOcean AMD NVMe Droplets (s-2vcpu-4gb-amd) and Managed Databases\"}\n\nevent: failed\ndata: {\"jobId\":\"job_do_amd_fail003\",\"status\":\"failed\",\"progress\":41,\"error\":\"PricingFetchError: DigitalOcean Pricing API returned 503 after 3 retries — unable to compute accurate cost estimates for s-2vcpu-4gb-amd AMD NVMe Droplet configurations\",\"failedAt\":\"2024-01-22T08:27:14Z\"}\n"},"digitaloceanAMDNVMeCancelledStream":{"summary":"DigitalOcean AMD NVMe — job cancellation via DELETE before completion","value":"event: init\ndata: {\"jobId\":\"job_do_amd_cancel004\",\"status\":\"running\",\"progress\":0,\"strategiesGenerated\":0}\n\nevent: progressUpdate\ndata: {\"jobId\":\"job_do_amd_cancel004\",\"progress\":47,\"strategiesGenerated\":18,\"message\":\"Ranking DigitalOcean AMD NVMe Droplet strategies (s-2vcpu-4gb-amd) by monthly cost; verifying all options satisfy 150 ms SLA\"}\n\nevent: cancelled\ndata: {\"jobId\":\"job_do_amd_cancel004\",\"status\":\"cancelled\",\"progress\":47,\"strategiesGenerated\":18,\"reason\":\"Cancelled by agent request via DELETE /api/multicloud/jobs/job_do_amd_cancel004\",\"cancelledAt\":\"2024-01-22T09:15:33Z\"}\n"}}}}},"401":{"description":"Unauthorized - invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Job not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded","headers":{"Retry-After":{"description":"Seconds until the rate-limit window resets","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/simulations/{simulationId}/bulk-resize":{"x-stability":"experimental","post":{"tags":["Simulations"],"summary":"Resize all compute resources to a new DigitalOcean Droplet size","description":"Resizes every `type: \"compute\"` resource in the simulation to the\nspecified DigitalOcean Droplet size tier (e.g. `s-2vcpu-4gb`,\n`s-4vcpu-8gb`, `s-8vcpu-16gb`). Useful for right-sizing experiments\nwhere you wan

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Undeclared x402 security schemes

Metered operations reference x402, and walletAuthX402Session references X402Payment, but neither name is declared under components.securitySchemes, making these security requirements invalid and causing OpenAPI validators or generators to reject the specification or omit the required payment authentication.

Knowledge Base Used: Provider registry (providers/)

title: "Cloud World Model API"
description: "Multi-cloud infrastructure simulation with RL, chaos, and AI analysis endpoints; x402 pay-per-call on Solana mainnet USDC and Base, no signup required."
use_case: "Use for training RL autoscaling policies, running chaos scenarios, comparing multi-cloud costs, and requesting AI architecture analysis — pay-per-call on Solana or Base USDC, no signup required."
category: developer-tools

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Unsupported provider category

When CI validates this provider, developer-tools falls outside the registry's fixed category enum, causing the static catalog check to fail and preventing the provider from being published.

Suggested change
category: developer-tools
category: devtools

Knowledge Base Used: Provider registry (providers/)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants