DOcu updated

This commit is contained in:
ValueOn AG 2026-04-06 00:46:32 +02:00
parent 580d663d1a
commit b66bb51df5
53 changed files with 2153 additions and 523 deletions

View file

@ -1,4 +1,4 @@
<!-- status: canonical -->
<!-- status: canonical -->
<!-- lastReviewed: 2026-04-05 -->
# Themen-Index für AI-Kontext
@ -32,15 +32,32 @@ Lade immer zuerst diese Datei. Dann gezielt die passende(n) Referenz-Datei(en).
| Thema | Datei | Wann laden |
|-------|-------|------------|
| Neutralisierung | b-reference/platform/neutralization.md | Datenschutz, AI-Call-Pipeline, Failsafe |
| RBAC | b-reference/platform/rbac.md | Berechtigungen, Mandate- und Feature-Rollen |
| RBAC | b-reference/platform/rbac.md | 4-Stufen-Modell, Template-Rollen, Resolution, Datenmodell |
| Datenbank-Architektur | b-reference/platform/database-architecture.md | Interface-Pattern, Connector, Auto-Init, DB-Liste |
| Navigation | b-reference/platform/navigation.md | Menü-Struktur, Admin-Seiten, API |
## Aktive Arbeiten (c-work)
| Thema | Datei | Wann laden |
|-------|-------|------------|
| Automation Unification | c-work/1-plan/2026-04-automation-unification.md | Refactoring v1/v2/Workspace |
| Web Image Search | c-work/1-plan/2026-03-web-image-search.md | WEB_SEARCH_MEDIA Feature |
## Prozess & Betrieb
| Thema | Datei | Wann laden |
|-------|-------|------------|
| Coding-Regeln | d-guides/coding-conventions.md | Naming, Patterns, Anti-Patterns |
| Testing-Strategie | d-guides/testing-strategy.md | Testpyramide, AC-Format, Test-Pfade |
| Deployment | d-guides/deployment.md | Release-Prozess, Environments |
| Dev-Setup | d-guides/dev-setup.md | Lokale Umgebung starten |
| Secrets-Verschluesselung | d-guides/encrypt-env-secrets.md | Env-Dateien verschluesseln |
| Google OAuth | d-guides/google-oauth-setup.md | OAuth Auth/Data Apps einrichten |
| Security-Migration | d-guides/security-migration-guide.md | JWT Cookie Migration |
| Doc-Sync Cursor-Rule | d-guides/doc-sync.mdc | Cursor-Regel installieren, Doku-Workflow |
## Compliance & Sicherheit
| Thema | Datei | Wann laden |
|-------|-------|------------|
| Sicherheitsuebersicht | e-compliance/security-overview.md | Kunden/Audit: DSGVO, RBAC, Verschluesselung |
| Neutralisierung Detail | e-compliance/neutralisierung-detail.md | Technische Neutralisierungs-Flows |

View file

Before

Width:  |  Height:  |  Size: 86 KiB

After

Width:  |  Height:  |  Size: 86 KiB

View file

Before

Width:  |  Height:  |  Size: 95 KiB

After

Width:  |  Height:  |  Size: 95 KiB

View file

Before

Width:  |  Height:  |  Size: 117 KiB

After

Width:  |  Height:  |  Size: 117 KiB

View file

Before

Width:  |  Height:  |  Size: 111 KiB

After

Width:  |  Height:  |  Size: 111 KiB

View file

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 99 KiB

View file

Before

Width:  |  Height:  |  Size: 118 KiB

After

Width:  |  Height:  |  Size: 118 KiB

View file

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 56 KiB

View file

@ -1,4 +1,4 @@
# Markteinschaetzung: Langdock (Berlin) vs. PowerON
# Markteinschaetzung: Langdock (Berlin) vs. PowerOn
**Dokument:** Markt- und Wettbewerbsanalyse mit strategischer Empfehlung
**Stand:** 23. Maerz 2026
@ -10,9 +10,9 @@
**Langdock** ist eine horizontale Enterprise-KI-Adoptionsplattform (Chat, Workflows, Agents, Integrationen, API) mit Fokus auf sichere, modellagnostische KI fuer das gesamte Unternehmen. Sitz Berlin, tausende Kunden, schnelles Wachstum.
**PowerON** positioniert sich auf poweron.swiss als *"The Swiss Pocket Knife of AI for SMEs"* -- eine universelle KI-Plattform fuer Schweizer KMU mit DSGVO-Compliance und Schweizer Hosting. Im Code steckt deutlich mehr: eine mandantenfaehige SaaS-Plattform mit RBAC, Feature-Instanzen, Billing/Subscription, vertikalen Fachmodulen (Treuhand, Immobilien, CommCoach) und einem einzigartigen **Daten-Neutralisierungs-Feature**.
**PowerOn** positioniert sich auf poweron.swiss als *"The Swiss Pocket Knife of AI for SMEs"* -- eine universelle KI-Plattform fuer Schweizer KMU mit DSGVO-Compliance und Schweizer Hosting. Im Code steckt deutlich mehr: eine mandantenfaehige SaaS-Plattform mit RBAC, Feature-Instanzen, Billing/Subscription, vertikalen Fachmodulen (Treuhand, Immobilien, CommCoach) und einem einzigartigen **Daten-Neutralisierungs-Feature**.
**Kernbefund:** Die oeffentliche Positionierung von PowerON ueberschneidet sich staerker mit Langdock als noetig. Das Produkt hat genuegend einzigartige Tiefe, um sich klar abzugrenzen -- die Botschaft muss geschaerft werden.
**Kernbefund:** Die oeffentliche Positionierung von PowerOn ueberschneidet sich staerker mit Langdock als noetig. Das Produkt hat genuegend einzigartige Tiefe, um sich klar abzugrenzen -- die Botschaft muss geschaerft werden.
---
@ -44,7 +44,7 @@
---
## 3. PowerON -- Oeffentliche Positionierung (poweron.swiss)
## 3. PowerOn -- Oeffentliche Positionierung (poweron.swiss)
### 3.1 Claim und Zielgruppe
@ -86,7 +86,7 @@ Im Code existiert dafuer ein eigenes Feature-Modul (`neutralization`) mit Playgr
---
## 4. PowerON -- Realitaet im Code (ueber die Homepage hinaus)
## 4. PowerOn -- Realitaet im Code (ueber die Homepage hinaus)
Die Codebase zeigt erheblich mehr Tiefe als die Website kommuniziert:
@ -115,13 +115,13 @@ Die Codebase zeigt erheblich mehr Tiefe als die Website kommuniziert:
### 4.3 Gap: Homepage vs. Code
Die Website zeigt PowerON als **generische KI-Plattform**. Der Code offenbart eine **spezialisierte SaaS mit Branchen-Features, Abrechnungslogik und tiefer Mandantenfaehigkeit** -- davon erfaehrt der Besucher von poweron.swiss aktuell nichts.
Die Website zeigt PowerOn als **generische KI-Plattform**. Der Code offenbart eine **spezialisierte SaaS mit Branchen-Features, Abrechnungslogik und tiefer Mandantenfaehigkeit** -- davon erfaehrt der Besucher von poweron.swiss aktuell nichts.
---
## 5. Ueberschneidungen PowerON vs. Langdock
## 5. Ueberschneidungen PowerOn vs. Langdock
| Thema | Langdock | PowerON |
| Thema | Langdock | PowerOn |
|-------------------------|-----------------------------------|-----------------------------------------------|
| Firmenweite KI-Nutzung | Kerngeschaeft | Workspace, Chatbot, Teamsbot |
| Chat & Konversationen | Chat-Produkt | Chatbot + Workspace-Editor |
@ -131,13 +131,13 @@ Die Website zeigt PowerON als **generische KI-Plattform**. Der Code offenbart ei
| Integrationen | Viele Standard-Integrationen | M365, Google, Jira, ClickUp, SharePoint, DB |
| Governance / Security | EU-Hosting, DSGVO | Schweizer Hosting, DSGVO, RBAC, Neutralisierung|
**Die generische KI-Schicht ueberschneidet sich stark.** Wer nur "ChatGPT im Unternehmen" sucht, sieht PowerON und Langdock in der gleichen Kategorie -- wobei Langdock dort durch Marktgroesse und Bekanntheit dominiert.
**Die generische KI-Schicht ueberschneidet sich stark.** Wer nur "ChatGPT im Unternehmen" sucht, sieht PowerOn und Langdock in der gleichen Kategorie -- wobei Langdock dort durch Marktgroesse und Bekanntheit dominiert.
---
## 6. Klare Unterschiede (PowerON-Vorteile)
## 6. Klare Unterschiede (PowerOn-Vorteile)
| Dimension | PowerON | Langdock |
| Dimension | PowerOn | Langdock |
|----------------------------|----------------------------------------------|---------------------------------|
| **Daten-Neutralisierung** | 5-Stufen-Prozess, eigenes Feature | Nicht vorhanden |
| **Schweizer Hosting** | 100% Schweiz, lokale Datensouveraenitaet | EU (nicht CH-spezifisch) |
@ -151,13 +151,13 @@ Die Website zeigt PowerON als **generische KI-Plattform**. Der Code offenbart ei
### 7.1 Kernproblem der aktuellen Positionierung
Die Homepage positioniert PowerON als **generische KI-Plattform** -- genau dort, wo Langdock (und Microsoft Copilot, und Google Gemini for Workspace, und dutzende weitere) bereits mit massiv mehr Ressourcen, Brand und Kundenstamm unterwegs sind.
Die Homepage positioniert PowerOn als **generische KI-Plattform** -- genau dort, wo Langdock (und Microsoft Copilot, und Google Gemini for Workspace, und dutzende weitere) bereits mit massiv mehr Ressourcen, Brand und Kundenstamm unterwegs sind.
Ein Drei-Personen-Team kann dieses Rennen nicht auf der generischen Ebene gewinnen. Jede Stunde, die in "wir sind auch ein KI-Chat mit Workflows" investiert wird, ist eine Stunde, in der Langdock mit tausenden Kunden und Millionen-Revenue den gleichen Pitch perfektioniert.
### 7.2 Empfohlene Richtung: Vertikale KI-SaaS mit Neutralisierungs-Moat
**PowerON sollte sich als vertikale, mandantenfaehige KI-SaaS positionieren -- nicht als generische KI-Plattform.**
**PowerOn sollte sich als vertikale, mandantenfaehige KI-SaaS positionieren -- nicht als generische KI-Plattform.**
Die empfohlene Positionierung stuetzt sich auf drei Saeulen:
@ -165,7 +165,7 @@ Die empfohlene Positionierung stuetzt sich auf drei Saeulen:
Die Daten-Neutralisierung ist das Feature, das kein Wettbewerber in dieser Form hat. Statt es als eines von fuenf technischen Features aufzulisten, sollte es das **zentrale Verkaufsargument** werden:
- *"PowerON ist die einzige KI-Plattform, bei der LLMs niemals Ihre sensiblen Daten sehen -- garantiert durch einen 5-Stufen-Neutralisierungsprozess."*
- *"PowerOn ist die einzige KI-Plattform, bei der LLMs niemals Ihre sensiblen Daten sehen -- garantiert durch einen 5-Stufen-Neutralisierungsprozess."*
- Besonders relevant fuer: Treuhand, Kanzleien, Gesundheitswesen, Finanzdienstleister, oeffentliche Verwaltung.
- Alle anderen Features (Chat, Automation, Workspace) werden **unter dem Neutralisierungs-Dach** vermarktet, nicht daneben.
@ -173,7 +173,7 @@ Die Daten-Neutralisierung ist das Feature, das kein Wettbewerber in dieser Form
Der Code enthaelt bereits Treuhand, Immobilien und CommCoach. Diese Module gehoeren sichtbar auf die Website und in den Pitch:
- Dedizierte Landing Pages pro Branche: "PowerON fuer Treuhandbueros", "PowerON fuer Immobilienverwaltungen".
- Dedizierte Landing Pages pro Branche: "PowerOn fuer Treuhandbueros", "PowerOn fuer Immobilienverwaltungen".
- Jede Branchenseite zeigt den konkreten Workflow: Dokument rein, Neutralisierung, KI-Verarbeitung, Ergebnis zurueck -- mit branchenspezifischem Vokabular.
- Der Feature-Store und das Mandantenmodell sind dabei die **technische Grundlage**, nicht der Marketing-Lead.
@ -198,7 +198,7 @@ Der Code enthaelt bereits Treuhand, Immobilien und CommCoach. Diese Module gehoe
### 7.4 Wovon ich abrate
1. **Breites Integrations-Rennen:** Langdock hat Dutzende Integrationen; dieses Rennen zu gewinnen ist mit kleinem Team unrealistisch. Besser: die vorhandenen Integrationen (M365, SharePoint, Google) **tief und zuverlaessig** halten und pro Branche gezielt erweitern (z. B. ABACUS/Bexio fuer Treuhand).
2. **"KI-Chat fuer alle":** Das ist Langdocks Kernmarkt. PowerON gewinnt nicht, indem es den gleichen Pitch macht, sondern indem es den Pitch **spezifischer** macht.
2. **"KI-Chat fuer alle":** Das ist Langdocks Kernmarkt. PowerOn gewinnt nicht, indem es den gleichen Pitch macht, sondern indem es den Pitch **spezifischer** macht.
3. **Feature-Breite vor Feature-Tiefe:** Lieber drei Branchen exzellent bedienen als zehn Features halb fertig zeigen (vgl. `features.feature5.title`-Platzhalter auf der Homepage).
### 7.5 Mittelfristiger Zielzustand (6-12 Monate)
@ -212,13 +212,13 @@ Der Code enthaelt bereits Treuhand, Immobilien und CommCoach. Diese Module gehoe
## 8. Zusammenfassung in einem Satz
> PowerON sollte aufhoeren, Langdocks Spiel zu spielen ("generische KI fuer alle"), und stattdessen das eigene Spiel spielen: **sichere, neutralisierte KI fuer regulierte Schweizer Branchen** -- ein Markt, den Langdock strukturell nicht adressiert.
> PowerOn sollte aufhoeren, Langdocks Spiel zu spielen ("generische KI fuer alle"), und stattdessen das eigene Spiel spielen: **sichere, neutralisierte KI fuer regulierte Schweizer Branchen** -- ein Markt, den Langdock strukturell nicht adressiert.
---
## 9. Quellen und Methodik
- **PowerON:** Homepage poweron.swiss (Stand Maerz 2026) sowie Codeanalyse gateway und frontend_nyla.
- **PowerOn:** Homepage poweron.swiss (Stand Maerz 2026) sowie Codeanalyse gateway und frontend_nyla.
- **Langdock:** Oeffentliche Website langdock.com und Produktunterseiten, Presseartikel (Stand Maerz 2026).
- Keine vertraulichen Materialien verwendet. Fuer verbindliche Pricing- und SLA-Vergleiche ist Herstellerkontakt noetig.

View file

