Skip to content

feat: create credential providers before synthesizing a deploy - #2123

Open
notgitika wants to merge 9 commits into
refactorfrom
feat/deploy-credential-providers
Open

feat: create credential providers before synthesizing a deploy#2123
notgitika wants to merge 9 commits into
refactorfrom
feat/deploy-credential-providers

Conversation

@notgitika

@notgitikanotgitika commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Creates a project's credential providers before synthesis, so the synthesized CDK app can read their ARNs out of deployed-state.json and wire them into the stack. Without this, deploying a project that declares any credential fails inside cdk synth.

What it does

  • Between the account preflight and synth, CdkBackend.deploy provisions each declared credential provider: reuse-if-present, create-if-absent, never update (so a redeploy neither mints a new secret version nor overwrites one rotated outside the CLI), then records the ARNs via updateTargetState.
  • Secrets come from the same place project add credentials writes them — .env.local (AGENTCORE_CREDENTIAL_<NAME>) or a Secrets Manager secretRef. The env-var name is now derived from one shared function in envLocal.ts so add and deploy agree.
  • Payment credentials are rejected up front (agentcore.json can't express their vendor config).

Note

  • Pre-synth by necessity: the vended app reads the ARNs at synth time.

Tested e2e

Ran the full flow against a real account (us-west-2): project add credentials api-keyproject deploy. Confirmed deploy creates the API-key credential provider imperatively before synth, writes it to agentcore/.cli/deployed-state.json, then deploys the stack and merges the stackArn into the same target entry (no clobbering). Resulting file held both resources.credentials.e2ekey and stackArn.

@github-actionsgithub-actionsBot added the size/l PR size: L label Aug 27, 2026
@agentcore-devx-automationagentcore-devx-automationBot added the agentcore-harness-reviewing AgentCore Harness review in progress label Aug 27, 2026

@agentcore-devx-automationagentcore-devx-automationBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

AgentCore Harness Review

Verdict: Looks good

Nice PR. The design of running credential provisioning before cdk synth and threading the ARNs through deployed-state.json is well-motivated and the comments do a great job of capturing why. The seam boundary (IdentityProviderClient) is drawn at the SDK client rather than at fs/process boundaries, so the tests avoid excessive mocking while still exercising the real spec + .env.local parsing paths. Sharing credentialEnvVarName/CLIENT_SECRET_SUFFIX between add and deploy via envLocal.ts (with a re-export from shared.ts) removes a latent format-drift bug.

A couple of small things that aren't blockers but worth confirming intentional:

  • Stale credential entries when the spec goes to zero credentials. In src/core/project/backends/cdk.ts (~L115) updateTargetState is only called when Object.keys(provisioned).length > 0. If a user deletes their last credential from agentcore.json and re-deploys, provisioned is {}, the state write is skipped, and the previous resources.credentials map is left on disk. The doc-comment on updateTargetState promises "A resource map provided in the patch replaces the previous map for that kind wholesale, so a credential dropped from the spec stops being advertised" — that guarantee is only actually delivered when at least one credential remains. Since the synthesized CDK app looks up credentials by name, this is likely inert in practice, but if you want the drop-to-zero case to behave the same as drop-one-of-many, you'd either always call updateTargetState({ resources: { credentials: provisioned } }) or explicitly write {} when declared is non-empty on the spec side but you provisioned nothing.

  • parseEnv cast in EnvLocalFile.read (src/core/project/envLocal.ts L90): parseEnv's declared return type is Record<string, string | undefined> (last-write-wins across duplicate keys), but you cast to Record<string, string>. All callers happen to use if (!value) so undefined is handled safely today; just be aware the type is a small lie and a future caller doing env[k].trim() would compile but crash.

Neither of these needs to block the merge.

@agentcore-devx-automationagentcore-devx-automationBot removed the agentcore-harness-reviewing AgentCore Harness review in progress label Aug 27, 2026
@notgitika
notgitikaforce-pushed the feat/deploy-credential-providers branch from 4b787a8 to 0db4266CompareAugust 27, 2026 15:43
@github-actionsgithub-actionsBot added size/l PR size: L and removed size/l PR size: L labels Aug 27, 2026
Base automatically changed from feat/deployed-state-top-level to refactorAugust 27, 2026 16:15
@github-actionsgithub-actionsBot added size/xl PR size: XL and removed size/l PR size: L labels Aug 27, 2026
The synthesized CDK app reads credential provider ARNs out of
deployed-state.json and fails to synth a project that declares credentials
until they exist. Provision them between the account preflight and the
build, then record their ARNs via updateTargetState so the assembly is
synthesized against a state file that already describes them.
Providers are created when absent and reused when present, never updated,
so a redeploy neither mints a new secret version nor overwrites one rotated
outside the CLI. Payment credentials are rejected up front (agentcore.json
can't express the vendor config they need). Secrets come from the same
place 'project add credentials' writes them, so the env-var name is now
derived from one function in envLocal.ts that both sides share.
@notgitika
notgitikaforce-pushed the feat/deploy-credential-providers branch from 0db4266 to 3c09c23CompareAugust 27, 2026 16:28
@github-actionsgithub-actionsBot added size/l PR size: L and removed size/xl PR size: XL size/l PR size: L labels Aug 27, 2026
@agentcore-devx-automationagentcore-devx-automationBot added the claude-security-reviewing Claude Code /security-review in progress label Aug 27, 2026
@codecov-commenter

codecov-commenter commented Aug 27, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.92531% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 97.29%. Comparing base (adb02a1) to head (c78add5).

Files with missing linesPatch %Lines
src/core/project/backends/cdk/credentials.ts97.73%5 Missing ⚠️
Additional details and impacted files
@@ Coverage Diff @@## refactor #2123 +/- ##
==========================================
Coverage 97.29% 97.29% ==========================================
Files 478 479 +1 Lines 29642 29880 +238 ==========================================
+ Hits 28840 29073 +233 - Misses 802 807 +5 

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@agentcore-devx-automation

Copy link
Copy Markdown
Contributor

Claude Security Review: no high-confidence findings. (run)

@agentcore-devx-automationagentcore-devx-automationBot removed the claude-security-reviewing Claude Code /security-review in progress label Aug 27, 2026
@notgitika
notgitika marked this pull request as ready for review August 27, 2026 16:38
@github-actionsgithub-actionsBot added size/l PR size: L and removed size/l PR size: L labels Aug 27, 2026
…v type
- Add SDK-mocked coverage for createIdentityProviderClient (the real Identity
factory the provisioner tests bypass): ~55% -> ~95% on credentials.ts.
- Always record the provisioned credential set, so removing the last credential
from the spec clears the stale entry instead of leaving it advertised.
- EnvLocalFile.read returns Record<string, string | undefined> (parseEnv's real
type) rather than casting it away.
- Tighten a few verbose comments.
@github-actionsgithub-actionsBot added size/l PR size: L and removed size/l PR size: L labels Aug 27, 2026
@agentcore-devx-automationagentcore-devx-automationBot added the claude-security-reviewing Claude Code /security-review in progress label Aug 27, 2026
@agentcore-devx-automation

Copy link
Copy Markdown
Contributor

Claude Security Review: no high-confidence findings. (run)

@agentcore-devx-automation

Copy link
Copy Markdown
Contributor

Claude Security Review: no high-confidence findings. (run)

@agentcore-devx-automationagentcore-devx-automationBot removed the claude-security-reviewing Claude Code /security-review in progress label Aug 27, 2026
… lint
- Keep the 'same environment variable' wording so the existing add-credentials
collision test still asserts it, and add an integration test for the
cross-type case (oauth 'foo' vs api-key 'foo_client_secret').
- Silence require-yield on a deploy-prereq spy generator that never runs.
@github-actionsgithub-actionsBot added size/xl PR size: XL and removed size/xl PR size: XL labels Aug 27, 2026
@agentcore-devx-automationagentcore-devx-automationBot added the claude-security-reviewing Claude Code /security-review in progress label Aug 27, 2026
@agentcore-devx-automation

Copy link
Copy Markdown
Contributor

Claude Security Review: no high-confidence findings. (run)

@agentcore-devx-automationagentcore-devx-automationBot removed the claude-security-reviewing Claude Code /security-review in progress label Aug 27, 2026
return {
customOauth2ProviderConfig: {
oauthDiscovery: { discoveryUrl: credential.discoveryUrl },
...(credential.clientId !== undefined && { clientId: credential.clientId }),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think older OAuth projects need a compatibility fallback here. v0.28.0 stored the client ID only in AGENTCORE_CREDENTIAL_<NAME>_CLIENT_ID, not in agentcore.json. If the provider needs to be recreated in another target/account or after deletion, this sends the discovery URL and secret without the client ID. Would it make sense to prefer credential.clientId and fall back to the legacy environment variable, with an upgrade test?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

good find, fixed!

Resolve conflicts in cdk.ts (combine credential provisioning + prereq/validate
with refactor's teardown/stack-probe deploy path) and cdk.test.ts (keep both
provisionCredentials and stackExists harness options; use the deployInput helper).
@github-actionsgithub-actionsBot added size/xl PR size: XL and removed size/xl PR size: XL labels Aug 28, 2026
@agentcore-devx-automationagentcore-devx-automationBot added the claude-security-reviewing Claude Code /security-review in progress label Aug 28, 2026
@agentcore-devx-automation

Copy link
Copy Markdown
Contributor

Claude Security Review: no high-confidence findings. (run)

@agentcore-devx-automationagentcore-devx-automationBot removed the claude-security-reviewing Claude Code /security-review in progress label Aug 28, 2026
Older CLIs stored an OAuth credential's client id in
AGENTCORE_CREDENTIAL_<NAME>_CLIENT_ID rather than agentcore.json. When
recreating such a provider, prefer credential.clientId and fall back to
that legacy variable so an upgraded project keeps its client id. Adds an
upgrade test.
@github-actionsgithub-actionsBot added size/xl PR size: XL and removed size/xl PR size: XL labels Aug 28, 2026
@agentcore-devx-automationagentcore-devx-automationBot added the claude-security-reviewing Claude Code /security-review in progress label Aug 28, 2026
@agentcore-devx-automation

Copy link
Copy Markdown
Contributor

Claude Security Review: no high-confidence findings. (run)

@agentcore-devx-automationagentcore-devx-automationBot removed the claude-security-reviewing Claude Code /security-review in progress label Aug 28, 2026
Refactor independently added the credential env-var collision check
(credentialEnvironmentVariableNames) and moved credentialEnvVarName into
projectSchemas/credential. Adopt those: drop the duplicate collision helper
and naming from shared.ts/envLocal.ts, source credentialEnvVarName from the
schema in the provisioner, and inline the _CLIENT_SECRET/_CLIENT_ID suffixes.
Keep the provisioner, EnvLocalFile.read(), and the legacy _CLIENT_ID clientId
fallback.
@github-actionsgithub-actionsBot added size/xl PR size: XL and removed size/xl PR size: XL labels Aug 28, 2026
@agentcore-devx-automationagentcore-devx-automationBot added the claude-security-reviewing Claude Code /security-review in progress label Aug 28, 2026
@agentcore-devx-automation

Copy link
Copy Markdown
Contributor

Claude Security Review: no high-confidence findings. (run)

@agentcore-devx-automationagentcore-devx-automationBot removed the claude-security-reviewing Claude Code /security-review in progress label Aug 28, 2026
? { name: credential.name, secretRef: credential.secretRef }
: {
name: credential.name,
apiKey: requireEnvSecret(credential.name, env, rootPath, "secretRef"),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think we already have all the code we need to do this in the existing core package. Let's reuse that. We can discuss strategies for making that code reusable if it's not clear.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/xlPR size: XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@notgitika@codecov-commenter@AlexanderRichey@tejaskash@aidandaly24