gabriel / musehub public
config.py python
99 lines 3.5 KB
c0f0b481 release: merge dev → main (#5) Gabriel Cardona <cgcardona@gmail.com> 5d ago
1 """
2 Muse Configuration
3
4 Environment-based configuration for the Muse service.
5 """
6 from __future__ import annotations
7
8 import logging
9 from functools import lru_cache
10
11 from pydantic import model_validator
12 from pydantic_settings import BaseSettings, SettingsConfigDict
13
14
15 def _app_version_from_package() -> str:
16 """Read version from the single source of truth (pyproject.toml via protocol.version)."""
17 from musehub.protocol.version import MUSE_VERSION
18 return MUSE_VERSION
19
20
21 class Settings(BaseSettings):
22 """Application settings loaded from environment variables."""
23
24 # Service Info
25 app_name: str = "Muse"
26 app_version: str = _app_version_from_package()
27 debug: bool = False
28
29 # Server Configuration
30 host: str = "0.0.0.0"
31 port: int = 10001
32
33 # Database Configuration
34 # PostgreSQL: postgresql+asyncpg://user:pass@localhost:5432/muse
35 # SQLite (dev): sqlite+aiosqlite:///./muse.db
36 database_url: str | None = None
37 db_password: str | None = None
38
39 # CORS Settings (fail closed: no default origins)
40 # Set CORS_ORIGINS (JSON array) in .env. Local dev: ["http://localhost:10003", "muse://"].
41 # Production: exact origins only. Never use "*" in production.
42 cors_origins: list[str] = []
43
44 @model_validator(mode="after")
45 def _warn_cors_wildcard_in_production(self) -> "Settings":
46 """Warn when CORS allows all origins in non-debug (production) mode."""
47 if not self.debug and self.cors_origins and "*" in self.cors_origins:
48 logging.getLogger(__name__).warning(
49 "CORS allows all origins (*) with DEBUG=false. "
50 "Set CORS_ORIGINS to exact origins in production."
51 )
52 return self
53
54 # Access Token Settings
55 # Generate secret with: openssl rand -hex 32
56 access_token_secret: str | None = None
57 access_token_algorithm: str = "HS256"
58 # In-memory revocation cache TTL (seconds). Reduces DB hits; revocation visible within this window.
59 token_revocation_cache_ttl_seconds: int = 60
60
61 # AWS S3 Asset Delivery (drum kits, GM soundfont)
62 # Region MUST match the bucket's region (S3 returns 301 if URL uses wrong region).
63 aws_region: str = "eu-west-1"
64 aws_s3_asset_bucket: str | None = None
65 aws_cloudfront_domain: str | None = None
66 presign_expiry_seconds: int = 1800 # 30-min default for presigned download URLs
67
68 # Asset endpoint rate limits (UUID-only auth, no JWT)
69 asset_rate_limit_per_device: str = "30/minute"
70 asset_rate_limit_per_ip: str = "120/minute"
71
72 # Stdio MCP server: proxy DAW tools to Muse backend
73 muse_mcp_url: str | None = None
74 mcp_token: str | None = None
75
76 # MuseHub object storage — binary artifacts (MIDI, MP3, WebP) stored as flat files
77 # under <musehub_objects_dir>/<repo_id>/<object_id>. Mount on a persistent volume in prod.
78 musehub_objects_dir: str = "/data/musehub/objects"
79
80 # Webhook secret encryption key — AES-256 (Fernet) key for encrypting webhook signing
81 # secrets at rest. Generate with:
82 # python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
83 webhook_secret_key: str | None = None
84
85 model_config = SettingsConfigDict(
86 env_file=".env",
87 env_file_encoding="utf-8",
88 extra="ignore", # silently discard unknown env vars (e.g. OPENROUTER_API_KEY from other tools)
89 )
90
91
92 @lru_cache()
93 def get_settings() -> Settings:
94 """Get cached settings instance."""
95 return Settings()
96
97
98 # Convenience access
99 settings = get_settings()