@ -30,7 +30,7 @@
1) **🔒 Neutralizer (Datenschutz-First)**
- **Automatische Anonymisierung** sensibler Daten vor der KI-Verarbeitung
- **DSGVO-konform**, transparente Regeln du bestimmst, was neutralisiert wird
- **Validierungs-Roadmap**: Level 1 Datenschutz jetzt; Level 2 kontextueller IP-Schutz folgt mit externem Validat/Cert (ETH/HSG/Partner)
- **Validierungs-Roadmap**: Level 1 Datenschutz jetzt; Level 2 kontextueller IP-Schutz folgt mit externem Validator/Zertifizierung (ETH/HSG/Partner)
2) **♾️ Unlimitierte Verarbeitung**
- **Keine Token-Limits** Dokumente jeder Grösse verarbeiten

View file

@ -106,7 +106,7 @@ PowerOn ist eine **Multi-Agent-KI-Plattform für Enterprise-Workflows** mit inte
### 2.2 Alleinstellungsmerkmale (USP)
#### **1. Integrierter Datenschutz-Neutralisierer**
- **Einzigartig**: Erste Plattform mit eingebautem Datenschutz-Schutz
- **Einzigartig**: Erste Plattform mit eingebautem Datenschutz-Neutralisierer
- **Vorteil**: Unternehmen können KI nutzen ohne Datenschutzrisiken
- **Differenzierung**: Kein Konkurrent bietet diese Funktionalität
@ -169,7 +169,7 @@ PowerOn ist eine **Multi-Agent-KI-Plattform für Enterprise-Workflows** mit inte
- **3 Workflow-Modi**:
- **Learning-Mode**: Iterative Plan-Act-Observe-Refine-Schleife
- **Actionplan-Mode**: Batch-Planung mit sequenzieller Ausführung
- **Automation-Mode**: Automatisierte vordefinierte Workflosw
- **Automation-Mode**: Automatisierte vordefinierte Workflows
- **Adaptive Learning Engine**: Lernt aus Validierungsergebnissen und passt Prompts an
- **Intelligente Task-Planung**: Automatische Aufschlüsselung komplexer Anfragen in ausführbare Schritte
- **Echtzeit-Validierung**: Kontinuierliche Qualitätskontrolle und automatische Verbesserungen
@ -701,7 +701,7 @@ Wir sind aktuell im Monat 10 des Projekts und haben noch 2 Monate bis zur Seed-R
#### **KI-Services**
- **OpenAI**: GPT-4, DALL-E, Whisper
- **Anthropic**: Claude, Constitutional AI
- **Google**: PaLM, Bard, Vertex AI
- **Google**: Gemini, Vertex AI
- **Azure**: Cognitive Services, OpenAI Service
---

View file

