Skip to content

feat(aws): assume customer roles with Maple's own OIDC tokens - #1715

Merged
Makisuo merged 6 commits into
mainfrom
feat/aws-oidc
Oct 22, 2026
Merged

Makisuo merged 6 commits into
mainfrom
feat/aws-oidc

Conversation

@Makisuo

@Makisuo Makisuo commented Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator

Customer AWS roles now trust Maple as an OIDC identity provider instead of a deploy-minted IAM user with a static access key and an external ID. Maple holds no AWS credential at all; previews and dev no longer create anything in AWS for the integration.

How it works

  • Issuer {MAPLE_API_BASE_URL}/oidc/aws (api.maple.dev/oidc/aws, api.eu.maple.dev/oidc/aws, api-pr-<n>.maple.dev/oidc/aws). apps/api serves /oidc/aws/.well-known/openid-configuration and /oidc/aws/jwks.json, built from configuration, never request headers. The issuer is permanent once an account trusts it.
  • Signing key: an alchemy KeyPair (RSA 2048) per stage, bound to api and alerting as MAPLE_AWS_OIDC_SIGNING_KEY. kid is the RFC 7638 thumbprint.
  • Check: Maple signs a 5-minute RS256 token (sub = aws-connection:<id>, aud = sts.amazonaws.com) and calls STS AssumeRoleWithWebIdentity unsigned over HttpClient (JSON answers); the IAM self-reads use the session as before.
  • Trust policy: Federated: arn:aws:iam::<acct>:oidc-provider/<issuer>, StringEquals on <issuer>:aud and <issuer>:sub. A token for another connection can't assume the role.
  • Shared provider: an account has one provider per issuer, shared by every Maple connection to it.
    • CloudShell script: registers it if missing (and adds Maple's audience to one made by hand), then deploys with CreateOidcProvider=false.
    • Console template: creates it by default, DeletionPolicy: Retain.
    • Terraform: looks it up with a data source, so a destroy can't take it from other connections.
    • Cleanup script: deletes it once no MapleIntegration-* role is left.
  • Errors: no provider / wrong audience / AccessDenied = setup not done (pending, or disconnected after a first success); a token STS can't verify = Maple's side; JWKS fetch failures and throttles = transient.

Removed

packages/infra/src/aws/integration-principal.ts (IAM user + access key), MAPLE_AWS_PRINCIPAL_ARN / _ACCESS_KEY_ID / _SECRET_ACCESS_KEY, the awsIntegrationPrincipal profile flag, and the aws_connections.external_id column (migration 20261022223850_aws_oidc). Public v2 external_id is replaced by oidc_issuer + oidc_subject. Production has no AWS connections (no POST /v2/integrations/aws/connections in prd since #1635 shipped), so nothing to migrate. AWS_TEMPLATE_VERSION is now 2.

Verification

  • The test AWS now enforces trust the way STS does: token signature against Maple's key, the account's provider for the token's issuer and its client ids, the role's aud/sub conditions, the template's Fn::If/Ref branch. Covered: a token for another connection, a key that isn't Maple's, an account without the provider, a provider without Maple's audience.
  • Signer: tokens verify with the published JWK alone, a forged claim does not, kid recomputed from RFC 7638.
  • Setup/cleanup scripts run against a fake aws CLI: provider created only when missing and before the stack, client id added to an existing one, provider kept while another Maple role exists and deleted with the last.
  • Issuer routes: discovery + JWKS under the configured origin despite forged Host/X-Forwarded-Host, public key only, 404 without a key.
  • bun typecheck for domain, backend, infra, db, api, alerting, web; backend integrations 783 tests, api routes, db parity, infra profile, web scenes, domain v2.
  • A review sub agent went over the diff; its findings (STS classification, Terraform owning the shared provider, clock skew) are fixed in the second commit.
  • Pending on the preview: AWS fetching the JWKS through Cloudflare, the real setup script in a real account, the connection check, the metric stream, and the cleanup removing the provider. Results will be posted here.

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

Customer roles now trust an IAM OIDC provider for {api}/oidc/aws instead of a
deploy-minted IAM user with an external ID. Maple signs a five-minute RS256
token per connection (sub aws-connection:<id>) and exchanges it with STS
AssumeRoleWithWebIdentity, unsigned. api serves the discovery document and
JWKS; the signing key is an alchemy KeyPair per stage, so no AWS credential
exists on Maple's side and previews need no AWS resources.

The setup script registers the provider when the account lacks it, the
template creates it only on request and retains it, and the cleanup removes
it with the last Maple role. external_id is dropped (no prd connections).
…red provider

A provider without Maple's audience is the customer's setup (the script adds
the client id), a JWKS fetch failure is transient, and Terraform looks the
account's provider up instead of owning it, so destroying one connection
cannot disconnect the others. Tokens are Redacted and allow a minute of skew.
@Makisuo Makisuo added the preview Deploy a full PR preview stack (Cloudflare + AWS ingest); removing it tears it down label Oct 22, 2026
@maple-review-bot

maple-review-bot Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Maple review

🟡 Confidence 6/10 · needs attention
One error-classification defect; the real STS exchange and JWKS fetch are still exercised only against the in-repo fake.
1 warning −1 · 1 note · tests partial −1 · risk high −1 · 2/3 new units observable −1

Replaces the customer-visible IAM user and external id with Maple-signed OIDC tokens: new issuer routes, a per-stage signing key, rewritten setup and cleanup scripts, and external_id dropped. Wiring is complete and tested against a fake AWS, though one STS error path blames Maple for a customer-side misconfiguration. The open AwsApiError issues in api-impl.ts are not addressed by this diff.

  • makeAwsOidcSigner mints RS256 tokens whose sub names the connection
  • /oidc/aws discovery and JWKS routes are built from configuration, never request headers
  • checkRole assumes roles with AssumeRoleWithWebIdentity instead of Maple's access key
  • external_id column and MAPLE_AWS_* access-key env vars removed

Before merge

Findings

🟠 Warning · F1 · A wrong OIDC thumbprint is reported as Maple's fault, not the customer's

correctness · packages/backend/src/services/integrations/aws/api-impl.ts:74-78

setupMissing only recognizes InvalidIdentityToken whose message names a missing provider or an audience. Any other InvalidIdentityToken — a provider created by hand with a stale or wrong certificate thumbprint, exactly the provider ensureOidcProvider falls back to adding a client id to — falls through to MAPLE_TOKEN_CODES and becomes maple_identity, which interpretCheck renders as "AWS could not verify Maple's identity token. This is on Maple's side; nothing to change in AWS" (AwsConnectionService.ts:317). The administrator is told to do nothing while the fix is in their account.

Match the provider-configuration `InvalidIdentityToken` messages (certificate/thumbprint does not match) into `setupMissing`, keeping `maple_identity` for a token STS genuinely cannot verify.
🔵 Note · F2 · STS assume call has no peer.service or client span

observability · MAP-01 · packages/backend/src/services/integrations/aws/api-impl.ts:135-137

The new POST to sts.amazonaws.com runs on the bare HttpClient inside checkRole and annotates only maple.aws.role_arn, while outbound providers elsewhere carry peer.service (platform/Apns.ts:311, services/alerts/delivery/Transport.ts:82). When STS is slow or throttling, its latency is folded into the same AwsApi.checkRole span as the IAM reads that follow, with no sts.amazonaws.com peer on the service map.

Wrap the execute in its own span carrying `peer.service: "sts.amazonaws.com"` (and `server.address`), so the STS leg is separable from the IAM self-reads.
🤖 Prompt to fix all 2 findings with an AI agent
Findings from an automated review of commit fd083cdd6f1b77dd5c13a5f1eb6a6ecbb725bdb3. Verify each one against the current code before changing anything, fix only those that still apply, and keep each fix to the lines it names.

---

F1 · Warning · correctness · packages/backend/src/services/integrations/aws/api-impl.ts:74-78
A wrong OIDC thumbprint is reported as Maple's fault, not the customer's
`setupMissing` only recognizes `InvalidIdentityToken` whose message names a missing provider or an audience. Any other `InvalidIdentityToken` — a provider created by hand with a stale or wrong certificate thumbprint, exactly the provider `ensureOidcProvider` falls back to adding a client id to — falls through to `MAPLE_TOKEN_CODES` and becomes `maple_identity`, which `interpretCheck` renders as "AWS could not verify Maple's identity token. This is on Maple's side; nothing to change in AWS" (`AwsConnectionService.ts:317`). The administrator is told to do nothing while the fix is in their account.
Suggested fix: Match the provider-configuration `InvalidIdentityToken` messages (certificate/thumbprint does not match) into `setupMissing`, keeping `maple_identity` for a token STS genuinely cannot verify.

---

F2 · Note · observability · MAP-01 · packages/backend/src/services/integrations/aws/api-impl.ts:135-137
STS assume call has no `peer.service` or client span
The new POST to `sts.amazonaws.com` runs on the bare `HttpClient` inside `checkRole` and annotates only `maple.aws.role_arn`, while outbound providers elsewhere carry `peer.service` (`platform/Apns.ts:311`, `services/alerts/delivery/Transport.ts:82`). When STS is slow or throttling, its latency is folded into the same `AwsApi.checkRole` span as the IAM reads that follow, with no `sts.amazonaws.com` peer on the service map.
Suggested fix: Wrap the execute in its own span carrying `peer.service: "sts.amazonaws.com"` (and `server.address`), so the STS leg is separable from the IAM self-reads.

Production impact

Open errors in the changed files
Issue Service Occurrences File
@maple/backend/integrations/RailwayApiError alerting 8 packages/backend/src/services/integrations/aws/api-impl.ts
@maple/backend/integrations/RailwayApiError alerting 6 packages/backend/src/services/integrations/aws/api-impl.ts
@maple/backend/integrations/AwsApiError maple-api 4 packages/backend/src/services/integrations/aws/api-impl.ts
@maple/backend/integrations/RailwayApiError alerting 4 packages/backend/src/services/integrations/aws/api-impl.ts

After this merges, Maple checks whether they stop.

Production traffic of the changed files (last 7 days)
File Calls/day Busiest operations
packages/backend/src/services/integrations/aws/setup.ts 56k maple 56k/day, 82.6% err
apps/api/src/worker.ts 12k POST /mcp 5.7k/day, 0.0% err
POST /mcp 5.7k/day, 0.1% err
GET /mcp 305/day, 0.0% err
apps/api/src/routes/aws-oidc.http.ts 897 GET 758/day, 1.9% err
GET 139/day, 0.0% err
packages/backend/src/services/integrations/AwsConnectionService.ts 8 AwsConnectionService.checkDue 8/day, 0.0% err
Telemetry this change adds and removes (1)
  • ➕ span name makeAwsOidcSigner · packages/backend/src/services/integrations/aws/oidc.ts:49
What was checked
  • Trust policy pins both ${issuer}:aud and ${issuer}:sub (packages/domain/src/aws.ts:216), so another connection's token cannot assume the role
  • api and alerting both derive the issuer from the same MAPLE_API_BASE_URL (packages/infra/src/env.ts:125), so minted tokens match the served JWKS
  • JWKS publishes only kty/n/e/kid/alg/use; aws-oidc.http.test.ts:65 asserts no private d is served
Observability coverage: 2 of 3 changes observable
Change Kind Observable Evidence
GET /oidc/aws/.well-known/openid-configuration and /oidc/aws/jwks.json (apps/api) inbound HTTP route yes registered in apps/api/src/runtime/http-graph.ts:245 alongside DiscoveryRouter, same raw-route stack
AssumeRoleWithWebIdentity call to sts.amazonaws.com in checkRole outbound HTTP call no bare httpClient.execute; only maple.aws.role_arn is annotated, no peer.service
AwsConnectionService.checkDue sweep (alerting cron) background work yes existing AwsConnectionService.checkDue.connection span per connection, AwsConnectionService.ts:698

fd083cd · Updated on every push. Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one. Check ids refer to Maple's instrumentation audit.

@coderabbitai

coderabbitai Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 19 seconds.

Check out review usage here.

View limit details

Limit details: You’ve used all 4 included reviews currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: e6c11861-f477-4889-b93d-8efd4971a3d8
📥 Commits

Reviewing files that changed from the base of the PR and between e035994 and ed14019.

📒 Files selected for processing: 41
  • .env.example
  • alchemy.run.ts
  • apps/alerting/src/worker.ts
  • apps/api/src/resources/env.ts
  • apps/api/src/routes/aws-oidc.http.test.ts
  • apps/api/src/routes/aws-oidc.http.ts
  • apps/api/src/routes/v2/integrations-aws.http.test.ts
  • apps/api/src/routes/v2/integrations-aws.http.ts
  • apps/api/src/runtime/http-graph.ts
  • apps/api/src/worker.ts
  • apps/landing/src/content/docs/integrations/aws.md
  • apps/web/src/page/integrations/aws.scene.test.ts
  • apps/web/src/page/integrations/view-aws.ts
  • apps/web/src/page/sources/detail/detail.scene.test.ts
  • docs/pr-previews.md
  • packages/backend/src/platform/Env.ts
  • packages/backend/src/services/integrations/AwsConnectionService.test.ts
  • packages/backend/src/services/integrations/AwsConnectionService.ts
  • packages/backend/src/services/integrations/SourceRemovalService.test.ts
  • packages/backend/src/services/integrations/aws/api-impl.ts
  • packages/backend/src/services/integrations/aws/api.ts
  • packages/backend/src/services/integrations/aws/errors.ts
  • packages/backend/src/services/integrations/aws/fake-aws.ts
  • packages/backend/src/services/integrations/aws/oidc.test.ts
  • packages/backend/src/services/integrations/aws/oidc.ts
  • packages/backend/src/services/integrations/aws/setup.test.ts
  • packages/backend/src/services/integrations/aws/setup.ts
  • packages/backend/src/services/org/OrganizationService.ts
  • packages/db/drizzle/20261022223850_aws_oidc/migration.sql
  • packages/db/drizzle/20261022223850_aws_oidc/snapshot.json
  • packages/db/drizzle/20261022230028_merge_aws_oidc/migration.sql
  • packages/db/drizzle/20261022230028_merge_aws_oidc/snapshot.json
  • packages/db/src/tables/aws-connections.ts
  • packages/domain/src/aws.ts
  • packages/domain/src/http/v2/integrations-aws.ts
  • packages/infra/src/aws/index.ts
  • packages/infra/src/aws/integration-principal.ts
  • packages/infra/src/aws/oidc-signing-key.ts
  • packages/infra/src/cloudflare/stack.ts
  • packages/infra/src/profile.test.ts
  • packages/infra/src/profile.ts
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@maple-review-bot maple-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

1 inline note from Maple's review. The score and summary are in the review comment above.

Comment thread packages/backend/src/services/integrations/aws/api-impl.ts Outdated
Comment thread packages/domain/src/aws.ts Fixed
@github-actions

github-actions Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

🍁 Maple PR preview

Note

Preview resources were removed when this pull request closed.

Final commit ed14019 · View workflow run

@maple-review-bot

maple-review-bot Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Maple review

🟢 Confidence 7/10 · likely safe to merge
The change swaps the AWS auth model and drops a column; I found no new defects, but the STS round trip is still unverified against real AWS and the two open findings stand.
1 warning −1 · 1 note · tests covered · risk high −1 · 1/2 new units observable −1

Customer roles now trust Maple's OIDC issuer: apps/api publishes discovery and JWKS, checkRole mints a 5-minute token and calls STS, and external_id is gone from the schema and the v2 API. The design holds up; the open thumbprint-misclassification finding is not addressed, and the STS call still has no client span.

  • checkRole assumes a customer role with a minted RS256 token, unsigned over HttpClient
  • AwsOidcRouter serves /oidc/aws discovery and JWKS from the configured issuer
  • Trust policies use a Federated principal with :aud/:sub conditions; one provider per account
  • aws_connections.external_id dropped; v2 returns oidc_issuer and oidc_subject

Before merge

  • Secret · Add secret MAPLE_AWS_OIDC_SIGNING_KEY to every environment · packages/backend/src/platform/Env.ts:285
  • Migration · Check it is safe on production data and with the code still deployed · packages/db/drizzle/20261022223850_aws_oidc/migration.sql
  • Migration · Check it is safe on production data and with the code still deployed · packages/db/drizzle/20261022230028_merge_aws_oidc/migration.sql
  • Infra · Check every environment has what this config now expects · alchemy.run.ts
  • Manual · Confirm MAPLE_API_BASE_URL is final for prd and prd-eu before any customer registers the issuer: it is permanent once trusted
  • Manual · Post the preview results (AWS fetching the JWKS through Cloudflare, a real setup script, cleanup removing the provider) before shipping
  • Manual · Land the destructive aws_oidc migration knowing it drops external_id; it is only safe because no production account has a connection

Still open from earlier reviews

Production impact

Open errors in the changed files
Issue Service Occurrences File
@maple/backend/integrations/RailwayApiError alerting 8 packages/backend/src/services/integrations/aws/api-impl.ts
@maple/backend/integrations/RailwayApiError alerting 6 packages/backend/src/services/integrations/aws/api-impl.ts
@maple/backend/integrations/AwsApiError maple-api 4 packages/backend/src/services/integrations/aws/api-impl.ts
@maple/backend/integrations/RailwayApiError alerting 4 packages/backend/src/services/integrations/aws/api-impl.ts

After this merges, Maple checks whether they stop.

Production traffic of the changed files (last 7 days)
File Calls/day Busiest operations
packages/backend/src/services/integrations/aws/setup.ts 56k maple 56k/day, 82.6% err
apps/api/src/worker.ts 12k POST /mcp 5.7k/day, 0.0% err
POST /mcp 5.6k/day, 0.1% err
GET /mcp 303/day, 0.0% err
apps/api/src/routes/aws-oidc.http.ts 895 GET 756/day, 1.9% err
GET 139/day, 0.0% err
packages/backend/src/services/integrations/AwsConnectionService.ts 8 AwsConnectionService.checkDue 8/day, 0.0% err
Telemetry this change adds and removes (1)
  • ➕ span name makeAwsOidcSigner · packages/backend/src/services/integrations/aws/oidc.ts:49
What was checked
  • No source still reads external_id or parseAwsMaplePrincipal; a head grep finds neither
  • Trust-policy condition keys use the scheme-less issuer (packages/domain/src/aws.ts:144), as IAM requires
  • The setup script registers the provider before the stack whose role references its ARN (setup.ts:382), and the fake AWS proves the trust it renders is the trust its check passes
Observability coverage: 1 of 2 changes observable
Change Kind Observable Evidence
GET /oidc/aws/.well-known/openid-configuration and /oidc/aws/jwks.json inbound HTTP yes Raw routes run through the same Worker fetch handler that emits http.server GET spans (apps/api/src/routes/scraper-internal.http.ts:275)
STS AssumeRoleWithWebIdentity call in assumeWithToken outbound HTTP no packages/backend/src/services/integrations/aws/api-impl.ts:120 runs httpClient.execute with no client span or peer.service (open finding F2)

c564d83 · Updated on every push. Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.

@Makisuo
Makisuo deployed to pr-preview October 22, 2026 23:03 — with GitHub Actions Active
@Makisuo

Makisuo commented Oct 22, 2026

Copy link
Copy Markdown
Collaborator Author

Preview test: real AWS, end to end (pr-1715, commit c564d83)

Issuer from the outside: GET https://api-pr-1715.maple.dev/oidc/aws/.well-known/openid-configuration and /jwks.json answer 200 with the configured issuer and one RS256 key (kid = thumbprint). Default, Java, AWS SDK and Apache user agents get 200; only Python-urllib gets a 403 from the existing Cloudflare rule, which AWS's own fetcher is not.

Lifecycle in account 465760687006 (the setup and cleanup scripts the preview served, byte-identical, sha256 checked, to the ones rendered from this branch):

  1. Connect AWS in the UI: the setup names https://api-pr-1715.maple.dev/oidc/aws, subject aws-connection:5ff554a9-…, template v2.
  2. Setup script: Registering Maple's identity provider… (no thumbprint passed; AWS computed 932bed33… itself), stack deployed with CreateOidcProvider=false.
  3. Check connection: Connected. CloudTrail:
    23:13:21Z aws-connection:5ff554a9-… AccessDenied   (polling before the stack existed: stays pending)
    23:13:30Z aws-connection:5ff554a9-… AccessDenied
    23:13:39Z aws-connection:5ff554a9-… OK  assumed-role/MapleIntegration-5ff554a9…/maple-5ff554a9…
    23:14:17Z aws-connection:5ff554a9-… OK
    
    The customer's CloudTrail now names the Maple connection in userIdentity.userName.
  4. Setup again: provider exists, so the client id is (idempotently) added, and the stack is No changes to deploy.
  5. Disconnect, cleanup script: stack deleted, then Removing Maple's identity provider: no Maple role is left in the account. Afterwards the account has only its pre-existing GitHub Actions provider.

Traces (deployment.environment = pr-1715), trace 1beb9b195ba65d62c874be1b7a9b20ea:

POST /v2/integrations/aws/connections/:id/check   1918 ms
└ AwsConnectionService.check                      1592 ms
  ├ SELECT aws_connections                          17 ms
  ├ AwsApi.checkRole                              1541 ms
  │ ├ POST sts AssumeRoleWithWebIdentity          1374 ms  (first call: STS fetches the JWKS)
  │ ├ POST iam GetRole                             140 ms
  │ └ POST iam ListAttachedRolePolicies             27 ms
  └ UPDATE aws_connections                          34 ms

With the JWKS cached, a whole check is ~143 ms (trace 5e8248020fca19a531a2a9f7b114677a). The pending checks fail typed (AssumeRoleWithWebIdentity: Not authorized…, 403) and record a pending state without failing the source. No N+1, no retries.

Not verified live: the hourly sweep from alerting (same signer and env binding as api; covered by the sweep tests). The metric stream path is unchanged by this PR (verified on #1680).

@maple-review-bot

maple-review-bot Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Maple review

🟢 Confidence 8/10 · likely safe to merge
The thumbprint reclassification is correct but untested, and the rewritten STS call still has no client span (F2).
2 notes · tests partial −1 · risk medium · 0/1 new units observable −1

The last commits make a stale OIDC thumbprint read as the customer's setup, which fixes F1; the STS call itself is unchanged and still uninstrumented (F2 open). None of the five open error issues in this file is addressed: toApiError is only renamed to toReadError, and the classified STS rejections still reach spans from stsError.

  • stsError now maps an InvalidIdentityToken naming a thumbprint to assume_denied

Before merge

Findings

🔵 Note · F3 · New InvalidIdentityToken thumbprint branch has no test in fake-aws

tests · packages/backend/src/services/integrations/aws/api-impl.ts:74-76

The setupMissing branch added in this commit only fires on an InvalidIdentityToken whose message names a thumbprint, and the fake STS never sends that message — it answers The token signature is invalid., No OpenIDConnect provider found… and Incorrect token audience (packages/backend/src/services/integrations/aws/fake-aws.ts:211, :218, :222), so nothing pins the classification this commit exists for. Add a fake-aws case returning AWS's thumbprint message and assert the connection stays pending instead of reading as maple_identity.

Add a thumbprint case to the fake STS trust check and a test asserting the resulting connection state.
🤖 Prompt to fix this finding with an AI agent
Findings from an automated review of commit ed1401996d931580fe7a5f896bce64f58279d437. Verify each one against the current code before changing anything, fix only those that still apply, and keep each fix to the lines it names.

---

F3 · Note · tests · packages/backend/src/services/integrations/aws/api-impl.ts:74-76
New `InvalidIdentityToken` thumbprint branch has no test in `fake-aws`
The `setupMissing` branch added in this commit only fires on an `InvalidIdentityToken` whose message names a thumbprint, and the fake STS never sends that message — it answers `The token signature is invalid.`, `No OpenIDConnect provider found…` and `Incorrect token audience` (`packages/backend/src/services/integrations/aws/fake-aws.ts:211`, `:218`, `:222`), so nothing pins the classification this commit exists for. Add a `fake-aws` case returning AWS's thumbprint message and assert the connection stays pending instead of reading as `maple_identity`.
Suggested fix: Add a thumbprint case to the fake STS trust check and a test asserting the resulting connection state.

Still open from earlier reviews

Fixed since the last review

  • ✅ F1 · A wrong OIDC thumbprint is reported as Maple's fault, not the customer's

Production impact

Open errors in the changed files
Issue Service Occurrences File
@maple/backend/integrations/RailwayApiError alerting 8 packages/backend/src/services/integrations/aws/api-impl.ts
@maple/backend/integrations/RailwayApiError alerting 6 packages/backend/src/services/integrations/aws/api-impl.ts
@maple/backend/integrations/AwsApiError maple-api 4 packages/backend/src/services/integrations/aws/api-impl.ts
@maple/backend/integrations/RailwayApiError alerting 4 packages/backend/src/services/integrations/aws/api-impl.ts
@maple/backend/integrations/AwsApiError maple-api 3 packages/backend/src/services/integrations/aws/api-impl.ts

After this merges, Maple checks whether they stop.

Production traffic of the changed files (last 7 days)
File Calls/day Busiest operations
packages/backend/src/services/integrations/aws/setup.ts 56k maple 56k/day, 82.6% err
apps/api/src/worker.ts 12k POST /mcp 5.7k/day, 0.0% err
POST /mcp 5.6k/day, 0.1% err
GET /mcp 303/day, 0.0% err
apps/api/src/routes/aws-oidc.http.ts 896 GET 757/day, 1.9% err
GET 139/day, 0.0% err
packages/backend/src/services/integrations/AwsConnectionService.ts 8 AwsConnectionService.checkDue 8/day, 0.0% err
Telemetry this change adds and removes (1)
  • ➕ span name makeAwsOidcSigner · packages/backend/src/services/integrations/aws/oidc.ts:49
What was checked
  • ThustsError order: throttle, assume_denied, then InvalidIdentityToken as maple_identity (api-impl.ts:73-81)
  • The audience case still matches the regex through Incorrect token audience (fake-aws.ts:222)
  • checkRole reads credentials through StsAnswer, so a changed STS shape fails as upstream
Observability coverage: 0 of 1 changes observable
Change Kind Observable Evidence
STS AssumeRoleWithWebIdentity call over raw HttpClient outbound call no api-impl.ts:127-137 executes on FetchHttpClient.layer with no OTel instrumentation; no Client span or peer.service

ed14019 · Updated on every push. Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.

@Makisuo
Makisuo deployed to pr-preview October 22, 2026 23:22 — with GitHub Actions Active
@Makisuo
Makisuo merged commit 137e60c into main Oct 22, 2026
31 checks passed
@Makisuo
Makisuo deleted the feat/aws-oidc branch October 22, 2026 23:41
@Makisuo
Makisuo deployed to pr-preview October 22, 2026 23:41 — with GitHub Actions Active
@maple-review-bot

Copy link
Copy Markdown

Merged with 5 steps from "Before merge" still open. If they are done, tick them on the review comment; if not, now is the time.

  • Infra · Check every environment has what this config now expects
  • Manual · Post the preview result the description still lists as pending: a real connection check against STS in a real account
  • Migration · Check it is safe on production data and with the code still deployed
  • Migration · Check it is safe on production data and with the code still deployed
  • Secret · Add secret MAPLE_AWS_OIDC_SIGNING_KEY to every environment

@maple-review-bot

Copy link
Copy Markdown

Maple: after this shipped

⚠️ Something changed after this shipped. Details below.

Deployed to maple-api (production) at 2026-10-22 23:55 UTC as 137e60c. Compared the 60 minutes before and after.

New errors since the deploy:

  • @maple/backend/ingest/IngestSendError (alerting, 5/h)
  • @maple/mcp/errors/McpQueryError (maple-ai, 3/h)

Errors in the changed files:

  • ➖ @maple/backend/integrations/RailwayApiError: 0/h → 0/h
  • ➖ @maple/backend/integrations/RailwayApiError: 0/h → 0/h
  • ➖ @maple/backend/integrations/AwsApiError: 0/h → 0/h
  • ➖ @maple/backend/integrations/RailwayApiError: 0/h → 0/h
  • 🔁 @maple/backend/integrations/AwsApiError: 3/h → 4/h, still occurring
Operation Calls/h Errors p95
maple 2.6k → 2.5k 83.8% → 84.2% 104 → 105 ms
POST /mcp 336 → 411 0.0% → 0.0% 501 → 480 ms
POST /mcp 336 → 411 0.0% → 0.0% 1896 → 2221 ms
GET 14 → 16 0.0% → 0.0% 3705 → 269 ms
GET /mcp 5 → 23 0.0% → 0.0% 106 → 159 ms
GET /mcp 5 → 23 0.0% → 0.0% 1056 → 1807 ms
GET 11 → 14 0.0% → 0.0% 782 → 207 ms
AwsConnectionService.checkDue 2 → 4 0.0% → 0.0% 167 → 645 ms
POST /mcp 0 → 0 0.0% → 0.0% 0 → 0 ms

Maple looks once, an hour after the first deploy that carries the merge.

This branch was successfully deployed

1 active deployment
pr-preview — ed140199 Deployed Oct 22, 2026 by Makisuo via deploy-pr-preview #6422
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

preview Deploy a full PR preview stack (Cloudflare + AWS ingest); removing it tears it down

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants