name: config-auth description: Use when working on config loading, authentication, AWS profiles, or data source resolution. Explains the two config systems (TS vs JSON), auth priority, and multi-profile setup.
Config & Auth Architecture
Agent Health uses two separate config systems for different purposes. Understanding when each applies prevents auth confusion when users have multiple AWS profiles.
Two Config Systems
| System | File | Format | Purpose | Managed By |
|---|---|---|---|---|
| Agent/Model Config | agent-health.config.ts |
TS/JS/MJS | Agents, models, connectors, hooks, judge | User edits manually |
| Data Source Config | agent-health.config.json |
JSON only | OpenSearch credentials (storage + observability) | CLI setup-telemetry / Settings UI |
Priority for TS/JS config loading (lib/config/loader.ts):
agent-health.config.ts(highest)agent-health.config.jsagent-health.config.mjs- Built-in defaults — only merged when
extends: falseis NOT set; settingextends: falsein your config disables default merging entirely
Why two systems?
- TS config can contain executable code (hooks, dynamic values, custom connectors)
- JSON config stores credentials that the server persists and the Settings UI manages
- Browser never sees JSON credentials — the server only exposes metadata (endpoint, authType, hasPassword) to the browser; all authenticated requests are proxied server-side
Auth Resolution Per Service
OpenSearch (Storage & Observability)
Resolution order (server/middleware/dataSourceConfig.ts):
agent-health.config.json(file) — highest priority- Environment variables (
OPENSEARCH_STORAGE_*/OPENSEARCH_LOGS_*) — fallback - null — features degrade gracefully
JSON config structure:
{
"storage": {
"endpoint": "https://search-....es.amazonaws.com",
"authType": "sigv4",
"awsProfile": "default",
"awsRegion": "us-west-2",
"awsService": "es"
},
"observability": {
"endpoint": "https://search-....es.amazonaws.com",
"authType": "sigv4",
"awsProfile": "default",
"awsRegion": "us-west-2",
"awsService": "es",
"tracesIndex": "otel-v1-apm-span-*"
}
}
Observability config is where Agent Health reads OTel traces from. This is configured by:
npx @opensearch-project/agent-health setup-telemetry(writes to JSON automatically)- Manual edit of
agent-health.config.json(addobservabilityblock with endpoint + auth)
The tracesIndex field defaults to otel-v1-apm-span-* if omitted. See /instrument-otel skill for the full instrumentation flow.
Auth types (server/services/opensearchClientFactory.ts):
| authType | Fields needed | How it works |
|---|---|---|
none |
endpoint only | No auth headers |
basic |
endpoint, username, password | HTTP Basic Auth |
sigv4 |
endpoint, awsRegion, awsService, awsProfile (optional) | AWS SigV4 credential chain |
SigV4 credential resolution:
// If awsProfile specified → use that profile
// If omitted → use default AWS credential chain
const provider = fromNodeProviderChain({
...(config.awsProfile && { profile: config.awsProfile })
});
Bedrock Judge (LLM Evaluation)
Resolution (server/services/bedrockService.ts):
- Uses AWS SDK default credential chain — picks up whatever
AWS_PROFILEis set in the server's environment - Region:
process.env.AWS_REGION || 'us-west-2' - Model:
process.env.BEDROCK_MODEL_ID || 'us.anthropic.claude-sonnet-4-5-20250929-v1:0' - No explicit
awsProfilefield — relies on environment
Claude Code Agent (Subprocess)
Resolution (agent-health.config.ts → agent connectorConfig.env):
{
key: "claude-code",
connectorConfig: {
env: {
CLAUDE_CODE_USE_BEDROCK: "1",
AWS_PROFILE: "Bedrock", // ← explicit profile for Claude subprocess
AWS_REGION: "us-west-2",
},
},
}
These env vars are set inside the spawned Claude Code process — isolated from the server's own credentials.
Multi-Profile Setup Pattern
A user with separate profiles for separate concerns:
| Purpose | AWS Profile | Account | Used By |
|---|---|---|---|
| OpenSearch reads | default |
651304888251 | Server → agent-health.config.json (awsProfile: "default") |
| Bedrock judge | Bedrock |
070785328308 | Server env: AWS_PROFILE=Bedrock |
| Claude Code agent | Bedrock |
070785328308 | TS config: connectorConfig.env.AWS_PROFILE |
| Telemetry sending | (none needed) | — | API Gateway is public (no SigV4 on client) |
Problem: The server process can only have ONE AWS_PROFILE env var. If Bedrock judge needs profile "Bedrock" but OpenSearch needs profile "default", you must:
- Set
AWS_PROFILE=Bedrockfor the server (judge uses default chain) - Set
"awsProfile": "default"inagent-health.config.jsonfor OpenSearch (explicit override viafromNodeProviderChain({ profile }))
This works because OpenSearch SigV4 uses explicit profile from config, while Bedrock uses ambient credentials from environment.
Key Files
| File | Purpose |
|---|---|
lib/config/loader.ts |
Load + merge TS/JS config with defaults |
lib/config/types.ts |
AgentHealthConfig, UserAgentConfig, UserModelConfig |
server/middleware/dataSourceConfig.ts |
Resolve OpenSearch config (file → env → null) |
server/services/configService.ts |
Read/write agent-health.config.json |
server/services/opensearchClientFactory.ts |
Build OpenSearch client with correct auth |
server/services/bedrockService.ts |
Bedrock client init (uses ambient AWS creds) |
server/config/index.ts |
Server environment variable defaults |
agent-health.config.example.ts |
Reference TS config |
Environment Variables Reference
OpenSearch Storage (fallback when not in JSON):
OPENSEARCH_STORAGE_ENDPOINT=https://...
OPENSEARCH_STORAGE_AUTH_TYPE=sigv4|basic|none
OPENSEARCH_STORAGE_USERNAME=admin
OPENSEARCH_STORAGE_PASSWORD=secret
OPENSEARCH_STORAGE_AWS_REGION=us-west-2
OPENSEARCH_STORAGE_AWS_PROFILE=default
OPENSEARCH_STORAGE_AWS_SERVICE=es|aoss
OpenSearch Observability (fallback when not in JSON):
OPENSEARCH_LOGS_ENDPOINT=https://...
OPENSEARCH_LOGS_AUTH_TYPE=sigv4|basic|none
OPENSEARCH_LOGS_AWS_REGION=us-west-2
OPENSEARCH_LOGS_AWS_PROFILE=default
OPENSEARCH_LOGS_AWS_SERVICE=es|aoss
Bedrock Judge:
AWS_PROFILE=Bedrock # Profile with Bedrock access
AWS_REGION=us-west-2 # Bedrock region
BEDROCK_MODEL_ID=us.anthropic.claude-sonnet-4-5-20250929-v1:0
Telemetry (Client-side, in shell RC):
CLAUDE_CODE_ENABLE_TELEMETRY=1
OTEL_EXPORTER_OTLP_ENDPOINT=https://<api-gw>.execute-api.<region>.amazonaws.com
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
OTEL_TRACES_EXPORTER=otlp
OTEL_LOGS_EXPORTER=otlp
OTEL_METRICS_EXPORTER=otlp
Design Principles
- JSON for credentials, TS for behavior — never put secrets in TS config (it's committed to git)
- Explicit profile wins —
awsProfilein JSON config overrides ambientAWS_PROFILE - Each service resolves independently — OpenSearch can use profile A while Bedrock uses profile B
- Degrade gracefully — missing config means features are unavailable, not errors
- Browser never sees secrets — the server proxies all authenticated requests; the browser only receives safe metadata (endpoint URL, authType, hasPassword boolean), never raw credentials
setup-telemetrywrites JSON — it's the only automated way to populate credentialsextends: falseopts out of defaults — without this flag, built-in defaults are always merged into your config; with it, you take full control of every value