Entwicklerhandbuch

Technische Architektur, Feature-Flag-Modell und Provider-Abstraktionen, die Owlat verwendet.

Dieses Handbuch behandelt die technische Architektur und die Entwicklungsmuster, die in Owlat verwendet werden. Owlat ist als eine Menge von Funktionsbereichen aufgebaut (Versand, Empfang, KI, Integrationen, Sicherheit, Zustellbarkeit), die sich eine gemeinsame Plattform teilen — das Convex-Backend, die E-Mail-Rendering-Engine und die Feature-Flag-Registry, die jeden Bereich ein- oder ausschaltet.

Schnellzugriff

Architektur & Kernsysteme

Ausgehende E-Mail

Eingehende E-Mail

  • Postbox-Architektur — Schema persönlicher Postfächer, IMAP-Server, App-Passwort-Authentifizierung, ausgehendes Relay

Self-Hosting

Referenz

Entwicklungsumgebung einrichten

Voraussetzungen

  • Node.js 22+
  • Bun als Paketmanager
  • Docker + Docker Compose v2 (für den vollständigen Self-Host-Stack)

Lokale Entwicklung

  1. Abhängigkeiten installieren:
    bun install
    
  2. Das Convex-Backend starten (läuft dauerhaft weiter):
    bun run dev:api
    
  3. Das Nuxt-Frontend starten:
    bun run dev
    

Umgebungsvariablen

Sowohl das Nuxt-Frontend als auch das Convex-Backend verwenden Umgebungsvariablen zur Konfiguration. Feature-Flags folgen einem Muster aus zwei Variablen: eine Convex-Umgebungsvariable für das Backend und eine NUXT_PUBLIC_*-Variable für das Frontend.

Auf der Seite Umgebungsvariablen finden Sie eine vollständige Referenz samt Einrichtungsanleitungen für Provider, Sicherheit und Analytics.

Kurzreferenz — wesentliche Variablen:

VariableWoZweck
BETTER_AUTH_SECRETConvexSecret zum Signieren von Sessions
SITE_URLConvexÖffentliche Site-URL für Weiterleitungen
EMAIL_PROVIDERConvexmta, ses, resend, smtp, mandrill oder emailit — kein impliziter Standardwert; nicht gesetzt bedeutet unkonfiguriert und Sendungen werden abgelehnt
LLM_PROVIDERConvexopenai (Standard), openrouter, ollama (alle OpenAI-kompatibel) — nur erforderlich, wenn das Flag ai aktiv ist. Ein Claude-Endpunkt ist nur erreichbar, indem Sie LLM_BASE_URL auf einen OpenAI-kompatiblen Proxy richten, nicht durch Setzen von LLM_PROVIDER=anthropic
UNSUBSCRIBE_SECRETConvexHMAC-Secret für Abmelde-Token
NUXT_PUBLIC_CONVEX_URLNuxt .envURL des Convex-Deployments
NUXT_PUBLIC_CONVEX_SITE_URLNuxt .envConvex-Site-URL (für die Authentifizierung)
NUXT_PUBLIC_SITE_URLNuxt .envÖffentliche Site-URL

Codequalität

bun run typecheck  # TypeScript checking
bun run lint       # Oxlint
bun run ox:fmt     # Format with Oxfmt

Projektstruktur

