You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Feature request: AWS / client-auth mode for lib/environments self-hosted worker
Summary
The lib/environments self-hosted worker helpers (EnvironmentWorker, WorkPoller) hard-require
an environment key and inject it as a per-request Authorization bearer on every call. This
makes it impossible to run a self-hosted worker against Claude Platform on AWS, where the
documented and supported auth model is AWS IAM (SigV4) or an AWS-Console API key — not an
environment key.
Please add an explicit opt-in that lets the worker authenticate with the base client's
credentials (SigV4 middleware or x-api-key) and skip the environment-key bearer entirely.
On Claude Platform on AWS, the worker authenticates with AWS IAM (SigV4) or an API key
generated in the AWS Console, not an environment key. Attach the AnthropicSelfHostedEnvironmentAccess managed policy to the IAM principal your worker runs as.
Environment keys generated in the Claude Console don't work with the Claude Platform on AWS
endpoint.
So on AWS the IAM principal (e.g. an EKS pod's IRSA role) is the work-lease credential — the
AWS-native equivalent of the personal platform's environment key. But the Go SDK worker helper
cannot express this: it requires an environment key and overrides the AWS credential on every
request.
The aws/ subpackage already produces a fully AWS-authed client (SigV4 middleware + anthropic-workspace-id header + regional base URL, exposed as aws.Client.Options). The gap is
purely that the worker helper strips/overrides that auth.
Affected code (verified identical in v1.50.1 and v1.50.2)
1. Environment key is hard-required — guards reject an empty key:
lib/environments/poller.go:74-75 — bearerReqOpts: if environmentKey == "" { return nil, errors.New("environments: environment key is required") }
lib/environments/poller.go:249-250 — NewWorkPoller: case opts.EnvironmentKey == "": p.err = errors.New("environments: WorkPollerOptions.EnvironmentKey is required")
lib/environments/worker.go:138-140 — EnvironmentWorker.Run: requires EnvironmentID and EnvironmentKey
2. The env-key bearer is injected unconditionally on every request — lib/environments/poller.go:73-81:
funcbearerReqOpts(environmentKeystring) ([]option.RequestOption, error) {
ifenvironmentKey=="" {
returnnil, errors.New("environments: environment key is required")
}
return []option.RequestOption{
option.WithHeaderDel("X-Api-Key"), // strips the base client's x-api-keyoption.WithAuthToken(environmentKey), // sets Authorization: Bearer <env-key>
}, nil
}
These opts are appended last at every call site, so they win over any base-client auth
(option header-setters are last-writer-wins, option/requestoption.go:231-253):
session tool-runner stream / list / send — worker.go:361-363
The net effect on an AWS-authed base client: WithHeaderDel("X-Api-Key") removes the AWS-Console
API key, and WithAuthToken overwrites the SigV4 Authorization header — so the request reaches
the wire with the (AWS-invalid) environment-key bearer instead of the AWS credential.
3. No escape hatch. None of WorkPollerOptions (poller.go:129-180), EnvironmentWorkerOptions (worker.go:25-91), or SessionToolRunnerOptions
(betasessiontoolrunner.go:71-129) exposes a SkipAuth / auth-mode / empty-key-allowed field.
4. No caller-side workaround. Caller-supplied RequestOptions are applied before the
env-key opts at every helper call site, so they can't re-add x-api-key or re-assert SigV4 after
the bearer; and the helpers hard-error on an empty key regardless. (SessionToolRunner used
directly is the one exception — it only appends a telemetry header — but that covers
stream/list/send only, not poll/heartbeat/skills, so there is no end-to-end workaround.)
Why the fix is clean
The environment key flows into nothing but the Authorization header — it is never used as a
query param, request body field, or routing header (work-item routing uses the separate EnvironmentID; the anthropic-beta header is auto-injected by the generated methods). So
removing the bearer injection and letting the base client's auth stand is sufficient at every
call site.
Proposed change
Add an explicit opt-in (preferred over silently treating an empty key as "use base auth", which
would be a footgun on the personal platform — it would let the worker fall back to an org API key
on the host, the exact thing the env-key model exists to prevent):
Add a field to EnvironmentWorkerOptions and WorkPollerOptions, e.g.:
// UseClientAuth authenticates work and session calls with the base client's// own credentials (e.g. AWS SigV4 or an x-api-key) instead of an environment// key. Required for Claude Platform on AWS, where the worker authenticates as// an IAM principal and environment keys are not accepted. When set,// EnvironmentKey is not required and is ignored.UseClientAuthbool
(An AuthMode enum would work equally well if that fits the SDK's conventions better.)
When UseClientAuth is set:
Relax the env-key requirement guards (poller.go:249-250, worker.go:138-140, worker.go:225-234) to require only EnvironmentID.
Make bearerReqOpts return a no-op slice (no WithHeaderDel("X-Api-Key"), no WithAuthToken) so the base client's auth flows through unchanged. This single change
propagates correctly to helperReqOpts and all the call sites listed above (they just append
an empty auth slice).
SessionToolRunner needs no change — it never required the key and only appends a
telemetry header; it's fully driven by the RequestOptions it's handed.
This is small, localized, and backward-compatible — the default path (env-key bearer) is untouched.
Intended usage after the change (Go, AWS SigV4)
// AWS-authed base client (SigV4 + workspace header + regional base URL):awsClient, err:=aws.NewClient(ctx, aws.ClientConfig{
AWSRegion: region,
WorkspaceID: workspaceID,
})
// ...client:=anthropic.NewClient(awsClient.Options...)
worker:=environments.NewEnvironmentWorker(client, environments.EnvironmentWorkerOptions{
EnvironmentID: environmentID,
UseClientAuth: true, // no EnvironmentKey on AWSWorkdir: "/workspace",
})
err=worker.Run(ctx)
Notes
The Python and TypeScript SDK worker helpers likely need the parallel change — their
self-hosted-sandbox worker examples still pass an environment key. Only the Go SDK was
source-verified here (it's what the ant CLI uses).
Every SDK worker example currently passes an environment key. Can you confirm the AWS endpoint
(aws-external-anthropic.{region}.api.aws) already serves the worker routes —
work poll/ack/stop/heartbeat, sessions.events stream/list/send, and skills download —
under IAM/SigV4 auth today (i.e. the doc reflects shipped server behavior)? That determines whether
this SDK change alone unblocks worker-on-AWS or whether server-side work is also pending.
Feature request: AWS / client-auth mode for
lib/environmentsself-hosted workerSummary
The
lib/environmentsself-hosted worker helpers (EnvironmentWorker,WorkPoller) hard-requirean environment key and inject it as a per-request
Authorizationbearer on every call. Thismakes it impossible to run a self-hosted worker against Claude Platform on AWS, where the
documented and supported auth model is AWS IAM (SigV4) or an AWS-Console API key — not an
environment key.
Please add an explicit opt-in that lets the worker authenticate with the base client's
credentials (SigV4 middleware or
x-api-key) and skip the environment-key bearer entirely.Background / why this matters
Per the self-hosted sandboxes docs:
So on AWS the IAM principal (e.g. an EKS pod's IRSA role) is the work-lease credential — the
AWS-native equivalent of the personal platform's environment key. But the Go SDK worker helper
cannot express this: it requires an environment key and overrides the AWS credential on every
request.
The
aws/subpackage already produces a fully AWS-authed client (SigV4 middleware +anthropic-workspace-idheader + regional base URL, exposed asaws.Client.Options). The gap ispurely that the worker helper strips/overrides that auth.
Affected code (verified identical in
v1.50.1andv1.50.2)1. Environment key is hard-required — guards reject an empty key:
lib/environments/poller.go:74-75—bearerReqOpts:if environmentKey == "" { return nil, errors.New("environments: environment key is required") }lib/environments/poller.go:249-250—NewWorkPoller:case opts.EnvironmentKey == "": p.err = errors.New("environments: WorkPollerOptions.EnvironmentKey is required")lib/environments/worker.go:138-140—EnvironmentWorker.Run: requiresEnvironmentIDandEnvironmentKeylib/environments/worker.go:225-234—HandleItem: requiresEnvironmentKey2. The env-key bearer is injected unconditionally on every request —
lib/environments/poller.go:73-81:These opts are appended last at every call site, so they win over any base-client auth
(option header-setters are last-writer-wins,
option/requestoption.go:231-253):poller.go:285-287worker.go:280-282worker.go:361-363The net effect on an AWS-authed base client:
WithHeaderDel("X-Api-Key")removes the AWS-ConsoleAPI key, and
WithAuthTokenoverwrites the SigV4Authorizationheader — so the request reachesthe wire with the (AWS-invalid) environment-key bearer instead of the AWS credential.
3. No escape hatch. None of
WorkPollerOptions(poller.go:129-180),EnvironmentWorkerOptions(worker.go:25-91), orSessionToolRunnerOptions(
betasessiontoolrunner.go:71-129) exposes a SkipAuth / auth-mode / empty-key-allowed field.4. No caller-side workaround. Caller-supplied
RequestOptionsare applied before theenv-key opts at every helper call site, so they can't re-add
x-api-keyor re-assert SigV4 afterthe bearer; and the helpers hard-error on an empty key regardless. (
SessionToolRunneruseddirectly is the one exception — it only appends a telemetry header — but that covers
stream/list/send only, not poll/heartbeat/skills, so there is no end-to-end workaround.)
Why the fix is clean
The environment key flows into nothing but the
Authorizationheader — it is never used as aquery param, request body field, or routing header (work-item routing uses the separate
EnvironmentID; theanthropic-betaheader is auto-injected by the generated methods). Soremoving the bearer injection and letting the base client's auth stand is sufficient at every
call site.
Proposed change
Add an explicit opt-in (preferred over silently treating an empty key as "use base auth", which
would be a footgun on the personal platform — it would let the worker fall back to an org API key
on the host, the exact thing the env-key model exists to prevent):
Add a field to
EnvironmentWorkerOptionsandWorkPollerOptions, e.g.:(An
AuthModeenum would work equally well if that fits the SDK's conventions better.)When
UseClientAuthis set:poller.go:249-250,worker.go:138-140,worker.go:225-234) to require onlyEnvironmentID.bearerReqOptsreturn a no-op slice (noWithHeaderDel("X-Api-Key"), noWithAuthToken) so the base client's auth flows through unchanged. This single changepropagates correctly to
helperReqOptsand all the call sites listed above (they just appendan empty auth slice).
SessionToolRunnerneeds no change — it never required the key and only appends atelemetry header; it's fully driven by the
RequestOptionsit's handed.This is small, localized, and backward-compatible — the default path (env-key bearer) is untouched.
Intended usage after the change (Go, AWS SigV4)
Notes
self-hosted-sandbox worker examples still pass an environment key. Only the Go SDK was
source-verified here (it's what the
antCLI uses).antCLI'sbeta:workercommand wraps this helper and is blocked onthis change for AWS support — see feat(auth): add a Claude Platform on AWS credential tier anthropic-cli#61.
Open question for the SDK / Managed Agents team
Every SDK worker example currently passes an environment key. Can you confirm the AWS endpoint
(
aws-external-anthropic.{region}.api.aws) already serves the worker routes —work
poll/ack/stop/heartbeat,sessions.eventsstream/list/send, and skills download —under IAM/SigV4 auth today (i.e. the doc reflects shipped server behavior)? That determines whether
this SDK change alone unblocks worker-on-AWS or whether server-side work is also pending.