| 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.js
agent-health.config.mjs
- Built-in defaults — only merged when
extends: false is NOT set; setting extends: false in 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 (add observability block 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:
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_PROFILE is 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
awsProfile field — 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",
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=Bedrock for the server (judge uses default chain)
- Set
"awsProfile": "default" in agent-health.config.json for OpenSearch (explicit override via fromNodeProviderChain({ 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
AWS_REGION=us-west-2
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 —
awsProfile in JSON config overrides ambient AWS_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-telemetry writes JSON — it's the only automated way to populate credentials
extends: false opts out of defaults — without this flag, built-in defaults are always merged into your config; with it, you take full control of every value