How-to: configure secrets and environment variables¶
The mult-agentes framework reads all credentials and operator-tunable settings from environment variables. This page is the single source of truth for every variable: what it does, default, how to generate, how to test.
Loading mechanism¶
When src is imported, src._settings._load_dotenv() walks up from the
package directory looking for a .env file, and loads it via
python-dotenv. The load is
non-overriding — existing shell env vars win over .env so production
deployments can use systemd / Docker / Kubernetes secrets without touching
the file.
Setup¶
.env is in .gitignore (lines 98-100). Never commit it.
To check what's wired:
python -c "from src._settings import settings; import json; print(json.dumps(settings.status(), indent=2))"
Returns booleans — no secret values are leaked.
The variables¶
PyPI publishing¶
| Variable | Required | Default | Used by |
|---|---|---|---|
TWINE_USERNAME |
optional | __token__ |
twine upload |
TWINE_PASSWORD |
for pypi-publish workflow |
— | twine upload |
TWINE_PASSWORD_TEST |
for TestPyPI dry-runs | — | twine upload -r testpypi |
Generate:
- PyPI: https://pypi.org/manage/account/token/ → "Add API token" → scope Project: mult-agentes (after first publish; "Entire account" only for first ever upload)
- TestPyPI: https://test.pypi.org/manage/account/token/
Test:
Rotation cadence: quarterly, or immediately if leaked. After publishing
once, generate a Project: mult-agentes-scoped token and delete the
"Entire account" one.
Anthropic API¶
| Variable | Required | Default | Used by |
|---|---|---|---|
ANTHROPIC_API_KEY |
only for headless mode | — | src.ai.AnthropicInvoker, src.orchestrators.EndToEndRunner |
Generate: https://console.anthropic.com/settings/keys
Default behavior without it: the bridge-driven flow (primary mode in
VS Code) works without this. Claude in your VS Code chat IS the LLM. Only
tests/smoke/test_real_llm_e2e.py and EndToEndRunner need this.
Test:
Audit chain (Rule 24)¶
| Variable | Required | Default | Used by |
|---|---|---|---|
AUDIT_HMAC_KEY |
YES in production | dev_only_audit_key_DO_NOT_USE_IN_PROD |
src.privacy.AuditChain |
Generate:
Critical: Losing this means the existing chain becomes
unverifiable. After setting in .env, back it up to an offline
location (printed paper, encrypted USB, password manager with offline copy).
Test:
python -c "
from src.privacy import AuditChain
c = AuditChain(path='/tmp/test.jsonl')
c.append(actor='t', action='WRITE', resource='r', purpose='p', tier='t0')
ok, err = c.verify()
print('valid:', ok, 'errors:', err)
"
Rotation: in v1.x rotation invalidates existing chains. v1.5+ will support multi-key chains with rolling rotation.
Dashboard¶
| Variable | Required | Default | Used by |
|---|---|---|---|
FRAMEWORK_DIR |
optional | _framework |
All file-backed stores |
DASHBOARD_API_KEY |
recommended in prod | — | Bearer auth on /api/chat |
DASHBOARD_RATE_LIMIT_PER_MIN |
optional | 60 |
Per-IP sliding window |
Generate DASHBOARD_API_KEY:
Test:
curl -H "Authorization: Bearer $DASHBOARD_API_KEY" \
http://localhost:8000/api/chat -d '{"prompt":"test"}' -H "Content-Type: application/json"
Note: localhost (127.0.0.1, ::1) is exempt from auth + rate-limit by design so local dev doesn't need a token.
Federation (multi-machine)¶
| Variable | Required | Default | Used by |
|---|---|---|---|
REDIS_URL |
optional | — | src.scalability.RedisFederationTransport |
Format: redis://[:password@]host:port/db (e.g. redis://localhost:6379/0)
Default behavior without it: src.scalability.FsFederationTransport
handles single-machine multi-process via shared filesystem at
$FRAMEWORK_DIR/federation/. No Redis needed for single-host deploys.
Test:
docker run --rm -d --name r -p 6379:6379 redis:7-alpine
REDIS_URL=redis://localhost:6379/0 python -c "
from src.scalability import RedisFederationTransport
t = RedisFederationTransport()
t.heartbeat()
print(t.peers())
"
docker rm -f r
OpenTelemetry¶
| Variable | Required | Default | Used by |
|---|---|---|---|
OTEL_EXPORTER_OTLP_ENDPOINT |
optional | — | src.observability.OtelAdapter (when enable_otlp_export=True) |
Format: host:port (gRPC) or host:port with protocol="http/protobuf"
Default behavior without it: traces + metrics are recorded internally but not exported. No external dependency required.
Setup with the deploy stack: cd deploy/ && docker compose up -d
otel-collector jaeger. Then set OTEL_EXPORTER_OTLP_ENDPOINT=localhost:4317.
Open Jaeger UI at http://localhost:16686.
AWS S3 Object Lock (audit cloud durability)¶
| Variable | Required | Default | Used by |
|---|---|---|---|
AWS_ACCESS_KEY_ID |
only with S3 backend | — | src.privacy.ObjectLockImmutability |
AWS_SECRET_ACCESS_KEY |
only with S3 backend | — | same |
AWS_DEFAULT_REGION |
optional | us-east-1 |
same |
S3_AUDIT_BUCKET |
only with S3 backend | — | bucket name |
Bucket prerequisites:
- Create bucket with Object Lock enabled at bucket creation time (cannot enable later)
- Set default retention: COMPLIANCE mode + N days (e.g. 2555 for 7-year compliance)
- IAM user with policy: s3:PutObject, s3:PutObjectRetention, s3:PutObjectLegalHold
Generate AWS keys: IAM Console → Users → Security credentials → Create access key
Test:
python -c "
from src.privacy import AuditChain, ObjectLockImmutability
backend = ObjectLockImmutability(bucket='\$S3_AUDIT_BUCKET', retention_days=1)
chain = AuditChain(path='/tmp/t.jsonl', immutability=backend)
chain.append(actor='t', action='WRITE', resource='r', purpose='p', tier='t0')
print(backend.status())
"
VS Code Marketplace¶
| Variable | Required | Default | Used by |
|---|---|---|---|
VSCE_PAT |
only for vsce publish |
— | vsce CLI |
Generate: - https://dev.azure.com/your-org/_usersSettings/tokens - New token → Scopes → Custom defined → check Marketplace → Manage - Validity 30-180 days (PAT max)
Test:
HuggingFace (model downloads)¶
| Variable | Required | Default | Used by |
|---|---|---|---|
HF_TOKEN |
optional | — | src.ai.EmbeddingsClient first model download |
Why: anonymous HF Hub downloads are rate-limited. A read-only token
removes the limit for sentence-transformers/all-MiniLM-L6-v2 initial fetch.
Generate: https://huggingface.co/settings/tokens → New token → Type: Read
Production deploy¶
| Variable | Required | Default | Used by |
|---|---|---|---|
ORG_DOMAIN |
only when behind Caddy | localhost |
deploy/Caddyfile, deploy/docker-compose.yml |
For real TLS: set to your registered domain. Caddy will issue a Let's Encrypt cert automatically on first HTTP request.
Where to put it¶
| Environment | Where the secrets live |
|---|---|
| Local dev | .env at repo root (gitignored) |
| CI (GitHub Actions) | Repository Settings → Secrets and variables → Actions |
| Production (Docker) | deploy/.env for compose, or --env-file flag |
| Production (systemd) | /etc/organismo/environment (chmod 600, owner organismo) |
| Production (K8s) | kubectl create secret generic organismo-env --from-env-file=.env |
How it's loaded (under the hood)¶
- Any
import src.*triggerssrc/__init__.py src/__init__.pyimportssrc._settings(side effect)src._settings._load_dotenv()walks upward looking for.envload_dotenv(path, override=False)populatesos.environsettings = Settings()is constructed; subsequent reads fromos.environ.get(...)find the values
The walk-up is rooted at Path(__file__).resolve() — works whether you run
from the repo root, from /, or from within a Docker container as long as
the .env is somewhere upstream of src/.
Programmatic access¶
from src._settings import settings
# Boolean status (safe to print/log)
status = settings.status()
print(status)
# Direct access (sensitive — do NOT log)
if settings.anthropic_api_key:
invoker = AnthropicInvoker(api_key=settings.anthropic_api_key)
Rotation runbook¶
When a secret leaks or quarterly review hits:
- Generate new at the provider (PyPI/Anthropic/AWS/etc.)
- Update
.envlocally - Update production secrets (GitHub Actions / systemd / docker / K8s)
- Restart processes that depend on it (
make dashboard-real, etc.) - Test with the snippet in the relevant section above
- Revoke old at the provider
- Audit: scan logs for any use of the revoked secret (
grep -r <token-prefix> ~/.bash_history)
Pre-commit guard against accidental commits¶
.gitignore already excludes .env*. To add belt-and-suspenders, install
the gitleaks pre-commit hook:
pre-commit install
echo " - repo: https://github.com/gitleaks/gitleaks
rev: v8.18.0
hooks:
- id: gitleaks" >> .pre-commit-config.yaml
pre-commit run --all-files
See also¶
src/_settings.py— the dotenv loader + Settings dataclass (see source on GitHub)- Tutorial 05 — Deploy to production
- Backup and recover — what to do if
AUDIT_HMAC_KEYis lost