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

@notgitika notgitika 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-actions github-actions Bot added the size/l PR size: L label Aug 27, 2026
@agentcore-devx-automation agentcore-devx-automation Bot added the agentcore-harness-reviewing AgentCore Harness review in progress label Aug 27, 2026

@agentcore-devx-automation agentcore-devx-automation Bot 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-automation agentcore-devx-automation Bot removed the agentcore-harness-reviewing AgentCore Harness review in progress label Aug 27, 2026
@notgitika
notgitika force-pushed the feat/deploy-credential-providers branch from 4b787a8 to 0db4266 Compare August 27, 2026 15:43
@github-actions github-actions Bot 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 refactor August 27, 2026 16:15
@github-actions github-actions Bot 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
notgitika force-pushed the feat/deploy-credential-providers branch from 0db4266 to 3c09c23 Compare August 27, 2026 16:28
@github-actions github-actions Bot added size/l PR size: L and removed size/xl PR size: XL size/l PR size: L labels Aug 27, 2026
@agentcore-devx-automation agentcore-devx-automation Bot 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 lines Patch % Lines
src/core/project/backends/cdk/credentials.ts 97.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-automation agentcore-devx-automation Bot 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-actions github-actions Bot 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-actions github-actions Bot added size/l PR size: L and removed size/l PR size: L labels Aug 27, 2026
@agentcore-devx-automation agentcore-devx-automation Bot 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-automation agentcore-devx-automation Bot 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-actions github-actions Bot added size/xl PR size: XL and removed size/xl PR size: XL labels Aug 27, 2026
@agentcore-devx-automation agentcore-devx-automation Bot 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 agentcore-devx-automation Bot 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
Contributor Author

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-actions github-actions Bot added size/xl PR size: XL and removed size/xl PR size: XL labels Aug 28, 2026
@agentcore-devx-automation agentcore-devx-automation Bot 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-automation agentcore-devx-automation Bot 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-actions github-actions Bot added size/xl PR size: XL and removed size/xl PR size: XL labels Aug 28, 2026
@agentcore-devx-automation agentcore-devx-automation Bot 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-automation agentcore-devx-automation Bot 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-actions github-actions Bot added size/xl PR size: XL and removed size/xl PR size: XL labels Aug 28, 2026
@agentcore-devx-automation agentcore-devx-automation Bot 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-automation agentcore-devx-automation Bot 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 free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/xl PR size: XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants