Files
channel-exit/app/config.py
T

52 lines
1.9 KiB
Python
Raw Normal View History

2026-08-18 15:41:28 -04:00
"""What the relay needs to know, and what it refuses to start without.
The bearer token is the only thing standing between this service and an open
spam relay, so an unset token is a startup failure rather than a service that
accepts everything. Provider credentials are the opposite: a missing SendGrid
key must not stop SMS working, so each channel is asked at send time whether it
is configured and answers for itself.
"""
from __future__ import annotations
import os
from dataclasses import dataclass, field
def _env(name: str, default: str = "") -> str:
return (os.environ.get(name, default) or "").strip()
@dataclass(frozen=True)
class Settings:
# The shared secret every caller must present. No default: a relay that
# falls back to a well-known token is worse than one that will not boot.
token: str = field(default_factory=lambda: _env("CHANNEL_EXIT_TOKEN"))
sendgrid_key: str = field(default_factory=lambda: _env("SENDGRID_API_KEY"))
# SendGrid rejects a From that is not a verified sender, so this is an
# operator setting and never anything a caller can influence.
email_from: str = field(default_factory=lambda: _env("EMAIL_FROM"))
twilio_sid: str = field(default_factory=lambda: _env("TWILIO_ACCOUNT_SID"))
twilio_token: str = field(default_factory=lambda: _env("TWILIO_AUTH_TOKEN"))
twilio_from: str = field(default_factory=lambda: _env("TWILIO_FROM"))
timeout: float = field(default_factory=lambda: float(_env("PROVIDER_TIMEOUT", "15") or 15))
@property
def email_ready(self) -> bool:
return bool(self.sendgrid_key and self.email_from)
@property
def sms_ready(self) -> bool:
return bool(self.twilio_sid and self.twilio_token and self.twilio_from)
def require_token(self) -> None:
if not self.token:
raise RuntimeError("CHANNEL_EXIT_TOKEN is unset; refusing to run an unauthenticated relay")
def get_settings() -> Settings:
return Settings()