From 364d1448c7708e582bba57ec8d5e3958831082a2 Mon Sep 17 00:00:00 2001 From: boban Date: Fri, 15 May 2026 22:08:27 +0200 Subject: [PATCH] chore: initial Nimuli design spec --- .../specs/2026-05-15-nimuli-design.md | 789 ++++++++++++++++++ 1 file changed, 789 insertions(+) create mode 100644 docs/superpowers/specs/2026-05-15-nimuli-design.md diff --git a/docs/superpowers/specs/2026-05-15-nimuli-design.md b/docs/superpowers/specs/2026-05-15-nimuli-design.md new file mode 100644 index 0000000..f942765 --- /dev/null +++ b/docs/superpowers/specs/2026-05-15-nimuli-design.md @@ -0,0 +1,789 @@ +# Nimuli — Master Design Spec + +**Datum:** 2026-05-15 +**Status:** Design abgeschlossen, bereit für Implementation-Planung +**Autor:** Boban Blaskovic +**Projekt-Typ:** Standalone SaaS (eigene Codebase, getrennt von CluPilot) + +--- + +## 1. Produkt-Übersicht + +**Nimuli** ist eine All-in-One Link-Management-Plattform. + +**Kern-Features:** +- Short-Links mit Whitelabel-Custom-Domains +- Dynamische QR-Codes (Logo, Farben, alle Types: URL/vCard/WiFi/PDF/MP3/Text) +- Link-in-Bio Microsites mit Block-Editor +- Smart-Routing (Geo, Device, OS, Time, Language, UTM) +- A/B-Testing mit Conversion-Tracking +- Pixel-Injection (Meta/Google/TikTok via Interstitial) +- Klick-Analytics mit Live-Dashboards +- AI-Suite (Auto-Slug, A/B-Generator, Anomalie-Detection, Insight-Reports, Routing-per-Prompt) +- REST-API + Outgoing-Webhooks +- Multi-Workspace (Agency-Use-Case) + +**Markt:** DACH-Start, DE+EN i18n, geplant EU-Expansion. + +**Marken + Domains:** +- Marke: **Nimuli** +- Marketing-Site: `nimuli.com` +- App/Dashboard: `app.nimuli.com` +- Short-Redirect-Domain: `nimu.li` +- Whitelabel-Subdomains: `*.nimu.li` (User-Subdomain) +- Custom-Domains: `{kunden-domain}.com` via CNAME → nimu.li + +**Differenzierung:** +- Pixel-Injection im Standard-Plan (Bitly ignoriert das) +- AI-Suite ab Tag 1 (Konkurrenz hat das nicht oder spärlich) +- Agency-fokussierte Features (Multi-Workspace, Whitelabel, Reseller) +- EU-Hosting, DSGVO-first + +--- + +## 2. Gesamt-Architektur + +### Lokale Entwicklung +Single-Host Docker Compose. Alle Services in einem Compose-File: +- `nginx`, `app` (Octane), `queue`, `scheduler`, `reverb`, `mysql`, `redis`, `mailpit` + +### Production-Cluster (Hetzner Cloud) + +``` + ┌──────────────────────┐ + │ Hetzner LB (Public) │ + │ Failover-IP, SSL │ + └──────────┬───────────┘ + │ + ┌──────────────────────┼──────────────────────┐ + │ │ │ + ▼ ▼ ▼ + ┌────────┐ ┌────────┐ ┌────────┐ + │ web-01 │ │ web-02 │ │ web-03 │ + │ Docker │ │ Docker │ │ Docker │ + │ app+ │ │ app+ │ │ app+ │ + │ nginx │ │ nginx │ │ nginx │ + └───┬────┘ └───┬────┘ └───┬────┘ + │ │ │ + └──────────┬──────────┴──────────┬──────────┘ + │ │ + ┌─────▼─────┐ ┌─────▼─────┐ + │ DB-01 │ │ Redis-01 │ + │ MySQL │ │ Cache+ │ + │ │ │ Queue+ │ + │ │ │ Sessions │ + └───────────┘ └───────────┘ + + ┌──────────────┐ ┌──────────────────┐ + │ worker-01 │ │ CDN (bunny.net │ + │ Queue + │ │ oder Hetzner │ + │ Scheduler + │ │ Storage Box CDN) │ + │ Reverb │ │ │ + └──────────────┘ └──────────────────┘ +``` + +**DNS-Routing:** +- `app.nimuli.com` → LB-IP → web-Pool +- `nimu.li`, `*.nimu.li` → LB-IP → web-Pool (Redirect-Service) +- `ws.nimuli.com` → direkt worker-01 (Reverb, kein LB) +- `{custom-domain}` → CNAME → nimu.li → LB + +**Kosten:** ~60-85€/Monat Hetzner. + +### Drei Haupt-Subsysteme + +1. **Redirect-Service** — Hot-Path. `nimu.li/{slug}` → Redis-Lookup → 302 + async Klick-Event. Ziel <10ms p95. +2. **App-Dashboard** — `app.nimuli.com`. Livewire-CRUD, Analytics, Workspace-Mgmt, Billing. +3. **Background-Worker** — Queue für Klick-Events → Rollups, Email, Stripe-Webhooks, AI-Calls, Pixel-Server-Side-Sends, Domain-SSL-Provisioning. + +--- + +## 3. Stack + +| Bereich | Technologie | +|---------|-------------| +| Backend | Laravel 12 + Octane (Swoole oder FrankenPHP) | +| Frontend | Livewire v3 + Alpine.js + Tailwind v4 (`@theme`) | +| Realtime | Laravel Reverb (WebSockets) | +| Auth | Laravel Breeze (Livewire-Stack) + `pragmarx/google2fa` (2FA) | +| Permissions | `spatie/laravel-permission` (per Workspace scoped) | +| Modals | Wire Elements Modal | +| Charts | Chart.js 4.x | +| QR-Codes | `chillerlan/php-qrcode` (Backend), QR-JS-Lib für Live-Preview | +| Image-Processing | `intervention/image-laravel` (Resize, WebP, EXIF-Strip, Auto-Rotate) | +| Media-Storage | `spatie/laravel-medialibrary` | +| Audit | `spatie/laravel-activitylog` | +| Settings | `spatie/laravel-settings` | +| Backup | `spatie/laravel-backup` (4×/Tag, 14d Retention) | +| Translations | DB-Table + Redis-Cache (Custom-Implementation, Admin-UI) + Spatie-Translatable für Model-Felder | +| Subscriptions | Laravel Cashier (Stripe) | +| Queue | Redis + Horizon | +| Search | Eloquent `WHERE LIKE` (MySQL FULLTEXT optional) | +| Testing | Pest 3 + Livewire-Testing + Browser-Tests | +| Static Analysis | PHPStan Level 6+ | +| Code-Style | Laravel Pint | +| CI/CD | GitHub Actions → SSH-Deploy zu Hetzner | +| Container | Docker Compose | +| Filesystem | Lokal NAS-Mount (dev), Hetzner Storage Box (prod) | + +--- + +## 4. Modul-Struktur (Domain-Driven) + +``` +app/ +├─ Domains/ +│ ├─ Workspace/ Tenant-Container, Members, Invitations +│ ├─ Domain/ Custom Domains, SSL-Provisioning +│ ├─ Link/ Short-Links, Variants (A/B) +│ ├─ QrCode/ QR-Generator, Templates, Renderers +│ ├─ Bio/ Link-in-Bio Microsites +│ ├─ Analytics/ Click-Tracking, Rollups, Queries +│ ├─ Routing/ Smart-Routing Rule-Engine, A/B, Pixel-Inject +│ ├─ Subscription/ Stripe-Billing, Plan-Limits +│ ├─ Translation/ i18n Admin +│ ├─ Ai/ Phase-1 AI-Suite +│ ├─ Webhook/ Outgoing-Webhooks +│ ├─ Api/ REST-API-Layer +│ └─ Ads/ Phase-3 (Meta + Google + TikTok) +├─ Http/ +│ ├─ Controllers/ RedirectController (Hot-Path, KEIN Livewire) +│ ├─ Middleware/ ResolveWorkspace, EnforceLimits, LocaleFromUser +│ └─ Resources/ API-Transformers +├─ Livewire/ +│ ├─ Layouts/ AppLayout, GuestLayout, PublicBioLayout +│ └─ Pages/ Per Domain +└─ Console/ + └─ Commands/ SyncStripe, ProvisionSsl, ResolveDomains, Rollups +``` + +**Architektur-Prinzipien:** + +1. **Domain-Folder-Struktur** statt Type-Folders. +2. **Actions-Pattern** für alle Writes (`CreateLink`, `UpdateLink`, etc.). +3. **Alle Views via Livewire-Components.** Normale Controller nicht für Views nutzen. Ausnahme: `RedirectController` (kein View, nur 302-Response, Hot-Path). + +--- + +## 5. Datenmodell + +### Kern-Tabellen + +```sql +-- USERS +users +├─ id, ulid, name, email (unique), password +├─ email_verified_at, two_factor_secret, two_factor_recovery +├─ locale (de/en), theme (dark/light/system) +├─ default_workspace_id (nullable, fk) +└─ timestamps, soft_deletes + +-- WORKSPACES (Tenants) +workspaces +├─ id, ulid (unique, in URL), name, slug (display) +├─ owner_id (fk users) +├─ plan_id (fk plans), trial_ends_at +├─ branding (json: logo, primary_color, accent_color) +├─ stripe_id, stripe_customer_metadata (json) +└─ timestamps, soft_deletes + +workspace_members +├─ id, workspace_id (fk), user_id (fk) +├─ role (enum: owner, admin, editor, viewer) +├─ invited_by, joined_at +└─ unique(workspace_id, user_id) + +workspace_invitations +├─ id, workspace_id, email, role, token (unique) +├─ invited_by, expires_at, accepted_at +└─ timestamps + +billing_profiles +├─ id, workspace_id (fk), billing_user_id (fk users) +├─ company_name (nullable), vat_number (nullable, VIES-validated) +├─ address_line_1, address_line_2, city, postal_code, country +├─ email_billing +└─ timestamps + +-- DOMAINS +domains +├─ id, workspace_id (fk), hostname (unique) +├─ type (enum: short, bio, both) +├─ verified_at, verification_token +├─ ssl_status (pending/active/failed), ssl_issued_at, ssl_expires_at +├─ is_default (bool, true für system nimu.li) +└─ timestamps + +-- LINKS +links +├─ id, ulid, workspace_id (fk), domain_id (fk, nullable → nimu.li default) +├─ slug (varchar 64, indexed mit domain_id unique) +├─ target_url (text) +├─ title, description, favicon_url +├─ status (active/disabled/expired) +├─ password (nullable, encrypted) +├─ expires_at, click_limit +├─ rules (json: smart-routing-config) +├─ pixel_config (json: meta_id, google_id, tiktok_id) +├─ tags (json array) +├─ created_by, updated_by +└─ timestamps, soft_deletes + +link_variants -- A/B-Testing +├─ id, link_id (fk), label, target_url, weight +├─ clicks_count, conversions_count +└─ timestamps + +-- QR CODES +qr_codes +├─ id, ulid, workspace_id, link_id (nullable für direct-QR) +├─ type (url/vcard/wifi/pdf/mp3/text) +├─ payload (json), style (json: colors, logo, frame) +├─ is_dynamic, scan_limit, expires_at +└─ timestamps, soft_deletes + +-- LINK-IN-BIO +bio_pages +├─ id, ulid, workspace_id, domain_id (nullable) +├─ slug (unique mit domain_id), title, description, og_image +├─ theme (json), is_published +└─ timestamps, soft_deletes + +bio_blocks +├─ id, bio_page_id (fk), position, type +├─ config (json: type-specific) +└─ timestamps + +-- ANALYTICS (Raw, 90 Tage retention, monatlich partitioniert) +clicks +├─ id (bigint), link_id (fk), variant_id (nullable) +├─ workspace_id (denormalized) +├─ clicked_at (indexed) +├─ ip_hash (sha256 mit daily-rotating salt — DSGVO) +├─ country, city, device, os, browser +├─ referrer_host, referrer_path +├─ utm_source, utm_medium, utm_campaign, utm_content, utm_term, language +└─ append-only, no updates + +-- ANALYTICS Rollups +clicks_hourly (link_id, hour, country, device, count, unique_visitors) +clicks_daily (link_id, day, country, count, unique_visitors) +clicks_by_referrer (link_id, day, referrer_host, count) +clicks_by_utm (link_id, day, utm_source, utm_medium, utm_campaign, count) + +-- BILLING +plans +├─ id, name, stripe_price_id, monthly_price_cents +├─ workspace_limit, member_limit, custom_domain_limit +├─ link_limit, click_limit_monthly +└─ features (json) + +subscriptions, subscription_items (Cashier-Standard) + +-- TRANSLATIONS +translations +├─ id, locale (de/en), key, value (text) +├─ group, namespace, updated_by +└─ unique(locale, namespace, group, key), INDEX(locale, group) + +-- API + WEBHOOKS +api_tokens (Sanctum-Standard) +webhooks +├─ id, workspace_id, url, secret, events (json), is_active +└─ timestamps + +webhook_deliveries +├─ id, webhook_id, event, payload, status, response_code, attempts +└─ timestamps + +-- AUDIT +activity_log (Spatie-Standard) +``` + +### Redis-Keys + +``` +link:{slug} Hash (default nimu.li-Domain) +link:{domain_id}:{slug} Hash (Custom-Domain) + → target, status, rules_json, expires_at, workspace_id, pixel_config + +clicks:{link_id}:{date} Counter (INCR pro Klick) +clicks:{workspace_id}:monthly:{ym} Counter für Plan-Enforcement + +lang:{locale} Hash mit allen Translation-Keys +workspace:{ulid} Hash mit cached Settings + +ratelimit:redirect:{ip} Sliding-Window-Counter +session:{id} Laravel-Session + +ab_sticky:{link_id}:{ip_hash} ULID des Variants, TTL 24h +``` + +### Wichtige Constraints + +- `(domain_id, slug)` unique — gleicher Slug auf verschiedenen Domains erlaubt +- `clicks` append-only +- Soft-Deletes auf User-Content +- Stripe-Customer auf `workspaces`, nie auf `users` +- IP-Adresse niemals roh — immer SHA256(ip + daily_salt) mit Salt-Rotation nach 24h + +--- + +## 6. Redirect-Flow + Smart-Routing + +### Request-Pipeline + +``` +1. GET https://nimu.li/x7k +2. nginx → app (Octane) +3. RedirectController::__invoke('x7k') +4. Domain-Resolve via Cache (Host-Header) +5. Redis HGETALL link:{domain_id}:{slug} (Fallback DB) +6. Pre-Checks: status, expires_at, click_limit, password, plan_click_limit +7. Bot-Detection (UA-Pattern, optional AbuseIPDB) +8. Smart-Routing-Resolve (Rules + Context: geo/device/os/time/lang/referrer/utm) +9. A/B-Variant-Pick mit Sticky-Hash (IP+Link, 24h Redis-TTL) +10. Pixel-Injection-Decision (Interstitial-HTML oder direkt 302) +11. Fire-and-forget Click-Event (Redis-INCR + Queue-Job) +12. 302-Response (Ziel <10ms p95) +``` + +### Smart-Routing Rule-Engine + +JSON-Konfiguration pro Link mit Priorisierten Rules: + +```json +{ + "rules": [ + { + "id": "rule-1", + "priority": 1, + "conditions": [ + { "field": "device", "op": "in", "value": ["mobile"] }, + { "field": "os", "op": "equals", "value": "ios" } + ], + "match": "all", + "target": "https://apps.apple.com/app/123" + }, + { + "id": "rule-2", + "priority": 2, + "conditions": [ + { "field": "country", "op": "in", "value": ["DE","AT","CH"] } + ], + "match": "all", + "target": "https://example.de" + } + ], + "fallback": "https://example.com" +} +``` + +**Rule-Fields:** `country`, `city`, `language`, `device`, `os`, `time_hour`, `weekday`, `date`, `referrer_host`, `utm_*` + +**Operators:** `equals`, `not_equals`, `in`, `not_in`, `contains`, `starts_with`, `between`, `greater_than`, `less_than` + +**Match-Modes:** `all` (AND), `any` (OR) + +**Engine** evaluiert in Priority-Reihenfolge, erste Match gewinnt. Fallback wenn keine Match. + +### Klick-Verarbeitung + +``` +RecordClickJob: + 1. INSERT INTO clicks (...) + 2. Broadcast(new ClickRecordedEvent($click)) → Reverb → Live-Dashboard + 3. Wenn Conversion-API enabled (Phase 2): SendMetaCapiJob, SendGoogleConversionJob + 4. Webhook-Trigger: workspace.webhooks where event='click.recorded' +``` + +### Rollup-Schedule + +| Cron | Job | +|------|-----| +| every 15 min | RollupHourlyJob (clicks → clicks_hourly) | +| daily 02:00 | RollupDailyJob (clicks_hourly → clicks_daily) | +| daily 03:00 | PruneRawClicksJob (DELETE WHERE clicked_at < now()-90d) | +| daily 04:00 | CheckSslExpiryJob | +| hourly | VerifyPendingDomainsJob | +| weekly mon 09:00 | SendWeeklyInsightReportJob (AI-Feature) | +| 00:00, 06:00, 12:00, 18:00 | DatabaseBackupJob | + +--- + +## 7. Multi-Tenancy + +### Workspace-Modell + +- Single-DB, `workspace_id` Foreign Key auf allen Workspace-Resourcen +- ULID statt sequenzieller ID in URL für Unguessability +- URL-Pattern: `app.nimuli.com/w/{workspace_ulid}/...` +- Solo-User: Auto-Redirect zu `default_workspace_ulid` +- Multi-Workspace-User (Agency): URL-Slug bestimmt aktiven Kontext, Multi-Tab-fähig + +### Workspace-Rollen + +| Rolle | Rechte | +|-------|--------| +| Owner | Alles + Billing + Workspace-Delete | +| Admin | Alles außer Billing + Workspace-Delete | +| Editor | Links/QR/Bio erstellen+bearbeiten+löschen | +| Viewer | Nur Lesen, Analytics | + +### Plan-Limits (vorläufig) + +| Plan | Workspaces/User | Members/Workspace | Custom Domains | Links | Klicks/Monat | Preis | +|------|----------------|-------------------|----------------|-------|--------------|-------| +| Free | 1 | 1 | 0 (nimu.li only) | 50 | 1.000 | 0€ | +| Pro | 1 | 3 | 1 | 1.000 | 25.000 | 19€ | +| Business | 3 | 10 | 5 | 10.000 | 250.000 | 79€ | +| Agency | unlimited | unlimited | unlimited | unlimited | 5M (pay-as-you-go danach) | 199€ | + +--- + +## 8. Billing + +### Stripe-Setup + +- Stripe + Stripe Tax + PayPal als Method + SEPA-Lastschrift +- Customer pro Workspace (nicht pro User) +- Cashier für Subscription-Management +- Webhook-Endpoint mit Signature-Verify + Idempotency-Check + +### Rechnungs-Konzept + +- Rechnung zeigt **Billing-Profile-Daten** (User oder Firma) oben +- Custom-Field "Workspace": {workspace.name} +- Footer: "Bezahlt für Workspace: {workspace.name}" +- VAT-ID-Validation via VIES (EU-VAT-MOSS-Konformität) +- Reverse-Charge B2B intra-EU automatisch via Stripe Tax + +### Plan-Enforcement + +- Limit-Checks vor Action (Pre-Flight) +- Redis-Counter für Klick-Limit (real-time) +- Soft-Block bei 90% Klick-Limit (Warning), Hard-Block bei 100% (Redirect-Page "Plan-Limit erreicht") +- Owner-Email bei Limit-Hit + Upgrade-CTA + +--- + +## 9. i18n + Translation-System + +### DB-backed Translations + +``` +DB-Table: translations +├─ id, locale, namespace, group, key, value, updated_by +└─ unique(locale, namespace, group, key) + +Redis: + lang:de → Hash { 'auth.login': 'Anmelden', ... } + lang:en → Hash { 'auth.login': 'Sign in', ... } +``` + +**Helper:** +```php +__('auth.login') + → Redis HGET lang:{current_locale} auth.login + → Fallback: DB → Cache → Lang-File +``` + +**Admin-UI:** Translation-Manager (CRUD über Keys, Diff-View zwischen Locales, Bulk-Import/Export, Missing-Key-Auto-Add bei `__()`-Call in Dev-Mode). + +### Sprachen Phase 1 + +DE + EN. Phase 2 EU-Expansion: FR, ES, IT, NL. + +### Model-Translations + +`spatie/laravel-translatable` für Bio-Pages (multi-language Bio-Inhalte für internationale Brands). + +--- + +## 10. Theme + Design-System + +### Dual-Mode + +- **Dark Mode (Default):** A-Style — CluPilot-inspiriert, Dark-Tech, Neon-Akzente, Mono-Zahlen +- **Light Mode:** B-Style — Vibrant-Modern, Gradients (Lila→Pink), friendly + +### Tailwind v4 `@theme` + +```css +@import "tailwindcss"; + +@theme { + --color-bg: #0a0a0f; + --color-s1: #111117; + --color-s2: #16161e; + --color-s3: #1c1c26; + --color-b1: rgb(255 255 255 / 6%); + --color-b2: rgb(255 255 255 / 10%); + --color-t1: #f0f0f5; + --color-t2: #8888a0; + --color-t3: #44445a; + --color-blue: #4f8ef7; + --color-green: #3ecf8e; + --color-amber: #f5a623; + --color-red: #f05252; + --font-mono: 'JetBrains Mono', monospace; + --font-sans: 'Inter', sans-serif; +} + +[data-theme="light"] { + --color-bg: #ffffff; + --color-s1: #ffffff; + --color-s2: #f9fafb; + --color-s3: #f3f4f6; + --color-b1: #e5e7eb; + --color-b2: #d1d5db; + --color-t1: #111827; + --color-t2: #6b7280; + --color-t3: #9ca3af; + --color-blue: #7c3aed; + --color-green: #10b981; + --color-amber: #f59e0b; + --color-red: #ef4444; +} +``` + +**Custom-Style-Regel:** Keine Inline-Styles im Markup außer für Progressbar-Width-Berechnungen. + +### Layout-Struktur + +``` +┌────────┬─────────────────────────────────────┐ +│Sidebar │ Topbar (Workspace-Switcher, │ +│ 240px │ Search, User-Menu, Theme-Toggle) │ +│fixed │─────────────────────────────────────│ +│ │ │ +│ Logo │ Content Area │ +│ WS-Box │ (Livewire-Page-Component) │ +│ Nav │ │ +│ Plan │ │ +│ Box │ │ +└────────┴─────────────────────────────────────┘ +``` + +Mobile (<768px): Sidebar als Drawer mit Hamburger. + +### Mobile-First + +- Default-Styles für Mobile, dann `md:`/`lg:` für Desktop +- Touch-Targets min 44×44px +- Drawer-Nav, Bottom-Action-Bars, Swipe-Gesten +- PWA-Manifest + Service-Worker (Offline-Read für Analytics) + +--- + +## 11. Sicherheit + DSGVO + +### Auth-Härtung + +- Argon2id Passwort-Hashing +- 2FA via TOTP + Recovery-Codes +- Cookie-Flags: `secure`, `httpOnly`, `SameSite=Lax` +- Login-Rate-Limit 5/15min/IP +- Pwned-Password-Check via HaveIBeenPwned-API + +### Workspace-Isolation + +- Global Scope `workspace_id = current_workspace` +- Policies + Middleware-Check +- 404 statt 403 bei Cross-Workspace (Existenz nicht leaken) + +### Input-Validation + +- Mass-Assignment: `$guarded = ['id', 'workspace_id', 'created_by']` global +- Validierung via FormRequests +- HTMLPurifier auf User-Content (Bio-Blocks) +- SVG-Sanitizer auf Logo-Uploads +- File-Upload-Whitelist, Mime-Check, EXIF-Strip + +### Webhooks + +- HMAC-SHA256-Signatur `X-Nimuli-Signature` auf Outgoing +- SSRF-Schutz: private IP-Ranges blockiert (10/8, 172.16/12, 192.168/16, 127/8, 169.254/16) +- Retry mit Exponential-Backoff (5 Versuche) + +### Custom-Domain + +- DNS-TXT-Record-Verifizierung +- Let's-Encrypt-ACME-SSL +- Renewal-Cron 30d vor Expiry mit Alerts + +### Redirect-Härtung + +- Rate-Limit 100 req/min/IP (Sliding-Window-Redis) +- Bot-Detection +- Google-Safe-Browsing-Check auf target_url bei Create + Daily-Re-Check +- Phishing-Domains werden auto-disabled + +### DSGVO + +- IP-Hash mit täglich-rotierendem Salt (24h irreversibel) +- Cookie-Banner-Logik in Bio-Pages (per Workspace-Setting) +- AVV-PDF auto-generierbar +- Datenexport (Art. 20): JSON-Bundle, Hintergrund-Job, Signed-URL 7d +- Recht auf Vergessen: 30d Soft-Delete → Hard-Delete +- Drittland-Transfers (Stripe, OpenAI/Anthropic, MaxMind): SCC + DPF aktiviert +- EU-Hosting (Hetzner DE) + +### Security-Headers + +- Strict-CSP, HSTS, X-Frame-Options DENY, X-Content-Type-Options nosniff, Referrer-Policy strict-origin + +--- + +## 12. Backup + Disaster-Recovery + +- `spatie/laravel-backup` +- 4× täglich MySQL-Dump (00:00, 06:00, 12:00, 18:00) +- Retention 14 Tage (= 56 Dumps gespeichert) +- Storage: Hetzner Storage Box (separate Region) +- Verschlüsselt +- Wöchentliche Restore-Drill auf Test-Instanz + +--- + +## 13. Test-Strategie + +| Layer | Tool | Ziel | +|-------|------|------| +| Unit | Pest | Actions, Resolvers, Rule-Engine | +| Feature | Pest | HTTP-Routes, Auth, Workspace-Scoping | +| Livewire | Pest + Livewire-Testing | Jede Page-Component, Forms, Actions | +| Integration | Pest | Stripe-Webhook (mocked), Email, Queue-Jobs | +| E2E | Pest 3 Browser | Critical-Paths: Register → Workspace → Link → Klick → Analytics | +| Performance | k6 | Redirect p95 <10ms, Dashboard <500ms | +| Security | OWASP-ZAP + manuell | Vor Launch | + +**TDD-Approach:** Red-Green-Refactor, Test zuerst. 80%+ Coverage als Smell-Indikator. + +**CI-Pipeline (GitHub Actions):** + +```yaml +on: [push, pull_request] +jobs: + test: + - composer install + - npm ci + npm run build + - php artisan migrate (test DB) + - vendor/bin/pest --parallel + - php artisan pint --test + - php artisan phpstan analyse + deploy (on main): + - SSH zu Hetzner + - Pull, migrate, restart-Container +``` + +**Health-Checks:** +- `/health` endpoint: DB, Redis, Disk, Queue-Length +- UptimeRobot/BetterStack 1min-Polling +- Status-Page öffentlich (status.nimuli.com) + +--- + +## 14. Phasen-Roadmap + +**Hinweis:** Big-Bang-Launch Phase 1 (Wunsch des Users, kein Wave-Split). Total 12-16 Wochen Vollzeit. + +### Phase 1 — Core + AI + API/Webhooks (12-16 Wochen) + +**Inhalt:** +- Setup, Auth+2FA, Workspaces, Domains, Links, Analytics, Billing, Whitelabel, Theme-Switch, i18n +- Smart-Routing, A/B-Testing, Link-Schutz, Pixel-Injection, QR-Codes, Link-in-Bio +- AI-Suite (Auto-Slug, A/B-Generator, A/B-Explainer, Routing-per-Prompt, Anomalie-Detection, Insight-Report) +- API v1 (REST + Sanctum + Rate-Limit per Plan) + Outgoing-Webhooks + +**Launch-Kriterien:** +- Stripe Live-Mode, 4 Pläne kaufbar +- DSGVO-Audit clean (AVV, Datenschutz, Cookie-Logik) +- Sentry, Uptime-Monitor, Status-Page +- Onboarding-Tour, Email-Sequenzen, Hilfe-Docs + +### Phase 2 — 3rd-Party Integrationen (6-8 Wochen) + +- Zapier-App-Listing +- Make.com-Modules +- Slack OAuth + Notifications +- Discord Webhooks +- Shopify-App-Listing +- Conversion-API: Meta CAPI + Google Enhanced Conversions Server-Side + +### Phase 2.5 (parallel) — Mobile App iOS+Android + +- React Native + Expo +- Features: Login, Workspace-Switch, Quick-Link-Create (Share-Extension), Analytics-Read-Only, Push-Notifications, QR-Scanner +- Push-Service: Expo Push oder Firebase FCM + +### Phase 3 — Ads-Management (10-14 Wochen) + +- Meta OAuth + Ad-Read + Ad-Create + CAPI-Full +- Google Ads OAuth + Read + Create + Enhanced Conversions +- TikTok Ads (optional) +- Unified Campaign-View Cross-Platform +- Budget-Alerts + Auto-Pause-Rules (CPL-basiert) + +### Post-Phase-3 (Optional/On-Demand) + +- ClickHouse-Migration bei >50M Klicks/Monat +- Edge-Redirects via Cloudflare Workers (globale Latenz <50ms) +- Marketplace für Bio-Templates (Revenue-Share) +- White-Label-Reseller-Tier +- Custom-Reports-Builder (Drag-Drop) + +--- + +## 15. Risiken + Mitigations + +| Risiko | Likelihood | Impact | Mitigation | +|--------|-----------|--------|-----------| +| Spam/Phishing-Missbrauch | hoch | hoch | Safe-Browsing-Check, Manual-Review-Queue für Free-Tier, Account-Auto-Suspend | +| DDoS auf Redirects | mittel | hoch | Rate-Limit Redis, LB-DDoS-Schutz, später Cloudflare-Edge | +| MySQL überlastet bei Klick-Spike | mittel | mittel | INSERT async via Queue, Rollups, Plan-Click-Limits | +| Custom-Domain SSL läuft ab | hoch | mittel | Cron-Renewal 30/14/7/1d-Alerts | +| OpenAI/Anthropic-API-Ausfall | mittel | niedrig | Graceful Degradation, Core läuft weiter | +| Stripe-Webhook-Verlust | niedrig | mittel | Idempotency-Store + Daily-Reconciliation-Job | +| Hetzner-Ausfall / DB-Korruption | niedrig | hoch | 4×/Tag Backup, 14d Retention, separate Region, Wochen-Restore-Drill | +| Konkurrenz launcht ähnliches AI-Feature | hoch | mittel | Iterations-Tempo, Agency-Nische-Fokus | +| Solo-Dev-Burnout (Big-Bang 16 Wochen) | hoch | hoch | Vom User akzeptiert; trotzdem regelmäßige Checkpoints, MVP-Mindset | +| GDPR-Klage Pixel-Injection | mittel | hoch | Consent-Banner Pflicht-Feature, AVV, transparente Datenschutzerklärung | + +--- + +## 16. Offene Punkte für Implementation-Plan + +Diese Details werden im Implementation-Plan konkretisiert: + +- Exakte Migrationen-Reihenfolge +- Konkrete Route-Definitionen (Web + API) +- Konkrete Livewire-Component-Liste mit View-Pfaden +- AI-Provider-Wahl (Anthropic Claude vs. OpenAI vs. beide via Adapter-Pattern) +- Reverb-Channel-Definitionen + Authorization-Logic +- Stripe-Product/Price-Setup-Skript +- Seed-Daten für Demo-Workspace +- E-Mail-Template-Liste +- Onboarding-Tour-Steps +- Marketing-Site (`nimuli.com`) — Stack: Statisches Astro/Next oder Teil der App? + +--- + +## 17. Erfolgs-Kriterien Spec + +- [x] Architektur klar (Dev + Prod) +- [x] Stack festgelegt +- [x] Modul-Struktur dokumentiert +- [x] Datenmodell komplett +- [x] Redirect-Pipeline beschrieben +- [x] Multi-Tenancy-Modell klar +- [x] Billing-Konzept (inkl. Rechnungs-Anzeige) +- [x] i18n-System mit DB+Redis +- [x] Theme + Tailwind-Setup +- [x] Sicherheit + DSGVO +- [x] Backup-Strategie +- [x] Test-Strategie +- [x] Phasen-Roadmap +- [x] Risiken + Mitigations + +**Spec-Status:** Bereit für `writing-plans`-Skill zur Erstellung des Implementation-Plans. \ No newline at end of file