Kansho/backend/env_loader.py
Lars af6fe55577
All checks were successful
Deploy Development / deploy (push) Successful in 54s
Test Suite / pytest-backend (push) Successful in 2m35s
Test Suite / smoke-dev (push) Successful in 1s
Test Suite / frontend-build (push) Successful in 15s
Allow remote detect in production until local Ollama is connected.
Keep KANSHO_ENV=production and require an explicit operator flag instead of treating Prod as Development. Promote to Prod only via merge commit on main.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-08 10:02:28 +02:00

88 lines
2.9 KiB
Python

"""Load backend/.env into os.environ without overwriting a real process environment."""
from __future__ import annotations
import os
from pathlib import Path
ENV_PATH = Path(__file__).resolve().parent / ".env"
def load_env_file(path: Path | None = None) -> None:
target = path or ENV_PATH
if not target.is_file():
return
for raw in target.read_text(encoding="utf-8").splitlines():
line = raw.strip()
if not line or line.startswith("#") or "=" not in line:
continue
key, value = line.split("=", 1)
key = key.strip()
if not key or key in os.environ:
continue
value = value.strip()
if len(value) >= 2 and value[0] == value[-1] and value[0] in {"'", '"'}:
value = value[1:-1]
os.environ[key] = value
def runtime_env() -> str:
"""development/test may use remote plaintext detect. production must not, unless overridden."""
raw = (os.environ.get("KANSHO_ENV") or "").strip().lower()
if raw in {"production", "prod"}:
return "production"
if raw in {"test", "testing"}:
return "test"
if raw in {"development", "dev"}:
return "development"
return "development"
def env_flag(name: str) -> bool:
raw = (os.environ.get(name) or "").strip().lower()
return raw in {"1", "true", "yes", "on"}
def remote_plaintext_detect_reason() -> str:
"""Why remote detect is allowed or blocked. Never logs secrets."""
if runtime_env() != "production":
return "non_production"
if env_flag("KANSHO_ALLOW_REMOTE_DETECT"):
return "operator_override"
return "blocked"
def allows_remote_plaintext_detect() -> bool:
"""Remote plaintext detect is default-off in production.
Endbetrieb remains local detect. Until Ollama is connected, an operator
may set KANSHO_ALLOW_REMOTE_DETECT. That does not disable the gateway.
"""
return remote_plaintext_detect_reason() != "blocked"
def upsert_env_value(name: str, value: str, path: Path | None = None) -> None:
"""Write one key into backend/.env and the current process. Never log the value."""
target = path or ENV_PATH
name = name.strip()
if not name:
raise ValueError("empty env name")
lines: list[str] = []
if target.is_file():
lines = target.read_text(encoding="utf-8").splitlines()
found = False
updated: list[str] = []
prefix = f"{name}="
for line in lines:
stripped = line.strip()
if stripped.startswith(prefix) or stripped.startswith(f"{name} ="):
updated.append(f"{name}={value}")
found = True
else:
updated.append(line)
if not found:
if updated and updated[-1] != "":
updated.append("")
updated.append(f"{name}={value}")
target.write_text("\n".join(updated) + "\n", encoding="utf-8")
os.environ[name] = value