All configuration for Seed & Vine is split across three files: .env (secrets and runtime flags), conf/llm_providers.yaml (LLM provider pool), and data/sources/sv-facts.yml (exfiltration credentials and endpoints). This document is a complete reference for every option in each file.
.env, Environment Variablesconf/llm_providers.yaml, LLM Provider Pooldata/sources/sv-facts.yml, Fact Source- Operational Constants
- Risk Matrix
- Exfiltration Method Mapping
- Threat Actor Profiles
- Shadow Logger
Copy .env.example to .env and fill in values before starting CALDERA. The file is loaded by python-dotenv inside app/constants.py and several other modules at import time.
# CALDERA REST API Key
# Must match the api_key.red value in conf/default.yml.
# In production, use a strong random key, not the default.
CALDERA_API_KEY=ADMIN123
# Gemini API Key
# Required if any provider in conf/llm_providers.yaml references GEMINI_API_KEY.
GEMINI_API_KEY=
# DSPy Temperature (0.0 to 1.0)
# Controls output randomness for all DSPy calls.
# Default: 0.0 (deterministic). Increase for more creative ability proposals.
DSPY_TEMPERATURE=0.0Anthropic keys referenced in conf/llm_providers.yaml use long, unique names (e.g., ANTHROPIC_CLAUDE_HAIKU_4_5_20251001_72D5E8F7_KEY). This allows multiple Anthropic accounts to coexist in the provider pool. Each key is set as its own environment variable:
ANTHROPIC_CLAUDE_HAIKU_4_5_20251001_72D5E8F7_KEY=sk-ant-api03-...
ANTHROPIC_CLAUDE_SONNET_4_6_EDEC95C7_KEY=sk-ant-api03-...| Variable | Required | Default | Description |
|---|---|---|---|
CALDERA_API_KEY |
Yes | ADMIN123 (dev fallback only when SEED_VINE_DEV_MODE=true) |
CALDERA REST API authentication key |
GEMINI_API_KEY |
Conditional | , | Google Gemini API key; required if any Gemini provider is configured |
DSPY_TEMPERATURE |
No | 0.0 |
Float 0.0-1.0; applies to all DSPy module calls |
SEED_VINE_DEV_MODE |
No | , | Set to true to allow ADMIN123 as fallback when CALDERA_API_KEY is unset |
CALDERA_PORT |
No | 8888 |
Injected into MCP subprocess environment |
MLFLOW_TRACKING_URI |
No | http://localhost:5000 |
MLflow server URL |
MLFLOW_EXPERIMENT_NAME |
No | seed-and-vine-operation |
MLflow experiment name |
SHADOW_LOG_ENABLED |
No | true |
Set to false to disable all shadow logging; checked at module import time, requires restart to take effect |
This file defines the ordered pool of LLM providers. TokenPoolManager loads it at startup and creates one dspy.LM instance per entry whose API key resolves.
fallback_settings:
auto_switch_on_error: true # Automatically try next provider on error
log_provider_switches: true # Log provider switches to shadow logger
retry_primary_after_minutes: 30 # Return to priority-1 provider after this interval
providers:
- name: anthropic # Provider family: "anthropic" | "gemini" | "groq" | "ollama"
model: claude-haiku-4-5-20251001
api_key_env: ANTHROPIC_CLAUDE_HAIKU_4_5_20251001_72D5E8F7_KEY # Env var name
api_base: '' # Override API base URL (leave blank for default)
max_rpm: 0 # Requests per minute (0 = no cap)
max_tpm: 0 # Tokens per minute (0 = no cap)
max_rpd: 0 # Requests per day (0 = no cap)
max_tokens: 0 # Max tokens per response (0 = model default)
priority: 1 # Lower number = higher priority in rotationThe TokenPoolManager selects a gating strategy per provider based on max_rpm:
max_rpm < 20→ RPM-gated: the request count is the limiting factor (typical for free-tier APIs with low RPM). A 60-second sliding window of request timestamps is maintained.max_rpm >= 20→ TPM-gated: the token count is the limiting factor. A 60-second sliding window of(timestamp, tokens)pairs is maintained.
Setting all limits to 0 disables per-provider rate tracking (useful for paid tiers with no hard caps or when you prefer the provider's own 429 signal to drive rotation).
Providers are sorted by priority (ascending) at load time. Requests go to the lowest-numbered available provider. When that provider is rate-limited, the next is tried. Providers are restored to rotation automatically after COOLDOWN_SECONDS = 60 following a 429 response.
- name: gemini
model: gemini-3.1-flash-lite-preview
api_key_env: GEMINI_API_KEY_ACCOUNT2 # Unique env var name
max_rpm: 0
max_tpm: 0
max_rpd: 0
max_tokens: 0
priority: 4Then in .env:
GEMINI_API_KEY_ACCOUNT2=AIza...Thinking mode is enabled implicitly when the model supports it. The seed_vine_planner_client.py detects the model family and applies the appropriate thinking parameters:
Claude (Anthropic): budget_tokens = max(1024, estimated_input_tokens × 0.5), capped at 32000. Temperature is removed from the request (Anthropic requirement).
Gemini: thinkingLevel is set to "low" / "medium" / "high" based on the model variant (flash / pro / ultra).
This file is the pre-loaded fact source for exfiltration abilities. CALDERA reads it at startup (when the plugin is enabled) and makes all listed facts available under the source ID abcde123-sv01-4444-8888-factsource0001.
id: abcde123-sv01-4444-8888-factsource0001
name: SV Facts
facts:
# ── Lazarus Group: Direct C2 Channel (sv-exfil-c2-channel.yml) ──
- trait: c2_exfil_url
value: http://YOUR_C2_SERVER:8888/file/upload
# Full URL of the CALDERA file-upload endpoint on the attacker's server.
# Format: http://<caldera_ip>:<port>/file/upload
# ── APT41: Cloudflare C2 (sv-exfil-cloudflare-c2.yml) ──
- trait: cloudflare_c2_url
value: https://YOUR_CLOUDFLARE_WORKER_URL
# HTTPS endpoint of a Cloudflare Worker that proxies data to the attacker.
# ── APT41: DNS Tunneling (sv-exfil-dns-tunnel.yml) ──
- trait: dns_exfil_domain
value: exfil.yourdomain.com
# Domain under attacker control. The ability sends DNS queries of the form
# {seq}.{base64_chunk}.{domain}. A DNS server listening on this domain
# must log all A-record queries to reconstruct the exfiltrated file.
# ── Lazarus Group: SMTP Exfiltration (sv-exfil-smtp.yml) ──
- trait: smtp_host
value: smtp.yourmailserver.com
- trait: smtp_port
value: "587"
- trait: smtp_user
value: sender@yourdomain.com
- trait: smtp_pass
value: your-smtp-password
- trait: smtp_recipient
value: receiver@yourdomain.com
# ── Lazarus Group: Dropbox (sv-exfil-dropbox.yml) ──
- trait: dropbox_access_token
value: your-dropbox-access-token
# Dropbox API access token (generate at https://www.dropbox.com/developers).
# Uploaded files go to the app's root folder.
# ── APT41: OneDrive (sv-exfil-onedrive.yml) ──
- trait: onedrive_access_token
value: your-onedrive-access-token
# Microsoft Graph API token with Files.ReadWrite scope.
# Uploaded files land in the default OneDrive drive.
# ── Resolved automatically, do not change unless running on a non-standard port ──
- trait: caldera_ip
value: 127.0.0.1
# CALDERA server IP as visible from the agent host.
# Used by DNS tunnel for the query server address.
- trait: caldera_api_key
value: ADMIN123
# Must match CALDERA_API_KEY in .env and conf/default.yml.| Exfil method | Required facts |
|---|---|
c2_channel |
c2_exfil_url, caldera_api_key |
cloudflare_c2 |
cloudflare_c2_url, caldera_api_key |
dns_tunnel |
dns_exfil_domain, caldera_ip |
smtp |
smtp_host, smtp_port, smtp_user, smtp_pass, smtp_recipient |
dropbox |
dropbox_access_token |
onedrive |
onedrive_access_token |
target_path and file_pattern are also declared in sv-facts.yml with empty default values (value: ""). They are populated either by the recon phase (the discovery ability writes discovered paths into the fact store) or by the operator filling them in directly in the YAML before starting CALDERA.
These constants are defined in app/constants.py. They control timeouts, loop limits, and buffer sizes. Edit constants.py directly if you need to tune them.
| Constant | Value | Description |
|---|---|---|
APPROVAL_TIMEOUT_SEC |
1800 |
Seconds before an unanswered approval gate auto-rejects (30 minutes) |
MAX_PLANNING_LOOPS |
30 |
Maximum Layer-1 planning iterations per operation |
MAX_CONSECUTIVE_REJECTS |
5 |
Operation stops if this many approval gate requests are rejected in a row |
EXEC_TIMEOUT_SEC |
120 |
Maximum seconds to wait for a CALDERA link to finish executing |
POLL_INTERVAL_SEC |
3 |
Seconds between link-status poll requests during execution |
OUTPUT_TRUNCATION_LIMIT |
500 |
Characters kept from ability stdout before passing to AI context |
SSE_QUEUE_MAX_SIZE |
64 |
Maximum buffered events per SSE subscriber |
MAX_SSE_SUBSCRIBERS |
10 |
Maximum concurrent SSE client connections |
CALDERA_BASE_URL |
http://localhost:8888 |
CALDERA server address used by all internal API clients |
MLFLOW_TRACKING_URI |
http://localhost:5000 |
Overridable via MLFLOW_TRACKING_URI env var |
Governor and task constants (in their respective files):
| Constant | File | Value | Description |
|---|---|---|---|
MAX_TOTAL_ITERATIONS |
kill_chain_governor.py |
50 |
Global loop cap; operation fails if exceeded |
MAX_TASK_RETRIES |
kill_chain_governor.py |
2 |
Per-task retry limit before marking failed |
MAX_BACKWARD_MOVES |
kill_chain_governor.py |
2 |
Phase oscillation limit |
DEFAULT_MAX_RETRIES |
task_state.py |
2 |
Default retry count for new TaskState objects |
ChromaDB index constants (in ability_index.py):
| Constant | Value | Description |
|---|---|---|
MIN_SIMILARITY |
0.25 |
Minimum cosine similarity for a search result to be returned |
_BATCH_SIZE |
500 |
Abilities inserted per ChromaDB batch call |
_META_DESC_MAX |
200 |
Character limit on description stored in ChromaDB metadata |
Token pool constants (in token_pool_manager.py):
| Constant | Value | Description |
|---|---|---|
COOLDOWN_SECONDS |
60 |
Seconds a rate-limited provider stays out of rotation |
SAFETY_MARGIN |
0.90 |
Fraction of provider capacity before soft-limiting kicks in |
RPM_GATE_THRESHOLD |
20 |
max_rpm < 20 triggers RPM-gated mode |
SLIDING_WINDOW_SEC |
60 |
Width of the sliding window for rate calculations |
The risk matrix in app/constants.py determines whether an ability requires operator approval before it executes. It is keyed by ATT&CK tactic name.
RISK_MATRIX = {
"discovery": {"level": "low", "auto_approve": False},
"defense-evasion": {"level": "medium", "auto_approve": True},
"collection": {"level": "medium", "auto_approve": True},
"privilege-escalation": {"level": "high", "auto_approve": False},
"credential-access": {"level": "high", "auto_approve": False},
"exfiltration": {"level": "critical", "auto_approve": False},
}auto_approve: True means the ability executes immediately without pausing for operator input. auto_approve: False triggers the approval gate: the operation pauses, emits an approval SSE event, and waits for POST /api/approve with {"decision": "APPROVE"} or {"decision": "REJECT"}.
Note on
discovery: althoughauto_approveisFalse, the discovery ability (sv-pre-op-recon) is never passed through this check. It runs via_run_discovery_ability(), a dedicated code path inseed_vine_svc.pythat callsexecute_ability_via_operation()directly, bypassing RISK_MATRIX evaluation entirely. Theauto_approveflag only applies to abilities dispatched through the standard approval gate.
To change the approval requirement for a tactic, edit RISK_MATRIX in app/constants.py. Changes take effect on the next operation start; a running operation uses the matrix loaded at its start time.
The exfil method selected in the GUI maps to a specific CALDERA ability UUID via EXFIL_METHOD_ABILITY_MAP in app/constants.py.
| GUI key | Ability UUID | Ability name | Threat actor |
|---|---|---|---|
c2_channel |
302bd758-df30-4188-8a96-4a301824b9c1 |
Lazarus - Direct C2 Channel | Lazarus Group |
cloudflare_c2 |
b7320035-571d-4909-940f-d2719b5ac868 |
APT41 - Cloudflare C2 Channel | APT41 |
dns_tunnel |
03d727b1-74da-4bc1-bb46-9bfc13b94292 |
APT41 - DNS Tunneling Exfiltration | APT41 |
dropbox |
8ed89ac3-d5a2-436c-8d71-72e70a0b9f76 |
Lazarus - Dropbox Cloud Storage | Lazarus Group |
onedrive |
3b5272f3-644d-48b7-996a-c1beb68b2237 |
APT41 - OneDrive Cloud Storage | APT41 |
smtp |
ad92decd-7dc4-409f-b742-3435524b38df |
Lazarus - SMTP Email Exfiltration | Lazarus Group |
Name note: the "Ability name" column above shows the canonical
name:field from each ability YAML, what appears in CALDERA's ability browser.EXFIL_METHOD_ABILITY_MAP_WITH_NAMESinconstants.pyuses slightly different display names for the LLM context prompt (e.g."Lazarus - C2 Channel Exfiltration"instead of"Lazarus - Direct C2 Channel"). The UUIDs are authoritative; use those when cross-referencing abilities.
The exfil_method_order field in the operation config specifies a fallback queue. If the primary method fails (missing facts, network error, non-zero exit code), the operation tries the next method in the queue. The default fallback order is set by get_default_exfil_order(threat_actor) in app/context_builder.py:
- APT41:
["cloudflare_c2", "dns_tunnel", "onedrive"] - Lazarus:
["c2_channel", "smtp", "dropbox"]
Threat actor selection affects three things: the default exfil method order, the TTP narrative injected into the AI system prompt (via _get_threat_actor_context() in context_builder.py), and the pre-populated exfil method ranking in the GUI.
MITRE ATT&CK Group ID: G0096
Attribution: Chinese Ministry of State Security (MSS) contractor
Profile injected into prompt: blended espionage and financial crime; use of living-off-the-land binaries; preference for cloud service abuse and DNS tunneling
Default exfil order: cloudflare_c2 → dns_tunnel → onedrive
Lazarus Group (HIDDEN COBRA)
MITRE ATT&CK Group ID: G0032
Attribution: North Korean Reconnaissance General Bureau (RGB)
Profile injected into prompt: long-dwell financial intrusions; custom malware and C2 infrastructure; spear-phishing initial access
Default exfil order: c2_channel → smtp → dropbox
Seed & Vine writes structured logs to three rotating files in data/logs/. These are independent of CALDERA's standard Python logger and are intended for post-operation forensics.
| File | Contents | Rotation |
|---|---|---|
seed_vine_debug.log |
General debug events, state transitions, phase changes, tool calls | 5 MB, 3 backups |
seed_vine_ai_trace.log |
AI-specific trace, provider selections, DSPy inputs/outputs, token counts | 5 MB, 3 backups |
seed_vine_error.log |
Errors and circuit breaker events only | 5 MB, 3 backups |
Log level is controlled by CALDERA's -l flag (python server.py -l DEBUG). Shadow logger always writes at DEBUG level regardless of the global flag.
To disable shadow logging entirely, set SHADOW_LOG_ENABLED=false in .env (the logger checks this variable at module import time).