blue/.blue/docs/rfcs/0032-per-repo-aws-profile-configuration.draft.md
Eric Garcia 02901dfec7 chore: batch commit - ADRs, RFCs, dialogues, spikes, and code updates
ADRs:
- Update 0008-honor, 0009-courage, 0013-overflow, 0015-plausibility
- Add 0017-hosted-coding-assistant-architecture

RFCs:
- 0032: per-repo AWS profile configuration (draft)
- 0033: round-scoped dialogue files (impl + plan)
- 0034: comprehensive config architecture (accepted)
- 0036: expert output discipline (impl)
- 0037: single source protocol authority (draft)
- 0038: SDLC workflow discipline (draft)
- 0039: ADR architecture greenfield clarifications (impl)
- 0040: divorce financial analysis (draft)
- 0042: alignment dialogue defensive publication (draft)

Spikes:
- Read tool token limit on assembled dialogues
- RFC ID collision root cause
- Expert agent output too long
- Judge writes expert outputs
- Blue MCP server on superviber infrastructure
- Playwright MCP multiple window isolation

Dialogues: 16 alignment dialogue records

Code:
- blue-core: forge module enhancements
- blue-mcp: env handlers and server updates
- alignment-expert agent improvements
- alignment-play skill refinements
- install.sh script

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 16:28:31 -05:00

5 KiB

RFC 0032: Per-Repo AWS Profile Configuration

Status Draft
Date 2026-01-26
Dialogue aws-profile-config.dialogue.md

Summary

Blue needs a way to configure per-repo AWS profiles in .blue/config.yaml so that all AWS operations during a Claude Code session use the correct credentials. Different repos require different AWS profiles (e.g., f-i-a uses cultivarium, hearth/aperture use muffinlabs).

Problem

When working across multiple repositories that deploy to different AWS accounts, developers and Claude Code sessions need to use the correct AWS profile for each repo. Currently:

  1. No mechanism exists to declare which AWS profile a repo expects
  2. Claude's bash commands inherit whatever AWS_PROFILE is set in the user's shell
  3. Worktree isolation (blue_env_mock) doesn't inject AWS profile settings
  4. First-run discovery is poor—new contributors don't know which profile to use

Design

Two-Layer Architecture

Layer Scope Implementation
Layer 1: Blue MCP Bash commands, Blue tools std::env::set_var("AWS_PROFILE", ...) in ensure_state()
Layer 2: Worktrees Isolated environments blue_env_mock writes AWS_PROFILE to .env.isolated
Layer 3: AWS MCP External server User responsibility (documented limitation)

Config Schema

Add aws section to .blue/config.yaml, parallel to existing forge:

forge:
  type: github
  host: ...
  owner: superviber
  repo: blue

aws:
  profile: cultivarium  # AWS profile name from ~/.aws/config

Precedence Rules

  1. Shell AWS_PROFILE always wins (standard AWS CLI behavior)
  2. Config provides repo default for Blue's process and .env.isolated
  3. CI/CD sets its own AWS_PROFILE externally

Rust Implementation

1. Add AwsConfig to BlueConfig

File: crates/blue-core/src/forge/mod.rs

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AwsConfig {
    pub profile: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BlueConfig {
    pub forge: Option<ForgeConfig>,
    pub aws: Option<AwsConfig>,  // NEW
}

2. Inject at MCP Server Startup

File: crates/blue-mcp/src/server.rs in ensure_state()

// After loading config, inject AWS_PROFILE into process environment
if let Some(aws) = &state.config.aws {
    // Only set if not already present (respect shell override)
    if std::env::var("AWS_PROFILE").is_err() {
        std::env::set_var("AWS_PROFILE", &aws.profile);
        tracing::info!(profile = %aws.profile, "AWS profile set for session");
    } else {
        let shell_profile = std::env::var("AWS_PROFILE").unwrap();
        if shell_profile != aws.profile {
            tracing::info!(
                config_profile = %aws.profile,
                shell_profile = %shell_profile,
                "Shell AWS_PROFILE differs from config—using shell value"
            );
        }
    }
}

3. Inject into .env.isolated

File: crates/blue-mcp/src/handlers/env.rs in generate_env_isolated()

// Add AWS profile from config if present
if let Some(aws) = &config.aws {
    lines.push(format!("AWS_PROFILE={}", aws.profile));
}

AWS MCP Server Limitation

The AWS MCP server (mcp__aws-api__call_aws) runs as a separate process that Blue cannot control. Users must either:

  1. Set AWS_PROFILE in their shell before launching Claude Code desktop app
  2. Use --profile flags explicitly in AWS CLI calls

This limitation should be documented in Blue's setup guide.

Per-Repo Configuration

Repo Profile Config
f-i-a cultivarium aws: { profile: cultivarium }
hearth muffinlabs aws: { profile: muffinlabs }
aperture muffinlabs aws: { profile: muffinlabs }

What Blue Controls vs. Documents

Aspect Blue Controls Blue Documents
Bash aws CLI commands Yes
Blue MCP tools touching AWS Yes
.env.isolated for worktrees Yes
AWS MCP server (call_aws) No User sets shell env or --profile

Test Plan

  • Add aws section to test config, verify BlueConfig deserializes it
  • Verify ensure_state() sets AWS_PROFILE when config present
  • Verify ensure_state() respects existing shell AWS_PROFILE
  • Verify blue_env_mock includes AWS_PROFILE in .env.isolated
  • Verify startup diagnostic logs active profile
  • Verify warning when shell profile differs from config

Alternatives Considered

  1. Environment-specific profiles (aws.profiles.{dev,ci,prod}) — Rejected as over-engineering; CI should set its own profile externally.

  2. Store in .env.example — Rejected; config.yaml is the single source of truth for repo settings.

  3. Enforce profile (override shell) — Rejected; violates standard AWS CLI precedence and surprises operators.


"Right then. Let's get to it."

— Blue