Skip to content

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

cp .env.example .env
$EDITOR .env       # fill in your real values

.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:

python -m twine upload --repository-url https://test.pypi.org/legacy/ \
  dist/mult_agentes-*.whl

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:

ANTHROPIC_API_KEY=sk-ant-... pytest tests/smoke/test_real_llm_e2e.py -m network
# ~$0.02 cost


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:

openssl rand -hex 32

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:

openssl rand -hex 24

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:

cd vscode-extension/
VSCE_PAT=<token> npx vsce verify-pat <publisher>


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)

  1. Any import src.* triggers src/__init__.py
  2. src/__init__.py imports src._settings (side effect)
  3. src._settings._load_dotenv() walks upward looking for .env
  4. load_dotenv(path, override=False) populates os.environ
  5. settings = Settings() is constructed; subsequent reads from os.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:

  1. Generate new at the provider (PyPI/Anthropic/AWS/etc.)
  2. Update .env locally
  3. Update production secrets (GitHub Actions / systemd / docker / K8s)
  4. Restart processes that depend on it (make dashboard-real, etc.)
  5. Test with the snippet in the relevant section above
  6. Revoke old at the provider
  7. 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