Files
windy-git/api/app/config.py
Grant Whitmer a68261a563 G1: Veron 1 host live behind Cloudflare Tunnel
app.windygit.com / api.windygit.com / models.windygit.com are serving over
HTTPS with ZERO inbound ports open on Grant's network.

  - tunnel 4e856c5d, 4 registered edge connections, systemd-managed and bounded
  - three proxied single-level CNAMEs (Free Universal SSL covers them; a
    two-level name would need ACM and would die in the TLS handshake)
  - services bound to 127.0.0.1 with configurable host ports — Veron 1 is
    Grant's workstation and 3000/3300 belong to other projects
  - docs/RUNBOOK-VERON.md

I-12 PROVEN IN PRODUCTION: /version reports source=baked with a sha equal to
the deployed HEAD.

Also fixed: the tunnel health probe targeted localhost from inside a container,
so it was permanently red. A check that is always red is as useless as one that
is always green — it is how a fleet canary goes 37 days dead unnoticed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 14:34:18 -04:00

126 lines
4.9 KiB
Python

"""Settings for the windy-git plane.
Every `env:` default in DNA_STRAND_MASTER_PLAN.md is shipped here as an actual
default, not a suggestion. Providers are FAIL-CLOSED (I-8): a provider whose
credentials are absent reports itself unconfigured and refuses to answer, rather
than answering from a mock. The domains cell shipped a portal on a mock registrar
and told the public that google.com was available for $18.00 a year. That failure
mode is banned here by construction.
"""
from __future__ import annotations
from functools import lru_cache
from pydantic import Field
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(
env_file=".env", env_file_encoding="utf-8", extra="ignore"
)
# ---- service identity -------------------------------------------------
environment: str = "development"
service_name: str = "windy-git"
port: int = 8600
# ---- database ---------------------------------------------------------
# Postgres schema `windgit`, own alembic (G0.4).
database_url: str = "postgresql+asyncpg://windygit:windygit@localhost:5432/windygit"
db_schema: str = "windgit"
# ---- Gitea (a COMPONENT behind an API membrane, never a merged tree) ---
gitea_base_url: str = "http://localhost:3000"
gitea_admin_token: str = ""
# ---- Cloudflare R2 (I-3: heavy bytes only, never git objects) ---------
r2_account_id: str = ""
r2_access_key_id: str = ""
r2_secret_access_key: str = ""
r2_bucket_lfs: str = "windy-git-lfs"
r2_bucket_artifacts: str = "windy-git-artifacts"
r2_bucket_backups: str = "windy-git-backups"
# ---- Eternitas (agent identity + trust) -------------------------------
eternitas_base_url: str = "https://api.eternitas.ai"
eternitas_platform_api_key: str = ""
# ---- account-server OIDC (human identity) -----------------------------
account_server_base_url: str = "https://account.windyword.ai"
# cloudflared binds its metrics on the HOST, so from inside a container
# `localhost` is the wrong box. A health check that is permanently red is as
# useless as one that is permanently green -- it trains people to ignore the
# dashboard, which is how a 37-day-dead fleet canary goes unnoticed.
tunnel_metrics_url: str = "http://host.docker.internal:2000/metrics"
# ---- storage law (I-3, G4.4) ------------------------------------------
# Git object databases MUST live on a POSIX filesystem. A test asserts this
# path does not resolve to a network mount.
git_data_root: str = "/srv/windygit/git"
# ---- LFS threshold (G4.5) ---------------------------------------------
# Small text files stay in git proper. LFS-for-everything makes clones slow
# and operations heavy.
lfs_threshold_bytes: int = 5 * 1024 * 1024 # env: 5 MB
lfs_extensions: tuple[str, ...] = (
".safetensors", ".bin", ".gguf", ".pt", ".ckpt", ".onnx",
".zip", ".tar", ".gz", ".mp4", ".wav", ".mov", ".psd",
)
# ---- velocity bases, multiplied by EI band (G3.4) ---------------------
# Platinum x10 / Gold x4 / Standard x1 / Watch x0.5 / Untrusted read-only
rate_pushes_per_day: int = 500
rate_repo_creates_per_day: int = 50
rate_grants_per_day: int = 100
rate_force_pushes_per_day: int = 10
# ---- mirror health (I-4) ----------------------------------------------
mirror_lag_p2_seconds: int = 3600 # env: 60 min -> P2
# ---- agent grants (G5.3) ----------------------------------------------
agent_grant_default_days: int = 90
# ---- repo types (I-7: first-class from migration 001) -----------------
repo_types_enabled: tuple[str, ...] = ("code",) # model/dataset are v2
# ---- feature gates ----------------------------------------------------
# Mirrors the sibling `edge_live` pattern (windy-cloud-sites, a8ff948):
# never claim live while a provider is mock.
hf_compat_enabled: bool = False # Grant-gated, DNA plan section 7.7
kit0_host: str = Field(
default="72.60.118.54",
description="Recorded ONLY so the G1 guard can refuse to deploy here (D-4).",
)
# ---- derived ----------------------------------------------------------
@property
def r2_configured(self) -> bool:
return bool(
self.r2_account_id and self.r2_access_key_id and self.r2_secret_access_key
)
@property
def gitea_configured(self) -> bool:
return bool(self.gitea_base_url and self.gitea_admin_token)
@property
def eternitas_configured(self) -> bool:
return bool(self.eternitas_base_url and self.eternitas_platform_api_key)
@property
def r2_endpoint_url(self) -> str:
return f"https://{self.r2_account_id}.r2.cloudflarestorage.com"
@property
def is_production(self) -> bool:
return self.environment.lower() in {"production", "prod"}
@lru_cache
def get_settings() -> Settings:
return Settings()