paladin-ai 0.4.3

Enterprise AI orchestration framework with multi-agent coordination patterns
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
# Paladin Configuration Guide

This guide explains how Paladin's configuration system works, best practices for different environments, and the clear separation of concerns between YAML files and environment variables.

## Table of Contents

- [Configuration Philosophy]#configuration-philosophy
- [Quick Start]#quick-start
- [Configuration Sources]#configuration-sources
- [Environment Variables Reference]#environment-variables-reference
- [Environment-Specific Setup]#environment-specific-setup
- [Feature Flags]#feature-flags
- [Security Best Practices]#security-best-practices
- [Advanced Topics]#advanced-topics

## Configuration Philosophy

Paladin uses a **dual-path configuration system** with clear separation of concerns:

| What | Where | Purpose | Example |
|------|-------|---------|---------|
| **Behavioral Config** | YAML files | Define *how* the system behaves | Timeouts, model names, strategies |
| **Secrets** | Environment variables | Credentials and sensitive data | API keys, passwords |
| **Overrides** | `APP_*` env vars | Deployment-time tuning | `APP_GARRISON_MAX_ENTRIES=500` |

### Why Both?

- **YAML files** are version-controlled, reviewed in PRs, and define the system's structure
- **Environment variables** are injected at deployment time and never committed to source control
- **This separation** enables security (secrets stay out of repos), flexibility (same code works in dev/staging/prod), and auditability (config changes are tracked in git)

## Quick Start

### Development (DevContainer)

1. **Copy the example environment file:**
   ```bash
   cp .env.example .env
   ```

2. **Edit `.env` and add your API keys:**
   ```bash
   # LLM Provider API Keys (choose one or more)
   OPENAI_API_KEY=sk-your-key-here
   DEEPSEEK_API_KEY=your-deepseek-key
   ANTHROPIC_API_KEY=your-anthropic-key
   ```

3. **Load the environment file** (automatic in DevContainer):
   ```bash
   # Manual loading if needed:
   set -a
   . /workspace/.env
   set +a
   ```

4. **Run Paladin:**
   ```bash
   cargo run
   ```

The `.env` file is automatically loaded by the application in debug builds.

### CI/CD

Set secrets as environment variables in your CI system:

**GitHub Actions:**
```yaml
env:
  OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
  DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }}
```

**GitLab CI:**
```yaml
variables:
  CONFIG_FILE: "config.test.yml"
script:
  - cargo test --features live-api-tests
```

### Production

Use a secrets manager:

**AWS Secrets Manager + ECS:**
```json
"secrets": [
  {
    "name": "OPENAI_API_KEY",
    "valueFrom": "arn:aws:secretsmanager:region:account:secret:paladin/openai"
  }
]
```

**Kubernetes Secrets:**
```yaml
apiVersion: v1
kind: Secret
metadata:
  name: paladin-secrets
type: Opaque
data:
  OPENAI_API_KEY: <base64-encoded-key>
---
apiVersion: apps/v1
kind: Deployment
spec:
  template:
    spec:
      containers:
      - name: paladin
        envFrom:
        - secretRef:
            name: paladin-secrets
```

## Configuration Sources

Paladin loads configuration in this **priority order** (later sources override earlier ones):

1. **`config.yml`** (or specified via `--config` flag) - Base configuration
2. **Environment-specific file** - `config.{APP_ENV}.yml` if `APP_ENV` is set
3. **`APP_*` environment variables** - Override any YAML value
4. **Direct environment variables** - LLM API keys bypass the config system

### Example: Loading Sequence

Given this setup:

**config.yml:**
```yaml
garrison:
  garrison_type: "in_memory"
  max_entries: 100
```

**Environment:**
```bash
APP_GARRISON_MAX_ENTRIES=500
OPENAI_API_KEY=sk-real-key
```

**Result:**
- Garrison type: `in_memory` (from config.yml)
- Max entries: `500` (overridden by `APP_*` env var)
- OpenAI key: `sk-real-key` (from direct env var, never in YAML)

## Environment Variables Reference

### LLM Provider API Keys (Direct Read)

These are **NOT** in `config.yml` — adapters read them directly from the environment:

| Variable | Provider | Required When |
|----------|----------|---------------|
| `OPENAI_API_KEY` | OpenAI GPT models | Using default_provider: "openai" |
| `DEEPSEEK_API_KEY` | DeepSeek models | Using default_provider: "deepseek" |
| `ANTHROPIC_API_KEY` | Anthropic Claude | Using default_provider: "anthropic" |

### APP_* Overrides (Settings System)

Override any YAML value using the `APP_` prefix + uppercase path with underscores:

