"""Fail-closed runtime binding for the shared social-media technical core.
Brand-specific state is selected only by an explicit, validated JSON profile.
The module contains no editorial defaults and never falls back across brands.
"""
from __future__ import annotations
import json, os
from pathlib import Path
CONFIG_ENV = "SMA_BRAND_CONFIG"
def load_brand_config() -> dict:
raw = os.environ.get(CONFIG_ENV)
if not raw:
raise RuntimeError(f"{CONFIG_ENV} is required; refusing implicit brand default")
path = Path(raw).expanduser().resolve()
if not path.is_file():
raise RuntimeError(f"brand config missing: {path}")
cfg = json.loads(path.read_text(encoding="utf-8"))
required = {"brand_id","brand_name","database","cases_database","runtime_database","knowledge_path","source_config","output_path"}
missing = sorted(required - cfg.keys())
if missing: raise RuntimeError(f"brand config incomplete: {missing}")
if cfg["brand_id"] not in {"mas", "as"}: raise RuntimeError("unknown brand_id")
paths = {k: Path(cfg[k]).expanduser().resolve() for k in required if k.endswith(("database","path"))}
if paths["database"] == paths["cases_database"]: raise RuntimeError("signals and cases databases must differ")
for k,p in paths.items():
if k in {"database","cases_database"} and p == Path("/var/lib/sma-data/signals.db") and cfg["brand_id"] != "mas":
raise RuntimeError("non-MAS brand cannot use MAS signals database")
return cfg
def validate_brand_runtime() -> dict:
cfg = load_brand_config()
expected = {"SMA_DATABASE_PATH": cfg["database"], "SMA_CASES_DATABASE_PATH": cfg["cases_database"]}
for env, value in expected.items():
actual = os.environ.get(env)
if actual is not None and Path(actual).expanduser().resolve() != Path(value).expanduser().resolve():
raise RuntimeError(f"{env} contradicts brand config for {cfg['brand_id']}")
if cfg["brand_id"] == "mas" and Path(cfg["database"]).resolve() != Path("/var/lib/sma-data/signals.db"):
raise RuntimeError("MAS must use canonical signals database")
if cfg["brand_id"] == "as" and Path(cfg["database"]).resolve() == Path("/var/lib/sma-data/signals.db"):
raise RuntimeError("AS must not use canonical MAS signals database")
for key in ("knowledge_path","source_config","output_path"):
p=Path(cfg[key]).expanduser().resolve()
if not p.exists(): raise RuntimeError(f"configured {key} missing: {p}")
return cfg
BRAND_CONFIG = load_brand_config() if os.environ.get(CONFIG_ENV) else None