Skip to content

feat(identity): add identity scaffolding and CRUDL operations for api key - #1811

Merged
nborges-aws merged 3 commits into
refactorfrom
identity-scaffolding
Jul 23, 2026
Merged

feat(identity): add identity scaffolding and CRUDL operations for api key#1811
nborges-aws merged 3 commits into
refactorfrom
identity-scaffolding

Conversation

@nborges-aws

@nborges-aws nborges-aws commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Description

Adds identity scaffolding, as well as command-line CRUDL for Identity API key credential providers:

  • identity api-key-credential-provider create
  • identity api-key-credential-provider get
  • identity api-key-credential-provider list
  • identity api-key-credential-provider update
  • identity api-key-credential-provider delete

Follows the structure established by harness and runtime for commands. Create/update accept --name and --api-key, get/delete accept just name, list accepts --max-results and --next-token. Similar to runtime, identity will appear in the TUI command menu at the root, but prints help pending TUI routing.

Additional change to move generic default help handler created for runtime to src/handlers/help.tsx to avoid duplication. This PR updates runtime call sites to point to new help handler.

Testing

Tested via fixture tests against live AWS resources, as well as manual dogfooding via running the build artifact in CLI.

Related Issue

N/A

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation update
  • Other (please describe):

Testing

Verified from current HEAD:

  • bun test (274 pass, 0 fail)
  • bun test src/handlers/identity src/handlers/runtime src/core (39 passed, 0 failed)
  • bun run lint:check
  • bun run format:check (prettier --check)
  • bun run typecheck (clean in src/)
  • bun run build

Additional verification:

  • All five CRUDL commands verified against live AWS (us-west-2)
  • Manual dogfooding via built CLI artifact (bun dist/index.js identity api-key-credential-provider {create,get,list,update,delete})
  • Help output confirmed for bare identity and identity api-key-credential-provider groups

The committed identity fixtures were recorded with RECORD=1 against a live account in us-west-2. The RECORD run creates two providers (agentcore-cli-identity-fixture, agentcore-cli-identity-fixture-2), exercises pagination, then deletes both. The fixture set contains no credential material (API key values are not returned by the service).

Checklist

  • I have read the CONTRIBUTING document
  • I have added any necessary tests that prove my fix is effective or my feature works
  • I have updated the documentation accordingly
  • I have added an appropriate example to the documentation to outline the feature, or no new docs are needed
  • My changes generate no new warnings
  • Any dependent changes have been merged and published

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the
terms of your choice.

@github-actions github-actions Bot added agentcore-harness-reviewing AgentCore Harness review in progress and removed agentcore-harness-reviewing AgentCore Harness review in progress labels Jul 22, 2026
@codecov-commenter

codecov-commenter commented Jul 22, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.11%. Comparing base (56f62e7) to head (33ef1b7).

Additional details and impacted files
@@             Coverage Diff              @@
##           refactor    #1811      +/-   ##
============================================
+ Coverage     93.94%   94.11%   +0.16%     
============================================
  Files           122      130       +8     
  Lines          6462     6648     +186     
============================================
+ Hits           6071     6257     +186     
  Misses          391      391              

☔ 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.