**YAML path** → **Environment variable**

```yaml
garrison:
  max_entries: 100
```
→ `APP_GARRISON_MAX_ENTRIES=500`

```yaml
llm:
  openai:
    default_model: "gpt-4"
```
→ `APP_LLM_OPENAI_DEFAULT_MODEL="gpt-4-turbo"`

### Common Overrides

#### Garrison (Memory System)
```bash
APP_GARRISON_TYPE=sqlite
APP_GARRISON_PATH=./custom_garrison.db
APP_GARRISON_MAX_ENTRIES=200
APP_GARRISON_MAX_TOKENS=8000
APP_GARRISON_TOKENIZER=gpt-4
APP_GARRISON_EVICTION_STRATEGY=fifo
APP_GARRISON_PRESERVE_RECENT_COUNT=20
```

#### Sanctum (Long-term Memory)
```bash
APP_SANCTUM_ENABLED=true
APP_SANCTUM_ADAPTER_TYPE=qdrant
APP_SANCTUM_QDRANT_URL=http://qdrant-server:6334
APP_SANCTUM_QDRANT_COLLECTION_NAME=custom_memories
APP_SANCTUM_QDRANT_VECTOR_DIMENSION=3072
```

#### Arsenal (Tool System)
```bash
APP_ARSENAL_DEFAULT_TIMEOUT_SECONDS=60
APP_ARSENAL_MAX_CONCURRENT_TOOLS=10
```

#### Citadel (State Persistence)
```bash
APP_CITADEL_ENABLED=true
APP_CITADEL_STATE_DIR=./custom-states
APP_CITADEL_AUTOSAVE_ENABLED=true
APP_CITADEL_CLEANUP_ENABLED=true
APP_CITADEL_MAX_STATE_AGE_DAYS=60
```

#### Redis Queue
```bash
APP_REDIS_HOST=redis-prod.example.com
APP_REDIS_PORT=6379
APP_REDIS_PASSWORD=secure-password
APP_REDIS_DB=2
APP_REDIS_POOL_SIZE=20
```

#### MinIO File Storage
```bash
APP_MINIO_ENDPOINT=https://s3.amazonaws.com
APP_MINIO_BUCKET=paladin-prod
APP_MINIO_ACCESS_KEY=AKIAIOSFODNN7EXAMPLE
APP_MINIO_SECRET_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
APP_MINIO_REGION=us-west-2
```

## Environment-Specific Setup

### Development (DevContainer)

**Config file:** `config.yml`

**Secrets source:** `.env` file (auto-loaded in debug builds)

**Setup:**
```bash
# 1. Copy template
cp .env.example .env

# 2. Edit .env with your keys
vim .env

# 3. The DevContainer post-start.sh loads it automatically
# Or manually in new terminals:
set -a && . /workspace/.env && set +a

# 4. Run
cargo run
```

**Benefits:**
- ✅ Fast iteration with hot-reload
- ✅ No need to export vars in every terminal
-`.env` is gitignored, so secrets stay local

### CI/CD (GitHub Actions, GitLab, etc.)

**Config file:** `config.test.yml`

**Secrets source:** CI secrets store

**Setup (GitHub Actions example):**
```yaml
name: Test
on: [push]
jobs:
  test:
    runs-on: ubuntu-latest
    env:
      # Use shorter timeouts and smaller limits for tests
      CONFIG_FILE: config.test.yml
    steps:
      - uses: actions/checkout@v4

      - name: Run tests with mocks
        run: cargo test

      - name: Run live API tests
        env:
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
        run: cargo test --features live-api-tests -- --ignored
```

**Benefits:**
- ✅ Different config for test environment (faster timeouts, smaller limits)
- ✅ Secrets managed by CI platform (encrypted, audited, rotated)
- ✅ Mock tests run without API keys, live tests only with secrets present

### Staging

**Config file:** `config.staging.yml` (set `APP_ENV=staging`)

**Secrets source:** Vault, AWS Secrets Manager, or K8s Secrets

**Setup (Kubernetes example):**
```yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: paladin-config
data:
  config.staging.yml: |
    llm:
      default_provider: "deepseek"  # Use cheaper model in staging
    garrison:
      garrison_type: "sqlite"
      max_entries: 200
---
apiVersion: v1
kind: Secret
metadata:
  name: paladin-secrets
type: Opaque
stringData:
  OPENAI_API_KEY: "sk-staging-key"
  DEEPSEEK_API_KEY: "staging-key"
---
apiVersion: apps/v1
kind: Deployment
spec:
  template:
    spec:
      containers:
      - name: paladin
        env:
        - name: APP_ENV
          value: "staging"
        envFrom:
        - secretRef:
            name: paladin-secrets
        volumeMounts:
        - name: config
          mountPath: /etc/paladin/config.staging.yml
          subPath: config.staging.yml
      volumes:
      - name: config
        configMap:
          name: paladin-config
```

