# Plan: Drei-Schichten-Architektur (Core / Lokal / Public) > Stand: 2026-07-12 · Status: **Architektur bestätigt** (Nutzer-Entscheidung), Implementierung > schrittweise. Kein Code in diesem Schritt. > Kontext: Neues WebUI-Design (mit Claude Design vorbereitet) wird nach der Schicht-Trennung > schrittweise implementiert. Public-Release ist noch fern, aber alles wird dafür vorbereitet. ## 0. Bestätigte Architektur Drei Schichten mit **genau einer** Integrationsschnittstelle zwischen intern und öffentlich: **der Datenbank**. ``` ┌─────────────────────────── LOKAL (eigener Rechner/LAN) ───────────────────────────┐ │ │ │ Schicht 1: CORE Schicht 2: LOKALE API + WebUI │ │ ───────────────── ─────────────────────────── │ │ Crawler, Worker, PnL-Engine, volle API (Read + Control), │ │ Analyse, Ingest keine Auth (localhost), │ │ │ schreibt serviert WebUI-SPA, │ │ ▼ zum Testen von Analysen & Design │ │ ┌──────────────────┐ liest/schreibt ▲ │ │ │ Analyse-DB │◄────────────────────────┘ │ │ └────────┬─────────┘ │ │ │ (nur SELECT-Grant) │ └────────────┼──────────────────────────────────────────────────────────────────────┘ │ ← EINZIGE Verbindung nach außen: read-only DB-Zugriff ┌────────────┼──────────────────────── EXTERNER WEBSERVER ───────────────────────────┐ │ ▼ │ │ Schicht 3: PUBLIC API + WebUI │ │ ──────────────────────────── │ │ nur Read-Endpunkte (identisch zur lokalen Read-Seite), │ │ Auth + Accounts, öffentliches Rate-Limiting, TLS, │ │ serviert dasselbe WebUI-SPA │ │ │ read-only │ read/write │ │ ▼ ▼ │ │ [Analyse-DB, RO] [User-DB (Accounts), separat] │ └────────────────────────────────────────────────────────────────────────────────────┘ ``` **Kernprinzipien (aus der Nutzer-Entscheidung):** - **Core ist rein lokal** und die einzige Instanz, die in die Analyse-DB **schreibt**. - **Public spricht ausschließlich mit der Datenbank** — nie mit dem Core-Prozess, nie mit der lokalen API. Public bekommt einen **read-only** DB-User auf die Analyse-DB und eine **separate User-DB** für Accounts. - **Lokale und Public WebUI sind optisch UND funktional gleich** (auf der Analyse-/Ansichtsseite), damit neue Analysen und Designs lokal getestet werden und 1:1 öffentlich aussehen. ## 1. Wie „identisch" technisch erreicht wird Damit beide WebUIs garantiert gleich sind, wird die **Read-/Analyse-Seite genau einmal** gebaut und von beiden Hosts wiederverwendet — kein Duplizieren: 1. **Geteilte Read-API-Library.** `Predictalytics.Api` ist bereits eine Library. Die Endpoint-Registrierung wird in zwei Gruppen gespalten: - `MapReadEndpoints()` — **die Analyse-/Ansichts-Endpunkte** (Trader-Liste/-Detail/-Profil/ -Positionen, Markets, Dashboard, Search, Watchlist-Ansicht, Traits). Wird von **beiden** Hosts registriert. - `MapControlEndpoints()` — **schreibende/steuernde** Endpunkte (Jobs-Trigger, `/api/dev`, Trader-Anlage, Watchlist-Mutation, Priority, AI-Analyse-Trigger). Wird **nur lokal** registriert. 2. **Ein einziges WebUI-SPA-Artefakt.** Dasselbe Build wird von der lokalen und der öffentlichen Schicht ausgeliefert. Es ist reiner API-Client (kein Server-Code, keine DB-Kenntnis). 3. **Capability-gesteuerte Admin-Bedienelemente.** Da die Control-Endpunkte öffentlich fehlen, fragt das SPA beim Start eine `GET /api/capabilities` ab (`{ canControl: bool, authRequired: bool }`) und blendet Admin-Aktionen (Sync/Analyze/Deep-Resync/Trader-Anlegen) nur ein, wenn vorhanden. → Analyse-Ansichten sind überall identisch; nur die lokalen Steuer-Buttons fehlen öffentlich. > **Wichtiger Audit-Punkt vor dem Teilen:** Manche „Read"-Endpunkte haben heute versteckte > Nebenwirkungen. Beispiel: `GET /api/traders/{id}/deep-dive` **holt Preis-Historie vom Provider > und speichert Snapshots** — also ein Schreibzugriff **und** ein externer API-Call. Solche > Endpunkte sind **nicht** public-tauglich (read-only DB verbietet den Write, und Public darf > Polymarket nicht anrufen). Vor dem Aufnehmen in `MapReadEndpoints()` jeden Endpunkt auf > versteckte Writes/Provider-Calls prüfen; die Public-Read-Seite muss **rein aus persistierten > Daten** bedienbar sein (das `/profile`-Design aus FIXPLAN E ist genau deshalb „persisted-only"). ## 2. Was heute wo läuft (Ist → Ziel) - **Heute:** Core + Lokale API + WebUI sind **ein** Prozess (WinFormsHost/EmbeddedWebServer: Worker + volle API + `wwwroot`-SPA, localhost, keine Auth, CORS `*`). Public existiert nicht. - **Ziel Lokal:** bleibt bequem **ein** Prozess (Core-Worker + Lokale API + WebUI zusammen ist zum Testen praktisch). Wichtig ist nur die **Code-Schichtung** (Read-Library getrennt von Control/Core), damit Public die Read-Library **ohne** Core/Worker/Control referenzieren kann. - **Ziel Public:** eigener schlanker Host (neues Projekt `Predictalytics.PublicApi`), der `MapReadEndpoints()` + Auth registriert, das SPA ausliefert, und **nur** die read-only Analyse-DB + die User-DB kennt. ## 3. Implementierungsplan (schrittweise) ### Phase 0 — Endpunkte klassifizieren & Read/Control trennen (Fundament, klein) - [ ] Jeden Endpunkt in `docs/API.md` als **Read** (public-tauglich) oder **Control** (nur lokal) markieren. Kandidaten Control: alle `POST/PUT/DELETE`, `/api/dev/*`, Job-Trigger. - [ ] Auf **versteckte Writes/Provider-Calls** in Read-Endpunkten prüfen (Deep-Dive!) und bereinigen oder als Control einstufen. - [ ] `MapPredictalyticsEndpoints()` in `MapReadEndpoints()` + `MapControlEndpoints()` splitten (rein struktureller Refactor, Verhalten unverändert; bestehende Tests bleiben grün). - [ ] `GET /api/capabilities` einführen. ### Phase 1 — WebUI als sauberes, gemeinsames SPA (parallel zum neuen Design) - [ ] Neues Design als **ein statisches Artefakt** bauen, reiner API-Client, `API_BASE` konfigurierbar (heute `''` = gleiche Origin bleibt lokal gültig). - [ ] Admin-Aktionen an `capabilities.canControl` koppeln. - [ ] Trait-Chips/-Filter im Design vorsehen — **erscheinen erst, wenn Backend-D2 steht** (kein UI-Bug, das Backend liefert Traits noch nicht). ### Phase 2 — Lokale Grenze härten (klein, jetzt schon sinnvoll) - [ ] CORS von `AllowAnyOrigin` auf konfigurierte Origin-Whitelist umstellen. - [ ] Bind-Adresse explizit (localhost/LAN), nie `0.0.0.0` ohne Firewall; Konfig-Kommentar „NIEMALS ins Internet — das ist die interne Schicht". ### Phase 3 — DB für Public vorbereiten (mittel, kann früh passieren) - [ ] **Read-only DB-User** auf der Analyse-DB anlegen (nur `SELECT`). Der Nutzer führt das SQL selbst aus (Grant-Statements liefern wir). - [ ] `AppDbContext` public-seitig read-only konfigurieren (read-only Connection; keine Migrations, kein SaveChanges). Sicherstellen, dass die Read-Endpunkte ohne Writes auskommen. - [ ] **Schema-Kopplung dokumentieren:** Weil Public direkt auf der Analyse-DB liest, ist das DB-Schema jetzt eine **Vertragsfläche**. Schema-Änderungen im Core müssen die Public-Read- Views berücksichtigen (Views/stabile Spalten als Puffer erwägen, damit interne Refactorings die öffentliche Sicht nicht brechen). ### Phase 4 — Public-Host bauen (groß, wenn Release näher rückt) - [ ] Neues Projekt `Predictalytics.PublicApi` (eigener Host, TLS, öffentliche Bind-Adresse). - [ ] `MapReadEndpoints()` + **Auth/Accounts** (separate **User-DB**; Passwörter/Sessions nach Stand der Technik — Secret-Handling lebt **nur hier**, nie im Core). - [ ] Öffentliches **Rate-Limiting & Quotas pro Account** (getrennt vom internen Polymarket-Limiter). - [ ] Mandanten-Sicht: Kunde sieht seine Watchlist/kopierten Master, nicht das gesamte Universum. - [ ] Dasselbe WebUI-SPA ausliefern. ## 4. Explizite Nicht-Ziele / Fallen - ❌ Public bekommt **keinen** Zugriff auf Core-Prozess, lokale API oder Control-Endpunkte. - ❌ Public bekommt **keinen** Schreibzugriff auf die Analyse-DB (nur `SELECT`). - ❌ **Kein** Nachrüsten von Kunden-Auth auf die lokale Vollschicht — Auth lebt nur im Public-Host. - ❌ Read-Endpunkte machen **keine** Provider-Calls und **keine** Writes (sonst public-untauglich). - ❌ WebUI kennt die DB nicht — immer nur über die API. ## 5. Reihenfolge / Aufwand 1. **Phase 0** (klein): Read/Control-Split + Capabilities + Deep-Dive-Audit. **Jetzt** — es ist die Voraussetzung dafür, dass das neue Design von Anfang an sauber sitzt. 2. **Phase 1+2** (klein, parallel): neues SPA als gemeinsames Artefakt, lokale Grenze härten. 3. **Phase 3** (mittel): read-only DB-User + Schema-als-Vertrag dokumentieren. 4. **Phase 4** (groß, später): Public-Host mit Auth/User-DB. Phase 0–2 begleiten das neue Design sofort. Phase 3 kann jederzeit vorgezogen werden (billig). Phase 4 erst zum Release — aber durch 0–3 ist dann nichts mehr umzubauen, nur zu ergänzen.