# portfolio-optimiser — environment template. Copy to `.env` and fill in. NEVER commit `.env`. # # Two backend profiles (D2): LOCAL (OpenAI-compatible local endpoint, dev default — D6) and # AZURE (Foundry, for targeted minimal verification against the private tenant). # # Named `env.template` (no leading dot) on purpose: a leading-dot `.env.template` is blocked by # the llm-security pre-write pathguard. Copy to `.env` locally (`.env` is gitignored). # --- LOCAL profile (dev default) — OpenAI-compatible local endpoint (Ollama / LM Studio) --- # No-egress notes (research 03): the endpoint MUST be loopback; a model pull is the ONLY # legitimate egress and is explicit. Harden Ollama: bind to 127.0.0.1, pin >= 0.17.1, and # leave OLLAMA_DEBUG unset. PORTFOLIO_LOCAL_BASE_URL=http://127.0.0.1:11434/v1 # loopback only — never a remote host PORTFOLIO_LOCAL_API_KEY=ollama # local servers accept any non-empty key # Recommended Ollama hardening (set in the Ollama service environment, not here): # OLLAMA_HOST=127.0.0.1:11434 # bind loopback only # (pin Ollama >= 0.17.1; leave OLLAMA_DEBUG unset) # --- AZURE / Foundry profile (targeted verification only — D6) --- # Deployment (model) names are tenant-specific and live in data/model_map.json (azure.*). # The credential is chosen by the ENVIRONMENT and is not configured here: # * developer host -> AzureCliCredential; constructing it acquires no token, so `az login` # stays your own manual step (docs/2026-07-15-foundry-auth-recipe.md). # * Foundry hosted agent -> ManagedIdentityCredential, selected when FOUNDRY_HOSTING_ENVIRONMENT # holds a non-empty value. A container has no Azure CLI; the platform # mints the agent a dedicated Entra identity at deploy time instead. PORTFOLIO_FOUNDRY_PROJECT_ENDPOINT= # tenant-specific Foundry project endpoint (operator supplies) # Hosted only: Foundry injects FOUNDRY_PROJECT_ENDPOINT itself, and the framework reads it as a # fallback. The name above wins whenever it holds a non-empty value, so an explicit export is never # shadowed by a platform default — and an exported-but-empty name falls through rather than masking. # --- Tracing (U14) — OFF unless this variable holds a value --- # Unset or empty: OpenTelemetry providers are NOT configured at all. Spans are still made (MAF's # ENABLE_INSTRUMENTATION defaults to true) and simply discarded, so nothing can leave the process. # console -> spans are written to STDERR. stdout is byte-identical to an untraced run, which is # what keeps the pinned demo transcript usable. Refused if any OTEL_EXPORTER_OTLP_* # endpoint is also set: those exporters are built unconditionally, so "console" # would otherwise be a false statement about where the run's contents went. # otlp -> spans are EXPORTED OVER THE NETWORK, and only to an endpoint you named below. # Requires an OTLP exporter package, which this framework deliberately does NOT # declare (`pip install opentelemetry-exporter-otlp-proto-grpc`). # PORTFOLIO_OTEL=console # OTEL_EXPORTER_OTLP_ENDPOINT= # required by PORTFOLIO_OTEL=otlp; egress — name it yourself