Skip to content

feat(cloud): AWS multi-account active-target wiring and docs#68

Merged
sourcehawk merged 5 commits into
feature/cloud-context-mcpfrom
feature/cloud-context-mcp--target-wiring
May 31, 2026
Merged

feat(cloud): AWS multi-account active-target wiring and docs#68
sourcehawk merged 5 commits into
feature/cloud-context-mcpfrom
feature/cloud-context-mcp--target-wiring

Conversation

@sourcehawk
Copy link
Copy Markdown
Owner

Description

Towards #44

PR B of the cloud active-target-selection feature: the provider-side and launcher-side wiring that lets the operator agent investigate across more than one AWS account from a single cloud source. PR A landed the provider-agnostic core (the set_active_target tool, the server's active-target state, the Provider interface). This PR realizes that core for AWS — a deployment-pinned accounts list, triagent-generated assume-role profiles, inventory that reflects the configured accounts, and the env wiring threading it all from the profile through both the serve subprocess and the launcher-side identity probe — and documents the whole multi-account/active-target model. GCP's active-target methods shipped final in PR A and are unchanged.

Changes

  • AWS multi-account profile config. A cloud source gains source_profile (the operator's SSO base) and an accounts list of {account_id, role_arn}. Validation requires source_profile + at least one account with source-unique, non-empty ids and role_arns when accounts is set; the single-assumed_identity form stays valid and is mutually exclusive with accounts.
  • Generated assume-role profiles. aws.New writes one read-only profile per account into a delimited, idempotent managed block (# BEGIN/END triagent-cloud-<alias>) in ~/.aws/config (or $AWS_CONFIG_FILE), tmp-file-then-rename, layering each role_arn over source_profile. Operator-authored profiles and other aliases' blocks survive. triagent holds no credential.
  • Configured targets + active-target env. The AWS provider surfaces its accounts as the agent's selectable ConfiguredTargets; ActiveTargetEnv pins AWS_PROFILE to each account's generated profile name.
  • Inventory honesty. With a configured accounts list, Inventory returns exactly those accounts and shells nothing — no more org-wide organizations list-accounts over-advertising accounts the role cannot enter. The single-account form keeps the org-list + caller-account fallback.
  • Launcher wiring. New cloud.EnvAWSAccounts (JSON), EnvAWSSourceProfile, and EnvAWSAlias; cloudSourceEnv emits them for a multi-account source; runCloud decodes them and builds the provider through the factory. The launcher-side probe authenticates as the default (first) account's generated profile.
  • Docs. docs/content/cloud-providers.md documents set_active_target, the multi-account AWS config with the generated-profiles model, the GCP-one-identity-many-projects vs AWS-one-account-per-role contrast, and the run_cli-requires-an-active-target rule.

Challenges

The plan under-specified the launcher-side path, and getting it right drove three interface changes beyond the plan:

  • Profiles must exist before any probe, including the launcher-side one. providers.ProbeSource (preflight/connections) constructs the AWS provider and runs the whoami under AWS_PROFILE, so the generated block must exist for both the serve subprocess and the launcher. Resolved by generating at aws.New (idempotent block-replace, safe from both paths) and threading accounts/source_profile/alias through providers.New (new variadic Options) and providers.Source (new Alias/SourceProfile/Accounts).
  • Which account the launcher probe targets. The default (first) configured account, via the exported aws.ProfileName. Per-account validity in the connections panel is out of scope for v1 (per the spec); the panel reflects the source's default-target validity.
  • EnvAWSAlias + JSON wire shape. Serve and the launcher must namespace generated profiles identically, so the alias is carried explicitly; profile.CloudAccount gained snake_case json tags to pin the accounts env wire format.

Testing

TDD throughout, one logical commit per task (B2–B6). New coverage: profile parse/validate for the accounts form (positive + duplicate-id/role_arn, missing source_profile, empty fields, mutual exclusion); the profile generator (block contents, idempotency, foreign-content preservation, two-alias coexistence); provider configured-targets and active-target env (multi- + single-account); inventory-uses-configured-accounts with a run func that fails the test if shelled; cloudSourceEnv accounts/source_profile emission and gcp-carries-none; the factory New("aws", Options{...}) path; credentialEnv targeting the default account's generated profile; serve's parseAWSAccounts decode + malformed-fails-closed.

Full gate green from the worktree root: make test-go (race-clean), make lint (0 issues), make build (both binaries + embedded bundle), make docs (cloud-providers page builds). Docs went through the writing-docs fresh-reader loop (multi-account operator + runtime-model personas). No frontend/ changes.

🤖 Generated with Claude Code

sourcehawk and others added 5 commits May 31, 2026 05:15
A multi-account aws cloud source carries a source_profile (the operator's SSO
base) and an accounts list (one read-only role per account). Validation requires
source_profile and at least one account with non-empty, source-unique account
ids and role_arns when accounts is set; the single-assumed_identity profile form
stays valid and is mutually exclusive with accounts.

Towards #44

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…et env

aws.New takes the source alias, source_profile, and account set. ConfiguredTargets
surfaces the accounts as the agent's selectable targets; ActiveTargetEnv pins
AWS_PROFILE to each account's generated profile name.

profiles.go writes a delimited, idempotent managed block per alias into
~/.aws/config (or $AWS_CONFIG_FILE), one assume-role profile per account layering
its role_arn over the operator's source_profile. The write is tmp-file-then-rename
and replaces only the alias's own block, so operator-authored profiles and other
aliases survive. New generates the block at construction, so the profiles exist
before any probe or run_cli on both the serve subprocess and launcher-side paths.

Towards #44

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…hole org

When the source carries a configured accounts list, Inventory returns exactly
those accounts as the reachable set and shells nothing — each account is its own
read-only role, so an org-wide list-accounts would advertise accounts run_cli
cannot enter. The single-account form keeps the organizations list-accounts +
caller-account fallback.

Towards #44

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…config

Adds cloud.EnvAWSAccounts (JSON), cloud.EnvAWSSourceProfile, and cloud.EnvAWSAlias.
cloudSourceEnv emits them for a multi-account aws source (and no static AWS_PROFILE,
since the server pins it per-exec from the active target); the single-account form
is unchanged. runCloud decodes them and builds the provider through the factory.

The factory (providers.New) and ProbeSource gain an Options/Source path carrying
the alias, source_profile, and accounts, so the launcher-side probe builds the aws
provider with its profile map — generating the same ~/.aws/config block the serve
subprocess does, before any whoami. The launcher probe targets the default (first)
account's generated profile; per-account validity is out of scope for v1.

Interface changes beyond the plan: providers.New gained a variadic Options arg and
providers.Source gained Alias/SourceProfile/Accounts, both required so the
launcher-side provider has the profile map the plan called out as under-specified;
cloud.EnvAWSAlias was added so serve and the launcher namespace generated profiles
identically; profile.CloudAccount gained snake_case json tags to fix the env wire
shape.

Towards #44

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds the set_active_target tool, the AWS accounts + source_profile multi-account
config (with a generated-profiles explanation and example), the GCP-one-identity-
many-projects vs AWS-one-account-per-role model, and the run_cli-requires-an-active-
target rule. Reconciles the pinned-identity, scope-by-omission, and cloud-block
sections with the new bounded-selection behavior.

Towards #44

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@sourcehawk sourcehawk merged commit 2c01ab1 into feature/cloud-context-mcp May 31, 2026
4 checks passed
@sourcehawk sourcehawk deleted the feature/cloud-context-mcp--target-wiring branch May 31, 2026 03:38
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