From 965a844b0d50ee4ac42d62f84548e1a629096d0c Mon Sep 17 00:00:00 2001 From: K7ZVX Date: Thu, 14 May 2026 15:14:12 +0000 Subject: [PATCH] feat(config): split monolithic config + extract secrets - Update .gitignore for v0.3 multi-file layout - Add config/.env.example template for secrets - Add config/local.yaml.example for operator values - Wire main.py to use new config_loader - Support both legacy and new layouts Co-Authored-By: Claude Opus 4.5 --- .gitignore | 20 ++++++++++++++ config/.env.example | 19 +++++++++++++ config/local.yaml.example | 57 +++++++++++++++++++++++++++++++++++++++ meshai/main.py | 22 ++++++++++----- 4 files changed, 112 insertions(+), 6 deletions(-) create mode 100644 config/.env.example create mode 100644 config/local.yaml.example diff --git a/.gitignore b/.gitignore index 87706b6..4aae211 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,13 @@ +# Operator-identifying config and secrets (v0.3 split) +/data/config/local.yaml +/data/config/secrets/ +/data/secrets/ +.env +.env.local +.env.* +!.env.example +local.yaml +!local.yaml.example # Python __pycache__/ *.py[cod] @@ -49,3 +59,13 @@ data/ # OS .DS_Store Thumbs.db +# Operator-identifying config and secrets (v0.3 split) +/data/config/local.yaml +/data/config/secrets/ +/data/secrets/ +.env +.env.local +.env.* +!.env.example +local.yaml +!local.yaml.example diff --git a/config/.env.example b/config/.env.example new file mode 100644 index 0000000..cc43131 --- /dev/null +++ b/config/.env.example @@ -0,0 +1,19 @@ +# MeshAI Secrets Template +# Copy to /data/secrets/.env and fill in your values +# This file is gitignored - never commit real secrets + +# LLM API Keys (only one needed based on your backend choice) +OPENAI_API_KEY= +ANTHROPIC_API_KEY= +GOOGLE_API_KEY= + +# Mesh Source Credentials +MESHMONITOR_API_TOKEN= +MQTT_PASSWORD= + +# Environmental Feed Keys +TOMTOM_API_KEY= +FIRMS_MAP_KEY= + +# Notification Credentials +SMTP_PASSWORD= diff --git a/config/local.yaml.example b/config/local.yaml.example new file mode 100644 index 0000000..8da62a9 --- /dev/null +++ b/config/local.yaml.example @@ -0,0 +1,57 @@ +# MeshAI Local Configuration Template +# Copy to /data/config/local.yaml and customize for your deployment +# This file is gitignored - contains operator-identifying values + +# Operator Identity +identity: + name: "" # Bot display name + owner: "" # Owner callsign/name + primary_node_id: "" # Your main mesh node ID + contact_email: "" # For NWS user_agent, SMTP from + +# Region Coordinates +# Map your region names to their lat/lon center points +regions: + "Example Region": + lat: 0.0 + lon: 0.0 + # Add more regions as needed: + # "Another Region": + # lat: 42.5 + # lon: -114.5 + +# Mesh Data Source URLs +mesh_sources: + meshmonitor_url: "" # Your MeshMonitor instance + sources: + # Per-source URL overrides (matches names in mesh_sources.yaml) + "My-Meshview": + url: "" + # "My-MeshMonitor": + # url: "" + +# Infrastructure Hosts +infrastructure: + tcp_host: "" # Meshtastic TCP host (meshtasticd) + qdrant_host: "" # Qdrant vector DB (optional) + tei_host: "" # TEI embedding service (optional) + sparse_host: "" # Sparse embedding service (optional) + +# Environmental Feed Center Point +env_center: + latitude: 0.0 # Center of your coverage area + longitude: 0.0 + +# Notification Targets +notification_targets: + smtp_from: "" # Email from address + smtp_recipients: [] # Default email recipients + webhook_urls: [] # Webhook endpoints + alert_node_ids: [] # Node IDs for mesh DM alerts + +# Critical Infrastructure Nodes (short names) +critical_nodes: [] +# Example: +# critical_nodes: +# - "MHR" +# - "HPR" diff --git a/meshai/main.py b/meshai/main.py index 48bc44b..6d1c41f 100644 --- a/meshai/main.py +++ b/meshai/main.py @@ -16,7 +16,8 @@ from .cli import run_configurator from .commands import CommandDispatcher from .commands.dispatcher import create_dispatcher from .commands.status import set_start_time -from .config import Config, load_config +from .config import Config +from .config_loader import load_config, get_config_dir_from_path from .connector import MeshConnector, MeshMessage from .context import MeshContext from .history import ConversationHistory @@ -712,12 +713,21 @@ def main() -> None: run_configurator(args.config_file) return - # Load config - config = load_config(args.config_file) + # Load config - support both old (/data/config.yaml) and new (/data/config/) layouts + config_path = args.config_file + config_dir = get_config_dir_from_path(config_path) - # Check if config exists - if not args.config_file.exists(): - logger.warning(f"Config file not found: {args.config_file}") + # Check for new multi-file layout first + if (config_dir / "config.yaml").exists(): + logger.info(f"Loading config from multi-file layout: {config_dir}") + config = load_config(config_dir) + elif config_path.exists(): + # Fall back to legacy single-file loading + logger.info(f"Loading legacy config: {config_path}") + from .config import load_config as legacy_load + config = legacy_load(config_path) + else: + logger.warning(f"Config not found at {config_path} or {config_dir}") logger.info("Run 'meshai --config' to create one, or copy config.example.yaml") sys.exit(1)