### Production

**Config file:** `config.production.yml` (set `APP_ENV=production`)

**Secrets source:** Enterprise secrets manager (Vault, AWS SM, Azure Key Vault)

**Setup (AWS ECS + Secrets Manager):**

1. **Store secrets:**
   ```bash
   aws secretsmanager create-secret \
     --name paladin/prod/openai \
     --secret-string "sk-prod-key-..."
   ```

2. **Task definition:**
   ```json
   {
     "family": "paladin-prod",
     "containerDefinitions": [{
       "name": "paladin",
       "image": "paladin:1.0.0",
       "command": ["--config", "/etc/paladin/config.production.yml"],
       "environment": [
         {"name": "APP_ENV", "value": "production"}
       ],
       "secrets": [
         {
           "name": "OPENAI_API_KEY",
           "valueFrom": "arn:aws:secretsmanager:region:account:secret:paladin/prod/openai"
         }
       ]
     }]
   }
   ```

**Benefits:**
- ✅ Secrets never touch disk or config files
- ✅ Automatic rotation with Secrets Manager
- ✅ Audit trail of all secret access
- ✅ Fine-grained IAM permissions

## Feature Flags

Paladin uses Cargo feature flags to control which dependencies and subsystems are compiled into your application. This enables:

- **Smaller binaries** - Include only what you need
- **Faster compilation** - Skip unused dependencies
- **Clear dependencies** - Explicit about infrastructure requirements
- **Provider choice** - Select specific LLM providers (OpenAI, Anthropic, DeepSeek)

### Quick Reference

**Default build** (minimal):
```toml
[dependencies]
paladin = "0.1"  # Only llm-openai enabled
```

**Full featured build** (development):
```toml
[dependencies]
paladin = { version = "0.1", features = ["full"] }
```

**Custom feature selection** (production):
```toml
[dependencies]
paladin = { version = "0.1", features = [
    "llm-anthropic",      # Anthropic Claude provider
    "redis-queue",        # Redis queue adapter
    "s3-storage",         # S3/MinIO storage
    "web-server"          # REST API server
] }
```

### Available Features

| Category | Flags | Description |
|----------|-------|-------------|
| **LLM Providers** | `llm-openai`, `llm-anthropic`, `llm-deepseek`, `llm-all` | Choose which LLM providers to support |
| **Subsystems** | `vision`, `content-processing`, `web-server`, `notifications` | Optional functional subsystems |
| **Infrastructure** | `redis-queue`, `s3-storage`, `openai-embeddings`, `qdrant` | Storage and queue adapters |
| **Convenience** | `full` | All optional features for development |

### Configuration Integration

Feature flags affect which adapters are available at runtime. Your `config.yml` should only reference adapters enabled by your feature flags:

**Example with `llm-anthropic` feature:**
```yaml
llm:
  default_provider: "anthropic"  # ✅ OK - anthropic adapter compiled
  anthropic:
    default_model: "claude-3-sonnet-20240229"
```

**Example WITHOUT `redis-queue` feature:**
```yaml
redis:
  host: "localhost"
  port: 6379
  # ❌ Error at runtime - Redis adapter not compiled
```

### Detailed Documentation

For complete feature flag documentation, see:
- **[Feature Flags Guide]FEATURE_FLAGS.md** - Comprehensive reference
- **[Migration Guide]MIGRATION.md** - Breaking changes and migration help

### Breaking Change Note

**⚠️ Default features changed in v0.1.0**

- **Old default**: `redis-queue`, `s3-storage`, `openai-embeddings`
- **New default**: `llm-openai` only

If you were relying on default features to provide Redis, S3, or embeddings, you must now explicitly add these features to your `Cargo.toml`. See [MIGRATION.md](MIGRATION.md) for details.

## Security Best Practices

### ✅ DO

1. **Keep secrets in environment variables only**
   ```bash
   export OPENAI_API_KEY="sk-..."
   ```

2. **Use `.env` files for local development**
   ```bash
   # .env (gitignored)
   OPENAI_API_KEY=sk-dev-key
   ```

3. **Use secrets managers in production**
   - AWS Secrets Manager
   - HashiCorp Vault
   - Kubernetes Secrets (with encryption at rest)
   - Azure Key Vault
   - GCP Secret Manager

4. **Set restrictive file permissions on .env**
   ```bash
   chmod 600 .env
   ```

5. **Rotate API keys regularly**