@ -0,0 +1,71 @@
<!-- status: canonical -->
<!-- lastReviewed: 2026-04-05 -->
# PowerOn PORTA -- Produktvision
## Was ist PowerOn PORTA?
PowerOn PORTA ist eine **Multi-Agent-KI-Plattform für Enterprise-Workflows** mit integriertem Datenschutz-Neutralisierer. Die Plattform ermöglicht Unternehmen, KI-Transformation zu beschleunigen, ohne Datenschutzrisiken einzugehen.
**PORTA** = **P**owerful **O**rchestration & **R**eal-**T**ime **A**utomation
## Value Proposition
| Problem | PowerOn-Lösung |
|---------|---------------|
| 80% der Unternehmen können ChatGPT/Copilot nicht nutzen wegen Datenschutz | Integrierter Datenschutz-Neutralisierer: sensible Daten verlassen nie das Unternehmen |
| Token-Limits bei grossen Dokumenten | Unlimitierte Verarbeitung durch intelligente Chunking-Strategien |
| Isolierte KI-Services arbeiten nicht zusammen | Einheitliche Plattform mit nahtloser Integration aller Services |
| Komplexe, unvorhersehbare Pricing-Modelle | Transparentes Pauschalpricing pro Monat/Jahr |
| End-to-End-Workflows sind Expertensache | Schrittweise Integration, beginnend mit einfachen Use Cases |
## Zielkunden
- **Primär:** Mittelständische Unternehmen (100-2'000 MA) in datenschutz-sensitiven Branchen
- **Branchen:** Finanzdienstleistungen, Treuhand, Immobilien, Professional Services, Healthcare
- **Geografie:** DACH (initial), EU + US (Expansion)
## Alleinstellungsmerkmale (USPs)
1. **Datenschutz-Neutralisierer:** Einzige Plattform mit integrierter Echtzeit-Neutralisierung, die sensible Daten vor dem Verlassen des Unternehmens anonymisiert und nach Verarbeitung reanonymisiert.
2. **Multi-Agent-Orchestrierung:** Spezialisierte KI-Agenten pro Aufgabe (Chat, Workflow, Voice, RAG, Automation), koordiniert durch eine zentrale Engine mit 40+ Tools.
3. **Modellunabhängigkeit:** Integration mit Anthropic, OpenAI, Mistral, Perplexity, Tavily, Private LLM -- kein Vendor Lock-in, immer das beste Modell pro Aufgabe.
4. **Feature-Store-Architektur:** Mandanten aktivieren Features modular (Workspace, Automation, CommCoach, Trustee etc.) -- skaliert von Einzelanwendung bis Full-Suite.
5. **Voice-First:** Integrierte STT/TTS in den meisten Sprachen, nahtlos im Workflow nutzbar.
## Technologie-Stack (Kurzübersicht)
| Komponente | Technologie |
|-----------|-------------|
| Frontend Nyla | React/TypeScript, Vite |
| Gateway | FastAPI, Python, PostgreSQL, pgvector |
| Private LLM | Python, Ollama |
| Teams Bot | TypeScript/Node.js |
Detaillierte Architektur: → `b-reference/product.md`
## Umsatzmodell
- **SaaS-Abonnement:** Pauschal pro Monat/Jahr, Features modular zubuchbar
- **Ziel-Bruttomarge:** 75-85% nach Skalierung
- **Preismodell:** Nicht nutzungsbasiert pro Token, sondern unlimitiert innerhalb des Abonnements
## Strategische Phasen
| Phase | Zeitraum | Fokus |
|-------|---------|-------|
| Launch | 2025-2026 | Core-Plattform, 3-5 Referenzkunden, DACH |
| Growth | 2026-2027 | Feature-Expansion, branchenspezifische Templates, Partner |
| Scale | 2027+ | Internationalisierung, Agenten-Marktplatz, Branchenlösungen |
## Verweise
- Strategie & PMF: → `a-strategy/product-strategy.md`
- Roadmap: → `a-strategy/roadmap.md`
- Marktanalyse: → `a-strategy/market/`
- Investor-Zusammenfassung: → `a-strategy/investor/investor-summary.md`
- Komponentenübersicht: → `b-reference/product.md`

View file

@ -17,7 +17,7 @@ Dieses Dokument beschreibt die vorhandenen Features der PowerOn-Plattform, das a
PowerOn ist eine **Multi-Mandanten-Plattform**:
- **Mandatenmodell:** Jede Organisation/Kunde wird als Mandant abgebildet.
- **Mandantenmodell:** Jede Organisation/Kunde wird als Mandant abgebildet.
- **Datenisolation:** Daten eines Mandanten sind strikt von anderen Mandanten getrennt.
- **Zugehörigkeitsprüfung:** Bei jedem Zugriff wird geprüft, ob der Nutzer dem Mandanten angehört (via `UserMandate`).
- **Request-Kontext:** `mandateId` und `featureInstanceId` werden über Header (`X-Mandate-Id`, `X-Feature-Instance-Id`) übergeben.

View file

@ -4,21 +4,19 @@
# Automation
## Überblick
## Ueberblick
PowerOn hat **mehrere Automatisierungs-Spuren**, die sich die **gleiche Unified Action Library** (`workflows/methods/` + `ActionExecutor`) teilen, aber unterschiedliche **Persistenz**, **Scheduler** und **Execution Engines** nutzen. Die Business Spec beschreibt eine **Ziel-Architektur**: konsolidierte Plattform mit klarer Trennung Mandant / Feature / Feature-Instanz, skalierbarer **Toolbox**-Schicht für Agent-Tools und einem **vereinheitlichten Workflow-Datenmodell** (Draft/Published, Runs, Tracing).
PowerOn hat **mehrere Automatisierungs-Spuren**, die sich die **gleiche Unified Action Library** (`workflows/methods/` + `ActionExecutor`) teilen, aber unterschiedliche **Persistenz**, **Scheduler** und **Execution Engines** nutzen. Die Business Spec beschreibt eine **Ziel-Architektur**: konsolidierte Plattform mit klarer Trennung Mandant / Feature / Feature-Instanz, skalierbarer **Toolbox**-Schicht fuer Agent-Tools und einem **vereinheitlichten Workflow-Datenmodell** (Draft/Published, Runs, Tracing).
**Leitentscheide (Business Spec):**
- Keine Migration von Altdaten für Automation v1 (nicht produktiv); v1 bleibt bis zur Ablösung.
- **Vier Säulen:** AI Service (Agent, Streaming, Neutralisierung, Failover), Graphical Editor (Flow + künftig Editor-Chat/UDB), Automation Scheduler (Cron/Events/Webhooks), **Unified Action Library** mit Toolboxes.
- **Inkonsistenzen heute:** zwei Workflow-Datenwelten (v1 vs Automation2), zwei DB-Schemas, zwei Scheduler-Patterns, zwei Engines — Ausrichtung über Zielmodell und Übernahme bewährter v1-Scheduler-Patterns in Richtung v2.
- Keine Migration von Altdaten fuer Automation v1 (nicht produktiv); v1 bleibt bis zur Abloesung.
- **Vier Saeulen:** AI Service (Agent, Streaming, Neutralisierung, Failover), Graphical Editor (Flow + kuenftig Editor-Chat/UDB), Automation Scheduler (Cron/Events/Webhooks), **Unified Action Library** mit Toolboxes.
- **Inkonsistenzen heute:** zwei Workflow-Datenwelten (v1 vs Automation2), zwei DB-Schemas, zwei Scheduler-Patterns, zwei Engines -- Ausrichtung ueber Zielmodell und Uebernahme bewaehrter v1-Scheduler-Patterns in Richtung v2.
---
## Business Spec (Zusammenfassung)
**Ist-Systeme:**
## Ist-Systeme (Gateway, Stand 2026-04-05)
| System | Kurzbeschreibung |
|--------|------------------|
@ -26,41 +24,9 @@ PowerOn hat **mehrere Automatisierungs-Spuren**, die sich die **gleiche Unified
| **Automation2** | n8n-Style Graph; Run/Task; Pause/Resume; Human-in-the-loop; weniger robuster Scheduler-Sync als v1. |
| **AI Workspace** | Chat-Agent mit vielen Tools + RAG; kein persistiertes Workflow-Modell wie im Graph-Editor. |
**Gemeinsame technische Basis:** Alle genannten Pfade führen über **`ActionExecutor.executeAction()`** zur Method/Action Library; der Workspace-Agent mappt `dynamicMode`-Actions via **`ActionToolAdapter`** auf Tools; Automation2-Nodes tragen `_method` / `_action`.
**Gemeinsame technische Basis:** Alle genannten Pfade fuehren ueber **`ActionExecutor.executeAction()`** zur Method/Action Library; der Workspace-Agent mappt `dynamicMode`-Actions via **`ActionToolAdapter`** auf Tools; Automation2-Nodes tragen `_method` / `_action`.
**Toolbox-Konzept (Ziel):** Statt alle Tools flach zu exponieren, **thematische Toolboxes** (`core`, `ai`, `email`, `sharepoint`, `workflow`, …) mit Meta-Tool **`requestToolbox`** — Agent startet schlank, fordert Spezial-Toolboxes nach Bedarf; **`availableToolboxes`** ist pro Feature/RBAC/Connections filterbar.
**Sub-Agents:** Datenintensive Features (z.B. Trustee) kapseln Schema-Wissen in einem **Feature Data Agent**; Main-Agent sieht ein aggregiertes Tool (`queryFeatureInstance`), nicht Dutzende Tabellen-Tools.
**Agent-Run (State Machine, konzeptionell):** `IDLE``THINKING` → bei Tool-Calls `EXECUTING_TOOLS` → optional `TOOLBOX_ESCALATION` / `SUB_AGENT_CALL``COMPLETED` / `FAILED` / `CANCELLED` (Limits: `maxRounds`, `maxCostCHF`).
**RBAC (Kurz):** Zwei getrennte Template-Systeme — **System-Template-Rollen** (Mandant: admin/user/viewer) vs **Feature-Template-Rollen** (Instanz: z.B. workspace-admin); keine Vermischung. Auflösung: höchste Priorität gewinnt bei DATA; View OR über Rollen der Top-Priorität; Item-Spezifität exact > prefix > generic.
---
## Datenmodell (Zusammenfassung)
**Gateway-Schichten (Data Model Doc):** Frontend → **Features** (instanzgebunden) → **Services** (request-scoped) → **Shared Infrastructure** (`workflows/methods`, `workflows/processing`, `workflows/automation2`, `interfaces`, `aicore`, `datamodels`, `security`).
**Feature-Container-Pattern:** `features/<name>/` mit `main*.py` (FEATURE_CODE, UI/RESOURCE/TEMPLATE_ROLES), `routeFeature*.py`, `interfaceFeature*.py`, `datamodelFeature*.py`, optional `nodeDefinitions/`, `service*/`. Discovery über `registry.py` / `ServiceHub` ohne manuelles Wiring sämtlicher Router.
**Ziel-Entitäten (vereinheitlichtes Workflow-Modell, Spec):**
| Entität | Zweck |
|---------|--------|
| **Workflow** | Metadaten, Mandant/Instanz, `active`, optional `eventId` (Scheduler-Job), Zeiger auf aktuelle Version |
| **WorkflowVersion** | Immutabler Snapshot: `graph` (Nodes/Connections), `invocations` (Trigger: manual, schedule, webhook, …), Status **draft / published / archived** |
| **WorkflowRun** | Eine Ausführung: Status (pending → running → completed/failed/paused/cancelled), Trigger-Metadaten, `nodeOutputs`, Pause-/Resume-Kontext, Kostenfelder |
| **RunStepLog** | Pro Node: Input-Snapshot, Output, Dauer, Fehler, Token-Nutzung |
| **HumanTask** | Menschliche Zwischenaufgabe bei Pause (approval/form); Status pending → completed/cancelled/expired |
**Invarianten (Zielmodell):** Pro Workflow höchstens **eine** `PUBLISHED` Version; Scheduler bindet an die veröffentlichte Version. Pause z.B. durch Human-Task-Nodes oder E-Mail-Warten; Resume mit geliefertem Ergebnis.
**Toolbox-Datenmodell (Ziel):** `ToolboxDefinition` (id, label i18n, `tools[]`, optional `requiresConnection`), `ToolboxRegistry`, erweiterte `AgentConfig` mit `initialToolboxes` / `availableToolboxes`.
---
## Automation v1 vs v2
### Automation v1 vs v2 (Ist-Vergleich)
| Aspekt | Automation v1 | Automation2 (v2) |
|--------|----------------|------------------|
@ -68,20 +34,74 @@ PowerOn hat **mehrere Automatisierungs-Spuren**, die sich die **gleiche Unified
| DB / Schema | `poweron_automation` | `poweron_automation2` |
| Scheduler | Inkrementell, Callback `automation.changed`, `eventId` auf `AutomationDefinition` | Full re-register, Callback `automation2.workflow.changed` |
| Engine | `WorkflowManager` + `WorkflowProcessor` + `modeAutomation` | `executionEngine.executeGraph` |
| Stärken | Bewährtes Scheduling, Execution-Logs | Branching, Loops, Pause/Resume, visuelles Editing |
| Staerken | Bewaehrtes Scheduling, Execution-Logs | Branching, Loops, Pause/Resume, visuelles Editing |
**Konsolidierungsrichtung (Spec):** v1-Scheduler-Muster (inkrementell, Job-Handle, Reload+`active`-Check vor Run, capped Logs, `sysCreatedBy` für Kontext) in die v2-Welt übernehmen; einheitliches **Workflow/Version/Run**-Konzept langfristig über beide Welten.
**Konsolidierungsrichtung (Spec):** v1-Scheduler-Muster (inkrementell, Job-Handle, Reload+`active`-Check vor Run, capped Logs, `sysCreatedBy` fuer Kontext) in die v2-Welt uebernehmen; einheitliches **Workflow/Version/Run**-Konzept langfristig ueber beide Welten.
---
## Schlüssel-Dateien
## Datenmodell
### Ist: Konkrete Modelle (Gateway, Stand 2026-04-05)
| Modell | Datei | DB | Beschreibung |
|--------|-------|-----|-------------|
| `AutomationDefinition` | `features/automation/datamodelFeatureAutomation.py` | `poweron_automation` | v1 Template mit `schedule`, `placeholders`, `eventId` |
| `ChatWorkflow` | `datamodels/datamodelChat.py` | `poweron_chat` | v1 Execution-Artefakt (Status, Tasks, Messages) |
| `Automation2Workflow` | `features/automation2/datamodelFeatureAutomation2.py` | `poweron_automation2` | v2 Graph mit `active`, inline `graph`, `invocations` |
| `Automation2WorkflowRun` | gleiche Datei | `poweron_automation2` | v2 Run: Status `running`/`paused`/`completed`/`failed`, `nodeOutputs`, `context` |
| `Automation2HumanTask` | gleiche Datei | `poweron_automation2` | Human-Task: Status `pending`/`completed`/`rejected` |
**Hinweis:** Es gibt **keine** `WorkflowVersion`-Tabelle -- v2 speichert den Graph direkt in `Automation2Workflow`. Tracing ist `nodeOutputs` im Run, nicht ein separates `RunStepLog`-Entity.
### Ziel: Vereinheitlichtes Workflow-Modell (Spec -- nicht implementiert)
| Entitaet | Zweck | Heute approximiert durch |
|----------|-------|--------------------------|
| **Workflow** | Metadaten, Mandant/Instanz, `active`, `eventId`, Zeiger auf aktuelle Version | `Automation2Workflow` (ohne Versioning) |
| **WorkflowVersion** | Immutabler Snapshot: `graph`, `invocations`, Status draft/published/archived | *nicht vorhanden* (Graph inline in Workflow) |
| **WorkflowRun** | Ausfuehrung mit Status, Trigger, `nodeOutputs`, Kostenfelder | `Automation2WorkflowRun` (ohne Kostenfelder, Statusmenge abweichend) |
| **RunStepLog** | Pro Node: Input, Output, Dauer, Fehler, Token | *nicht vorhanden* (approximiert durch `nodeOutputs`) |
| **HumanTask** | Menschliche Aufgabe bei Pause; Status pending/completed/cancelled/expired | `Automation2HumanTask` (Status: `pending`/`completed`/`rejected`, kein `expired`) |
**Invarianten (Zielmodell):** Pro Workflow hoechstens **eine** `PUBLISHED` Version; Scheduler bindet an die veroeffentlichte Version.
### Toolbox-Datenmodell (Ziel -- nicht im Gateway implementiert)
`ToolboxDefinition` (id, label i18n, `tools[]`, optional `requiresConnection`), `ToolboxRegistry`, erweiterte `AgentConfig` mit `initialToolboxes` / `availableToolboxes`.
> Kein Match im Gateway-Code fuer `requestToolbox`, `ToolboxDefinition` oder `availableToolboxes` (Stand 2026-04-05).
### Gateway-Schichten
Frontend -> **Features** (instanzgebunden) -> **Services** (request-scoped) -> **Shared Infrastructure** (`workflows/methods`, `workflows/processing`, `workflows/automation2`, `interfaces`, `aicore`, `datamodels`, `security`).
**Feature-Container-Pattern:** `features/<name>/` mit `main*.py` (FEATURE_CODE, UI/RESOURCE/TEMPLATE_ROLES), `routeFeature*.py`, `interfaceFeature*.py`, `datamodelFeature*.py`, optional `nodeDefinitions/`, `service*/`. Discovery ueber `registry.py` / `ServiceHub`.
---
## Ziel-Konzepte (Business Spec, nicht implementiert)
**Toolbox-Konzept:** Statt alle Tools flach zu exponieren, **thematische Toolboxes** (`core`, `ai`, `email`, `sharepoint`, `workflow`, ...) mit Meta-Tool **`requestToolbox`** -- Agent startet schlank, fordert Spezial-Toolboxes nach Bedarf; **`availableToolboxes`** ist pro Feature/RBAC/Connections filterbar.
**Sub-Agents:** Datenintensive Features (z. B. Trustee) kapseln Schema-Wissen in einem **Feature Data Agent**; Main-Agent sieht ein aggregiertes Tool (`queryFeatureInstance`), nicht Dutzende Tabellen-Tools.
**Agent-Run (State Machine, konzeptionell -- nicht im Code):** `IDLE` -> `THINKING` -> bei Tool-Calls `EXECUTING_TOOLS` -> optional `TOOLBOX_ESCALATION` / `SUB_AGENT_CALL` -> `COMPLETED` / `FAILED` / `CANCELLED` (Limits: `maxRounds`, `maxCostCHF`).
**RBAC (Kurz):** Zwei getrennte Template-Systeme -- **System-Template-Rollen** (Mandant: admin/user/viewer) vs **Feature-Template-Rollen** (Instanz: z. B. workspace-admin); keine Vermischung.
---
## Schluessel-Dateien
| Bereich | Typische Pfade (`gateway/modules/`) |
|---------|-------------------------------------|
| Action Library | `workflows/methods/`, `workflows/processing/` (`ActionExecutor`, `methodDiscovery`) |
| Automation v1 Feature | `features/automation/` |
| Automation2 Feature | `features/automation2/`, `workflows/automation2/` |
| Agent ↔ Actions | `serviceCenter/services/serviceAgent/` (u.a. Tool-Registrierung, `ActionToolAdapter`) |
| Automation v1 Runtime | `workflows/automation/` (`mainWorkflow.py`, `subAutomationSchedule.py`) |
| Automation2 Feature | `features/automation2/` |
| Automation2 Runtime | `workflows/automation2/` (`executionEngine.py`, `subAutomation2Schedule.py`) |
| Agent <-> Actions | `serviceCenter/services/serviceAgent/` (Tool-Registrierung, `ActionToolAdapter`) |
| Feature-Discovery | `system/registry.py`, `serviceHub/__init__.py` |
| RBAC | `interfaces/interfaceRbac.py`, `security/`, Rollen/AccessRules in Datamodels |
@ -89,9 +109,9 @@ PowerOn hat **mehrere Automatisierungs-Spuren**, die sich die **gleiche Unified
## Regeln / Invarianten
1. **Eine Action Library:** Änderungen an Methods/Actions betreifen Workspace, Automation v1, Automation2 und Agent-Tools gleichzeitig — Kompatibilität und `actionId`-Stabilität beachten.
2. **RBAC strikt trennen:** Mandantenrollen vs Feature-Instanz-Rollen nicht mischen; Permissions über AccessRules und Prioritätsregeln.
3. **Scheduler vs Editor:** Ausführung und Zeitplanung gehören zur **Scheduler-Säule**; der Graph ist **Version**-gebunden (Draft vs Published).
4. **Tool-Skalierung:** Flache Tool-Listen skalieren nicht; Toolbox + `requestToolbox` + connection-basierte Verfügbarkeit sind das vorgesehene Gegenmittel.
5. **Zielmodell vs Ist:** `Workflow` / `WorkflowVersion` / `WorkflowRun` in der Spec sind **Zielarchitektur** — Abgleich mit produktivem Schema bei Implementierungsarbeiten erforderlich.
1. **Eine Action Library:** Aenderungen an Methods/Actions betreffen Workspace, Automation v1, Automation2 und Agent-Tools gleichzeitig -- Kompatibilitaet und `actionId`-Stabilitaet beachten.
2. **RBAC strikt trennen:** Mandantenrollen vs Feature-Instanz-Rollen nicht mischen; Permissions ueber AccessRules und Prioritaetsregeln.
3. **Scheduler vs Editor:** Ausfuehrung und Zeitplanung gehoeren zur **Scheduler-Saeule**; der Graph ist **Version**-gebunden (Draft vs Published).
4. **Tool-Skalierung:** Flache Tool-Listen skalieren nicht; Toolbox + `requestToolbox` + connection-basierte Verfuegbarkeit sind das vorgesehene Gegenmittel (Ziel, nicht implementiert).
5. **Zielmodell vs Ist:** `Workflow` / `WorkflowVersion` / `WorkflowRun` in der Spec sind **Zielarchitektur**. Ist-Modelle siehe oben (`Automation2Workflow`, `Automation2WorkflowRun` etc.). Abgleich bei Implementierungsarbeiten erforderlich.
6. **Neutralisierung / KI:** Plattformweit gelten die zentralen KI-Datenpfade (siehe `workflow.md` / Compliance); Automation nutzt dieselben Services wie der Rest des Gateways.

View file

@ -1,4 +1,4 @@
<!-- status: canonical -->
<!-- status: canonical -->
<!-- lastReviewed: 2026-04-05 -->
<!-- verifiedAgainst: gateway (codebase audit 2026-04-05) -->
@ -24,27 +24,20 @@ Ohne gültige, zahlungswirksame Subscription (soweit implementiert) sollen **nur
- **Mandant** ist die zentrale Kostenstelle; Benutzer können mehreren Mandanten angehören — Verbrauch wird pro Mandant getrennt erfasst.
- Kosten entstehen an **AICore-Connectoren** (z. B. `anthropic`, `openai`, `perplexity`, `tavily`, `internal`); die erlaubte Provider-Liste ist dynamisch (Model Registry / Plugins).
### Abrechnungsmodelle (`BillingSettings.billingModel`)
### Abrechnungsmodell
| Modell | Kostenstelle | Kurzbeschreibung |
|--------|--------------|------------------|
| `PREPAY_MANDATE` | Mandant | Gemeinsames Guthaben für alle User des Mandanten |
| `PREPAY_USER` | User im Mandanten-Kontext | Eigenes Guthaben pro User; automatisches Startguthaben v. a. für **Root-Mandant** / Bootstrap (`defaultUserCredit`, z. B. 5 CHF) |
| `UNLIMITED` | — | Keine Guthabenlimitierung (interne Mandanten) |
- **`CREDIT_POSTPAY`** ist entfernt; gespeicherte Legacy-Werte werden beim Lesen auf **`PREPAY_MANDATE`** normalisiert (ohne separate DB-Migration).
- **Audit-Hinweis:** Im Gateway-Kontext wird der operative Fokus als **PREPAY_MANDATE** (kein PREPAY_USER im Sinne des Standard-Mandanten-Produkts) geführt; **Root** bleibt im Konzept mit **PREPAY_USER** und Startguthaben beschrieben.
Im Gateway gibt es aktuell **kein `billingModel`-Feld** auf `BillingSettings`. Das operative Modell ist faktisch **PREPAY_MANDATE** (gemeinsames Mandantenkonto). Konzeptionell waren weitere Modelle (`PREPAY_USER`, `UNLIMITED`, `CREDIT_POSTPAY`) vorgesehen — diese sind im Code nicht implementiert.
### Kernentitäten
- **`BillingAccount`:** Guthaben (`balance` CHF), optional `userId` bei `PREPAY_USER`, `accountType` MANDATE | USER, `warningThreshold`, `enabled`.
- **`BillingAccount`:** Guthaben (`balance` CHF), `mandateId`, optional `userId`, `warningThreshold`, `enabled`. Kein explizites `accountType`-Feld im Code — Mandant vs. User wird über Vorhandensein von `userId` unterschieden.
- **`BillingTransaction`:** `CREDIT` | `DEBIT` | `ADJUSTMENT`; Referenzen (`referenceType` / `referenceId`, `workflowId`, `featureInstanceId`, `aicoreProvider`). Mandanten-Kontext über **`BillingAccount.mandateId`** — Chat-Modelle bleiben user-owned ohne `mandateId` in `ChatWorkflow`; Zuordnung für Statistiken über Transaktionen.
- **`BillingSettings`:** Modell, `defaultUserCredit`, `warningThresholdPercent`, `notifyEmails`, `notifyOnWarning`; Erweiterung **`stripeCustomerId`** (Organisation = Mandant, nicht einzelne Subscription).
- **`BillingSettings`:** `warningThresholdPercent`, `notifyEmails`, `notifyOnWarning`, `stripeCustomerId` (Organisation = Mandant). Zusätzlich: `autoRechargeEnabled`, `rechargeAmountCHF`, `rechargeThresholdCHF` (Auto-Recharge) sowie Storage-Felder (`storageLimitGB`, `STORAGE_PRICE_PER_GB_CHF`).
- **`UsageStatistics`:** Aggregationen nach Periode, Provider und Feature (Konzept; Auswertung aus Transaktionen).
### Preise und Währung
- Verrechnung in **CHF**; Plugin-seitig typisch **`calculatePriceCHF()`** mit dokumentiertem Aufschlag (Konzept: z. B. 50 % auf Provider-Kosten), Werte in den AICore-Plugins gepflegt.
- Verrechnung in **CHF**; zentraler Aufschlag via `calculatePriceWithMarkup()` in `mainServiceBilling.py` mit `BILLING_MARKUP_PERCENT = 400` (Multiplikator ×5 auf Provider-Kosten). **Achtung:** Code-Kommentare referenzieren inkonsistent „Faktor 2.0" und „50 %", die Konstante ist aktuell 400 %.
### Provider-Steuerung (RBAC)
@ -112,7 +105,9 @@ Referenz **Gateway-Audit** zu Prepaid-Beispielen: Trial **5 CHF**, Standard **10
| **PAST_DUE** | Zahlung fehlgeschlagen, Stripe-Retry-Phase |
| **EXPIRED** | Beendet (terminal) |
**AI-Gate (Produktregel aus Mandanten-Konzept):** Für die Freigabe von AI-Calls gelten **`ACTIVE`** und **`TRIALING`**; bei **`PAST_DUE`**, **`EXPIRED`** und fehlender Subscription sind **AI-Calls blockiert** (auch wenn die Stripe-State-Maschine `PAST_DUE` als „Grace“ modelliert).
**AI-Gate (Code-Stand 2026-04-05):** Im Code ist `PAST_DUE` Teil der operativen Statusmenge (`OPERATIVE_STATUSES = ACTIVE, TRIALING, PAST_DUE`). `_checkSubscription` in `mainServiceBilling.py` gibt fuer alle drei Status `None` (erlaubt) zurueck. AI-Calls werden bei `PAST_DUE` **nicht blockiert**. Nur `EXPIRED`, `PENDING`, `SCHEDULED` und fehlende Subscription blockieren AI.
> Konzept vs. Code: Eine strengere Produktregel (PAST_DUE blockiert AI) war konzeptionell vorgesehen, ist aber aktuell nicht implementiert.
### Erlaubte Statusübergänge (Kernmenge)
@ -176,7 +171,7 @@ Typische Trigger: Stripe-Webhooks (`checkout.session.completed`, `invoice.paymen
1. **Zwei Ebenen:** LLM-Verbrauch (Prepaid/Transaktionen) und Plattform-Subscription (Pläne, Stripe, Caps) — gemeinsames Gate vor AI-Calls.
2. **Mandant** ist die primäre Verrechnungseinheit für Usage; Transaktionen tragen den Mandanten-Kontext über das **BillingAccount**, nicht über Chat-Stammdaten.
3. **Pro Mandant** höchstens eine operative Subscription **`ACTIVE`/`TRIALING`** für KI-Freigabe; **`PAST_DUE`** und **`EXPIRED`** blockieren AI gemäß Mandanten-Konzept.
3. **Pro Mandant** hoechstens eine operative Subscription; `ACTIVE`, `TRIALING` und `PAST_DUE` gelten als **operativ** (AI erlaubt). Nur `EXPIRED`, `PENDING`, `SCHEDULED` und fehlende Subscription blockieren AI.
4. **Kündigung:** Nutzung bis **Ende der bezahlten Periode** (`recurring = false`, Stripe `cancel_at_period_end`); kein separater Status `CANCELLED`.
5. **Status-Änderungen** nur über definierte Transitionen mit **`subscriptionId`** — kein implizites Scannen nach „aktueller“ Zeile allein über `mandateId` bei Writes.
6. **Provider-Zugriff** über RBAC `resource.aicore.*`, konsistent mit Billing-Erfassung pro `aicoreProvider`.

View file

@ -0,0 +1,271 @@
<!-- status: canonical -->
<!-- lastReviewed: 2026-04-05 -->
<!-- verifiedAgainst: gateway (codebase audit 2026-04-05) -->
# Datenbank-Architektur
## Ueberblick
PowerOn verwendet **PostgreSQL** mit einer klaren Trennung: jedes Modul (App, Chat, Billing, Knowledge, Features) hat eine eigene physische Datenbank (`poweron_*`). Datenbank-Zugriffe laufen ueber **Interfaces** (`interfaceDb*.py`), die auf dem zentralen **DatabaseConnector** (`connectorDbPostgre.py`) aufbauen. Datenbanken und Tabellen werden **automatisch** beim ersten Zugriff erzeugt -- kein manuelles Schema-Management noetig.
---
## Architektur-Stack
```
Route / Service
|
v
Interface (interfaceDb*.py)
|-- XxxObjects Klasse
|-- getInterface() Factory (cached)
|-- setUserContext() -> RBAC-Anbindung
|
v
DatabaseConnector (connectorDbPostgre.py)
|-- Auto-Create Database
|-- Auto-Create/Extend Tables (aus Pydantic-Modellen)
|-- CRUD-Operationen (getRecordset, recordCreate, recordUpdate, recordDelete)
|
v
PostgreSQL (+ pgvector Extension)
```
---
## Datenbanken
### Inventar
| Datenbank | Modul / Zweck | Interface |
|-----------|---------------|-----------|
| `poweron_app` | Kernapplikation: UAM, Mandanten, Rollen, RBAC-Regeln, Navigation, Config | `interfaceDbApp.py` |
| `poweron_chat` | Chat-Verlaeufe, Messages, Attachments | `interfaceDbChat.py` |
| `poweron_management` | System-Management, Logs | `interfaceDbManagement.py` |
| `poweron_knowledge` | RAG Knowledge-Store (mit pgvector) | `interfaceDbKnowledge.py` |
| `poweron_billing` | Billing Accounts, Transactions, Usage | `interfaceDbBilling.py` |
| `poweron_billing` | Subscriptions (gleiche DB wie Billing) | `interfaceDbSubscription.py` |
| `poweron_workspace` | AI Workspace Feature-Daten | Feature-Interface |
| `poweron_automation` | Automation v1 Workflows, Runs | Feature-Interface |
| `poweron_automation2` | Automation v2 (Graph-Editor) Workflows, Runs | Feature-Interface |
| `poweron_chatbot` | Chatbot Feature-Daten | Feature-Interface |
| `poweron_trustee` | Trustee Feature-Daten | Feature-Interface |
| `poweron_commcoach` | CommCoach Feature-Daten | Feature-Interface |
| `poweron_neutralization` | Neutralisierungs-Daten | Feature-Interface |
| `poweron_realestate` | RealEstate Feature-Daten | Feature-Interface |
| `poweron_teamsbot` | Teams-Bot Feature-Daten | Feature-Interface |
| `poweron_test` | Test-Datenbank | Nur in Tests |
### Namenskonvention
Feature-Datenbanken folgen dem Pattern `poweron_{featureCode.lower()}`. Der Datenbankname wird hart-codiert in der `_initializeDatabase()` Methode des jeweiligen Interfaces.
### Konfiguration
Verbindungsparameter kommen aus `APP_CONFIG` (config.ini / Environment):
| Key | Beschreibung |
|-----|-------------|
| `DB_HOST` | PostgreSQL Host |
| `DB_PORT` | PostgreSQL Port |
| `DB_USER` | Datenbank-Benutzer |
| `DB_PASSWORD_SECRET` | Passwort (ggf. verschluesselt) |
| `DB_DATABASE` | Default-DB-Name (Standard: `poweron_app`) |
---
## Interface-Pattern
Jedes Interface folgt einem einheitlichen Aufbau:
### XxxObjects Klasse
```python
class AppObjects:
db: DatabaseConnector # Connector zur eigenen DB
rbac: RbacClass # RBAC-Integration
currentUser: dict # Aktueller Benutzer
mandateId: str # Aktueller Mandant
featureInstanceId: str # (nur bei Feature-Interfaces)
```
### _initializeDatabase()
Setzt den Datenbanknamen und erstellt den Connector:
```python
def _initializeDatabase(self):
dbDatabase = "poweron_app" # hart-codiert pro Interface
self.db = DatabaseConnector(
host=APP_CONFIG.get("DB_HOST"),
database=dbDatabase,
user=APP_CONFIG.get("DB_USER"),
password=APP_CONFIG.get("DB_PASSWORD_SECRET"),
port=APP_CONFIG.get("DB_PORT")
)
```
### setUserContext()
Bindet Benutzer und RBAC an die Instanz:
```python
def setUserContext(self, currentUser, mandateId):
self.currentUser = currentUser
self.userId = currentUser["id"]
self.mandateId = mandateId
self.rbac = RbacClass(self.db, dbApp=dbApp)
self.db.updateContext(self.userId)
```
**RBAC-Besonderheit:** `poweron_app` ist Sonderfall (`dbApp=self.db`), weil RBAC-Regeln selbst in `poweron_app` liegen. Alle anderen Interfaces nutzen `getRootDbAppConnector()` um Rules aus der App-DB zu lesen:
```python
dbApp = getRootDbAppConnector()
self.rbac = RbacClass(self.db, dbApp=dbApp)
```
### getInterface() Factory
Cached pro Kontext:
| Interface | Cache-Key | Ergebnis |
|-----------|-----------|----------|
| App | `{mandateId}_{userId}` | `AppObjects` |
| Chat | `{mandateId}_{featureInstanceId}_{userId}` | `ChatObjects` |
| Billing | `{userId}_{mandateId}` | `BillingObjects` |
| Knowledge | `"default"` (Singleton) | `KnowledgeObjects` |
---
## DatabaseConnector
### Auto-Initialisierung
Beim Erstellen eines `DatabaseConnector` (`initDbSystem()`):
```
1. _create_database_if_not_exists()
-> Verbindet sich zu DB "postgres"
-> Prueft pg_database auf Existenz
-> CREATE DATABASE "{name}" falls fehlend
2. _create_tables()
-> Erstellt nur die _system Registry-Tabelle
-> Applikations-Tabellen werden lazy durch Interfaces erzeugt
3. _connect()
-> psycopg2-Verbindung zur Ziel-DB
4. _initializeSystemTable()
-> _ensureTableExists(SystemTable)
```
### Lazy Table Creation
Tabellen werden **bei erstem Zugriff** automatisch aus Pydantic-Modellen erzeugt (`_ensureTableExists(model_class)`):
- **Tabellenname** = Python-Klassenname des Pydantic-Modells (z.B. `Role`, `AccessRule`, `UserMandate`)
- **Spalten** werden aus Modellfeldern abgeleitet
- **JSONB** fuer komplexe Typen (dict, list, Pydantic-Submodelle)
- **vector** fuer pgvector-Felder (Knowledge-Store)
- **Additive Migration**: Wenn die Tabelle existiert, werden fehlende Spalten automatisch hinzugefuegt. Bestehende Spalten werden **nicht** geaendert oder entfernt.
### Connector-Caching
```python
_get_cached_connector(host, database, port)
```
Wiederverwendet einen `DatabaseConnector` pro Datenbank-Identitaet (`host:database:port`). Genutzt von App, Management, Knowledge. Chat und Billing instanziieren direkt (kein Cache).
### userId Context
`userId` wird ueber eine `contextvar` am Connector gesetzt (`updateContext(userId)`). Damit werden System-Felder (`_createdBy`, `_updatedBy`) automatisch befuellt.
---
## RBAC-Integration in DB-Abfragen
Die RBAC-Schicht greift direkt in Datenbank-Abfragen ein:
### Kern-Funktionen in interfaceRbac.py
| Funktion | Zweck |
|----------|-------|
| `getRecordsetWithRBAC()` | Listet Datensaetze mit RBAC-Filter in SQL WHERE |
| `buildRbacWhereClause()` | Uebersetzt Access Levels in SQL-Bedingungen |
| `buildDataObjectKey()` | Baut Keys wie `data.uam.UserInDB` fuer Rule-Lookup |
| `TABLE_NAMESPACE` | Mapping von Modellnamen zu logischen Namespaces |
### Namespace-Mapping
`TABLE_NAMESPACE` ordnet Pydantic-Modellnamen logischen Bereichen zu:
| Namespace | Modelle (Beispiele) |
|-----------|-------------------|
| `uam` | UserInDB, UserMandate, Role, AccessRule |
| `chat` | ChatHistory, ChatMessage |
| `files` | FileRecord, FileFolder |
| `feature.trustee` | TrusteePosition, TrusteeDocument |
| `feature.workspace` | Workspace-spezifische Modelle |
### SQL-Filter nach Access Level
| Level | SQL WHERE |
|-------|-----------|
| `a` (ALL) | Kein Filter |
| `m` (MANDATE) | `mandateId = :mandateId` |
| `o` (OWN) | `_createdBy = :userId` |
| `n` (NONE) | `1=0` (kein Ergebnis) |
---
## System-Felder
Jedes `PowerOnModel` erbt automatisch System-Felder:
| Feld | Beschreibung | Schutz |
|------|-------------|--------|
| `id` | UUID, auto-generiert | Immutable |
| `_createdBy` | userId des Erstellers | Immutable |
| `_createdAt` | Zeitstempel Erstellung | Immutable |
| `_updatedBy` | userId der letzten Aenderung | Auto-gesetzt |
| `_updatedAt` | Zeitstempel letzte Aenderung | Auto-gesetzt |
Felder mit fuehrendem `_` sind fuer Anwendungs-CUD geschuetzt -- der Connector erzwingt das unabhaengig von Access Rules.
---
## Feature-DB-Registrierung
Features registrieren sich **nicht** in einer zentralen DB-Liste. Stattdessen:
1. `registry.py``registerAllFeaturesInCatalog()` laedt Feature-Main-Module
2. Jedes Feature-Main definiert seinen `FEATURE_CODE` und `dbDatabase`
3. Der Datenbank-Name wird in `_initializeDatabase()` des Feature-Interfaces gesetzt
4. Der `DatabaseConnector` erzeugt die DB automatisch beim ersten Zugriff
### Admin-Sicht
`routeSecurityAdmin.py` bietet eine API, die alle `poweron_%`-Datenbanken aus PostgreSQL auflistet -- fuer Systemadministration und Diagnostik.
---
## Schluessel-Dateien
| Thema | Pfad |
|-------|------|
| PostgreSQL Connector | `gateway/modules/connectors/connectorDbPostgre.py` |
| App-DB Interface | `gateway/modules/interfaces/interfaceDbApp.py` |
| Chat-DB Interface | `gateway/modules/interfaces/interfaceDbChat.py` |
| Management-DB Interface | `gateway/modules/interfaces/interfaceDbManagement.py` |
| Knowledge-DB Interface | `gateway/modules/interfaces/interfaceDbKnowledge.py` |
| Billing-DB Interface | `gateway/modules/interfaces/interfaceDbBilling.py` |
| Subscription Interface | `gateway/modules/interfaces/interfaceDbSubscription.py` |
| RBAC in DB-Schicht | `gateway/modules/interfaces/interfaceRbac.py` |
| Feature-Interface (Template) | `gateway/modules/interfaces/interfaceFeatures.py` |
| Bootstrap / DB-Seed | `gateway/modules/interfaces/interfaceBootstrap.py` |
| DB-Migration Script | `gateway/scripts/script_db_export_migration.py` |
| Admin DB-Listing | `gateway/modules/routes/routeSecurityAdmin.py` |
| Datenmodelle (Pydantic) | `gateway/modules/datamodels/` |

View file

@ -168,7 +168,7 @@ This diagram shows the complete microservices structure, organized into Core Ser
graph TB
subgraph "Core Services"
CHAT[self.services.chat<br/>Chat & Conversation]
WF_SVC[self.services.workflow<br/>Workflow State]
WF_SVC[self.services.workflow<br/>Runtime Workflow State<br/>not a registered service]
UTILS[self.services.utils<br/>Utilities]
end
@ -185,6 +185,14 @@ graph TB
TICKET[self.services.ticket<br/>Jira, ClickUp, etc.]
end
subgraph "Additional Services not shown"
AGENT[self.services.agent]
KNOWLEDGE[self.services.knowledge]
MESSAGING[self.services.messaging]
BILLING[self.services.billing]
SUBSCRIPTION[self.services.subscription]
end
subgraph "Security"
SEC_SVC[self.services.security<br/>Auth, Authorization<br/>Token Management]
end
@ -415,7 +423,7 @@ graph TB
subgraph "Integration Points"
WF_DIR[gateway/modules/workflows/methods/]
SVC_DIR[gateway/modules/services/]
SVC_DIR[gateway/modules/serviceCenter/services/]
CONN_DIR[gateway/modules/connectors/]
ROUTE_DIR[gateway/modules/routes/]
FEAT_DIR[gateway/modules/features/]
@ -481,4 +489,4 @@ graph TB
---
*Last updated: 2025*
*Last updated: 2026-04-05 (audit: service paths, missing services, date corrected)*

View file

@ -4,66 +4,334 @@
# RBAC-System
## Überblick
## Ueberblick
Das Role-Based Access Control bündelt Berechtigungen in **Access Rules** pro Rollenlabel und **Kontext** (DATA, UI, RESOURCE). Auswertung erfolgt zentral über `interfaceRbac.py`; Zielbild laut Architekturdokumentation: möglichst **filternd in der Datenbank** statt voller Tabellen-Scans in Python. Nutzer können **mehrere Rollenlabels** gleichzeitig tragen; die effektive Berechtigung entsteht aus **Öffnungslogik (Union)** über alle Rollen.
Das Role-Based Access Control baut auf vier Stufen auf: **System**, **Mandant**, **Feature** und **Feature-Instanz**. Berechtigungen werden in **Access Rules** pro Rollenlabel und Kontext (DATA, UI, RESOURCE) gebunden. Auswertung erfolgt zentral ueber `interfaceRbac.py`; Zielbild: moeglichst **filternd in SQL** statt vollstaendiger Tabellen-Scans in Python. Nutzer koennen mehrere Rollenlabels gleichzeitig tragen; die effektive Berechtigung entsteht aus **Oeffnungslogik (Union)** ueber alle Rollen.
## Zwei getrennte Rollensysteme
---
| | **Mandantenrollen** | **Feature-Instanz-Rollen** |
|--|---------------------|----------------------------|
| **Labels (Beispiele)** | `admin`, `user`, `viewer` | `workspace-admin`, `workspace-user`, `workspace-viewer`, feature-spezifisch |
| **Scope** | `mandateId` | `mandateId` + `featureCode` + `featureInstanceId` |
| **Zuordnung** | `UserMandateRole` | `FeatureAccessRole` |
## 4-Stufen-Hierarchie
**Invariante:** Mandantenrollen und Feature-Instanz-Rollen **nicht vermischen** — kein Zuweisen eines Mandanten-Labels zu `FeatureAccessRole` oder umgekehrt.
```
System (PowerOn Platform)
|
+-- System Template Rollen (isSystemRole=true, mandateId=null, featureCode=null)
| Labels: "admin", "user", "viewer"
| -> kopiert bei Mandant-Erstellung via copySystemRolesToMandate()
|
+-- Feature Template Rollen (isSystemRole=false, mandateId=null, featureCode=X)
| Labels: "workspace-admin", "workspace-user", "workspace-viewer",
| "graphicalEditor-admin", "graphicalEditor-user", ...
| -> kopiert bei Feature-Instanz-Erstellung via _copyTemplateRoles()
|
+-- Mandant (Tenant)
+-- Mandate-Rollen (Instanzen der System Templates)
| mandateId=X, featureInstanceId=null, featureCode=null
| "admin", "user", "viewer"
| Zuordnung: UserMandateRole <- UserMandate <- User
|
+-- Feature-Instanz A (z.B. "AI Workspace Produktion")
| +-- Feature-Instanz-Rollen (Instanzen der Feature Templates)
| | mandateId=X, featureInstanceId=Y, featureCode="workspace"
| | "workspace-admin", "workspace-user", "workspace-viewer"
| | Zuordnung: FeatureAccessRole <- FeatureAccess <- User
| +-- Daten (Workflows, Files, Chats, ...)
|
+-- Feature-Instanz B (z.B. "Graphical Editor Dev")
+-- Feature-Instanz-Rollen
+-- Daten (Workflows, Runs, Tasks, ...)
```
Zusätzlich (Systemebene, laut RBAC-Dokument): globale Rollen wie **SysAdmin** / **SystemUser** für mandantenübergreifende Sonderfälle; Mandantenrollen **Admin** / **User** / **Viewer** für Zugriff innerhalb eines Mandats.
### Sonderfaelle
## Access Rules (DATA / UI / RESOURCE)
- **SysAdmin**: Eine globale Rolle auf Root-Mandant-Ebene. Wird in `_initSysAdminRole()` beim Bootstrap angelegt -- keine Template-Kopie.
- **SystemUser / EventUser**: Technische System-Accounts, nicht an echte Benutzer gebunden.
### Kontexte
---
- **DATA:** Tabellen und Felder; CRUD über `read`, `create`, `update`, `delete` mit Access Levels `a` (all), `m` (my), `g` (group / Mandat), `n` (none); zusätzlich `view`.
- **UI:** Sichtbarkeit/Aktivierung von Oberflächenelementen; relevant ist **`view`** (boolean). Item-Format: kaskadierender String (z. B. `playground.voice.settings`).
- **RESOURCE:** Systemressourcen (KI-Modelle, Aktionen, …); ebenfalls primär **`view`**. Item-Format: kaskadierend (z. B. `ai.model.anthropic`).
## Zwei getrennte Template-Rollen-Systeme
### Spezifität (pro Rolle)
Templates sind **Blaupausen**. Sie definieren welche Rollen und Berechtigungen ein neuer Mandant oder eine neue Feature-Instanz erhaelt. Die beiden Template-Systeme sind **vollstaendig getrennt** -- kein Kreuz-Zuweisen.
Innerhalb **einer** Rolle gewinnt die **spezifischste** passende Regel (längster passender Präfix/exakter `item`) gegenüber generischen Regeln (`item = null`).
### System Template Rollen (fuer Mandanten)
### Mehrere Rollen
| Feld | Wert |
|------|------|
| `isSystemRole` | `true` |
| `mandateId` | `null` |
| `featureInstanceId` | `null` |
| `featureCode` | `null` |
| `roleLabel` | `"admin"`, `"user"`, `"viewer"` |
| Definiert in | `interfaceBootstrap.py``initRoles()` (Zeilen ~473-540) |
| Kopier-Mechanismus | `copySystemRolesToMandate()` in `interfaceBootstrap.py` |
| Ausgeloest bei | Mandant-Erstellung (`createMandate()`, `_provisionMandateForUser()`) |
| Bootstrap-Sync | `_ensureAllMandatesHaveSystemRoles()` beim Startup |
Über Rollen hinweg: **Union** — wenn eine Rolle nach interner Auflösung `view: true` liefert, gilt das Element als sichtbar/erlaubt (OR über Rollen). Für **DATA** werden die **freizügigsten** Stufen über Rollen kombiniert.
### Feature Template Rollen (fuer Feature-Instanzen)
### DATA: Öffnungsrechte (CUD vs. Read)
| Feld | Wert |
|------|------|
| `isSystemRole` | `false` |
| `mandateId` | `null` |
| `featureInstanceId` | `null` |
| `featureCode` | z.B. `"workspace"`, `"automation2"`, `"trustee"` |
| `roleLabel` | z.B. `"workspace-admin"`, `"workspace-user"` |
| Definiert in | `TEMPLATE_ROLES` in jedem Feature-Main (z.B. `mainWorkspace.py` Zeilen ~83-135) |
| Sync in DB | `_syncTemplateRolesToDb()` bei `registerFeature()` |
| Kopier-Mechanismus | `_copyTemplateRoles()` in `interfaceFeatures.py` (Zeilen ~209-304) |
| Ausgeloest bei | Feature-Instanz-Erstellung (`createFeatureInstance(copyTemplateRoles=True)`) |
- Ohne Leserecht (`read = n`) keine CUD.
- Mit `read = m` sind CUD höchstens `m` oder `n`.
- Mit `read = g` sind CUD höchstens `g`, `m` oder `n`.
- Mit `read = a` sind alle Stufen für CUD zulässig.
### TEMPLATE_ROLES Format (Beispiel Workspace)
### Systemfelder (DATA)
```python
TEMPLATE_ROLES = [
{
"roleLabel": "workspace-admin",
"description": {"en": "Workspace Administrator", "de": "Workspace-Administrator"},
"accessRules": [
{"context": "UI", "item": "ui.feature.workspace", "view": True},
{"context": "RESOURCE", "item": "resource.feature.workspace", "view": True},
{"context": "DATA", "item": None, "read": "a", "create": "a", "update": "a", "delete": "a", "view": True}
]
},
{
"roleLabel": "workspace-user",
"description": {"en": "Workspace User", "de": "Workspace-Benutzer"},
"accessRules": [
{"context": "UI", "item": "ui.feature.workspace", "view": True},
{"context": "RESOURCE", "item": "resource.feature.workspace", "view": True},
{"context": "DATA", "item": None, "read": "m", "create": "m", "update": "m", "delete": "m", "view": True}
]
}
]
```
Felder `id` und Namen mit führendem `_` sind für Anwendungs-CUD geschützt; Connector erzwingt das unabhängig von Access Rules.
### Kopier-Invariante
### Frontend-Optionen für Rollen
Templates werden **nur bei Erstellung** kopiert. Spaetere Aenderungen an Templates werden **nicht** automatisch an bestehende Mandanten oder Feature-Instanzen propagiert. Fuer nachtraeglichen Sync existiert `syncRolesFromTemplate()` in `interfaceFeatures.py` -- aber dies muss explizit aufgerufen werden.
`frontend_options` an Feldern kann statische Listen oder String-Referenzen (z. B. `"user.role"`) nutzen; dynamische Optionen über `/api/options/{optionsName}`. Typ-Hilfen: `gateway/modules/shared/frontendTypes.py`.
---
## Schlüssel-Dateien
## Datenmodell
| Rolle | Pfad |
### Role
```python
class Role(PowerOnModel):
id: str # UUID, auto-generiert
mandateId: Optional[str] # null = Global/Template
featureInstanceId: Optional[str] # null = nicht Instanz-spezifisch
featureCode: Optional[str] # z.B. "workspace", "graphicalEditor"
roleLabel: str # z.B. "workspace-admin"
description: TextMultilingual # {"en": "...", "de": "..."}
isSystemRole: bool # true nur fuer System Template Rollen
```
**Immutable nach Erstellung:** `mandateId`, `featureInstanceId`, `featureCode` (erzwungen in `datamodelRbac.py`).
### AccessRule
```python
class AccessRule(PowerOnModel):
id: str
roleId: str # FK -> Role
context: AccessRuleContext # DATA | UI | RESOURCE
item: Optional[str] # null = generic, oder spezifischer Key
view: bool
read: Optional[AccessLevel] # nur bei DATA relevant
create: Optional[AccessLevel]
update: Optional[AccessLevel]
delete: Optional[AccessLevel]
```
### AccessLevel
```python
class AccessLevel(str, Enum):
NONE = "n" # Kein Zugriff
OWN = "o" # Nur eigene Datensaetze (userId-Match)
MANDATE = "m" # Alle im gleichen Mandanten
ALL = "a" # Alle (mandantenuebergreifend, nur SysAdmin)
```
### Membership-Modelle
```python
class UserMandate(PowerOnModel):
userId: str
mandateId: str
enabled: bool
class UserMandateRole(PowerOnModel):
userMandateId: str # FK -> UserMandate
roleId: str # FK -> Role (Mandate-Rolle)
class FeatureAccess(PowerOnModel):
userId: str
featureInstanceId: str
enabled: bool
class FeatureAccessRole(PowerOnModel):
featureAccessId: str # FK -> FeatureAccess
roleId: str # FK -> Role (Feature-Instanz-Rolle)
```
### ER-Diagramm (Vereinfacht)
```
User
|
+-- UserMandate (1:N)
| |
| +-- UserMandateRole (1:N) ---> Role (mandateId=X)
| |
| +-- AccessRule (1:N)
|
+-- FeatureAccess (1:N)
|
+-- FeatureAccessRole (1:N) ---> Role (featureInstanceId=Y)
|
+-- AccessRule (1:N)
```
---
## Access-Rule-Kontexte
### DATA
Steuert CRUD-Berechtigungen auf Datenbank-Ebene.
- **Item-Format**: Auto-generiert aus Tabellenname + Namespace (z.B. `data.uam.UserInDB`, `data.feature.trustee.TrusteePosition`)
- **Prueft**: `read`, `create`, `update`, `delete` als Access Levels
- **`view`**: Boolean, steuert Grundsichtbarkeit
- **Mapping**: `TABLE_NAMESPACE` in `interfaceRbac.py` ordnet Modellnamen logischen Namespaces zu
- **`buildDataObjectKey()`**: Baut Keys wie `data.{namespace}.{ModelName}` oder `data.feature.{featureCode}.{ModelName}`
### UI
Steuert Sichtbarkeit und Aktivierung von Oberflaechenelementen.
- **Item-Format**: Kaskadierende Strings (z.B. `playground.voice.settings`, `ui.feature.workspace.editor`)
- **Prueft**: Nur `view` (Boolean)
- **Verwendung**: Navigation, Seitenleiste, Buttons, Tabs
### RESOURCE
Steuert Zugriff auf Systemressourcen.
- **Item-Format**: Kaskadierende Strings (z.B. `ai.model.anthropic`, `resource.feature.workspace.execute`)
- **Prueft**: Primaer `view` (Boolean)
- **Verwendung**: KI-Modelle, Aktionen, Features
---
## Resolution-Algorithmus
### Priority-System
| Rollen-Typ | Scope | Priority |
|------------|-------|----------|
| **Global/Template** | `mandateId=null, featureInstanceId=null` | 1 (niedrigste) |
| **Mandate-Rolle** | `mandateId=X, featureInstanceId=null` | 2 |
| **Feature-Instanz-Rolle** | `mandateId=X, featureInstanceId=Y` | 3 (hoechste) |
### Resolution-Ablauf
1. **Rollen laden**: Mandate-Rollen via `UserMandate``UserMandateRole`; Feature-Rollen via `FeatureAccess``FeatureAccessRole` (`getRulesForUserBulk()` in `rbac.py`)
2. **AccessRules laden** fuer alle gefundenen Rollen
3. **Gruppierung** nach Priority-Stufe
4. **Hoechste Prioritaet gewinnt** bei DATA-Permissions
5. **View wird OR-verknuepft** ueber alle Rollen der hoechsten Prioritaetsstufe
6. **Item-Spezifitaet** innerhalb einer Stufe: exact > prefix > generic (laengster passender Praefix gewinnt)
### DATA: Oeffnungsrechte (CUD vs. Read)
| Read-Level | Maximales CUD |
|------------|---------------|
| `n` (NONE) | Kein CUD moeglich |
| `o` (OWN) | CUD hoechstens `o` oder `n` |
| `m` (MANDATE) | CUD hoechstens `m`, `o` oder `n` |
| `a` (ALL) | Alle Stufen fuer CUD zulaessig |
### Mehrere Rollen (Union-Logik)
Ueber Rollen hinweg gilt **Union** (OR): wenn eine Rolle nach interner Aufloesung `view: true` liefert, gilt das Element als sichtbar/erlaubt. Fuer DATA werden die **freizuegigsten** Stufen ueber Rollen kombiniert.
### SQL-Integration
`buildRbacWhereClause()` in `interfaceRbac.py` uebersetzt die aufgeloesten Access Levels in SQL-WHERE-Bedingungen:
- `a`: Kein Filter
- `m`: `WHERE mandateId = :mandateId`
- `o`: `WHERE _createdBy = :userId`
- `n`: `WHERE 1=0` (kein Zugriff)
RBAC-gefilterte Abfragen laufen ueber `getRecordsetWithRBAC()`.
---
## Bootstrap-Ablauf
Beim Gateway-Start (`initBootstrap()` in `interfaceBootstrap.py`):
```
1. initRootMandate() -- Root-Mandant sicherstellen
2. _deduplicateRoles() -- Duplikate bereinigen, isSystemRole-Flags fixen
3. initRoles() -- System Template Rollen (admin/user/viewer) anlegen
4. initRbacRules() -- Default AccessRules auf Template-Rollen
5. _ensureAllMandatesHaveSystemRoles()
-- copySystemRolesToMandate() fuer jeden Mandanten
6. _initSysAdminRole() -- SysAdmin-Rolle auf Root-Mandant
7. _ensureUiContextRules() -- UI-Kontext-Regeln pruefen
8. Admin/Event-User sichern
9. assignInitialUserMemberships()
-- UserMandate + UserMandateRole fuer Admin/SysAdmin
```
Danach bei Feature-Registrierung (`registerAllFeaturesInCatalog()` → `registerFeature()`):
```
10. _syncTemplateRolesToDb() -- Feature Template Rollen upserten
-- AccessRules fuer Templates synchronisieren
```
Bei Laufzeit:
```
- createMandate() -- copySystemRolesToMandate() fuer neuen Mandanten
- createFeatureInstance() -- _copyTemplateRoles() fuer neue Feature-Instanz
```
---
## Systemfelder (DATA)
Felder `id` und Namen mit fuehrendem `_` (z.B. `_createdBy`, `_createdAt`) sind fuer Anwendungs-CUD geschuetzt. Der Connector erzwingt das unabhaengig von Access Rules.
## Frontend-Optionen fuer Rollen
`frontend_options` an Feldern kann statische Listen oder String-Referenzen (z.B. `"user.role"`) nutzen. Dynamische Optionen ueber `/api/options/{optionsName}`. Typ-Hilfen: `gateway/modules/shared/frontendTypes.py`.
---
## Schluessel-Dateien
| Thema | Pfad |
|-------|------|
| RBAC-Auswertung / DB-Schicht | `gateway/modules/interfaces/interfaceRbac.py` |
| AccessRule- und Permission-Modelle | `gateway/modules/datamodels/` (u. a. RBAC-bezogene Modelle) |
| Nutzer / Rollenlabels | User-Modell mit `roleLabels` (Multiselect, Union) |
| Konzept & Feldsemantik | `wiki/z-archive/appdoc/doc_security_role_based_access.md` |
| RBAC-Auswertung, SQL-Integration | `gateway/modules/interfaces/interfaceRbac.py` |
| AccessRule/Permission-Modelle | `gateway/modules/datamodels/datamodelRbac.py` |
| Membership-Modelle | `gateway/modules/datamodels/datamodelMembership.py` |
| Bootstrap (System Templates, Copy) | `gateway/modules/interfaces/interfaceBootstrap.py` |
| Feature-Template-Copy | `gateway/modules/interfaces/interfaceFeatures.py` |
| Feature-Registrierung | `gateway/modules/system/registry.py` |
| RBAC-Rule-Resolution | `gateway/modules/security/rbac.py` |
| TEMPLATE_ROLES (Beispiel) | `gateway/modules/features/workspace/mainWorkspace.py` |
## Regeln / Invarianten
- Mandanten- vs. Feature-Rollen **strikt trennen** (keine Kreuz-Zuweisung der Label-Typen).
- **Spezifisch vor generisch** innerhalb einer Rolle; **Union** über Rollen.
- UI/RESOURCE: Effektiv sichtbar nur, wenn nach Regelauflösung **`view: true`** (kein `hasAccess: false` in Navigationskonzept — nicht berechtigte UI-Einträge werden gar nicht geliefert).
- DATA: Validierung der Regeln so, dass CUD nicht über dem jeweiligen Read-Level liegen.
- Performance-Ziel: DATA-Zugriffe mit RBAC-Filter in SQL, nicht erst vollständige Tabellen in Python filtern.
- Mandanten-Rollen und Feature-Instanz-Rollen **strikt trennen** (keine Kreuz-Zuweisung der Label-Typen)
- Templates werden nur bei Erstellung kopiert -- keine automatische Propagierung
- **Spezifisch vor generisch** innerhalb einer Rolle; **Union** ueber Rollen
- UI/RESOURCE: Sichtbar nur wenn `view: true`; nicht berechtigte UI-Eintraege werden gar nicht geliefert
- DATA: CUD darf nicht ueber dem jeweiligen Read-Level liegen
- Performance-Ziel: DATA-Zugriffe mit RBAC-Filter in SQL

View file

@ -1,361 +1,83 @@
# Refactoring Concept: Add WEB_SEARCH_MEDIA Operation Type
<!-- status: plan -->
<!-- started: 2026-03-01 -->
<!-- component: gateway -->
## Executive Summary
# Web Image Search -- WEB_SEARCH_MEDIA Operation Type
**Refactoring Goal**: Add image search capability by introducing `WEB_SEARCH_MEDIA` operation type and Google Custom Search connector.
## Beschreibung und Kontext
**Current State**:
- ✅ `WEB_SEARCH_DATA` already exists (replaces former `WEB_SEARCH`)
- ✅ Tavily connector handles `WEB_SEARCH_DATA` for web page/content search
- ✅ `AiCallPromptWebSearchData` model exists
PowerOn unterstützt Web-Suche über den Tavily-Connector (`WEB_SEARCH_DATA`). Es fehlt eine dedizierte Bildersuche. Ziel ist ein neuer OperationType `WEB_SEARCH_MEDIA` mit Google Custom Search Connector, damit der AI Agent Bilder suchen und als ActionDocuments zurückgeben kann.
**Target State**:
- Add `WEB_SEARCH_MEDIA` operation type for image/media search
- Create Google Custom Search connector for `WEB_SEARCH_MEDIA`
- Create `AiCallPromptWebSearchMedia` model
- Add `ai.searchImages` action using `WEB_SEARCH_MEDIA`
**Business-Treiber:** Mandanten benötigen Bildmaterial (z.B. für Immobilien-Exposés, Marketing-Content) direkt im AI Workspace.
**Estimated Complexity**: Medium (1-2 days development)
**Risiko bei Nicht-Umsetzung:** Manuelle Bildersuche ausserhalb der Plattform, kein automatisierter Image-Workflow.
**Integration Impact**: Low - additive changes, no breaking changes
## Fokus und kritische Details
---
- Google Custom Search API erfordert API Key + Search Engine ID mit aktivierter Image Search
- Model-Selection muss `WEB_SEARCH_MEDIA` korrekt zum Google-Connector routen
- Rückgabeformat: JSON-Array mit Image-URLs, konsistent mit Tavily-Format
- Rate Limiting der Google API (100 queries/Tag im Free Tier)
## 1. Current Architecture
## Ziel und Nicht-Ziele
### 1.1 Operation Types
- **Ziel:** `WEB_SEARCH_MEDIA` OperationType in `OperationTypeEnum`
- **Ziel:** Google Custom Search Connector (`aicorePluginGoogle.py`)
- **Ziel:** `AiCallPromptWebSearchMedia` Pydantic Model
- **Ziel:** `ai.searchImages` Action in `methodAi`
- **Explizit NICHT:** Video-Suche, Tavily-Erweiterung, SharePoint-Integration
**Current Operation Types** (`gateway/modules/datamodels/datamodelAi.py`):
```python
class OperationTypeEnum(str, Enum):
# ... existing operations ...
# Web Operations
WEB_SEARCH_DATA = "webSearchData" # Web page search (Tavily) ✅ EXISTS
WEB_CRAWL = "webCrawl" # Web crawl for a given URL
```
## Betroffene Module
**Target Operation Types**:
```python
class OperationTypeEnum(str, Enum):
# ... existing operations ...
# Web Operations
WEB_SEARCH_DATA = "webSearchData" # Web page search (Tavily) ✅ EXISTS
WEB_SEARCH_MEDIA = "webSearchMedia" # Image/media search (Google) ⬅️ NEW
WEB_CRAWL = "webCrawl" # Web crawl for a given URL
```
- Gateway: `modules/datamodels/datamodelAi.py`, `modules/aicore/aicorePluginGoogle.py` (NEU), `modules/services/serviceAi/mainServiceAi.py`, `modules/workflows/methods/methodAi/`
- Frontend: keine (Agent nutzt Action intern)
- DB-Migration: nein
- Andere: Google Cloud Console Setup (API Key)
### 1.2 Model Capabilities
## Entscheidungen
**Tavily Connector** (`gateway/modules/aicore/aicorePluginTavily.py`):
- ✅ Registered for `WEB_SEARCH_DATA` (rating: 9)
- ✅ Registered for `WEB_CRAWL` (rating: 10)
- ❌ Does NOT support image search - designed for web/text content only
| Datum | Entscheidung | Begründung |
|-------|-------------|------------|
| 2026-03-01 | Google Custom Search statt Tavily | Tavily unterstützt keine Bildersuche |
| 2026-03-01 | Eigener Connector statt Tavily-Erweiterung | Saubere Trennung, unabhängige Skalierung |
**Model Selection**:
- Dynamic model selection routes based on `OperationTypeEnum` in `AiCallOptions`
- Models register capabilities via `operationTypes` with ratings (1-10)
- System automatically selects best model for each operation type
## Umsetzungs-Checkliste
---
- [ ] `WEB_SEARCH_MEDIA` in `OperationTypeEnum` hinzufügen
- [ ] `AiCallPromptWebSearchMedia` Model erstellen
- [ ] `aicorePluginGoogle.py` Connector implementieren
- [ ] Connector in Discovery registrieren
- [ ] `mainServiceAi.py` für `WEB_SEARCH_MEDIA` erweitern
- [ ] `searchImages` Action erstellen
- [ ] Action in `methodAi.py` registrieren
- [ ] RBAC / Permissions: nicht betroffen (nutzt bestehende AI-Berechtigung)
- [ ] Neutralisierung betroffen? nein
- [ ] Navigation / Routing: nicht betroffen
- [ ] Billing-Impact? prüfen (API-Kosten pro Query)
## 2. Refactoring Plan
## Akzeptanzkriterien
### 2.1 Add WEB_SEARCH_MEDIA Operation Type
| # | Kriterium (Given-When-Then) | Prio |
|---|---------------------------|------|
| 1 | Given ein AI Agent mit `searchImages` Tool, When der User "Suche Bilder von Zürich" eingibt, Then gibt der Agent eine Liste von Bild-URLs zurück | must |
| 2 | Given kein Google API Key konfiguriert, When `WEB_SEARCH_MEDIA` aufgerufen wird, Then wird ein klarer Fehler zurückgegeben | must |
| 3 | Given `maxResults=5` und `imageType=photo`, When die Suche ausgeführt wird, Then werden max. 5 Fotos zurückgegeben | should |
**File**: `gateway/modules/datamodels/datamodelAi.py`
## Testplan
**Changes**:
```python
class OperationTypeEnum(str, Enum):
# ... existing operations ...
# Web Operations
WEB_SEARCH_DATA = "webSearchData" # Web page search (Tavily)
WEB_SEARCH_MEDIA = "webSearchMedia" # Image/media search (Google) ⬅️ ADD
WEB_CRAWL = "webCrawl" # Web crawl for a given URL
```
| ID | AC | Art | Automatisiert | Repo-Pfad | Status |
|----|----|-----|--------------|-----------|--------|
| T1 | 1 | integration | ja | gateway/tests/test_google_connector.py | pending |
| T2 | 2 | unit | ja | gateway/tests/test_google_connector.py | pending |
| T3 | 3 | api | ja | gateway/tests/test_search_images_action.py | pending |
### 2.2 Create AiCallPromptWebSearchMedia Model
## Links
**File**: `gateway/modules/datamodels/datamodelAi.py`
- Google Custom Search API: https://developers.google.com/custom-search/v1/overview
- Bestehender Tavily Connector: `gateway/modules/aicore/aicorePluginTavily.py`
**Add after `AiCallPromptWebSearchData`**:
```python
class AiCallPromptWebSearchMedia(BaseModel):
"""Structured prompt format for WEB_SEARCH_MEDIA operation - returns list of image URLs."""
instruction: str = Field(description="Search instruction/query for finding relevant images")
maxResults: Optional[int] = Field(default=10, description="Maximum number of images to return (default: 10)")
imageType: Optional[str] = Field(default=None, description="Image type filter: 'photo', 'clipart', 'lineart', 'animated'")
size: Optional[str] = Field(default=None, description="Image size filter: 'small', 'medium', 'large', 'xlarge'")
color: Optional[str] = Field(default=None, description="Color filter: 'color', 'grayscale', 'transparent'")
country: Optional[str] = Field(default=None, description="Two-digit country code (lowercase, e.g., ch, us, de, fr)")
language: Optional[str] = Field(default=None, description="Language code (lowercase, e.g., de, en, fr)")
```
## Abschluss
### 2.3 Create Google Custom Search Connector
**New File**: `gateway/modules/aicore/aicorePluginGoogle.py`
**Structure** (similar to `aicorePluginTavily.py`):
```python
class AiGoogle(BaseConnectorAi):
"""Google Custom Search connector for image search."""
def getModels(self) -> List[AiModel]:
"""Get Google Custom Search model."""
return [
AiModel(
name="google-custom-search",
displayName="Google Custom Search",
connectorType="google",
apiUrl="https://www.googleapis.com/customsearch/v1",
# ... model configuration ...
operationTypes=createOperationTypeRatings(
(OperationTypeEnum.WEB_SEARCH_MEDIA, 9)
),
functionCall=self._routeWebOperation,
# ...
)
]
async def _routeWebOperation(self, modelCall: AiModelCall) -> "AiModelResponse":
"""Route web operation based on operation type."""
operationType = modelCall.options.operationType
if operationType == OperationTypeEnum.WEB_SEARCH_MEDIA:
return await self.webSearchMedia(modelCall)
else:
return AiModelResponse(
content="",
success=False,
error=f"Unsupported operation type: {operationType}"
)
async def webSearchMedia(self, modelCall: AiModelCall) -> "AiModelResponse":
"""WEB_SEARCH_MEDIA operation - returns list of image URLs using Google Custom Search."""
# Parse AiCallPromptWebSearchMedia from messages
# Call Google Custom Search API with searchType=image
# Return JSON array of image URLs (same format as Tavily for consistency)
```
**Key Implementation Points**:
- Use Google Custom Search API with `searchType=image`
- Return image URLs in JSON array format (consistent with Tavily)
- Support filters: imageType, size, color, country, language
- Handle API errors and rate limiting
### 2.4 Update Service AI Handler
**File**: `gateway/modules/services/serviceAi/mainServiceAi.py`
**Update `_handleWebOperations()` method**:
```python
async def _handleWebOperations(
self,
prompt: str,
options: AiCallOptions,
opType: OperationTypeEnum,
aiOperationId: str
) -> AiResponse:
"""Handle WEB_SEARCH_DATA, WEB_SEARCH_MEDIA, and WEB_CRAWL operation types."""
# Existing logic handles WEB_SEARCH_DATA and WEB_CRAWL
# Add support for WEB_SEARCH_MEDIA
if opType == OperationTypeEnum.WEB_SEARCH_DATA or opType == OperationTypeEnum.WEB_SEARCH_MEDIA or opType == OperationTypeEnum.WEB_CRAWL:
# ... existing implementation ...
```
### 2.5 Add searchImages Action
**New File**: `gateway/modules/workflows/methods/methodAi/actions/searchImages.py`
**Implementation**:
```python
async def searchImages(self, parameters: Dict[str, Any]) -> ActionResult:
"""Search for images on the web using a prompt and return them as documents."""
prompt = parameters.get("prompt")
if not prompt:
return ActionResult.isFailure(error="Search prompt is required")
maxResults = parameters.get("maxResults", 5)
imageType = parameters.get("imageType")
size = parameters.get("size")
color = parameters.get("color")
# Build AiCallPromptWebSearchMedia
searchPromptModel = AiCallPromptWebSearchMedia(
instruction=prompt,
maxResults=maxResults,
imageType=imageType,
size=size,
color=color
)
# Call AI with WEB_SEARCH_MEDIA operation
searchOptions = AiCallOptions(
operationType=OperationTypeEnum.WEB_SEARCH_MEDIA,
resultFormat="json"
)
# System will automatically route to Google connector
searchResponse = await self.services.ai.callAiContent(
prompt=searchPromptModel.model_dump_json(exclude_none=True, indent=2),
options=searchOptions,
outputFormat="json"
)
# Parse response to extract image URLs
# Download images in parallel
# Create ActionDocument for each image
# Return ActionResult with list of documents
```
**Update**: `gateway/modules/workflows/methods/methodAi/methodAi.py`
**Add action definition**:
```python
"searchImages": WorkflowActionDefinition(
actionId="ai.searchImages",
description="Search for images on the web using a prompt and return them as documents",
dynamicMode=True,
parameters={
"prompt": WorkflowActionParameter(...),
"maxResults": WorkflowActionParameter(...),
"imageType": WorkflowActionParameter(...),
"size": WorkflowActionParameter(...)
},
execute=searchImages.__get__(self, self.__class__)
)
```
---
## 3. Implementation Steps
### Phase 1: Add WEB_SEARCH_MEDIA Operation Type (30 min)
1. ✅ Add `WEB_SEARCH_MEDIA = "webSearchMedia"` to `OperationTypeEnum`
2. ✅ Create `AiCallPromptWebSearchMedia` model
3. ✅ Update imports/exports
### Phase 2: Create Google Connector (4-6 hours)
1. ✅ Create `aicorePluginGoogle.py` file
2. ✅ Implement `AiGoogle` connector class
3. ✅ Implement `webSearchMedia()` method
4. ✅ Register connector in connector discovery
5. ✅ Test Google API integration
6. ✅ Handle errors and rate limiting
### Phase 3: Update Service Handlers (1-2 hours)
1. ✅ Update `serviceAi/mainServiceAi.py` to handle `WEB_SEARCH_MEDIA`
2. ✅ Ensure routing works correctly
3. ✅ Test model selection routes to Google connector
### Phase 4: Add searchImages Action (3-4 hours)
1. ✅ Create `actions/searchImages.py`
2. ✅ Implement image search logic
3. ✅ Implement parallel image download
4. ✅ Add action definition to `methodAi.py`
5. ✅ Add to `actions/__init__.py`
6. ✅ Write unit tests
### Phase 5: Testing & Integration (2-3 hours)
1. ✅ Test Google connector with real API
2. ✅ Test searchImages action end-to-end
3. ✅ Verify dynamic model selection
4. ✅ Test error handling
5. ✅ Integration testing
**Total Estimated Time**: 1-2 days
---
## 4. Configuration Requirements
### 4.1 Environment Variables
**Required**:
- `GOOGLE_SEARCH_API_KEY` - Google Custom Search API key
- `GOOGLE_SEARCH_ENGINE_ID` - Custom Search Engine ID
- Note: Must enable "Image Search" in Google Custom Search Engine settings
### 4.2 Google Custom Search Setup
1. Create Google Custom Search Engine at https://programmablesearchengine.google.com/
2. Enable "Image Search" in settings
3. Get API key from Google Cloud Console
4. Configure environment variables
---
## 5. Files to Create/Modify
### New Files
- `gateway/modules/aicore/aicorePluginGoogle.py` - Google connector
- `gateway/modules/workflows/methods/methodAi/actions/searchImages.py` - Image search action
### Modified Files
- `gateway/modules/datamodels/datamodelAi.py` - Add `WEB_SEARCH_MEDIA` and `AiCallPromptWebSearchMedia`
- `gateway/modules/services/serviceAi/mainServiceAi.py` - Handle `WEB_SEARCH_MEDIA`
- `gateway/modules/workflows/methods/methodAi/methodAi.py` - Add `searchImages` action
- `gateway/modules/workflows/methods/methodAi/actions/__init__.py` - Export `searchImages`
- Connector discovery module (if exists) - Register Google connector
---
## 6. Testing Requirements
### Unit Tests
- Google connector `webSearchMedia()` method
- `searchImages` action with various parameters
- Error handling (API errors, rate limits, invalid responses)
- Image download and validation
### Integration Tests
- End-to-end image search workflow
- Dynamic model selection routes to Google connector
- Multiple image downloads in parallel
- Verify ActionDocuments are created correctly
---
## 7. Risks & Mitigation
| Risk | Impact | Probability | Mitigation |
|------|--------|-------------|------------|
| Google API setup complexity | Medium | Medium | Provide clear setup instructions, validate API keys at startup |
| Dynamic model selection routing | Medium | Low | Thoroughly test operation type routing |
| API rate limiting (Google) | Low | Medium | Implement retry logic with exponential backoff |
| Missing connector registration | Medium | Low | Ensure connector is registered in discovery system |
---
## 8. Dependencies
- **Google Custom Search API** - REQUIRED
- **Google API Client Library** - May need `google-api-python-client` package
- **HTTP Client** - For image downloads (existing)
- **Base64 Encoding** - Python standard library (no dependency)
---
## 9. Success Criteria
`WEB_SEARCH_MEDIA` operation type exists
✅ Google connector is registered and functional
`searchImages` action works end-to-end
✅ Dynamic model selection routes `WEB_SEARCH_MEDIA` to Google connector
✅ Images are downloaded and returned as ActionDocuments
✅ All tests pass
---
**Document Version**: 2.0
**Last Updated**: 2026-01-01
**Status**: Refactoring Concept - Ready for Implementation
- [ ] b-reference/gateway/ai-agent.md aktualisiert (neuer OperationType)
- [ ] TOPICS.md aktualisiert (falls neues Thema)
- [ ] Dieses Dokument → z-archive/ verschoben

File diff suppressed because it is too large Load diff

View file

@ -5,16 +5,61 @@
## Naming
- Alle internen Funktionen beginnen mit `_` Prefix
- camelCase für Variablen und Funktionsnamen (kein snake_case)
- Alle internen Funktionen beginnen mit `_` Prefix (nicht exportierbar)
- **camelCase** fuer Variablen und Funktionsnamen (kein snake_case)
- **PascalCase** fuer Klassen und Pydantic-Models
- Dateien: `camelCase` fuer Module (z.B. `mainServiceAi.py`, `routeBilling.py`)
## Frontend-Regeln
- Keine Browser-Dialoge (alert/confirm/prompt) -- stattdessen `useConfirm()` und `usePrompt()` Hooks
- CSS Modules für Komponenten-Styles
## Backend-Regeln
## Frontend (React/TypeScript)
- Keine Browser-Dialoge (`alert`, `confirm`, `prompt`) -- stattdessen `useConfirm()` / `usePrompt()` Hooks
- CSS Modules fuer Styling
- Hooks-Pattern fuer State und API-Zugriffe (`useApiRequest`, `useBilling`, etc.)
- Fehler propagieren -- keine stillen Fallbacks bei kritischen Pfaden
- Pydantic-Models als einzige Quelle für UI-Feld-Definitionen
- `PowerOnModel` als Basis mit `sysCreatedAt`, `sysCreatedBy`, `sysModifiedAt`, `sysModifiedBy`
## Backend (FastAPI/Python)
- **Pydantic-Models** als einzige Quelle fuer UI-Feld-Definitionen
- **`PowerOnModel`** als Basis mit System-Audit-Feldern (`sysCreatedAt`, `sysCreatedBy`, `sysModifiedAt`, `sysModifiedBy`)
- Fehler propagieren -- Exceptions explizit werfen, nicht schlucken
- Config ueber `APP_CONFIG` (aus `modules/shared/configuration.py`)
## Projektstruktur Gateway
```
gateway/
app.py # FastAPI-App, Middleware, Startup
config.ini # Statische Konfiguration
modules/
auth/ # JWT, OAuth, CSRF, Authentication
datamodels/ # Pydantic-Models (zentrale Quelle)
features/ # Feature-Module (workspace, automation, ...)
<name>/
main<Name>.py # FEATURE_CODE, Registrierung
routeFeature<Name>.py # HTTP-Endpunkte
interfaceFeature<Name>.py # DB-Interface
datamodelFeature<Name>.py # Feature-spezifische Models
interfaces/ # DB-Interfaces (CRUD, Queries)
routes/ # Core-Routes (billing, admin, GDPR, ...)
security/ # RBAC-Engine
serviceCenter/
core/ # serviceSecurity, serviceUtils, serviceStreaming
services/ # serviceAi, serviceChat, serviceAgent, ...
registry.py # Service-Registrierung und Dependencies
shared/ # configuration.py, Utilities
system/ # registry.py (Feature-Discovery)
workflows/
methods/ # Unified Action Library
processing/ # WorkflowProcessor, Modes
automation/ # v1 Runtime
automation2/ # v2 Engine
connectors/ # Externe Systeme (DB, SharePoint, Jira, ...)
aicore/ # AI-Provider-Plugins, Model-Selector
```
## Anti-Patterns
- Keine impliziten Type-Conversions in API-Responses
- Keine DB-Queries in Route-Handlern (immer ueber Interfaces)
- Kein direkter `os.environ`-Zugriff -- immer `APP_CONFIG`
- Keine hartkodierten Secrets -- verschluesselt in Env-Dateien

View file

Before

Width:  |  Height:  |  Size: 84 KiB

After

Width:  |  Height:  |  Size: 84 KiB

View file

@ -1,25 +1,85 @@
<!-- status: draft -->
<!-- status: canonical -->
<!-- lastReviewed: 2026-04-05 -->
# Dev-Setup
## Voraussetzungen
- Python (Anaconda/Miniconda)
- Node.js
- PostgreSQL mit pgvector
- **Python 3.11** (CI-Standard; Minimum 3.10)
- **Node.js** (aktuelles LTS empfohlen)
- **PostgreSQL** mit **pgvector**-Extension
- **Conda** oder **venv** fuer Python-Umgebung
## Gateway starten
```bash
conda activate <env>
cd gateway/
# Python-Umgebung aktivieren (Conda oder venv)
conda activate <env>
# oder: python -m venv .venv && .\.venv\Scripts\Activate.ps1
pip install -r requirements.txt
# Config vorbereiten:
# 1. config.ini muss im gateway/ Verzeichnis liegen (statische Settings)
# 2. .env Datei aus env_dev.env kopieren/symlinken:
# copy env_dev.env .env
# 3. APP_KEY_SYSVAR in config.ini zeigt auf Master-Key (OS-Variable oder Dateipfad)
uvicorn app:app --host 0.0.0.0 --port 8000
```
### Konfigurationsdateien
| Datei | Zweck |
|-------|-------|
| `gateway/config.ini` | Statische Settings (Auth-Defaults, Upload-Limits, Operator-Text) |
| `gateway/.env` | Runtime-Env (DB, API-Keys, Secrets) -- wird aus `env_dev.env` kopiert |
| `gateway/env_dev.env` | Dev-Umgebung Template (verschluesselte Secrets mit `DEV_ENC:` Prefix) |
| `gateway/env_int.env` | Integration-Umgebung |
| `gateway/env_prod.env` | Produktion |
### Wichtige Env-Variablen (Auszug)
| Variable | Beschreibung |
|----------|-------------|
| `DB_HOST`, `DB_USER`, `DB_PASSWORD_SECRET` | PostgreSQL-Verbindung |
| `APP_KEY_SYSVAR` | Master-Key fuer Secrets-Entschluesselung |
| `APP_API_URL` | API-URL (bestimmt Cookie-Secure-Flag) |
| `APP_ALLOWED_ORIGINS` | CORS Origins (inkl. `http://localhost:5176`) |
| `Service_GOOGLE_AUTH_*` | Google OAuth Auth-App |
| `Service_GOOGLE_DATA_*` | Google OAuth Data-App |
## Frontend Nyla starten
```bash
cd frontend_nyla/
npx vite --port 8080 --mode dev
npm install
npm run dev
# oder: npx vite --port 5176 --mode dev
```
Frontend laeuft auf **http://localhost:5176** und erwartet Gateway auf **http://localhost:8000**.
### Frontend-Konfiguration
| Datei | Zweck |
|-------|-------|
| `frontend_nyla/config/.env.dev` | Dev-Mode Env (VITE_API_BASE_URL etc.) |
| `frontend_nyla/config/.env.int` | Integration |
| `frontend_nyla/config/.env.prod` | Produktion |
## Secrets-Verwaltung
Env-Dateien enthalten verschluesselte Werte (`DEV_ENC:`, `INT_ENC:`, `PROD_ENC:`).
Zur Entschluesselung wird der Master-Key ueber `APP_KEY_SYSVAR` geladen.
Details: -> `d-guides/encrypt-env-secrets.md`
## Weiterführende Guides
- Secrets verschluesseln: `d-guides/encrypt-env-secrets.md`
- Google OAuth einrichten: `d-guides/google-oauth-setup.md`
- Cookie/JWT-Migration: `d-guides/security-migration-guide.md`
- Coding-Konventionen: `d-guides/coding-conventions.md`
- Testing: `d-guides/testing-strategy.md`

View file

@ -1,6 +1,6 @@
### Admin-Leitfaden: *_SECRET-Werte in Env-Dateien verschlüsseln
Diese Anleitung beschreibt, wie das PowerOn-Gateway-Tool `tool_security_encrypt_all_env_files.py` alle unverschlüsselten `*_SECRET`-Variablen in `env_dev.env`, `env_int.env` und `env_prod.env` verschlüsselt.
Diese Anleitung beschreibt, wie das PowerOn-Gateway-Tool `scripts/script_security_encrypt_all_env_files.py` alle unverschlüsselten `*_SECRET`-Variablen in `env_dev.env`, `env_int.env` und `env_prod.env` verschlüsselt.
---
@ -17,7 +17,7 @@ Das Tool kann in jedem Repository eingesetzt werden, sofern die untenstehenden V
- Die zu verarbeitenden Env-Dateien: `env_dev.env`, `env_int.env`, `env_prod.env` (oder eine Teilmenge via `--files`).
- **Rechte**: Schreibrechte auf die Env-Dateien, Leserechte auf die Masterkey-Quelle (siehe Punkt 2).
Hinweis: Das Tool importiert `encrypt_value` aus `modules/shared/configuration.py`. Stellen Sie sicher, dass dieser Pfad importierbar ist (Start im Gateway-Verzeichnis oder `PYTHONPATH` setzen).
Hinweis: Das Tool importiert `encryptValue` aus `modules/shared/configuration.py`. Stellen Sie sicher, dass dieser Pfad importierbar ist (Start im Gateway-Verzeichnis oder `PYTHONPATH` setzen). Das Script fuegt `gateway` automatisch zu `sys.path` hinzu.
---
@ -25,12 +25,12 @@ Hinweis: Das Tool importiert `encrypt_value` aus `modules/shared/configuration.p
1. `requirements.txt` im Projekt (Minimalbeispiel):
```
cryptography>=42.0.0
cryptography>=41.0.0
```
Falls Sie das Tool im bestehenden Gateway verwenden, nutzen Sie stattdessen die dortige Datei `gateway/requirements.txt`.
2. Notwendige Gateway-Komponenten (sofern außerhalb von `gateway` verwendet):
- `modules/shared/configuration.py` (liefert `APP_CONFIG`, `_get_master_key`, `encrypt_value`, `decrypt_value`)
- `modules/shared/configuration.py` (liefert `APP_CONFIG`, `_getMasterKey`, `encryptValue`, `decryptValue`)
- Optional, aber unterstützt: `modules/shared/auditLogger.py` (Audit-Events; Ausfall ist toleriert)
- `config.ini` im gleichen Baum wie `modules`, mit mindestens:
- `APP_ENV_TYPE = dev|int|prod` (kann pro Env-Datei zusätzlich gesetzt werden)
@ -80,7 +80,7 @@ Tool: `gateway/tool_security_encrypt_all_env_files.py`
- Liest je Datei `APP_ENV_TYPE`.
- Findet alle Schlüssel, deren Name auf `_SECRET` endet, und deren Werte noch kein `*_ENC:`-Präfix haben.
- Unterstützt einzeilige und mehrzeilige JSON-Werte (Klammer-Auf/Zu wird erkannt).
- Verschlüsselt die Werte mit dem passenden Umgebungs-Key und ersetzt die Originalwerte in der Datei durch `ENV_ENC:<payload>`.
- Verschlüsselt die Werte mit dem passenden Umgebungs-Key und ersetzt die Originalwerte in der Datei durch den umgebungsspezifischen Prefix, z.B. `DEV_ENC:<payload>`, `INT_ENC:<payload>`, `PROD_ENC:<payload>` (abhaengig von `APP_ENV_TYPE`).
- Legt vor Änderungen eine Backup-Datei mit Zeitstempel neben der Originaldatei an, z. B. `env_prod.env.20251015_101530.backup`.
- Trockenlauf (nur anzeigen, nichts schreiben):

View file

@ -15,11 +15,10 @@ This guide explains how to set up Google OAuth 2.0 authentication for the PowerO
4. Enter a project name (e.g., "PowerOn OAuth")
5. Click "Create"
## Step 2: Enable Google+ API
## Step 2: Configure OAuth Consent Screen
1. In your new project, go to "APIs & Services" > "Library"
2. Search for "Google+ API" or "Google Identity"
3. Click on "Google+ API" and click "Enable"
1. In your new project, go to "APIs & Services" > "OAuth consent screen"
2. Configure the consent screen (Google+ API is deprecated, use Google Identity Services / OIDC)
## Step 3: Create OAuth 2.0 Credentials
@ -35,9 +34,10 @@ This guide explains how to set up Google OAuth 2.0 authentication for the PowerO
4. Back to creating OAuth client ID:
- Application type: "Web application"
- Name: "PowerOn Web Client"
- Authorized redirect URIs: Add your redirect URI
- For development: `http://localhost:8000/api/google/auth/callback`
- For production: `https://yourdomain.com/api/google/auth/callback`
- Authorized redirect URIs: Add **two** redirect URIs per environment:
- Login callback: `http://localhost:8000/api/google/auth/login/callback`
- Data connect callback: `http://localhost:8000/api/google/auth/connect/callback`
- For production: replace `localhost:8000` with your domain (HTTPS)
5. Click "Create"
6. **Important**: Copy the Client ID and Client Secret - you'll need these for the next step
@ -48,12 +48,19 @@ This guide explains how to set up Google OAuth 2.0 authentication for the PowerO
2. Replace the placeholder values with your actual Google OAuth credentials:
```env
# Google OAuth Configuration
Service_GOOGLE_CLIENT_ID = your-actual-client-id-from-google-console
Service_GOOGLE_CLIENT_SECRET = your-actual-client-secret-from-google-console
Service_GOOGLE_REDIRECT_URI = http://localhost:8000/api/google/auth/callback
# Google OAuth -- Auth App (Login)
Service_GOOGLE_AUTH_CLIENT_ID = your-auth-client-id
Service_GOOGLE_AUTH_CLIENT_SECRET = your-auth-client-secret
Service_GOOGLE_AUTH_REDIRECT_URI = http://localhost:8000/api/google/auth/login/callback
# Google OAuth -- Data App (SharePoint/Drive Connection)
Service_GOOGLE_DATA_CLIENT_ID = your-data-client-id
Service_GOOGLE_DATA_CLIENT_SECRET = your-data-client-secret
Service_GOOGLE_DATA_REDIRECT_URI = http://localhost:8000/api/google/auth/connect/callback
```
> **Hinweis:** Das Gateway unterscheidet zwei OAuth-Apps: eine fuer Login (Auth) und eine fuer Daten-Connections (Data). Details: `routeSecurityGoogle.py`.
3. Save the file
4. Restart your PowerOn gateway server

View file

@ -1,26 +1,105 @@
<!-- status: draft -->
<!-- status: canonical -->
<!-- lastReviewed: 2026-04-05 -->
# Testing-Strategie
## Testpyramide
- **Unit Tests:** pytest (Gateway), vitest (Frontend)
- **Integration Tests:** API-Tests gegen laufenden Gateway
- **E2E Tests:** (zu definieren)
```
/ E2E \ <- Manuell / geplant
/ Functional \ <- Standalone-Scripts (AI, real calls)
/ Integration \ <- pytest (DB, Workflows, RBAC)
/ Unit \ <- pytest (Models, Utils, Logic)
```
## Akzeptanzkriterien-Format
## Gateway: `gateway/tests/`
Given-When-Then Sätze in Arbeits-Dokumenten (`c-work/_TEMPLATE.md`)
### Test-Kategorien
## Testplan-Tabelle
| Kategorie | Pfad | Beschreibung | Ausfuehrung |
|-----------|------|-------------|-------------|
| **Unit** | `tests/unit/` | Datamodels, RBAC-Logic, Utils, Workflows | `pytest tests/unit/` |
| **Integration** | `tests/integration/` | DB-abhaengig (RBAC, Workflow-Execution) | `pytest tests/integration/` |
| **Validation** | `tests/validation/` | Architektur-Validierung | `pytest tests/validation/` |
| **Functional** | `tests/functional/` | AI-Modell-Tests, KPI, JSON-Verarbeitung | `python tests/functional/<script>.py` (standalone, nicht pytest) |
Jedes Arbeits-Dokument enthält eine Testplan-Tabelle:
### Test-Verzeichnisstruktur
```
tests/
conftest.py # sys.path Setup
README.md # Ausfuehrungshinweise
test_phase123_basic.py # Root-Level Basistest
unit/
datamodels/ # test_workflow_models, test_docref
rbac/ # test_rbac_bootstrap, test_rbac_permissions
services/ # test_renderer_pdf_smoke, test_json_extraction
utils/ # test_json_utils
workflows/ # test_automation2_graphUtils, test_state_management
integration/
rbac/ # test_rbac_database
workflows/ # test_workflow_execution
validation/
test_architecture_validation.py
functional/
test01_ai_model_selection.py # Nummerierte AI-Testscripts
... # test14_json_continuation_context.py etc.
```
### Tests ausfuehren
```bash
cd gateway/
# Standard (ohne expensive-Marker):
pytest
# Alle Tests (inkl. expensive):
pytest -m ""
# Nur Unit:
pytest tests/unit/
# Mit Coverage:
pytest --cov=modules tests/unit/
```
### Marker
- `@pytest.mark.expensive` -- Standardmaessig uebersprungen (Default: `-m 'not expensive'` in `pytest.ini`)
- `@pytest.mark.asyncio` -- Fuer async Tests
### Konfiguration
`pytest.ini` im `gateway/`-Root:
- `testpaths = tests`, `pythonpath = .`
- Log-Output: `logs/test_logs.log`
- Default: `-v --tb=short -m 'not expensive'`
## Frontend Nyla
**Aktueller Stand (2026-04-05):** Kein automatisiertes Test-Framework installiert. `package.json` enthaelt keinen `test`-Script, keine Vitest/Jest/Cypress-Dependency.
**Qualitaets-Gate:** Nur ESLint (`npm run lint`).
**Empfehlung:** Vitest fuer Unit/Component-Tests, Playwright oder Cypress fuer E2E.
## Akzeptanzkriterien (AC-Format)
In Work-Items (`c-work/`) verwenden wir Given-When-Then:
```
Given <Vorbedingung>
When <Aktion>
Then <erwartetes Ergebnis>
```
### Testplan-Mapping
| ID | AC | Art | Automatisiert | Repo-Pfad | Status |
|----|----|-----|--------------|-----------|--------|
| T1 | 1 | unit | ja | gateway/tests/unit/... | pending |
| T2 | 2 | integration | ja | gateway/tests/integration/... | pending |
| T3 | 3 | api | nein | -- | pending |
## Test-Pfade im Repo
- Gateway: `gateway/tests/`
- Frontend: `frontend_nyla/src/**/*.test.ts`
Referenz: -> `c-work/_TEMPLATE.md` (Testplan-Section)

View file

@ -1,4 +1,4 @@
# Neutralisierung
# Neutralisierung
**Stand:** 2026-03-29
@ -54,11 +54,11 @@ Eine Engine, drei Einstiege, aufgerufen an den Content-Einstiegspunkten aus Absc
| `processText(text)` | Rohtext (Prompt, Message, Text-ContentPart, Text-Chunk) | **Ist** |
| `processFile(fileId)` | Datei aus DB — wird nach MIME geroutet | **Ist** |
| `processBinaryBytes` / `Async` | Bytes + Dateiname + MIME | **Ist** |
| **`processImage(imageBytes, fileName)`** | Bild-Datei oder Bild-ContentPart (image/png, image/jpeg, …) | **Soll — fehlt heute** |
| **`processImage(imageBytes, fileName)`** / `processImageAsync` | Bild-Datei oder Bild-ContentPart (image/png, image/jpeg, …) | **Ist** (seit ~2026 Q1) |
**Ist-Zustand Bilder:** `_processBinaryFile` überspringt Parts mit `typeGroup == 'image'` (unverändert durchgereicht). Eigenständige Bild-Dateien (`image/*` MIME) werden in `_isBinaryMimeType` als Skip behandelt. **Es gibt keinen Bild-Neutralisierungs-Pfad.**
**Ist-Zustand Bilder (aktualisiert 2026-04-05):** `processImageAsync` ist implementiert und nutzt `NEUTRALIZATION_IMAGE` (Private LLM Vision). `_processBinaryFile` verarbeitet jetzt Bild-Parts ueber `processImageAsync`. **Einschraenkung:** Eigenstaendige Bild-Dateien (`image/*` MIME direkt an `processFile`) werden in `_isBinaryMimeType` weiterhin als Skip behandelt.
**Soll:** `processImage` als eigener Einstieg. Ruft internes Vision-Modell (`NEUTRALIZATION_IMAGE` → Private-LLM) auf, um sensible Inhalte in Bildern zu erkennen/entfernen. Kein externer Provider. Modell nicht verfügbar → Bild blockieren (nicht unbehandelt weiterreichen).
**Ist (aktualisiert):** `processImageAsync` existiert als Einstieg. Ruft internes Vision-Modell (`NEUTRALIZATION_IMAGE` → Private-LLM) auf, um sensible Inhalte in Bildern zu erkennen/entfernen. Kein externer Provider. Modell nicht verfügbar → Bild blockieren (nicht unbehandelt weiterreichen).
**Ablauf nach MIME:**

View file

@ -170,7 +170,7 @@ PowerOn implementiert Schutzmassnahmen gegen die gängigsten Angriffsvektoren f
### 6.1 Cross-Site Request Forgery (CSRF)
Alle datenverändernden Operationen (Erstellen, Ändern, Löschen) erfordern ein gültiges Sicherheitstoken. Damit wird verhindert, dass schädliche Webseiten im Namen eines eingeloggten Nutzers Aktionen auslösen können.
Alle datenverändernden Operationen (POST, PUT, DELETE, PATCH) erfordern einen `X-CSRF-Token` Header. Die aktuelle Implementierung validiert das Tokenformat (16-64 Zeichen Hex-String); es gibt kein serverseitiges Session-Binding (kein klassisches Double-Submit oder Synchronizer Token). In Kombination mit `SameSite=Strict` Cookies und CORS bietet dies Basisschutz gegen CSRF.
### 6.2 Ratenbegrenzung (Rate Limiting)
@ -178,7 +178,7 @@ Jede API-Funktion ist mit individuellen Zugriffslimits versehen, die automatisie
| Funktion | Limit |
|---|---|
| Anmeldung | 5 Versuche pro Minute |
| Anmeldung | 30 Versuche pro Minute |
| Datenexport (DSGVO) | 5 Anfragen pro Minute |
| Kontolöschung | 1 Anfrage pro Stunde |
| Chatbot-Nutzung | 120 Anfragen pro Minute |