"""Icarus Shadow Mode Configuration — COMPLETELY ISOLATED. Shadow mode is for silent observation only. The bot reads messages, extracts entities, runs tripwire detection, and logs to shadow.db. HARD CONSTRAINTS (non-negotiable): - SPEAK_ENABLED = False (bot never sends messages) - SHADOW_MODE = True (enables shadow behavior) - Database: shadow.db (isolated from staging/production) - Family context: Real Hoffmann family data This configuration is used when ENV=shadow. """ import os from pathlib import Path # Environment ICARUS_ENV = "shadow" ICARUS_VERSION = "0.0.1" # --------------------------------------------------------------------------- # SHADOW MODE FLAGS (HARD DISABLES) # --------------------------------------------------------------------------- # SPEAK IS HARD-DISABLED. The bot will NEVER send messages. # This is a safety constant, not a configuration option. SPEAK_ENABLED = False SHADOW_MODE = True # Safety assertions — fail fast if these are wrong assert not SPEAK_ENABLED, "CRITICAL: SPEAK_ENABLED must be False in shadow mode" assert SHADOW_MODE, "CRITICAL: SHADOW_MODE must be True" # --------------------------------------------------------------------------- # Data paths (ISOLATED from staging and production) # --------------------------------------------------------------------------- DATA_DIR = Path(os.environ.get("DATA_DIR", Path.home() / ".icarus" / "shadow")) SHADOW_DB_PATH = DATA_DIR / "shadow.db" EXPORT_DIR = DATA_DIR / "exports" LOG_DIR = DATA_DIR / "logs" # Ensure directories exist DATA_DIR.mkdir(parents=True, exist_ok=True) EXPORT_DIR.mkdir(parents=True, exist_ok=True) LOG_DIR.mkdir(parents=True, exist_ok=True) # --------------------------------------------------------------------------- # Telegram (staging bot token, but different group) # --------------------------------------------------------------------------- TELEGRAM_BOT_TOKEN = os.environ.get("TELEGRAM_BOT_TOKEN") TELEGRAM_GROUP_ID = os.environ.get("TELEGRAM_GROUP_ID") # Family Logistics group # Shadow mode bot info TELEGRAM_BOT_USERNAME = "hoffdesk_staging_bot" # Staging bot username # --------------------------------------------------------------------------- # Ollama (Gaming PC via Tailscale) # --------------------------------------------------------------------------- OLLAMA_BASE_URL = os.environ.get( "OLLAMA_BASE_URL", "http://matt-pc.tail864e81.ts.net:11434" ) OLLAMA_PRIMARY_MODEL = os.environ.get("OLLAMA_PRIMARY_MODEL", "qwen2.5-coder:7b") OLLAMA_UTILITY_MODEL = os.environ.get("OLLAMA_UTILITY_MODEL", "llama3.2:3b") OLLAMA_EMBED_MODEL = os.environ.get("OLLAMA_EMBED_MODEL", "nomic-embed-text") # LLM endpoints for extraction LLM_URL = f"{OLLAMA_BASE_URL}/v1/chat/completions" LLM_MODEL = OLLAMA_PRIMARY_MODEL # --------------------------------------------------------------------------- # Family Context (REAL HOFFMANN FAMILY) # --------------------------------------------------------------------------- FAMILY_CONFIG_PATH = os.environ.get( "FAMILY_CONFIG_PATH", str(Path(__file__).parent.parent / "family_context.yaml") ) # The shadow bot uses real family data: # - Matt (dad) # - Aundrea (mom) # - Sullivan/Sully (son) # - Harper (daughter) # - Maggie (dog) # --------------------------------------------------------------------------- # Shadow Mode Behavior # --------------------------------------------------------------------------- # Tripwire threshold (0.0 - 1.0) # Messages scoring above this trigger LLM extraction TRIPWIRE_THRESHOLD = 0.4 # Minimum confidence for auto-validation MIN_EXTRACTION_CONFIDENCE = 0.7 # Export settings DAILY_EXPORT_TIME = "23:59" # UTC time for daily export EXPORT_RETENTION_DAYS = 30 # Polling settings POLLING_TIMEOUT = 60 # seconds POLLING_BACKOFF = 5 # seconds on error # --------------------------------------------------------------------------- # Admin DM for Enriched Shadow Mode # --------------------------------------------------------------------------- ADMIN_CHAT_ID = os.environ.get("ADMIN_CHAT_ID") LOG_LEVEL = os.environ.get("LOG_LEVEL", "INFO") LOG_FORMAT = "%(asctime)s - [SHADOW] - %(levelname)s - %(message)s" # --------------------------------------------------------------------------- # Safety Checks # --------------------------------------------------------------------------- def _safety_check(): """Verify shadow mode safety constraints.""" errors = [] if SPEAK_ENABLED: errors.append("SPEAK_ENABLED must be False in shadow mode") if not SHADOW_MODE: errors.append("SHADOW_MODE must be True") if DATA_DIR.exists(): # Check we're not accidentally pointing to staging/prod if "staging" in str(DATA_DIR) and "shadow" not in str(DATA_DIR): errors.append(f"DATA_DIR {DATA_DIR} appears to be staging, not shadow") if "production" in str(DATA_DIR) or "prod" in str(DATA_DIR): errors.append(f"DATA_DIR {DATA_DIR} appears to be production, not shadow") if errors: raise RuntimeError("SHADOW MODE SAFETY CHECK FAILED: " + "; ".join(errors)) # Run safety check on import _safety_check() print(f"[SHADOW MODE] Initialized with DATA_DIR={DATA_DIR}") print(f"[SHADOW MODE] SPEAK_ENABLED={SPEAK_ENABLED}, SHADOW_MODE={SHADOW_MODE}")