# Aether – Projektkonventionen für Claude Code ## Projekt-Überblick Self-hosted Cloud-Alternative zu Nextcloud. Go-Backend + React-Frontend, modularer Monolith. Projektplan: `cloud-projekt-plan.md` ## Monorepo-Struktur ``` aether/ ├── backend/ # Go – modularer Monolith │ ├── cmd/aether/ # main.go – Einstiegspunkt │ ├── internal/ # Domänen-Pakete (nie von außen importieren) │ │ ├── auth/ # Login, Sessions, CSRF, Argon2id, OIDC-User-Anlage │ │ ├── storage/ # Storage-Interface + LocalFS (atomar, SHA-256) │ │ ├── files/ # Datei-/Ordner-CRUD, Trash, Versionen, Favoriten │ │ ├── sharing/ # Öffentliche Share-Links │ │ ├── admin/ # Admin-Panel: User, Gruppen(-mitgliedschaft), Settings/Theming │ │ ├── oidc/ # OIDC-Login (optional, via .env) │ │ ├── avatar/ # Profilbild: zuschneiden, ablegen, ausliefern │ │ ├── webdav/ # WebDAV-Endpoint /dav/ (Basic Auth – nur App-Passwörter) │ │ ├── acl/ # Interne Freigaben an User/Gruppen (Rechteauflösung) │ │ ├── audit/ # Audit-Log (Schreibpfad; Endpunkte liegen in admin/) │ │ ├── gc/ # Blob-GC: Platte gegen DB abgleichen │ │ ├── thumbnails/ # Vorschaubild-Generierung │ │ ├── groups/ # LEERER STUB – Gruppenordner liegen in files/groupfolder.go │ │ ├── documents/ # Volltext: OCR-Warteschlange + Arbeiter (Phase 6, Teil 1) │ │ ├── tags/ # Ordnungsmerkmale einer Datei: Etiketten, Belegdatum, Absender │ │ └── office/ # WOPI-Host: Collabora/OnlyOffice, ein Adapter fuer beide │ ├── db/migrations/ # SQL-Migrations – Spiegelkopie (siehe Migrations-Regeln!) │ └── pkg/ # Hilfspakete: config, middleware, response, db (embed migrations) ├── frontend/ # React + TS + Vite + Tailwind │ └── src/ │ ├── components/ │ ├── pages/ │ ├── api/ # Typed API-Client │ └── hooks/ ├── build/ # Dockerfile + Compose zum Bauen aus dem Quelltext └── CLAUDE.md ``` ## Backend-Konventionen (Go) - **Module-Pfad**: `aether` - **Go-Version**: 1.25 (siehe `go.mod`) - **Router**: `github.com/go-chi/chi/v5` - **DB-Treiber**: `github.com/jackc/pgx/v5` (pgxpool für Connection-Pool) - **Migrations**: `github.com/golang-migrate/migrate/v4` - **Logging**: `github.com/rs/zerolog` – strukturiert, immer mit Kontext (`log.Ctx(ctx)`) - **Hashing**: `golang.org/x/crypto/argon2` (Argon2id) – App-Passwörter dagegen SHA-256 (zufälliges Token, jeder WebDAV-Request authentifiziert einzeln) - **Sessions**: `github.com/gorilla/sessions` mit securecookie ### Paket-Regeln - Alle Domänen-Pakete unter `internal/` – kein Import von außen - Keine zirkulären Abhängigkeiten; Dependency-Richtung: `cmd → internal/* → pkg/*` - Handler in `internal//handler.go`, Business-Logic in `internal//service.go`, DB in `internal//store.go` - Interfaces für alle externen Abhängigkeiten (Storage, DB) → leicht mockbar ### Code-Stil - `gofmt` + `goimports` – immer formatiert - Fehler nie ignorieren (`_`), immer wrappen mit `fmt.Errorf("context: %w", err)` - Keine Magic-Numbers – Konstanten mit sprechenden Namen - HTTP-Handler signieren: `func (h *Handler) MethodName(w http.ResponseWriter, r *http.Request)` - Middleware-Chain: Auth → CSRF → RateLimit → Handler ### Tests - Testdatei neben der Quelldatei: `auth_test.go` neben `auth.go` - Integrationstests mit echtem Postgres via `testcontainers-go` - Pflicht-Tests: Path-Traversal, Upload-Abbruch, Checksum-Verifikation, Soft-Delete-Recovery - `go test ./...` muss grün sein vor jedem Commit ## Frontend-Konventionen (React + TS) - **Bundler**: Vite - **Styling**: Tailwind CSS + CSS-Variablen für Design-Tokens - **Testing**: Vitest + Testing Library; E2E: Playwright - **API-Client**: typed fetch-Wrapper unter `src/api/` ### Design-Tokens (CSS-Variablen) Alle Farben laufen über CSS-Custom-Properties: ```css :root { --color-primary: ...; --color-accent: ...; --color-background: ...; --color-surface: ...; --color-text: ...; } ``` Admin setzt globale Tokens; User-Override nur für eigene Palette (Scope: `[data-user-theme]`). ### Komponenten-Regeln - Komponenten in `PascalCase.tsx` - Kein `dangerouslySetInnerHTML` mit User-Input (XSS) - API-Calls nur über `src/api/`-Funktionen, nie direkt `fetch` in Komponenten ## Sicherheits-Pflichten - Path-Traversal: jeden Dateipfad kanonisieren und gegen Storage-Root prüfen - CSRF: Double-Submit-Cookie oder synchronizer-Token - Rate-Limiting: Login-Endpunkt + Admin-Login strenger - Auth-Check: jede Route serverseitig prüfen – nie Frontend vertrauen - SQL: ausschließlich parametrisierte Queries (pgx unterstützt das nativ) - Uploads: außerhalb Webroot, keine ausführbaren Rechte, MIME-Prüfung - WebDAV: **nur App-Passwörter**, nie das Kontopasswort – es ist zugleich der Admin-Zugang - Kein Zugriff auf fremde IDs → `404`, nicht `403`: sonst verrät die Antwort die Existenz - Listen und Suche schneiden den erreichbaren Bereich **in der Abfrage** zu, nie nachträglich im Handler - Hochgeladene Dateien nur für eine Positivliste harmloser Typen `inline` ausliefern (sonst läuft fremdes HTML im eigenen Ursprung) ## Datenintegrität (nicht verhandelbar) 1. **Atomares Schreiben**: Temp-Datei → `fsync` → `rename`. Niemals in-place. 2. **SHA-256**: beim Upload berechnen + in DB; beim Download verifizieren. 3. **Soft-Delete only**: kein hartes Löschen. Bytes erst per GC entfernen. 4. **DB-Commit nach Blob-Write**: Metadaten erst committen, wenn Blob erfolgreich. ## Migrations-Regeln - Dateien: `000001_init.up.sql` / `000001_init.down.sql` - **Jede Migration in BEIDE Verzeichnisse legen:** `backend/pkg/db/migrations/` (wird ins Binary embedded und beim Start ausgeführt) UND `backend/db/migrations/` (Spiegelkopie). Beide müssen identisch sein. - Keine destruktiven Änderungen ohne explizites Down-Migration - Migration beim App-Start automatisch ausführen (via `migrate.Up()`) - Aktueller Stand: bis `000019_user_avatar` (Profilbild eines Kontos) ## Commit-Regeln Format: `(): ` Typen: `feat`, `fix`, `test`, `refactor`, `docs`, `chore`, `security` Beispiele: - `feat(auth): add argon2id password hashing` - `test(storage): add checksum verification test` - `security(admin): enforce server-side admin check on all routes` Kein Commit ohne grüne Tests. ## Reverse Proxy & Proxy-Awareness - **Kein Proxy-Container** in docker-compose.yml – TLS und Domain gehören zum Wirt, nicht ins Projekt. Nichts hier soll einen mitgelieferten Proxy voraussetzen. - App lauscht auf reinem HTTP (`APP_LISTEN_ADDR`). TLS = externer Proxy. - `X-Forwarded-For` / `X-Forwarded-Proto` werden **nur** von `TRUSTED_PROXIES` (IPs/CIDRs) akzeptiert. - Echte Client-IP immer über `middleware.RealIP(r)` abrufen – niemals `r.RemoteAddr` direkt. - `Secure`-Cookie-Flag: über `middleware.IsHTTPS(r)` setzen (berücksichtigt Forwarded-Proto). - `PUBLIC_URL` für OIDC-Redirects, WOPI-Callbacks und Share-Links verwenden. - HSTS gehört auf den Edge-Proxy, **nicht** in die App-Header. ## Docker & Secrets - **Backend immer via Docker starten/laufen lassen (`docker compose -f build/docker-compose.yml ...`), nie `go run`.** Der Host hat kein Go-Toolchain installiert; Builds/Compile-Checks laufen über das `golang:1.25-alpine`-Image. Migrations + DB-Verbindung sind auf die Compose-Umgebung ausgelegt. - Secrets **nur** über `.env` / Compose-Env – nie im Image oder Repo - Multi-Stage-Build: Builder-Stage (Go-Compiler) → Alpine final - `.env` ist in `.gitignore`; `.env.example` zeigt alle Keys ohne Werte ## Phasen-Stopp Nach Phase 1: **STOPP** – Zusammenfassung + Phase-2-Vorschlag, erst nach OK weitermachen.