From 347ac8f8fc3734aa5b07660d84788715687dd83b Mon Sep 17 00:00:00 2001 From: Daniel Wirjo Date: Tue, 7 Jul 2026 00:56:19 +0000 Subject: [PATCH 1/4] feat: add Claude Platform on AWS migration skill MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a structured .claude/skills/ directory with a migration skill for converting Anthropic 1P (direct API) code to Claude Platform on AWS (CPOA). Files: - .claude/skills/claude-platform-on-aws/SKILL.md - .claude/skills/claude-platform-on-aws/references/code-patterns.md Covers: - 1P → CPOA conversion for messages API, managed agents, and self-hosted environments - Both auth modes: SigV4 (IAM) and API key - Python, Node.js, and curl code patterns (before/after) - Common pitfalls (workspace header, environment keys, WorkPoller) - Validation checklist --- .../skills/claude-platform-on-aws/SKILL.md | 89 +++++++ .../references/code-patterns.md | 230 ++++++++++++++++++ 2 files changed, 319 insertions(+) create mode 100644 .claude/skills/claude-platform-on-aws/SKILL.md create mode 100644 .claude/skills/claude-platform-on-aws/references/code-patterns.md diff --git a/.claude/skills/claude-platform-on-aws/SKILL.md b/.claude/skills/claude-platform-on-aws/SKILL.md new file mode 100644 index 00000000..14c4d7c7 --- /dev/null +++ b/.claude/skills/claude-platform-on-aws/SKILL.md @@ -0,0 +1,89 @@ +--- +name: claude-platform-on-aws +description: Convert Anthropic 1P (direct API) code samples to Claude Platform on AWS (CPOA). Use when migrating from api.anthropic.com to aws-external-anthropic.{region}.api.aws, converting environment key auth to IAM SigV4 or CPOA API key auth, adding workspace headers, adapting MicroVM workers for CPOA, or when someone asks about differences between Anthropic 1P and CPOA authentication/endpoints. Triggers on phrases like "convert to CPOA", "migrate to Claude Platform on AWS", "CPOA auth", "workspace header", "aws-external-anthropic", "SigV4 anthropic". +--- + +# Claude Platform on AWS — Migration Skill + +Convert Anthropic 1P API code to Claude Platform on AWS (CPOA). + +## Key Differences: 1P vs CPOA + +| Aspect | Anthropic 1P | CPOA | +|--------|-------------|------| +| Base URL | `https://api.anthropic.com` | `https://aws-external-anthropic.{region}.api.aws` | +| Auth | API key (`sk-ant-api03-...`) in `x-api-key` header | IAM SigV4 OR CPOA API key (`aws-external-anthropic-api-key-...`) | +| Required headers | `x-api-key`, `anthropic-version` | `anthropic-workspace-id` (on all requests) | +| Billing | Anthropic billing | AWS Marketplace | +| Inference | Anthropic infra | Anthropic infra (same — CPOA only changes auth/billing layer) | +| Env keys (agents) | `sk-ant-env01-...` | NOT used — use IAM role or CPOA API key | +| VPC access | Direct HTTPS to api.anthropic.com | PrivateLink available (VPC→AWS hop only; inference still on Anthropic) | +| Credential rotation | Manual API key management | Automatic via STS (IAM mode) | + +## Conversion Steps + +### 1. Identify Auth Mode + +CPOA supports two auth modes: + +- **IAM SigV4** (preferred for production): Uses execution role credentials. No secrets to manage. +- **API key** (dev/quick-start): CPOA-issued key with `aws-external-anthropic-api-key-` prefix. Short-lived (~15 min from Quick Start, longer for production keys). + +### 2. Update Base URL + +``` +# Before (1P) +https://api.anthropic.com + +# After (CPOA) +https://aws-external-anthropic.{region}.api.aws +``` + +Replace `{region}` with the AWS region (e.g., `us-west-2`). + +### 3. Add Workspace Header + +**Critical**: ALL CPOA API calls require `anthropic-workspace-id` header. + +``` +anthropic-workspace-id: wrkspc_XXXXX +``` + +### 4. Convert Client Initialization + +See `references/code-patterns.md` for language-specific conversion patterns (Python, Node.js, curl). + +### 5. Update Agent/Environment Workflows + +For managed agents (self-hosted environments): +- Remove `environmentKey` usage — not available on CPOA +- Use `client.beta.environments.work.poll()` directly (bypasses WorkPoller sub-client limitation) +- Session creation: `client.beta.sessions.create(agent=agent_id, environment_id=env_id)` +- Event sending: `client.beta.sessions.events.send(session_id=..., events=[{type: 'user.message', content: [...]}])` + +### 6. IAM Permissions (SigV4 mode) + +Attach `AnthropicSelfHostedEnvironmentAccess` managed policy to execution role, which includes: +- `aws-external-anthropic:ProcessEnvironmentWork` +- `aws-external-anthropic:GetEnvironment` +- Work polling actions + +### 7. Security Comparison + +CPOA advantages for sandboxed/multi-tenant environments: +- No long-lived secrets — IAM creds rotate hourly via STS +- IAM policy scoping — restrict to specific actions +- Condition keys — `aws:SourceVpc`, `aws:SourceAccount` +- CloudTrail audit — native AWS logging +- SCP enforcement at org level + +Tradeoff: more operational complexity (IAM setup, workspace subscription, headers). + +## Common Pitfalls + +1. **Missing workspace header** → 400 "Missing header" error on environments endpoints +2. **Using `AnthropicAWS` class** → Not available in Node.js SDK (Python-only); use standard `Anthropic` with `authToken` +3. **Using environment keys** → Not supported on CPOA; use IAM or API key directly +4. **Quick Start keys expire fast** → ~15 min (embedded STS session); use long-lived IAM for production +5. **WorkPoller requires `environmentKey`** → Bypass by calling `client.beta.environments.work.poll()` directly +6. **Account not CPOA-subscribed** → SigV4 will fail; need API key from a subscribed account/workspace diff --git a/.claude/skills/claude-platform-on-aws/references/code-patterns.md b/.claude/skills/claude-platform-on-aws/references/code-patterns.md new file mode 100644 index 00000000..75927277 --- /dev/null +++ b/.claude/skills/claude-platform-on-aws/references/code-patterns.md @@ -0,0 +1,230 @@ +# Code Patterns: 1P → CPOA Conversion + +## Python + +### Before (Anthropic 1P) + +```python +from anthropic import Anthropic + +client = Anthropic(api_key="sk-ant-api03-...") + +response = client.messages.create( + model="claude-sonnet-4-6", + max_tokens=1024, + messages=[{"role": "user", "content": "Hello"}], +) +``` + +### After (CPOA — API Key Mode) + +```python +from anthropic import Anthropic + +REGION = "us-west-2" +WORKSPACE_ID = "wrkspc_XXXXX" +API_KEY = "aws-external-anthropic-api-key-..." + +client = Anthropic( + auth_token=API_KEY, + base_url=f"https://aws-external-anthropic.{REGION}.api.aws", + default_headers={"anthropic-workspace-id": WORKSPACE_ID}, +) + +response = client.messages.create( + model="claude-sonnet-4-6", + max_tokens=1024, + messages=[{"role": "user", "content": "Hello"}], +) +``` + +### After (CPOA — SigV4 IAM Mode) + +```python +from anthropic import Anthropic + +REGION = "us-west-2" +WORKSPACE_ID = "wrkspc_XXXXX" + +client = Anthropic( + credentials={"type": "aws_iam", "region": REGION}, + base_url=f"https://aws-external-anthropic.{REGION}.api.aws", + default_headers={"anthropic-workspace-id": WORKSPACE_ID}, +) + +response = client.messages.create( + model="claude-sonnet-4-6", + max_tokens=1024, + messages=[{"role": "user", "content": "Hello"}], +) +``` + +## Node.js / TypeScript + +### Before (Anthropic 1P) + +```javascript +import Anthropic from "@anthropic-ai/sdk"; + +const client = new Anthropic({ apiKey: "sk-ant-api03-..." }); + +const response = await client.messages.create({ + model: "claude-sonnet-4-6", + max_tokens: 1024, + messages: [{ role: "user", content: "Hello" }], +}); +``` + +### After (CPOA — API Key Mode) + +```javascript +import Anthropic from "@anthropic-ai/sdk"; + +const region = "us-west-2"; +const workspaceId = "wrkspc_XXXXX"; +const apiKey = "aws-external-anthropic-api-key-..."; + +const client = new Anthropic({ + authToken: apiKey, + baseURL: `https://aws-external-anthropic.${region}.api.aws`, + defaultHeaders: { "anthropic-workspace-id": workspaceId }, +}); + +const response = await client.messages.create({ + model: "claude-sonnet-4-6", + max_tokens: 1024, + messages: [{ role: "user", content: "Hello" }], +}); +``` + +### After (CPOA — SigV4 IAM Mode) + +```javascript +import Anthropic from "@anthropic-ai/sdk"; + +const region = "us-west-2"; +const workspaceId = "wrkspc_XXXXX"; + +const client = new Anthropic({ + credentials: { type: "aws_iam", region }, + baseURL: `https://aws-external-anthropic.${region}.api.aws`, + defaultHeaders: { "anthropic-workspace-id": workspaceId }, +}); + +const response = await client.messages.create({ + model: "claude-sonnet-4-6", + max_tokens: 1024, + messages: [{ role: "user", content: "Hello" }], +}); +``` + +**Note:** `AnthropicAWS` class is Python-only. In Node.js, always use the standard `Anthropic` class with `authToken` or `credentials`. + +## curl + +### Before (Anthropic 1P) + +```bash +curl -X POST https://api.anthropic.com/v1/messages \ + -H "x-api-key: sk-ant-api03-..." \ + -H "anthropic-version: 2023-06-01" \ + -H "content-type: application/json" \ + -d '{"model":"claude-sonnet-4-6","max_tokens":1024,"messages":[{"role":"user","content":"Hello"}]}' +``` + +### After (CPOA — API Key Mode) + +```bash +curl -X POST https://aws-external-anthropic.us-west-2.api.aws/v1/messages \ + -H "Authorization: Bearer aws-external-anthropic-api-key-..." \ + -H "anthropic-workspace-id: wrkspc_XXXXX" \ + -H "anthropic-version: 2023-06-01" \ + -H "content-type: application/json" \ + -d '{"model":"claude-sonnet-4-6","max_tokens":1024,"messages":[{"role":"user","content":"Hello"}]}' +``` + +## Managed Agents (Self-Hosted Environments) + +### Before (1P — Environment Key) + +```python +from anthropic import Anthropic +from anthropic.helpers.beta.environments import WorkPoller + +client = Anthropic(api_key="sk-ant-api03-...") +poller = WorkPoller(client, environment_key="sk-ant-env01-...") + +for work in poller: + # process work items + pass +``` + +### After (CPOA — Direct Polling) + +```python +from anthropic import Anthropic + +REGION = "us-west-2" +WORKSPACE_ID = "wrkspc_XXXXX" +API_KEY = "aws-external-anthropic-api-key-..." +ENVIRONMENT_ID = "env_XXXXX" + +client = Anthropic( + auth_token=API_KEY, + base_url=f"https://aws-external-anthropic.{REGION}.api.aws", + default_headers={"anthropic-workspace-id": WORKSPACE_ID}, +) + +# Direct polling (bypasses WorkPoller's environmentKey requirement) +while True: + work = client.beta.environments.work.poll( + environment_id=ENVIRONMENT_ID, + timeout=30, + ) + if work: + # process work items + pass +``` + +### Session Creation (CPOA) + +```python +# Create session targeting an agent +session = client.beta.sessions.create( + agent="agent_XXXXX", + environment_id="env_XXXXX", +) + +# Send user message +client.beta.sessions.events.send( + session_id=session.id, + events=[{ + "type": "user.message", + "content": [{"type": "text", "text": "Analyze the data in /mnt/data/"}], + }], +) + +# Session transitions: idle → running (work queued for MicroVM pickup) +``` + +## Environment Variables Mapping + +| 1P Variable | CPOA Variable | Notes | +|-------------|---------------|-------| +| `ANTHROPIC_API_KEY` | `ANTHROPIC_AWS_API_KEY` | Prefix: `aws-external-anthropic-api-key-` | +| (none) | `ANTHROPIC_AWS_WORKSPACE_ID` | Required for all CPOA calls | +| (none) | `AWS_REGION` | Region for endpoint URL | +| `ANTHROPIC_BASE_URL` | (derived) | `https://aws-external-anthropic.{region}.api.aws` | +| `ENVIRONMENT_KEY` | (not used) | CPOA doesn't use environment keys | + +## Validation Checklist + +After conversion, verify: + +1. [ ] Base URL uses `aws-external-anthropic.{region}.api.aws` +2. [ ] `anthropic-workspace-id` header present on all requests +3. [ ] API key has `aws-external-anthropic-api-key-` prefix (if using key mode) +4. [ ] No references to `sk-ant-api03-` or `sk-ant-env01-` keys +5. [ ] No `AnthropicAWS` imports in Node.js code +6. [ ] IAM role has `AnthropicSelfHostedEnvironmentAccess` policy (if SigV4 mode) +7. [ ] `/v1/models` endpoint returns 200 (basic connectivity test) From 2113c16999d834063442ca1267713de6d2bf39bf Mon Sep 17 00:00:00 2001 From: Daniel Wirjo Date: Wed, 8 Jul 2026 08:39:26 +0000 Subject: [PATCH 2/4] fix: address all review comments from @scouturier Changes based on official CPOA documentation: - Use AnthropicAWS (Python) and AnthropicAws (TypeScript) platform clients instead of manual base_url/defaultHeaders configuration - Remove invented credentials={"type": "aws_iam"} parameter; SigV4 is simply the platform client with no API key (uses AWS provider chain) - Fix auth header: CPOA uses x-api-key, not Authorization: Bearer - Correct token lifetime: 12 hours default (not 15 min), capped at min(requested, AWS creds expiry, 12h) - Split IAM guidance: AnthropicInferenceAccess for /v1/messages vs AnthropicSelfHostedEnvironmentAccess for workers (no CreateInference) - Clarify environment keys: Console-generated env keys don't work on CPOA; workers auth via IAM or CPOA API key instead - Remove undocumented API key prefix assertion - Replace WorkPoller with documented EnvironmentWorker SDK helper - Add SigV4 curl example with --aws-sigv4 flag - Add short-term token generation examples (Python + TypeScript) - Add outbound web identity federation prerequisite - Note: region is required with no fallback (unlike AnthropicBedrock) Refs: - https://platform.claude.com/docs/en/build-with-claude/claude-platform-on-aws - https://platform.claude.com/docs/en/api/claude-platform-on-aws-iam-actions - https://platform.claude.com/docs/en/managed-agents/self-hosted-sandboxes --- .../skills/claude-platform-on-aws/SKILL.md | 77 ++++---- .../references/code-patterns.md | 187 ++++++++++-------- 2 files changed, 148 insertions(+), 116 deletions(-) diff --git a/.claude/skills/claude-platform-on-aws/SKILL.md b/.claude/skills/claude-platform-on-aws/SKILL.md index 14c4d7c7..2b5f1653 100644 --- a/.claude/skills/claude-platform-on-aws/SKILL.md +++ b/.claude/skills/claude-platform-on-aws/SKILL.md @@ -1,6 +1,6 @@ --- name: claude-platform-on-aws -description: Convert Anthropic 1P (direct API) code samples to Claude Platform on AWS (CPOA). Use when migrating from api.anthropic.com to aws-external-anthropic.{region}.api.aws, converting environment key auth to IAM SigV4 or CPOA API key auth, adding workspace headers, adapting MicroVM workers for CPOA, or when someone asks about differences between Anthropic 1P and CPOA authentication/endpoints. Triggers on phrases like "convert to CPOA", "migrate to Claude Platform on AWS", "CPOA auth", "workspace header", "aws-external-anthropic", "SigV4 anthropic". +description: Convert Anthropic 1P (direct API) code samples to Claude Platform on AWS (CPOA). Use when migrating from api.anthropic.com to aws-external-anthropic.{region}.api.aws, converting auth to IAM SigV4 or CPOA API key, adding workspace headers, adapting self-hosted sandbox workers for CPOA, or when someone asks about differences between Anthropic 1P and CPOA authentication/endpoints. Triggers on phrases like "convert to CPOA", "migrate to Claude Platform on AWS", "CPOA auth", "workspace header", "aws-external-anthropic", "SigV4 anthropic". --- # Claude Platform on AWS — Migration Skill @@ -12,11 +12,12 @@ Convert Anthropic 1P API code to Claude Platform on AWS (CPOA). | Aspect | Anthropic 1P | CPOA | |--------|-------------|------| | Base URL | `https://api.anthropic.com` | `https://aws-external-anthropic.{region}.api.aws` | -| Auth | API key (`sk-ant-api03-...`) in `x-api-key` header | IAM SigV4 OR CPOA API key (`aws-external-anthropic-api-key-...`) | -| Required headers | `x-api-key`, `anthropic-version` | `anthropic-workspace-id` (on all requests) | +| Auth | API key (`sk-ant-api03-...`) in `x-api-key` header | IAM SigV4 OR CPOA API key (generated in AWS Console) | +| Required headers | `x-api-key`, `anthropic-version` | `anthropic-workspace-id` (handled by platform SDK clients) | +| SDK client | `Anthropic` | `AnthropicAWS` (Python) / `AnthropicAws` (TypeScript) | | Billing | Anthropic billing | AWS Marketplace | | Inference | Anthropic infra | Anthropic infra (same — CPOA only changes auth/billing layer) | -| Env keys (agents) | `sk-ant-env01-...` | NOT used — use IAM role or CPOA API key | +| Env keys (self-hosted workers) | `sk-ant-env01-...` (from Claude Console) | NOT used — workers authenticate via IAM or CPOA API key | | VPC access | Direct HTTPS to api.anthropic.com | PrivateLink available (VPC→AWS hop only; inference still on Anthropic) | | Credential rotation | Manual API key management | Automatic via STS (IAM mode) | @@ -26,49 +27,49 @@ Convert Anthropic 1P API code to Claude Platform on AWS (CPOA). CPOA supports two auth modes: -- **IAM SigV4** (preferred for production): Uses execution role credentials. No secrets to manage. -- **API key** (dev/quick-start): CPOA-issued key with `aws-external-anthropic-api-key-` prefix. Short-lived (~15 min from Quick Start, longer for production keys). +- **IAM SigV4** (preferred for production): Uses execution role credentials via standard AWS provider chain. No secrets to manage. +- **API key** (dev/quick-start): Generated in AWS Console under Claude Platform on AWS → API keys. Short-term tokens generated from AWS credentials default to 12 hours (capped at the lesser of requested duration, AWS credentials' expiry, and 12 hours). -### 2. Update Base URL +### 2. Install the Platform SDK -``` -# Before (1P) -https://api.anthropic.com +The platform-specific SDK client handles SigV4 signing, base URL construction, and the `anthropic-workspace-id` header automatically. + +```bash +# Python +pip install -U "anthropic[aws]" -# After (CPOA) -https://aws-external-anthropic.{region}.api.aws +# TypeScript +npm install @anthropic-ai/aws-sdk ``` -Replace `{region}` with the AWS region (e.g., `us-west-2`). +### 3. Convert Client Initialization -### 3. Add Workspace Header +See `references/code-patterns.md` for language-specific conversion patterns (Python, Node.js, curl). -**Critical**: ALL CPOA API calls require `anthropic-workspace-id` header. +The platform client reads `AWS_REGION` and `ANTHROPIC_AWS_WORKSPACE_ID` from environment variables. Set them: -``` -anthropic-workspace-id: wrkspc_XXXXX +```bash +export AWS_REGION='us-west-2' +export ANTHROPIC_AWS_WORKSPACE_ID='wrkspc_XXXXX' ``` -### 4. Convert Client Initialization +### 4. Update Self-Hosted Sandbox Workers -See `references/code-patterns.md` for language-specific conversion patterns (Python, Node.js, curl). +For self-hosted environments on CPOA: +- Environment keys from the Claude Console **do not work** on the CPOA endpoint +- Workers authenticate via IAM (attach `AnthropicSelfHostedEnvironmentAccess` policy) or CPOA API key +- Use the `EnvironmentWorker` SDK helper (`.run()` / `.run_one()`) or call `GET /v1/environments/{id}/work/poll` directly -### 5. Update Agent/Environment Workflows +### 5. IAM Permissions -For managed agents (self-hosted environments): -- Remove `environmentKey` usage — not available on CPOA -- Use `client.beta.environments.work.poll()` directly (bypasses WorkPoller sub-client limitation) -- Session creation: `client.beta.sessions.create(agent=agent_id, environment_id=env_id)` -- Event sending: `client.beta.sessions.events.send(session_id=..., events=[{type: 'user.message', content: [...]}])` +Different policies for different use cases: -### 6. IAM Permissions (SigV4 mode) +- **Inference** (`/v1/messages`): Attach `AnthropicInferenceAccess` managed policy (includes `CreateInference`) +- **Self-hosted workers**: Attach `AnthropicSelfHostedEnvironmentAccess` managed policy (includes `ProcessEnvironmentWork`, `GetEnvironment`, `GetSession`, `UpdateSession`, `GetSkill`, `CallWithBearerToken` — but NOT `CreateInference`) -Attach `AnthropicSelfHostedEnvironmentAccess` managed policy to execution role, which includes: -- `aws-external-anthropic:ProcessEnvironmentWork` -- `aws-external-anthropic:GetEnvironment` -- Work polling actions +A role with only `AnthropicSelfHostedEnvironmentAccess` will 403 on `/v1/messages`. Combine both policies if the worker also needs to make inference calls. -### 7. Security Comparison +### 6. Security Comparison CPOA advantages for sandboxed/multi-tenant environments: - No long-lived secrets — IAM creds rotate hourly via STS @@ -77,13 +78,13 @@ CPOA advantages for sandboxed/multi-tenant environments: - CloudTrail audit — native AWS logging - SCP enforcement at org level -Tradeoff: more operational complexity (IAM setup, workspace subscription, headers). +Tradeoff: more operational complexity (IAM setup, workspace subscription, outbound web identity federation enablement). ## Common Pitfalls -1. **Missing workspace header** → 400 "Missing header" error on environments endpoints -2. **Using `AnthropicAWS` class** → Not available in Node.js SDK (Python-only); use standard `Anthropic` with `authToken` -3. **Using environment keys** → Not supported on CPOA; use IAM or API key directly -4. **Quick Start keys expire fast** → ~15 min (embedded STS session); use long-lived IAM for production -5. **WorkPoller requires `environmentKey`** → Bypass by calling `client.beta.environments.work.poll()` directly -6. **Account not CPOA-subscribed** → SigV4 will fail; need API key from a subscribed account/workspace +1. **Missing workspace header** → 400 error. Use the platform SDK client (`AnthropicAWS`/`AnthropicAws`) which adds it automatically, or set `ANTHROPIC_AWS_WORKSPACE_ID` env var. +2. **Outbound web identity federation disabled** → Every request fails with "Outbound web identity federation is disabled for your account". Run `aws iam enable-outbound-web-identity-federation` once per AWS account. +3. **Using Claude Console environment keys on CPOA** → Won't work. Workers on CPOA authenticate via IAM or CPOA API key instead. +4. **Confusing IAM policies** → `AnthropicSelfHostedEnvironmentAccess` does NOT grant inference. Need `AnthropicInferenceAccess` for `/v1/messages`. +5. **Region not set** → Unlike `AnthropicBedrock` (which falls back to `us-east-1`), the platform client raises an error if no region is set. +6. **Account not CPOA-subscribed** → SigV4 will fail; ensure the AWS account has completed Claude Platform on AWS sign-up. diff --git a/.claude/skills/claude-platform-on-aws/references/code-patterns.md b/.claude/skills/claude-platform-on-aws/references/code-patterns.md index 75927277..62111a19 100644 --- a/.claude/skills/claude-platform-on-aws/references/code-patterns.md +++ b/.claude/skills/claude-platform-on-aws/references/code-patterns.md @@ -19,17 +19,11 @@ response = client.messages.create( ### After (CPOA — API Key Mode) ```python -from anthropic import Anthropic - -REGION = "us-west-2" -WORKSPACE_ID = "wrkspc_XXXXX" -API_KEY = "aws-external-anthropic-api-key-..." +from anthropic import AnthropicAWS # pip install -U "anthropic[aws]" -client = Anthropic( - auth_token=API_KEY, - base_url=f"https://aws-external-anthropic.{REGION}.api.aws", - default_headers={"anthropic-workspace-id": WORKSPACE_ID}, -) +# API key generated in AWS Console under Claude Platform on AWS → API keys +# Region + workspace from AWS_REGION / ANTHROPIC_AWS_WORKSPACE_ID env vars +client = AnthropicAWS(api_key="") response = client.messages.create( model="claude-sonnet-4-6", @@ -41,16 +35,29 @@ response = client.messages.create( ### After (CPOA — SigV4 IAM Mode) ```python -from anthropic import Anthropic +from anthropic import AnthropicAWS # pip install -U "anthropic[aws]" -REGION = "us-west-2" -WORKSPACE_ID = "wrkspc_XXXXX" +# No API key → SigV4 via default AWS credential chain +# (env → ~/.aws → IRSA → ECS → IMDS) +client = AnthropicAWS(aws_region="us-west-2") -client = Anthropic( - credentials={"type": "aws_iam", "region": REGION}, - base_url=f"https://aws-external-anthropic.{REGION}.api.aws", - default_headers={"anthropic-workspace-id": WORKSPACE_ID}, +response = client.messages.create( + model="claude-sonnet-4-6", + max_tokens=1024, + messages=[{"role": "user", "content": "Hello"}], ) +``` + +### After (CPOA — Short-Term Token) + +```python +from token_generator_for_aws_external_anthropic import TokenGenerator +from anthropic import AnthropicAWS + +# Token defaults to 12h, capped at min(requested, AWS creds expiry, 12h) +token = TokenGenerator(region="us-west-2").get_token() + +client = AnthropicAWS(api_key=token, aws_region="us-west-2") response = client.messages.create( model="claude-sonnet-4-6", @@ -63,7 +70,7 @@ response = client.messages.create( ### Before (Anthropic 1P) -```javascript +```typescript import Anthropic from "@anthropic-ai/sdk"; const client = new Anthropic({ apiKey: "sk-ant-api03-..." }); @@ -77,17 +84,13 @@ const response = await client.messages.create({ ### After (CPOA — API Key Mode) -```javascript -import Anthropic from "@anthropic-ai/sdk"; - -const region = "us-west-2"; -const workspaceId = "wrkspc_XXXXX"; -const apiKey = "aws-external-anthropic-api-key-..."; +```typescript +import AnthropicAws from "@anthropic-ai/aws-sdk"; // npm install @anthropic-ai/aws-sdk -const client = new Anthropic({ - authToken: apiKey, - baseURL: `https://aws-external-anthropic.${region}.api.aws`, - defaultHeaders: { "anthropic-workspace-id": workspaceId }, +// API key generated in AWS Console under Claude Platform on AWS → API keys +const client = new AnthropicAws({ + apiKey: "", + awsRegion: "us-west-2", }); const response = await client.messages.create({ @@ -99,17 +102,29 @@ const response = await client.messages.create({ ### After (CPOA — SigV4 IAM Mode) -```javascript -import Anthropic from "@anthropic-ai/sdk"; +```typescript +import AnthropicAws from "@anthropic-ai/aws-sdk"; -const region = "us-west-2"; -const workspaceId = "wrkspc_XXXXX"; +// No API key → SigV4 via default AWS credential chain +const client = new AnthropicAws({ awsRegion: "us-west-2" }); -const client = new Anthropic({ - credentials: { type: "aws_iam", region }, - baseURL: `https://aws-external-anthropic.${region}.api.aws`, - defaultHeaders: { "anthropic-workspace-id": workspaceId }, +const response = await client.messages.create({ + model: "claude-sonnet-4-6", + max_tokens: 1024, + messages: [{ role: "user", content: "Hello" }], }); +``` + +### After (CPOA — Short-Term Token) + +```typescript +import { getTokenProvider } from "@aws/token-generator-for-aws-external-anthropic"; +import AnthropicAws from "@anthropic-ai/aws-sdk"; + +const tokenProvider = getTokenProvider({ region: "us-west-2" }); +const token = await tokenProvider(); + +const client = new AnthropicAws({ apiKey: token, awsRegion: "us-west-2" }); const response = await client.messages.create({ model: "claude-sonnet-4-6", @@ -118,8 +133,6 @@ const response = await client.messages.create({ }); ``` -**Note:** `AnthropicAWS` class is Python-only. In Node.js, always use the standard `Anthropic` class with `authToken` or `credentials`. - ## curl ### Before (Anthropic 1P) @@ -136,60 +149,78 @@ curl -X POST https://api.anthropic.com/v1/messages \ ```bash curl -X POST https://aws-external-anthropic.us-west-2.api.aws/v1/messages \ - -H "Authorization: Bearer aws-external-anthropic-api-key-..." \ + -H "x-api-key: " \ -H "anthropic-workspace-id: wrkspc_XXXXX" \ -H "anthropic-version: 2023-06-01" \ -H "content-type: application/json" \ -d '{"model":"claude-sonnet-4-6","max_tokens":1024,"messages":[{"role":"user","content":"Hello"}]}' ``` -## Managed Agents (Self-Hosted Environments) +### After (CPOA — SigV4 Mode) + +```bash +# Uses curl's --aws-sigv4 flag (requires curl 7.75+) +curl -X POST "https://aws-external-anthropic.us-west-2.api.aws/v1/messages" \ + --aws-sigv4 "aws:amz:us-west-2:aws-external-anthropic" \ + --user "$AWS_ACCESS_KEY_ID:$AWS_SECRET_ACCESS_KEY" \ + -H "x-amz-security-token: $AWS_SESSION_TOKEN" \ + -H "anthropic-workspace-id: $ANTHROPIC_AWS_WORKSPACE_ID" \ + -H "anthropic-version: 2023-06-01" \ + -H "content-type: application/json" \ + -d '{"model":"claude-sonnet-4-6","max_tokens":1024,"messages":[{"role":"user","content":"Hello"}]}' +``` + +## Self-Hosted Sandbox Workers ### Before (1P — Environment Key) ```python from anthropic import Anthropic -from anthropic.helpers.beta.environments import WorkPoller client = Anthropic(api_key="sk-ant-api03-...") -poller = WorkPoller(client, environment_key="sk-ant-env01-...") -for work in poller: - # process work items - pass +# Start worker with environment key from Claude Console +worker = client.beta.environments.worker.run( + environment_key="sk-ant-env01-...", +) ``` -### After (CPOA — Direct Polling) +### After (CPOA — Worker with IAM Auth) ```python -from anthropic import Anthropic +from anthropic import AnthropicAWS -REGION = "us-west-2" -WORKSPACE_ID = "wrkspc_XXXXX" -API_KEY = "aws-external-anthropic-api-key-..." -ENVIRONMENT_ID = "env_XXXXX" +# Worker authenticates via IAM (AnthropicSelfHostedEnvironmentAccess policy) +# Environment keys from Claude Console do NOT work on CPOA +client = AnthropicAWS(aws_region="us-west-2") -client = Anthropic( - auth_token=API_KEY, - base_url=f"https://aws-external-anthropic.{REGION}.api.aws", - default_headers={"anthropic-workspace-id": WORKSPACE_ID}, +# Use EnvironmentWorker SDK helper +worker = client.beta.environments.worker.run( + environment_id="env_XXXXX", ) +``` + +### After (CPOA — Worker with API Key) + +```python +from anthropic import AnthropicAWS -# Direct polling (bypasses WorkPoller's environmentKey requirement) -while True: - work = client.beta.environments.work.poll( - environment_id=ENVIRONMENT_ID, - timeout=30, - ) - if work: - # process work items - pass +# Worker authenticates via CPOA API key +client = AnthropicAWS(api_key="", aws_region="us-west-2") + +worker = client.beta.environments.worker.run( + environment_id="env_XXXXX", +) ``` ### Session Creation (CPOA) ```python -# Create session targeting an agent +from anthropic import AnthropicAWS + +client = AnthropicAWS(aws_region="us-west-2") + +# Create session targeting an agent in a self-hosted environment session = client.beta.sessions.create( agent="agent_XXXXX", environment_id="env_XXXXX", @@ -204,27 +235,27 @@ client.beta.sessions.events.send( }], ) -# Session transitions: idle → running (work queued for MicroVM pickup) +# Session transitions: idle → running (work queued for worker pickup) ``` ## Environment Variables Mapping | 1P Variable | CPOA Variable | Notes | |-------------|---------------|-------| -| `ANTHROPIC_API_KEY` | `ANTHROPIC_AWS_API_KEY` | Prefix: `aws-external-anthropic-api-key-` | -| (none) | `ANTHROPIC_AWS_WORKSPACE_ID` | Required for all CPOA calls | -| (none) | `AWS_REGION` | Region for endpoint URL | -| `ANTHROPIC_BASE_URL` | (derived) | `https://aws-external-anthropic.{region}.api.aws` | -| `ENVIRONMENT_KEY` | (not used) | CPOA doesn't use environment keys | +| `ANTHROPIC_API_KEY` | (constructor `api_key`) | CPOA keys generated in AWS Console | +| (none) | `ANTHROPIC_AWS_WORKSPACE_ID` | Required — SDK reads automatically | +| (none) | `AWS_REGION` | Required — no fallback default | +| `ANTHROPIC_BASE_URL` | (handled by SDK) | `AnthropicAWS`/`AnthropicAws` sets it from region | +| `ENVIRONMENT_KEY` | (not used on CPOA) | Workers use IAM or API key instead | ## Validation Checklist After conversion, verify: -1. [ ] Base URL uses `aws-external-anthropic.{region}.api.aws` -2. [ ] `anthropic-workspace-id` header present on all requests -3. [ ] API key has `aws-external-anthropic-api-key-` prefix (if using key mode) -4. [ ] No references to `sk-ant-api03-` or `sk-ant-env01-` keys -5. [ ] No `AnthropicAWS` imports in Node.js code -6. [ ] IAM role has `AnthropicSelfHostedEnvironmentAccess` policy (if SigV4 mode) -7. [ ] `/v1/models` endpoint returns 200 (basic connectivity test) +1. [ ] Using platform SDK client (`AnthropicAWS` / `AnthropicAws`), not base `Anthropic` with manual URL +2. [ ] `AWS_REGION` and `ANTHROPIC_AWS_WORKSPACE_ID` environment variables set +3. [ ] No references to `sk-ant-api03-` or `sk-ant-env01-` keys +4. [ ] Outbound web identity federation enabled (`aws iam enable-outbound-web-identity-federation`) +5. [ ] Correct IAM policy: `AnthropicInferenceAccess` for `/v1/messages`, `AnthropicSelfHostedEnvironmentAccess` for workers +6. [ ] `/v1/models` endpoint returns 200 (basic connectivity test) +7. [ ] Short-term token refresh logic in place if using generated tokens (no auto-refresh) From 928b6ee4c141f823ceb5fdf65d9c62a90e359cd3 Mon Sep 17 00:00:00 2001 From: Daniel Wirjo Date: Wed, 8 Jul 2026 08:45:17 +0000 Subject: [PATCH 3/4] fix: correct EnvironmentWorker SDK usage and environment key prefix Additional fixes from docs validation: - Use actual EnvironmentWorker import: from anthropic.lib.environments - Fix environment key prefix: sk-ant-oat01-... (not sk-ant-env01-...) - Replace invented client.beta.environments.worker.run() with actual EnvironmentWorker(client, ...).run() pattern from official docs - Show proper async pattern matching SDK docs examples - Fix validation checklist to reference correct key prefix Refs: - https://platform.claude.com/docs/en/managed-agents/self-hosted-sandboxes#sdk-helpers - SDK code: from anthropic.lib.environments import EnvironmentWorker --- .../skills/claude-platform-on-aws/SKILL.md | 4 +- .../references/code-patterns.md | 81 ++++++++++++------- 2 files changed, 55 insertions(+), 30 deletions(-) diff --git a/.claude/skills/claude-platform-on-aws/SKILL.md b/.claude/skills/claude-platform-on-aws/SKILL.md index 2b5f1653..89f71533 100644 --- a/.claude/skills/claude-platform-on-aws/SKILL.md +++ b/.claude/skills/claude-platform-on-aws/SKILL.md @@ -17,7 +17,7 @@ Convert Anthropic 1P API code to Claude Platform on AWS (CPOA). | SDK client | `Anthropic` | `AnthropicAWS` (Python) / `AnthropicAws` (TypeScript) | | Billing | Anthropic billing | AWS Marketplace | | Inference | Anthropic infra | Anthropic infra (same — CPOA only changes auth/billing layer) | -| Env keys (self-hosted workers) | `sk-ant-env01-...` (from Claude Console) | NOT used — workers authenticate via IAM or CPOA API key | +| Env keys (self-hosted workers) | `sk-ant-oat01-...` (from Claude Console) | NOT used — workers authenticate via IAM or CPOA API key | | VPC access | Direct HTTPS to api.anthropic.com | PrivateLink available (VPC→AWS hop only; inference still on Anthropic) | | Credential rotation | Manual API key management | Automatic via STS (IAM mode) | @@ -58,7 +58,7 @@ export ANTHROPIC_AWS_WORKSPACE_ID='wrkspc_XXXXX' For self-hosted environments on CPOA: - Environment keys from the Claude Console **do not work** on the CPOA endpoint - Workers authenticate via IAM (attach `AnthropicSelfHostedEnvironmentAccess` policy) or CPOA API key -- Use the `EnvironmentWorker` SDK helper (`.run()` / `.run_one()`) or call `GET /v1/environments/{id}/work/poll` directly +- Use the `EnvironmentWorker` SDK helper (from `anthropic.lib.environments`) or call `GET /v1/environments/{id}/work/poll` directly ### 5. IAM Permissions diff --git a/.claude/skills/claude-platform-on-aws/references/code-patterns.md b/.claude/skills/claude-platform-on-aws/references/code-patterns.md index 62111a19..d240db7c 100644 --- a/.claude/skills/claude-platform-on-aws/references/code-patterns.md +++ b/.claude/skills/claude-platform-on-aws/references/code-patterns.md @@ -175,42 +175,67 @@ curl -X POST "https://aws-external-anthropic.us-west-2.api.aws/v1/messages" \ ### Before (1P — Environment Key) ```python -from anthropic import Anthropic - -client = Anthropic(api_key="sk-ant-api03-...") - -# Start worker with environment key from Claude Console -worker = client.beta.environments.worker.run( - environment_key="sk-ant-env01-...", -) +import asyncio +import os +from anthropic import AsyncAnthropic +from anthropic.lib.environments import EnvironmentWorker + +async def main() -> None: + environment_key = os.environ["ANTHROPIC_ENVIRONMENT_KEY"] # sk-ant-oat01-... + environment_id = os.environ["ANTHROPIC_ENVIRONMENT_ID"] + async with AsyncAnthropic(auth_token=environment_key) as client: + await EnvironmentWorker( + client, + environment_id=environment_id, + environment_key=environment_key, + workdir="/workspace", + ).run() + +asyncio.run(main()) ``` ### After (CPOA — Worker with IAM Auth) ```python -from anthropic import AnthropicAWS - -# Worker authenticates via IAM (AnthropicSelfHostedEnvironmentAccess policy) -# Environment keys from Claude Console do NOT work on CPOA -client = AnthropicAWS(aws_region="us-west-2") - -# Use EnvironmentWorker SDK helper -worker = client.beta.environments.worker.run( - environment_id="env_XXXXX", -) +import asyncio +import os +from anthropic import AsyncAnthropic +from anthropic.lib.environments import EnvironmentWorker + +async def main() -> None: + # On CPOA: no environment key needed — authenticate via IAM + # Requires AnthropicSelfHostedEnvironmentAccess IAM policy + environment_id = os.environ["ANTHROPIC_ENVIRONMENT_ID"] + async with AsyncAnthropic() as client: # SigV4 via AWS credential chain + await EnvironmentWorker( + client, + environment_id=environment_id, + workdir="/workspace", + ).run() + +asyncio.run(main()) ``` ### After (CPOA — Worker with API Key) ```python -from anthropic import AnthropicAWS - -# Worker authenticates via CPOA API key -client = AnthropicAWS(api_key="", aws_region="us-west-2") - -worker = client.beta.environments.worker.run( - environment_id="env_XXXXX", -) +import asyncio +import os +from anthropic import AsyncAnthropic +from anthropic.lib.environments import EnvironmentWorker + +async def main() -> None: + # On CPOA: use API key generated in AWS Console + api_key = os.environ["ANTHROPIC_AWS_API_KEY"] + environment_id = os.environ["ANTHROPIC_ENVIRONMENT_ID"] + async with AsyncAnthropic(auth_token=api_key) as client: + await EnvironmentWorker( + client, + environment_id=environment_id, + workdir="/workspace", + ).run() + +asyncio.run(main()) ``` ### Session Creation (CPOA) @@ -246,7 +271,7 @@ client.beta.sessions.events.send( | (none) | `ANTHROPIC_AWS_WORKSPACE_ID` | Required — SDK reads automatically | | (none) | `AWS_REGION` | Required — no fallback default | | `ANTHROPIC_BASE_URL` | (handled by SDK) | `AnthropicAWS`/`AnthropicAws` sets it from region | -| `ENVIRONMENT_KEY` | (not used on CPOA) | Workers use IAM or API key instead | +| `ANTHROPIC_ENVIRONMENT_KEY` | (not used on CPOA) | 1P uses `sk-ant-oat01-...`; CPOA workers use IAM or API key instead | ## Validation Checklist @@ -254,7 +279,7 @@ After conversion, verify: 1. [ ] Using platform SDK client (`AnthropicAWS` / `AnthropicAws`), not base `Anthropic` with manual URL 2. [ ] `AWS_REGION` and `ANTHROPIC_AWS_WORKSPACE_ID` environment variables set -3. [ ] No references to `sk-ant-api03-` or `sk-ant-env01-` keys +3. [ ] No references to `sk-ant-api03-` or `sk-ant-oat01-` keys (1P credentials) 4. [ ] Outbound web identity federation enabled (`aws iam enable-outbound-web-identity-federation`) 5. [ ] Correct IAM policy: `AnthropicInferenceAccess` for `/v1/messages`, `AnthropicSelfHostedEnvironmentAccess` for workers 6. [ ] `/v1/models` endpoint returns 200 (basic connectivity test) From 14a6b0de5dffa60ad574cf94a61480320c3d578f Mon Sep 17 00:00:00 2001 From: Daniel Wirjo Date: Wed, 8 Jul 2026 20:47:00 +0000 Subject: [PATCH 4/4] chore: update model ID to claude-sonnet-5 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Swap claude-sonnet-4-6 → claude-sonnet-5 across all examples to match the current official CPOA documentation. --- .../references/code-patterns.md | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.claude/skills/claude-platform-on-aws/references/code-patterns.md b/.claude/skills/claude-platform-on-aws/references/code-patterns.md index d240db7c..59f253a4 100644 --- a/.claude/skills/claude-platform-on-aws/references/code-patterns.md +++ b/.claude/skills/claude-platform-on-aws/references/code-patterns.md @@ -10,7 +10,7 @@ from anthropic import Anthropic client = Anthropic(api_key="sk-ant-api03-...") response = client.messages.create( - model="claude-sonnet-4-6", + model="claude-sonnet-5", max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], ) @@ -26,7 +26,7 @@ from anthropic import AnthropicAWS # pip install -U "anthropic[aws]" client = AnthropicAWS(api_key="") response = client.messages.create( - model="claude-sonnet-4-6", + model="claude-sonnet-5", max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], ) @@ -42,7 +42,7 @@ from anthropic import AnthropicAWS # pip install -U "anthropic[aws]" client = AnthropicAWS(aws_region="us-west-2") response = client.messages.create( - model="claude-sonnet-4-6", + model="claude-sonnet-5", max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], ) @@ -60,7 +60,7 @@ token = TokenGenerator(region="us-west-2").get_token() client = AnthropicAWS(api_key=token, aws_region="us-west-2") response = client.messages.create( - model="claude-sonnet-4-6", + model="claude-sonnet-5", max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], ) @@ -76,7 +76,7 @@ import Anthropic from "@anthropic-ai/sdk"; const client = new Anthropic({ apiKey: "sk-ant-api03-..." }); const response = await client.messages.create({ - model: "claude-sonnet-4-6", + model: "claude-sonnet-5", max_tokens: 1024, messages: [{ role: "user", content: "Hello" }], }); @@ -94,7 +94,7 @@ const client = new AnthropicAws({ }); const response = await client.messages.create({ - model: "claude-sonnet-4-6", + model: "claude-sonnet-5", max_tokens: 1024, messages: [{ role: "user", content: "Hello" }], }); @@ -109,7 +109,7 @@ import AnthropicAws from "@anthropic-ai/aws-sdk"; const client = new AnthropicAws({ awsRegion: "us-west-2" }); const response = await client.messages.create({ - model: "claude-sonnet-4-6", + model: "claude-sonnet-5", max_tokens: 1024, messages: [{ role: "user", content: "Hello" }], }); @@ -127,7 +127,7 @@ const token = await tokenProvider(); const client = new AnthropicAws({ apiKey: token, awsRegion: "us-west-2" }); const response = await client.messages.create({ - model: "claude-sonnet-4-6", + model: "claude-sonnet-5", max_tokens: 1024, messages: [{ role: "user", content: "Hello" }], }); @@ -142,7 +142,7 @@ curl -X POST https://api.anthropic.com/v1/messages \ -H "x-api-key: sk-ant-api03-..." \ -H "anthropic-version: 2023-06-01" \ -H "content-type: application/json" \ - -d '{"model":"claude-sonnet-4-6","max_tokens":1024,"messages":[{"role":"user","content":"Hello"}]}' + -d '{"model":"claude-sonnet-5","max_tokens":1024,"messages":[{"role":"user","content":"Hello"}]}' ``` ### After (CPOA — API Key Mode) @@ -153,7 +153,7 @@ curl -X POST https://aws-external-anthropic.us-west-2.api.aws/v1/messages \ -H "anthropic-workspace-id: wrkspc_XXXXX" \ -H "anthropic-version: 2023-06-01" \ -H "content-type: application/json" \ - -d '{"model":"claude-sonnet-4-6","max_tokens":1024,"messages":[{"role":"user","content":"Hello"}]}' + -d '{"model":"claude-sonnet-5","max_tokens":1024,"messages":[{"role":"user","content":"Hello"}]}' ``` ### After (CPOA — SigV4 Mode) @@ -167,7 +167,7 @@ curl -X POST "https://aws-external-anthropic.us-west-2.api.aws/v1/messages" \ -H "anthropic-workspace-id: $ANTHROPIC_AWS_WORKSPACE_ID" \ -H "anthropic-version: 2023-06-01" \ -H "content-type: application/json" \ - -d '{"model":"claude-sonnet-4-6","max_tokens":1024,"messages":[{"role":"user","content":"Hello"}]}' + -d '{"model":"claude-sonnet-5","max_tokens":1024,"messages":[{"role":"user","content":"Hello"}]}' ``` ## Self-Hosted Sandbox Workers