@@ -0,0 +1,8 @@
{
"apiKeySecretArn": {
"secretArn": "arn:aws:secretsmanager:us-west-2:314146320088:secret:bedrock-agentcore-identity!default/apikey/agentcore-cli-identity-fixture-2-7fd24230-KEL3mK"

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.

Could these be regenerated using the E2E account. 685197708687

@aidandaly24 aidandaly24 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.

A few comments, but it looks good to me and matches what I did for Runtime well. One small thing also is we probably want to update the README.md.

description: "create an API key credential provider",
flags: [
flag("name", "the name of the API key credential provider", z.string().optional()),
flag("api-key", "the API key value", z.string().optional()),

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.

Adding --api-key puts the plaintext credential into the generic flags object. The root withLogging middleware currently logs all flags at DEBUG, and production file logging is always configured at DEBUG, so create/update will persist the key under ~/.agentcore/logs.

We should probably mark this flag as sensitive and update withLogging to redact sensitive flag values before binding { flags, args }. We could cover this generically in withLogging.test.ts, verifying that sensitive values are redacted while ordinary flags remain visible.

The same thing should effect update as well.

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 call. I'll make the change to redact sensitive values in a separate PR to keep separation of concerns. I'll rebase the change into this PR after its merged and update the api-key flag.

description: "create an API key credential provider",
flags: [
flag("name", "the name of the API key credential provider", z.string().optional()),
flag("api-key", "the API key value", z.string().optional()),

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.

In the doc I had wrote, the Identity contract supports managed keys through source-aware --api-key values (inline, file://path, or -) and external secrets through a structured-reference flag containing the secret ID and JSON key. Could we support both mutually exclusive input forms here?

I sent you the doc line over slack. This could also be a follow up because I think this PR is well structured where it is now. Also tagging.

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.

Thanks for the context! I'll do this in a follow-up PR

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.

Yeah, we'll have to accept the key via stdin so that it doesn't get into the user's shell history.

@aidandaly24 aidandaly24 Jul 23, 2026

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.

Currently working on the Runtime invoke, and I have to do something similar with the keys for the bearer token. I am currently using this function which I have within the Runtime handlers request.ts, but it should eventually be a util we use:

async function readSource(
  source: string,
  input?: NodeJS.ReadStream,
  signal?: AbortSignal,
): Promise<Uint8Array> {
  signal?.throwIfAborted();
  if (source.startsWith("file://")) {
    try {
      return await readFile(source.slice("file://".length), { signal });
    } catch (error) {
      if ((error as Error)?.name === "AbortError") throw error;
      throw new UsageError("Unable to read request source file");
    }
  }
  if (source !== "-") return new TextEncoder().encode(source);
  if (!input) throw new UsageError("stdin is not available for this request source");

  const stream = signal ? addAbortSignal(signal, input) : input;
  return buffer(stream);
}

and it's used like so:

export async function resolveRuntimeInvokeSources(
  sources: { payload: string; bearerToken?: string },
  stdin?: NodeJS.ReadStream,
  signal?: AbortSignal,
): Promise<{ payload: Uint8Array; bearerToken?: string }> {
  if (sources.payload === "-" && sources.bearerToken === "-") {
    throw new UsageError("Payload and bearer token cannot both read from stdin");
  }

  const payload = await readSource(sources.payload, stdin, signal);
  if (sources.bearerToken === undefined) return { payload };
  const token = await readSource(sources.bearerToken, stdin, signal);
  return { payload, bearerToken: new TextDecoder().decode(token) };
}

Comment thread src/handlers/identity/identity.test.tsx Outdated
const FIXTURES = join(import.meta.dir, "__fixtures__");

// Record with AWS_PROFILE=YOUR_PROFILE RECORD=1 bun test src/handlers/identity/identity.test.tsx
// The test account must have two providers pre-created for pagination tests.

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 believe RECORD=1 creates both fixed provider names in the tests below, so pre-creating them would cause those create calls to fail with ConflictException. I think we should instead state that neither provider should exist before recording, or clean up stale providers before the create tests.

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 call, never updated comment after I changed test to create both providers

@AlexanderRichey AlexanderRichey 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.

Looks good! Let's merge this in but follow up with the fix to move the api key input to stdin right away.

description: "create an API key credential provider",
flags: [
flag("name", "the name of the API key credential provider", z.string().optional()),
flag("api-key", "the API key value", z.string().optional()),

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.

Yeah, we'll have to accept the key via stdin so that it doesn't get into the user's shell history.

import { createUpdateApiKeyCredentialProviderHandler } from "./update";

export function createApiKeyCredentialProviderHandler(core: Core, io: AppIO): Router {
return new Router("api-key-credential-provider", "manage API key credential providers")

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 wonder if we could shorten this to api-key. api-key-credential-provider is so looooong.

@nborges-aws
nborges-aws merged commit 3bf082c into refactor Jul 23, 2026
8 checks passed
@nborges-aws
nborges-aws deleted the identity-scaffolding branch July 23, 2026 18:52
This was referenced Sep 3, 2026
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.

4 participants