feat(cloud): AWS multi-account active-target wiring and docs#68
Merged
sourcehawk merged 5 commits intoMay 31, 2026
Merged
Conversation
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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_targettool, the server's active-target state, theProviderinterface). This PR realizes that core for AWS — a deployment-pinnedaccountslist, 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
source_profile(the operator's SSO base) and anaccountslist of{account_id, role_arn}. Validation requiressource_profile+ at least one account with source-unique, non-empty ids and role_arns whenaccountsis set; the single-assumed_identityform stays valid and is mutually exclusive withaccounts.aws.Newwrites 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 eachrole_arnoversource_profile. Operator-authored profiles and other aliases' blocks survive. triagent holds no credential.ConfiguredTargets;ActiveTargetEnvpinsAWS_PROFILEto each account's generated profile name.accountslist,Inventoryreturns exactly those accounts and shells nothing — no more org-wideorganizations list-accountsover-advertising accounts the role cannot enter. The single-account form keeps the org-list + caller-account fallback.cloud.EnvAWSAccounts(JSON),EnvAWSSourceProfile, andEnvAWSAlias;cloudSourceEnvemits them for a multi-account source;runClouddecodes them and builds the provider through the factory. The launcher-side probe authenticates as the default (first) account's generated profile.docs/content/cloud-providers.mddocumentsset_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:
providers.ProbeSource(preflight/connections) constructs the AWS provider and runs the whoami underAWS_PROFILE, so the generated block must exist for both the serve subprocess and the launcher. Resolved by generating ataws.New(idempotent block-replace, safe from both paths) and threading accounts/source_profile/alias throughproviders.New(new variadicOptions) andproviders.Source(newAlias/SourceProfile/Accounts).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.CloudAccountgained 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;
cloudSourceEnvaccounts/source_profile emission and gcp-carries-none; the factoryNew("aws", Options{...})path;credentialEnvtargeting the default account's generated profile; serve'sparseAWSAccountsdecode + 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). Nofrontend/changes.🤖 Generated with Claude Code