6. **Use different keys per environment**
   - Dev key: Limited quota, separate account
   - Staging key: Separate from prod
   - Prod key: High quota, monitored

### ❌ DON'T

1. **Never commit secrets to git**
   ```yaml
   # ❌ BAD - Don't do this!
   api_key: "sk-real-key-here"
   ```

2. **Never use production keys in development**

3. **Never share .env files via Slack/email**

4. **Never log API keys**
   ```rust
   // ❌ BAD
   println!("API key: {}", api_key);
   ```

5. **Never put secrets in Docker images**
   ```dockerfile
   # ❌ BAD
   ENV OPENAI_API_KEY=sk-...
   ```

## Advanced Topics

### Custom Configuration Files

Specify a different config file:

```bash
cargo run -- --config my-custom-config.yml
```

### Environment-Specific Configs

Set `APP_ENV` to automatically load environment-specific files:

```bash
export APP_ENV=staging
cargo run
# Loads config.yml first, then overrides with config.staging.yml
```

### Configuration Validation

The application validates configuration on startup:

```rust
let settings = Settings::new()?; // Returns error if invalid
```

Common validation errors:
- Missing required fields
- Invalid enum values
- Out-of-range numbers
- Unreachable URLs (for live validation)

### Programmatic Configuration

For tests or embedded usage:

```rust
use paladin::config::application_settings::Settings;

// Load from specific file
let settings = Settings::load_from_file("config.test.yml")?;

// Access config values
let garrison_config = settings.get_garrison_config()?;
assert_eq!(garrison_config.max_entries, 100);
```

### MCP Server Configuration

MCP servers are defined in YAML but may reference env vars:

```yaml
arsenal:
  mcp_servers:
    - name: "github"
      type: "stdio"
      command: "npx"
      args: ["-y", "@modelcontextprotocol/server-github"]
      env:
        GITHUB_TOKEN: "${GITHUB_TOKEN}"  # ❌ Won't interpolate!

    - name: "web-search"
      type: "sse"
      url: "http://localhost:8080/mcp"
```

**Note:** The `${VAR}` syntax in YAML is **not interpolated** by the config crate. Set env vars directly:

```bash
export GITHUB_TOKEN="ghp_..."
cargo run
```

### Debugging Configuration

Enable debug logging to see config loading:

```bash
RUST_LOG=debug cargo run
```

Check what config values are loaded:

```rust
use log::info;

let settings = Settings::new()?;
info!("Loaded config: {:?}", settings);
```

### Configuration Schema

For IDE autocomplete and validation, generate a JSON schema:

```bash
# Future feature - not yet implemented
cargo run -- config schema > config-schema.json
```

## Troubleshooting

### "Missing API key" errors

**Symptom:** `Error: Missing OPENAI_API_KEY environment variable`

**Solutions:**
1. Check the variable is set: `echo $OPENAI_API_KEY`
2. Load .env file: `set -a && . .env && set +a`
3. Export manually: `export OPENAI_API_KEY="sk-..."`
4. In DevContainer, restart terminal or source ~/.bashrc

### Config file not found

**Symptom:** `Failed to load configuration: config.yml not found`

**Solutions:**
1. Check current directory: `pwd`
2. Verify file exists: `ls -la config.yml`
3. Specify absolute path: `--config /workspace/config.yml`
4. Use correct filename: `config.yml` not `config.yaml`

### APP_* overrides not working

**Symptom:** Environment variable set but value not changing

**Solutions:**
1. Check variable name matches YAML structure: `garrison.max_entries``APP_GARRISON_MAX_ENTRIES`
2. Use uppercase and underscores
3. Verify with: `env | grep APP_`
4. Check the getter method exists in `application_settings.rs`

### Permissions errors on .env

**Symptom:** `.env` file readable by others

**Solution:**
```bash
chmod 600 .env
ls -la .env
# Should show: -rw------- (owner read/write only)
```

## Further Reading

- [Garrison (Memory) Documentation]GARRISON.md
- [Sanctum (Long-term Memory) Documentation]SANCTUM.md
- [Arsenal (Tool System) Documentation]ARSENAL.md
- [CLI Usage Guide]CLI_USAGE.md
- [Deployment Guide]deployment/README.md
- [Contributing Guide]../CONTRIBUTING.md

## Support

For configuration issues:
1. Check this guide first
2. Search [existing issues]https://github.com/DF3NDR/paladin-dev-env/issues
3. Ask in [Discussions]https://github.com/DF3NDR/paladin-dev-env/discussions
4. Open a new issue with:
   - Your config.yml (redact secrets!)
   - Environment variables (redact secrets!)
   - Error messages
   - Rust version and OS