owlat/
├── apps/
│   ├── web/                # Nuxt 4 dashboard
│   │   └── app/
│   │       ├── components/     # Vue components
│   │       │   └── ui/         # App-specific UI components
│   │       ├── composables/    # Vue composables
│   │       ├── layouts/        # Page layouts
│   │       ├── pages/          # File-based routing
│   │       └── plugins/        # Nuxt plugins
│   ├── api/                # Convex backend
│   │   └── convex/
│   │       ├── _generated/         # Auto-generated (don't edit)
│   │       ├── lib/                # Shared utilities + provider factories
│   │       │   ├── llm/             # LLM helpers
│   │       │   ├── llmProvider.ts   # OpenAI-compatible LLM dispatch
│   │       │   ├── sendProviders/      # MTA, Resend, SES senders + routing/dispatch
│   │       │   ├── emailProviders/     # Identity + domain verification (mtaIdentity, sesIdentity, domainVerification)
│   │       │   └── posthog.ts       # PostHog analytics
│   │       ├── schema.ts           # Database schema
│   │       ├── mail/               # Postbox / mailbox modules
│   │       └── *.ts                # API functions
│   ├── mta/                # Outbound Mail Transfer Agent (SMTP sender + scan endpoint)
│   ├── imap/               # IMAP4rev1 server (Postbox personal mail)
│   ├── mail-sync/          # Syncs users' external IMAP/SMTP accounts (IMAP IDLE inbound + outbound relay)
│   ├── setup-cli/          # `owlat-setup` — wizard, feature/pack/env management
│   ├── updater/            # In-place update sidecar
│   ├── docs/               # Nuxt Content documentation
│   ├── marketing/          # Marketing / landing page (Nuxt)
│   ├── desktop/            # Desktop client shell (Tauri)
│   └── code-worker/        # Code-task worker (for `codeWorkTasks`)
├── infra/
│   └── templates/          # Per-instance VPS compose, ACME (lego) sidecar, env template
├── docker-compose.yml      # Root self-host stack (web/convex/mta + optional Caddy --profile tls)
├── Caddyfile.example       # Reference reverse-proxy config for --profile tls
└── packages/
    ├── email-builder/      # Vue email editor component
    ├── email-renderer/     # JSON blocks → HTML renderer
    ├── email-scanner/      # Email security scanning
    ├── email-previewer/    # Email preview / compatibility analysis
    ├── channels/           # Notification channel abstractions
    ├── shared/             # Shared types, validation, feature flag registry
    ├── ui/                 # Nuxt layer: shared UI components and composables
    ├── sdk-js/             # Official TypeScript SDK
    └── sdk-java/           # Official Java SDK

Zentrale Technologien

TechnologieZweck
Nuxt 4Vue-3-Full-Stack-Framework
ConvexServerloses Echtzeit-Backend
BetterAuthAuthentifizierung mit Organisationsunterstützung
Tailwind CSS 4Utility-First-Styling
@owlat/email-rendererEigenes Rendering für HTML-E-Mails
HonoHTTP-Framework (MTA-Scan-/Relay-Endpunkte)
GroupMQ + RedisMTA-Job-Queue
CaddyMitgelieferter HTTPS-Reverse-Proxy für Web/Convex/MTA (--profile tls)
lego / ACMETLS im Self-Hosting über DNS-01 für IMAP-/SMTP-Mail-Zertifikate
Docker-Compose-ProfileOptionale Dienste je Feature-Flag
LucideIcon-Bibliothek

Muster und Konventionen

Dateibenennung

  • Vue-Komponenten: PascalCase.vue
  • Composables: useCamelCase.ts
  • Convex-Funktionen: camelCase.ts
  • Seiten: kebab-case.vue oder [param].vue

Imports

Verwenden Sie den Alias ~/ für Imports innerhalb der Nuxt-App:

import { useAuth } from '~/composables/useAuth';

Die Feature-Flag-Registry wird in jeder Schicht aus dem Shared-Package importiert:

import { FEATURE_FLAGS, resolveFlags, applyToggle } from '@owlat/shared/featureFlags';

TypeScript

Der gesamte Code ist TypeScript. Verwenden Sie explizite Typen für Funktionsparameter und Rückgabewerte:

function formatDate(timestamp: number): string {
    return new Date(timestamp).toLocaleDateString();
}

Fehlerbehandlung

  • Frontend: useToast() für Rückmeldungen an die Nutzerin oder den Nutzer verwenden
  • Backend: Error mit aussagekräftigen Meldungen werfen
  • API: strukturierte Fehlerantworten zurückgeben
// Frontend
const { showToast } = useToast();
try {
    await mutation();
    showToast('Saved successfully');
} catch (e) {
    showToast('Failed to save', 'error');
}

// Backend (Convex)
if (!organizationId) {
    throw new Error('Organization not found');
}

Feature-gesteuerte Routen

Seiten, die zu einer schaltbaren Funktion gehören, deklarieren dies über definePageMeta:

definePageMeta({
    layout: 'dashboard',
    middleware: 'auth',
    requiresFeature: 'postbox',
});

Eine globale Middleware leitet auf eine „Funktion deaktiviert“-Seite um, wenn das Flag ausgeschaltet ist. Convex-Queries prüfen das Flag zusätzlich serverseitig, damit ein veralteter Client es nicht umgehen kann.