G0: cell substrate — invariants made executable
Strand G0 complete and VERIFIED against real Postgres, not asserted.
- FastAPI plane, fail-closed provider seams, repair-pointer error taxonomy
- migration 001: all 10 tables incl. repo_type NOT NULL and model_cards (I-7)
- 17 invariant tests, ruff clean, vocabulary audit clean
Two bugs found by RUNNING it that review would not have caught:
1. SQLAlchemy Enum persists .name, not .value — so RepoState.deleted_soft
and CreatedVia.imported would have written labels migration 001 never
declared, failing at runtime rather than at review. Pinned via
values_callable.
2. op.create_table asks each Enum to emit its own CREATE TYPE with no
checkfirst, so the second reference raised DuplicateObject and the
migration died halfway. Types are now created once, referenced with
create_type=False.
Proven live, with the hostile env var set:
- I-12: COMMIT_SHA=deadbeef... in the environment, /version reports real HEAD.
That env pin is the documented root cause of nine sibling services
misreporting their commit; here it is structurally ignored.
- I-8: three unconfigured providers -> status degraded, HTTP 503, each saying
'refusing to report healthy'. No mock, no false green.
- G0.4: upgrade -> downgrade -> upgrade round-trip clean (10 -> 0 -> 10).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
119
api/app/config.py
Normal file
119
api/app/config.py
Normal file
@@ -0,0 +1,119 @@
|
||||
"""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"
|
||||
|
||||
# ---- 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()
|
||||
Reference in New Issue
Block a user