#!/usr/bin/env python3 """Rotate API key for AegisGitea MCP.""" import re import sys from datetime import datetime, timedelta, timezone from pathlib import Path # Add src to path for imports sys.path.insert(0, str(Path(__file__).parent.parent / "src")) from aegis_gitea_mcp.auth import generate_api_key def main() -> None: """Rotate API key in .env file.""" print("=" * 70) print("AegisGitea MCP - API Key Rotation") print("=" * 70) print() # Find .env file env_file = Path(__file__).parent.parent / ".env" if not env_file.exists(): print("❌ Error: .env file not found") print(f" Expected location: {env_file}") print() print(" Please create .env from .env.example first") sys.exit(1) # Read current .env with open(env_file, "r", encoding="utf-8") as f: env_content = f.read() # Check if MCP_API_KEYS exists if "MCP_API_KEYS" not in env_content: print("❌ Error: MCP_API_KEYS not found in .env file") print() print(" Please add MCP_API_KEYS to your .env file first") sys.exit(1) # Extract current key match = re.search(r"MCP_API_KEYS=([^\n]+)", env_content) if not match: print("❌ Error: Could not parse MCP_API_KEYS from .env") sys.exit(1) current_keys = match.group(1).strip() key_list = [k.strip() for k in current_keys.split(",") if k.strip()] print(f"Found {len(key_list)} existing key(s)") print() # Show current keys (first 8 chars only) for i, key in enumerate(key_list, 1): key_hint = f"{key[:8]}...{key[-4:]}" if len(key) >= 12 else "invalid" print(f" {i}. {key_hint}") print() print("-" * 70) print() # Generate new key new_key = generate_api_key(length=64) created_at = datetime.now(timezone.utc) expires_at = created_at + timedelta(days=90) print("✓ New API key generated!") print() print(f"Created: {created_at.strftime('%Y-%m-%d %H:%M:%S UTC')}") print(f"Expires: {expires_at.strftime('%Y-%m-%d %H:%M:%S UTC')} (90 days)") print() print("-" * 70) print() # Ask what to do with old keys print("Rotation Strategy:") print() print(" 1. Replace all keys with new key (recommended)") print(" 2. Add new key, keep old keys (grace period)") print(" 3. Cancel") print() choice = input("Choose option [1/2/3]: ").strip() if choice == "3": print("\nRotation cancelled.") sys.exit(0) elif choice == "2": # Add new key to list key_list.append(new_key) new_keys_str = ",".join(key_list) print("\n✓ New key will be added (total: {} keys)".format(len(key_list))) print("\n⚠️ IMPORTANT: Remove old keys manually after updating ChatGPT config") elif choice == "1": # Replace with only new key new_keys_str = new_key print("\n✓ All old keys will be replaced with new key") else: print("\n❌ Invalid choice. Operation cancelled.") sys.exit(1) print() print("-" * 70) print() # Update .env file new_env_content = re.sub(r"MCP_API_KEYS=([^\n]+)", f"MCP_API_KEYS={new_keys_str}", env_content) # Backup old .env backup_file = env_file.with_suffix(f".env.backup-{datetime.now().strftime('%Y%m%d-%H%M%S')}") with open(backup_file, "w", encoding="utf-8") as f: f.write(env_content) print(f"✓ Backed up old .env to: {backup_file.name}") # Write new .env with open(env_file, "w", encoding="utf-8") as f: f.write(new_env_content) print(f"✓ Updated .env file with new key(s)") print() print("-" * 70) print() print("📋 Next Steps:") print() print("1. Restart the MCP server:") print() print(" docker-compose restart aegis-mcp") print() print("2. Update ChatGPT Business configuration:") print() print(" - Go to ChatGPT Settings > MCP Servers") print(" - Update Authorization header:") print(f" Authorization: Bearer {new_key}") print() print("3. Test the connection:") print() print(" Ask ChatGPT: 'List my Gitea repositories'") print() print("4. If using grace period (option 2):") print() print(" - After confirming ChatGPT works with new key") print(" - Manually remove old keys from .env") print(" - Restart server again") print() print("-" * 70) print() print("⚠️ IMPORTANT:") print() print(f" • New API Key: {new_key}") print(" • Store this securely - it won't be shown again") print(" • Set a reminder to rotate in 90 days") print(" • Old .env backed up to:", backup_file.name) print() print("=" * 70) if __name__ == "__main__": try: main() except KeyboardInterrupt: print("\n\nOperation cancelled.") sys.exit(1) except Exception as e: print(f"\n❌ Error: {e}", file=sys.stderr) import traceback traceback.print_exc() sys.exit(1)