Add a new highest-priority, opt-in credential tier that routes requests
through the Claude Platform on AWS gateway (which does not support OAuth),
unblocking the version-controlled Managed Agents workflow on AWS.
Selected only by explicit opt-in — the --aws flag or the persistent
ANTHROPIC_USE_AWS toggle (not ambient AWS env vars). When active,
getDefaultRequestOptions short-circuits before the first-party credential
switch and builds request options from the SDK's aws/ backend (SigV4 via
the default AWS credential chain, or x-api-key when --aws-api-key is set).
All ~40 Stainless-generated commands work unchanged over the AWS transport.
Changes are confined to hand-written files:
- extras.go: register --aws, --aws-region, --aws-workspace-id, --aws-api-key
global flags (each paired with its env Source).
- cmdutil.go: buildAWSConfig pure helper, the AWS tier-0 short-circuit, and
warnIfAWSConflict (warns when --aws overrides a first-party cred; excludes
--aws-api-key, which only selects API-key mode).
- cmd_auth.go: awsAuthStatus renders AWS as the tier-0 winner with the active
mode label and backend-resolved base URL, redacting the API key.
- cmd_aws_test.go: buildAWSConfig pass-through, conflict warning fires/silent,
auth status row, and the cross-site precedence invariant.
The anthropic-sdk-go pin (v1.50.1) is unchanged — its aws/ subpackage was
already present; go.mod only records the AWS SDK transitive indirect deps it
newly makes reachable.
Summary
Adds a new, highest-priority, opt-in credential tier that routes requests
through the Claude Platform on AWS gateway. Claude Platform on AWS does not
support OAuth, and today the CLI authenticates only via OAuth login, a
first-party
ANTHROPIC_API_KEY, or first-party WIF — none of which reach anAWS-provisioned org, and the CLI can neither send the required
anthropic-workspace-idheader nor SigV4-sign requests. That blocks theversion-controlled Managed Agents workflow (
ant beta:agents create < agent.yamlfrom CI) on AWS, which is the CLI's canonical use.
The tier is selected only by explicit opt-in — the
--awsflag or thepersistent
ANTHROPIC_USE_AWStoggle (theCLAUDE_CODE_USE_BEDROCKidiom), bothsurfaced through one
cmd.Bool("aws"). It is not inferred from ambient AWSenv vars (
AWS_REGION/AWS_PROFILEetc.), which legitimately leak into anyshell on an EC2 host or AWS-configured laptop. When active,
getDefaultRequestOptionsshort-circuits before the existing first-partyprecedence switch and builds request options from the SDK's
aws/backend:--aws-api-key/ANTHROPIC_AWS_API_KEYset → authenticatewith
x-api-key+ the workspace header. No AWS IAM creds needed.(IAM role /
AWS_PROFILE/ env keys), using the region + workspace header.All ~40 Stainless-generated commands work unchanged over the AWS transport —
full
antparity includingbeta:agents,--transform,@file, andYAML/JSON stdin.
What changed
The diff is confined to hand-written files — no Stainless-generated command
file is touched, and there is no SDK version bump (the pinned
anthropic-sdk-go v1.50.1already ships theaws/subpackage).pkg/cmd/extras.go— register four global flags in the existinghand-written
init()block, each paired with its envSources:(matching the--profile/--federation-ruleconvention):--aws(ANTHROPIC_USE_AWS) — opt into the AWS backend.--aws-region(AWS_REGION,AWS_DEFAULT_REGION) — load-bearing for thegateway URL and the SigV4 signing scope.
--aws-workspace-id(ANTHROPIC_AWS_WORKSPACE_ID).--aws-api-key(ANTHROPIC_AWS_API_KEY) — optional; its presence selectsAPI-key mode.
Deliberately not added:
--aws-profile/--aws-access-key/--aws-secret-access-key/--aws-session-token. The principled line is thatthe CLI flags the Anthropic-gateway config and lets the AWS default
credential chain resolve every AWS credential itself. Region is the one
exception that earns a flag because it is load-bearing for the URL and signing
scope, not merely a credential input.
pkg/cmd/cmdutil.gobuildAWSConfig(cmd) aws.ClientConfig— a pure, unit-tested helper that mapsthe four flags onto the SDK config. Empty values are intentional: the SDK's
awsauth.ResolveConfigtreats an empty string as "fall back to env /regional derivation" for every field, so no
IsSetguards are needed.cmd.Bool("aws"), placed right after thebase
optsslice and before the credential-resolution block — so an AWScall never runs
loadProfileIfUsable(disk I/O + possibleclient_idshadow-warning) or
warnIfMultipleAuthSources, all wasted/misleading for anAWS request.
aws.NewClient's clean "no region / no workspace ID / no baseURL" error is surfaced via the existing federation-path
os.Exit(1)style(rather than re-threading an error return through ~30 generated handlers),
instead of a downstream 401.
warnIfAWSConflict— warns when--awsoverrides a first-party credential(
--api-key,--auth-token, an explicit profile, or federation), naming theignored source. Critically excludes
--aws-api-key, which only selectsAPI-key mode within the AWS tier — otherwise every API-key-mode CI run would
spuriously warn.
pkg/cmd/cmd_auth.go—authStatusshort-circuits to a focusedawsAuthStatusrow whenBool("aws")is set, showing AWS as the tier-0winner with the active mode label (
API key …redacted, vsSigV4 via AWS credential chain), the region/workspace, and the backend-resolved base URL.This matters in the default CI case: with
ANTHROPIC_USE_AWSpersistent,Bool("aws")is true without typing--aws, so without this rowauth statuswould print "(no credential configured…)" while requests in the same env
succeed.
go.mod/go.sum— record the AWS SDK transitive// indirectdeps thatthe
aws/subpackage newly makes reachable. Theanthropic-sdk-gopin isunchanged.
Because the AWS tier short-circuits before the first-party precedence switch,
it does not slot into that switch's ordering. Instead it owns the two surfaces a
short-circuiting tier-0 needs: a dedicated
warnIfAWSConflict(parallel to thefirst-party
warnIfMultipleAuthSources, which the AWS path never reaches), and adedicated
awsAuthStatusbranch inauthStatus(ahead of thecredWinnerlogic). So it reads as a first-class credential tier with its own conflict
diagnostic and status row, not a bolt-on — without entangling the existing
first-party precedence code.
Tests
New unit tests in
pkg/cmd/cmd_aws_test.go(no network, no live AWS — they testlogic, following the existing
cmd_auth_test.go/cmdutil_test.gopatterns):TestBuildAWSConfig— flag→config pass-through, including the empty-flag case.TestAWSConflictWarning— fires for each first-party cred; silent for--aws-api-keyalone (the must-not-warn case); emits once.TestAWSPrecedenceInvariant— AWS wins at both observable precedence sites(request options +
auth status) when paired with the top first-party tier.TestAuthStatusAWSRow— API-key vs SigV4 mode labels, secret redaction, base-URL override vs regional derivation, and that first-party rows aren't shown.
Test plan
go build ./...andgo vet ./pkg/cmd/pass;gofmtclean.go test ./pkg/cmd/ -run 'AWS|MultiAuth'passes. (The repo's full suitealso requires a mock Steady server on
localhost:4010for the generatedcommand tests — unrelated to this change.)
aws-external-anthropic.{region}.api.aws, theanthropic-workspace-idheader, the mode-appropriate auth header, and a real HTTP 200 (not the
prior
401 x-api-key header is required).beta:agents create < agent.yaml --transform id -r,list,retrieve,then
update --version N(optimistic lock; version advanced 1 → 2 and astale
--versionwas rejected) over the AWS transport, thenarchive.no AWS region found;missing workspace →
no workspace ID found;--aws+ first-party--api-key→ conflict warning.Live verification summary
Verified end-to-end against a real Claude-Platform-on-AWS workspace
(
us-east-1) in all three credential configurations, all green:ANTHROPIC_AWS_API_KEY,x-api-key)x-api-key+ workspace header on the wire; key redacted inauth statusAWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY)Authorizationsignature + workspace header on the wireAWS_PROFILE, default credential chain)Each configuration ran the full sequence:
auth statustier-0/mode label →messages create(asserting the actualHTTP/… 200status line) → thebeta:agentscreate/list/retrieve/update/archive round-trip with optimisticlocking → the negative checks above. The harness used is intentionally not
part of this PR (it reads live credentials); it lives locally only.
Notes for reviewers
aws.NewClienterror uses the existing federation path'sos.Exit(1)fatal (which itself carries a TODO about bypassing urfave's error pipeline).
Matches current style; happy to switch to proper error propagation if
preferred.
context.Background()is used at the short-circuit becausegetDefaultRequestOptionstakes only*cli.Command(urfave/cli v3 exposes noctx on
Command) and is called at ~30 generated sites without ctx threading;aws.NewClientuses ctx only for eager SigV4 credential resolution, with nolong-lived request I/O.