# Design Rationale — gitea-reports Miksi gitea-reports on rakennettu näin. Periaatteet, jotka pitävät arkkitehtuurin koossa. Teknologiavalinnat ovat näiden periaatteiden seurauksia — eivät erillisiä preferenssejä. Tämä dokumentti on normatiivinen. Arkkitehtuurin tulee noudattaa sen periaatteita. Ehdotus, joka on ristiriidassa dokumentin kanssa, on konflikti — ei hiljainen ohitus. ## The problem this solves CI-testiajoista syntyy HTML-raportteja. Gitea ei tarjoa web-selaimella selattavaa arkistoa näille raporteille. Vaihtoehdot eivät sovi: - **Gitea Actions artifactit**: vain ZIP-lataus — HTML ei renderöidy selaimessa - **Gitea pages-branch**: yksi branch per repo; rinnakkaiset buildit törmäävät - **Gitea Releases**: sotkee julkaisuhistorian sadoilla CI-buildien raporteilla ## How this is solved Yksi klusteri, yksi apex-host, monta Gitea-repoa. URL on suoraan FS-polku: `/{owner}/{repo}/{branch}/{sha8}/{suite}/`. Ei rewritea, ei subdomain-per-owner, ei slugia. Palvelu on read/write -jaettu: luku ja julkaisu eri sovelluksilla, eri porteilla, eri Traefik-säännöillä. ## Why selvä URL + Gitea-yhteensopiva polku Repo tunnistetaan polusta `/{owner}/{repo}/...`, ei hostista. Kaikki URL-taso (branch, sha8, suite) on suoraan FS-polku. Tämä mahdollistaa yhden TLS-sertifikaatin, yhden IngressRouten ja URL:n, joka on suoraan kopioitavissa commit-statusiin ilman rewritea. **Hylätty:** subdomain-per-owner (`{owner}.pages...`). Vaati per-owner Traefik- rewritea ja wildcard-TLS:ää. Julkaisu-URL ja luku-URL olivat eri muodossa — kehittäjälle vaikea ymmärtää ja debugata. ## Why luku ja kirjoitus eri sovelluksilla Yksi binary, joka palvelee sekä lukua että kirjoitusta samassa prosessissa, on yksinkertainen mutta yhdistää kaksi eri asiaa. Tässä arkkitehtuurissa nginx palvelee staattisia tiedostoja; Python-skripti purkaa tar.gz-julkaisun PV:lle. Ne eivät tiedä toisistaan. Luku ei tarvitse julkaisulogiikkaa. Julkaisu ei tarvitse optimoitua staattista palvelua. Erottamalla ne kumpikin voidaan valita parhaaseen tarkoitukseensa. ## Why security klusterin reunalla — ei sovelluksessa Sovellus on "tyhmä": se palvelee mitä PV:llä on ja kirjoittaa mitä sille annetaan. Valtuutus tapahtuu yksinomaan Traefik BasicAuth -middlewaressa. Tämä mahdollistaa: - Julkaisu- ja lukuoikeuksien erillisen hallinnan eri Traefik-säännöillä - Sovelluksen vaihtamisen ilman turvallisuusauditointia - Token-rotaation ilman sovelluksen uudelleenkäynnistystä **Hylätty:** sovelluksen sisäinen forge-auth (Gitea PAT, DNS TXT -haaste). Sitoo sovelluksen tiettyyn autentikointimekanismiin ja vaatii laajat oikeudet (`write:repository`) pelkkää raporttijulkaisua varten. ## Why retention erillisenä CronJobina Retention ei tarvitse olla aina käynnissä — se ajetaan kerran vuorokaudessa. CronJob lukee PV:tä suoraan (find, ls, stat), ei HTTP API:n kautta. Tämä pitää retention poissa podin kriittiseltä polulta (luku/julkaisu). **Hylätty:** sidecar samassa podissa. Sidecar kuluttaa resursseja aina, ja retention-logiikka sekoittuu julkaisu- ja lukukoodiin. ## Why yksi token, kaksi säilöä Sama plaintext-arvo: htpasswd-hashina K8s Secretissä (Traefik lukee), plaintextina Gitea Actions Secretissä (julkaisija lukee). Yksi rotaatio päivittää molemmat. Token antaa vain julkaisuoikeuden tähän palveluun — ei Gitea PAT:ia eikä `write:repository`-oikeutta. ## Why PVC suoraan FS-muodossa Ei objektivarastoa, ei HTTP API:a retentionille. Sisältö on suoraan luettavissa kubectl exec + ls/find. Yksinkertaisin mahdollinen tallennusmuoto. Yksi replica, ei synkronointia. ## References - [architecture.md](architecture.md) — komponentit ja vuokaavio - [tech-stack.md](tech-stack.md) — teknologiavalinnat listana - [secrets.md](secrets.md) — secret-arkkitehtuuri ja käyttöönotto