fix: require production backend URLs

This commit is contained in:
beubax
2026-06-12 12:42:06 +05:30
parent d4595568ec
commit be5c646b5b
6 changed files with 66 additions and 5 deletions
+1
View File
@@ -49,6 +49,7 @@ services:
AUTHSOME_HOST: 0.0.0.0
AUTHSOME_PORT: "7998"
AUTHSOME_HOME: /data/authsome
AUTHSOME_ENV: prod
AUTHSOME_BASE_URL: ${AUTHSOME_BASE_URL:-http://localhost:7998}
AUTHSOME_DATABASE_URL: postgresql://${AUTHSOME_POSTGRES_USER:-authsome}:${AUTHSOME_POSTGRES_PASSWORD:?set AUTHSOME_POSTGRES_PASSWORD}@postgres:5432/${AUTHSOME_POSTGRES_DB:-authsome}
AUTHSOME_REDIS_URL: redis://redis:6379/0
+2 -1
View File
@@ -37,6 +37,7 @@ Do not commit production secrets. Use your platform secret store or Docker secre
| Variable | Default | Description |
|---|---|---|
| `AUTHSOME_ENV` | `dev` | Runtime mode. Set to `prod` for production deployments; in `prod`, `AUTHSOME_DATABASE_URL` and `AUTHSOME_REDIS_URL` are required. |
| `AUTHSOME_DATABASE_URL` | none | Postgres DSN for the daemon-owned registries. The compose file points this at the bundled Postgres service. |
| `AUTHSOME_REDIS_URL` | none | Redis URL for shared runtime state and the encrypted vault raw KV backend. |
| `AUTHSOME_POSTGRES_PASSWORD` | none | Required password used by the bundled Postgres service and the daemon's database URL. |
@@ -54,7 +55,7 @@ Do not commit production secrets. Use your platform secret store or Docker secre
| `AUTHSOME_POSTHOG_API_KEY` | none | Enables PostHog analytics when present and telemetry is not opted out. |
| `AUTHSOME_POSTHOG_HOST` | `https://us.i.posthog.com` | Override the PostHog ingestion host if needed. |
The daemon still accepts the legacy `DATABASE_URL` alias, but production deployments should set `AUTHSOME_DATABASE_URL`.
The daemon still accepts the legacy `DATABASE_URL` alias, but production deployments should set `AUTHSOME_DATABASE_URL`. The included compose file sets `AUTHSOME_ENV=prod`, which makes the Postgres and Redis URLs mandatory at startup.
The included compose file hard-requires `AUTHSOME_MASTER_KEY` and `AUTHSOME_UI_SESSION_KEY` from the host environment; it does not mount secret files or pass `_FILE` paths for you.
## Secret resolution
+1 -1
View File
@@ -15,7 +15,7 @@ class AuthsomeConfig(BaseSettings):
model_config = SettingsConfigDict(env_prefix="AUTHSOME_")
version: str = __version__
env: Literal["prod", "dev", "test"] = "prod"
env: Literal["prod", "dev", "test"] = "dev"
home: Path = Field(default=Path.home() / ".authsome")
base_url: str = Field(default="http://127.0.0.1:7998")
+7
View File
@@ -28,6 +28,13 @@ class ServerConfig(AuthsomeConfig):
def validate_postgres_pool_sizes(self) -> "ServerConfig":
if self.postgres_pool_min_size > self.postgres_pool_max_size:
raise ValueError("postgres_pool_min_size must be less than or equal to postgres_pool_max_size")
if self.env == "prod":
if not self.database_url:
raise ValueError("AUTHSOME_DATABASE_URL is required when AUTHSOME_ENV=prod")
if not self.database_url.startswith(("postgresql://", "postgres://")):
raise ValueError("AUTHSOME_DATABASE_URL must be a Postgres URL when AUTHSOME_ENV=prod")
if not self.redis_url:
raise ValueError("AUTHSOME_REDIS_URL is required when AUTHSOME_ENV=prod")
return self
# Lifetimes, in seconds
+6 -3
View File
@@ -117,13 +117,16 @@ class StoreDatabase:
async def execute_rowcount(self, sql: str, params: Sequence[Any] = ()) -> int:
if self.backend == "sqlite":
cursor = await self._connection.execute(sql, params)
await self._connection.commit()
connection = self._connection
assert connection is not None
cursor = await connection.execute(sql, params)
await connection.commit()
rowcount = cursor.rowcount
await cursor.close()
return rowcount
status = await self._connection.execute(self._sql(sql), *params)
async with self._postgres_connection() as connection:
status = await connection.execute(self._sql(sql), *params)
_, _, count = status.partition(" ")
return int(count) if count else 0
+49
View File
@@ -13,6 +13,14 @@ def test_server_config_reads_redis_url(monkeypatch) -> None:
assert config.redis_url == "redis://localhost:6379/0"
def test_server_config_defaults_to_dev_env(monkeypatch) -> None:
monkeypatch.delenv("AUTHSOME_ENV", raising=False)
config = ServerConfig()
assert config.env == "dev"
def test_server_config_reads_authsome_database_url(monkeypatch) -> None:
monkeypatch.setenv("AUTHSOME_DATABASE_URL", "postgresql://authsome:secret@localhost/authsome")
monkeypatch.delenv("DATABASE_URL", raising=False)
@@ -52,6 +60,47 @@ def test_server_config_defaults_preserve_local_paths(tmp_path: Path) -> None:
assert config.kv_store_dir == tmp_path / "server" / "kv_store"
def test_server_config_requires_database_url_in_prod(monkeypatch) -> None:
monkeypatch.setenv("AUTHSOME_ENV", "prod")
monkeypatch.delenv("AUTHSOME_DATABASE_URL", raising=False)
monkeypatch.delenv("DATABASE_URL", raising=False)
monkeypatch.setenv("AUTHSOME_REDIS_URL", "redis://localhost:6379/0")
with pytest.raises(ValueError, match="AUTHSOME_DATABASE_URL is required when AUTHSOME_ENV=prod"):
ServerConfig()
def test_server_config_requires_redis_url_in_prod(monkeypatch) -> None:
monkeypatch.setenv("AUTHSOME_ENV", "prod")
monkeypatch.setenv("AUTHSOME_DATABASE_URL", "postgresql://authsome:secret@localhost/authsome")
monkeypatch.delenv("DATABASE_URL", raising=False)
monkeypatch.delenv("AUTHSOME_REDIS_URL", raising=False)
with pytest.raises(ValueError, match="AUTHSOME_REDIS_URL is required when AUTHSOME_ENV=prod"):
ServerConfig()
def test_server_config_requires_postgres_database_url_in_prod(monkeypatch) -> None:
monkeypatch.setenv("AUTHSOME_ENV", "prod")
monkeypatch.setenv("AUTHSOME_DATABASE_URL", "sqlite:////tmp/authsome.db")
monkeypatch.setenv("AUTHSOME_REDIS_URL", "redis://localhost:6379/0")
with pytest.raises(ValueError, match="AUTHSOME_DATABASE_URL must be a Postgres URL when AUTHSOME_ENV=prod"):
ServerConfig()
def test_server_config_accepts_prod_with_database_and_redis_urls(monkeypatch) -> None:
monkeypatch.setenv("AUTHSOME_ENV", "prod")
monkeypatch.setenv("AUTHSOME_DATABASE_URL", "postgresql://authsome:secret@localhost/authsome")
monkeypatch.setenv("AUTHSOME_REDIS_URL", "redis://localhost:6379/0")
config = ServerConfig()
assert config.env == "prod"
assert config.database_url == "postgresql://authsome:secret@localhost/authsome"
assert config.redis_url == "redis://localhost:6379/0"
def test_server_config_rejects_invalid_postgres_pool_range() -> None:
with pytest.raises(ValueError, match="postgres_pool_min_size must be less than or equal to postgres_pool_max_size"):
ServerConfig(postgres_pool_min_size=10, postgres_pool_max_size=2)