Skip to content

feat(aws): resolve endpoints by service - #415

Open
aryasaatvik wants to merge 2 commits into
alchemy-run:mainfrom
aryasaatvik:feat/aws-service-endpoints-upstream
Open

feat(aws): resolve endpoints by service#415
aryasaatvik wants to merge 2 commits into
alchemy-run:mainfrom
aryasaatvik:feat/aws-service-endpoints-upstream

Conversation

@aryasaatvik

@aryasaatvik aryasaatvik commented Aug 4, 2026

Copy link
Copy Markdown

Endpoint overrides the endpoint for every operation in context — the right shape for a full local gateway (AWS_ENDPOINT_URL, LocalStack convention). But the AWS SDKs also specify service-specific endpoints — AWS_ENDPOINT_URL_<SERVICE> and per-service services sections in ~/.aws/config — for routing only some services elsewhere: an emulator that implements a subset, a private interface endpoint for s3 only. Distilled can't express that: the operation's service is only known at request time, inside the protocol layer.

Add ServiceEndpoint, a resolver keyed by the SDK service ID exactly as it appears in the model's aws.api#service trait:

const endpoints: Record<string, string> = {
  "S3": "http://127.0.0.1:9000",
  "DynamoDB": "http://127.0.0.1:8000",
};

Effect.provideService(ServiceEndpoint, {
  resolve: (service) => endpoints[service],
})

protocol.ts and presign.ts now resolve through Endpoint.resolve(service):

  1. an operation-scoped Endpoint wins,
  2. then the ServiceEndpoint resolver,
  3. undefined falls through to the generated Smithy endpoint rules, selecting the AWS endpoint as before.

An absent ServiceEndpoint behaves exactly as today.

The key is the SDK service ID rather than the SigV4 signing name because signing names collide — SES and SESv2 both sign as ses — while SDK IDs keep every service independently routable, and they are the identifier AWS's own service-specific endpoint configuration is derived from. Two natural follow-ups: a generated ServiceName union (from the models' sdkIds) to tighten the string key, and a ServiceEndpoint.fromEnv layer honoring AWS_ENDPOINT_URL_<SERVICE>/AWS_IGNORE_CONFIGURED_ENDPOINT_URLS.

Add `ServiceEndpoint`, a resolver keyed by the SDK service ID exactly as
it appears in the model's `aws.api#service` trait (`"S3"`, `"SESv2"`,
`"S3 Control"`), and `Endpoint.resolve(service)` which layers it under
the existing `Endpoint` override:

1. an explicit operation-scoped `Endpoint` wins,
2. then the `ServiceEndpoint` resolver,
3. otherwise `undefined`, leaving the generated Smithy endpoint rules to
   select the AWS endpoint as before.

`protocol.ts` and `presign.ts` now go through `Endpoint.resolve` instead
of reading `Endpoint` directly, so a process-wide per-service policy
(local emulator, private endpoints) applies without every call site
providing an `Endpoint`.

The key is the SDK service ID rather than the SigV4 signing name because
signing names collide (SES and SESv2 both sign as `ses`); SDK IDs keep
every service independently routable, and they are the identifier AWS's
own service-specific endpoint configuration
(`AWS_ENDPOINT_URL_<SERVICE>`, `~/.aws/config` `services` sections) is
derived from.
@aryasaatvik
aryasaatvik force-pushed the feat/aws-service-endpoints-upstream branch from f25e582 to df5b912 Compare August 4, 2026 20:22
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.

1 participant