Setup-CLI & Installer

Betreiberreferenz für das Owlat-Self-Hosting-Tooling: der install.sh-Einzeiler, das owlat-setup-CLI, der convex-deploy-Ablauf und der Admin-Bootstrap.

Owlat liefert eine einzige Self-Hosting-Toolchain: einen Einzeiler-Installer (install.sh), der einen Host aufsetzt, und das owlat-setup-CLI (gebaut aus apps/setup-cli/), das alles von der Konfiguration bis zur vollständig deployten, mit Daten befüllten Instanz erledigt. Diese Seite ist die Kommandoreferenz für beides. Für die erzählende Deploy-Anleitung beginnen Sie bei Self-Hosting; diese Seite dokumentiert die einzelnen Kommandos, die jene Anleitungen aufrufen.

Der Einzeiler-Installer

install.sh wird unter https://get.owlat.app ausgeliefert. Auf einem frischen Linux-VPS mit installiertem Docker und Docker Compose v2:

curl -fsSL https://get.owlat.app | bash

Das Skript (install.sh) erledigt vier Dinge und übergibt dann an das CLI:

Preflight

Prüft curl, git, docker, ob der Docker-Daemon erreichbar ist (docker info) und ob docker compose version funktioniert (Compose v2 ist erforderlich).

Klonen oder erkennen

Wenn Sie sich bereits in einem Klon befinden (es wird nach scripts/setup.sh + docker-compose.yml gesucht), wird dieser verwendet. Andernfalls wird github.com/wolvesdotink/owlat flach nach /opt/owlat geklont (der Standardwert von OWLAT_INSTALL_DIR). Ist das übergeordnete Verzeichnis des Installationsverzeichnisses nicht beschreibbar, wird es mit sudo angelegt und Ihnen per chown übertragen. Ein vorhandener Klon wird gefetcht und (detached) auf die Ziel-Ref ausgecheckt.

Das owlat-CLI installieren

Verlinkt scripts/owlat per Symlink nach /usr/local/bin/owlat (bei Bedarf mit sudo), sodass Sie anschließend owlat <command> von überall ausführen können.

Den Assistenten starten

Führt den containerisierten Quickstart aus: scripts/owlat quickstart --terminal. Das Flag --terminal wird erzwungen, weil der browserbasierte Web-Assistent nicht innerhalb des containerisierten Installers laufen kann.

Da der Assistent im Docker-Image wolvesdotink/setup läuft, benötigt der Host nur Docker und Compose — kein Bun und kein Node.

Bestehende MTA-Installationen erfordern eine explizite FBL-Umstellung

Ein frisches Setup schreibt das Owned-v2-Beschwerdeprotokoll mit einer fresh-install-Bestätigung. Ein erneuter Setup-Lauf gegen eine bestehende Umgebung erfindet diesen Zustand niemals. Bevor Sie eine ältere MTA aktualisieren, folgen Sie der koordinierten Ruhigstellungs- und Drain-Prozedur unter FBL-Deduplizierungs-Upgrade und setzen Sie anschließend FBL_DEDUP_PROTOCOL=owned-v2 und FBL_DEDUP_CUTOVER_ACK=quiesced-v1-intake explizit.

Umgebungsvariablen des Installers

VariableWirkung
OWLAT_REPOZu klonende Git-URL (Standard https://github.com/wolvesdotink/owlat.git)
OWLAT_REFZu installierende Tag/Branch/Ref (Standard: neuestes Release-Tag; fällt nur dann auf main zurück, wenn das Repository kein veröffentlichtes Release hat — HTTP 404. Ein harter API-Fehler oder ein Rate-Limit bricht die Installation ab, statt stillschweigend main zu installieren.)
OWLAT_BRANCHWird nur als Fallback berücksichtigt, wenn OWLAT_REF nicht gesetzt ist; es fällt nicht auf main zurück
OWLAT_INSTALL_DIRWohin geklont wird (Standard /opt/owlat; wird mit sudo angelegt und Ihnen per chown übertragen, wenn das übergeordnete Verzeichnis nicht beschreibbar ist)
OWLAT_ASSUME_YES1 übergibt --assume-yes an den Assistenten (CI / Ansible)
OWLAT_CONFIG_FILEPfad zu einer Antwortdatei, wird als --config <path> übergeben
OWLAT_LEGACY_WIZARD1 startet stattdessen den alten Bash-Assistenten (scripts/setup.sh)

Nicht-interaktive Beispiele:

# Accept all defaults
OWLAT_ASSUME_YES=1 curl -fsSL https://get.owlat.app | bash

# Drive from a config file (CI / Ansible)
OWLAT_CONFIG_FILE=/path/to/answers.env curl -fsSL https://get.owlat.app | bash

owlat quickstart — Deploy von Anfang bis Ende

quickstart (apps/setup-cli/src/commands/quickstart.ts) bringt einen frischen Klon bis hin zu einer laufenden, mit Admin-Konto versehenen und optional befüllten Instanz. Jeder Schritt ist idempotent, ein erneuter Lauf ist also unbedenklich.

owlat quickstart

Der Ablauf:

  1. Plausibilitätsprüfungen — bestätigt, dass Sie sich im Monorepo befinden (unter --owlat-dir existiert eine turbo.json) und dass der Docker-Daemon erreichbar ist.
  2. Konfiguration — fehlt .env oder docker-compose.override.yml, wird zunächst der Setup-Assistent ausgeführt; andernfalls wird die vorhandene Konfiguration weiterverwendet.
  3. Modus-Abfragepopulated (Admin + Demodaten, der Standard), blank (kein Admin, keine Daten — um den echten Registrierungsablauf /auth/register zu testen) oder custom (Entscheidung pro Schritt).
  4. docker compose up -d — startet den Self-Hosting-Stack.
  5. Auf Convex warten — pollt /version auf dem Convex-Cloud-Port (3210). Die HTTP-Routen der Anwendung existieren erst nach dem Deploy der Functions, deshalb wird hier der Cloud-Port abgefragt.
  6. Backend deployen — siehe Convex-Runtime-Umgebungsvariablen pushen. Dieser Schritt erzeugt den Admin-Key, deployt die Functions und pusht die Runtime-Umgebungsvariablen.
  7. Auf HTTP-Routen warten — pollt /api/v1/health auf dem Convex-Site-Proxy (3211), wo die frisch deployten http.route-Handler (/seed/*, /dev/*, Tracking, Webhooks) leben.
  8. Admin-Bootstrap (außer bei blank) — siehe Admin-Bootstrap.
  9. Beispieldaten (außer bei blank oder --no-seed) — POST /sample-data/install. Ein lokales dev-Deployment, bei dem OWLAT_DEV_MODE=true bereits gesetzt ist, erhält stattdessen den umfangreicheren Datensatz /seed/demo (Dummy-Anmeldungen von Teamkolleginnen und -kollegen + Postbox-Postfächer); kein Installationsablauf schaltet dieses Flag jemals ein.
  10. Zusammenfassung — gibt die URL der Web-App (http://localhost:3000), die Convex-URL und die Admin-E-Mail-Adresse aus.
Warum ein frisches Backend den Deploy-Schritt braucht

Ein frisch gestartetes selbst gehostetes Convex-Backend ist leer: Es bedient das Sync-Protokoll und /version auf dem Cloud-Port, hat aber keinerlei Anwendungs-Functions, kein Schema und keine Umgebungsvariablen für die Function-Runtime. Schritt 6 verwandelt dieses leere Backend in ein funktionierendes Deployment — es ist genau der Schritt, den frühere Installer komplett ausgelassen haben.

Quickstart-Flags

FlagZweck
--mode <m>populated | blank | custom (überspringt die Modus-Abfrage)
--email <e>Admin-E-Mail-Adresse für den Bootstrap
--name <n>Anzeigename des Admins
--password <p>Admin-Passwort (mindestens 12 Zeichen)
--no-seedBefüllung mit Demodaten überspringen
--assume-yes, -yStandardwerte übernehmen (impliziert Modus populated, Admin dev@example.com)
--owlat-dir <dir>Installationsverzeichnis (Standard: aufwärts bis zur Monorepo-Wurzel suchen)
# Fully scripted populated install
owlat quickstart -y --email admin@example.com --name "Admin" --password "a-strong-password"

Kommandoreferenz

Führen Sie jedes Kommando als owlat <command> aus (nach dem Symlink des Installers) oder als bunx owlat-setup <command> aus einem Klon heraus. Mit --help erhalten Sie das vollständige Usage-Banner.

KommandoWas es tut
quickstartDeploy von Anfang bis Ende (Konfiguration → up → Deploy → Bootstrap → Seed).
setupKonfigurationsassistent für den Erststart — schreibt ausschließlich .env + docker-compose.override.yml.
configÖffnet den Assistenten für eine bestehende Installation erneut (überspringt bereits gesetzte Felder).
bootstrap-orgLegt den ersten Admin-Benutzer + die Singleton-Organisation an.
sample-data <install|remove|status>Optionale Demo-Inhalte für eine echte Installation und deren exakte Entfernung. Kein Dev-Modus erforderlich.
seed [--reset]Vollständiger Demo-Seed nur für die Entwicklung (OWLAT_DEV_MODE), inklusive der Dummy-Anmeldungen von Teammitgliedern.
resetSetzt die Instanz komplett auf leer zurück (um den Registrierungsablauf erneut zu testen).
feature <key> <on|off>Schaltet ein einzelnes Feature-Flag um und erzeugt das Compose-Override neu.
pack <key> <on|off>Schaltet alle Flags eines Feature-Pakets um.
env <KEY> <VALUE>Setzt eine einzelne Umgebungsvariable in .env.
env --showListet die Umgebungsvariablen auf, die der aktuelle Feature-Flag-Zustand erfordert, ob sie jeweils gesetzt sind (Geheimnisse maskiert) und welches Flag sie erfordert.
doctorDiagnostiziert eine defekte Installation (.env, erforderliche Variablen, Override, Container-Zustand).

bootstrap-org

Legt den ersten Admin-Benutzer und die Singleton-Organisation an, indem /seed/admin per POST aufgerufen wird (siehe Admin-Bootstrap). Das Passwort wird clientseitig im scrypt-Format von BetterAuth gehasht, sodass das Backend eine Zugangsinformation speichert, die zu einer normalen Anmeldung passt. Das Kommando ist idempotent: Ein 409 (Admin existiert bereits) beendet es mit 0.

owlat bootstrap-org --email admin@example.com --name "Admin" --password "a-strong-password"

sample-data

Der Demo-Datensatz, den eine echte Installation tatsächlich verwenden kann. install sendet ein POST an /sample-data/install, remove an /sample-data/remove, status an /sample-data/status — alle drei authentifiziert über den Header X-Instance-Secret (das On-Box-Betreibergeheimnis aus .env, dasselbe, das auch /seed/admin verwendet) und nicht durch OWLAT_DEV_MODE gesteuert. Diese Unterscheidung ist Absicht: Den Dev-Modus einzuschalten, um /seed/demo zu erreichen, würde auch den Komplettlösch-Endpunkt /dev/reset freischalten und das Rate-Limiting von BetterAuth auf einer Produktivinstanz aushebeln.

owlat sample-data install   # idempotent — re-running inserts nothing new
owlat sample-data status    # how many sample rows are left
owlat sample-data remove    # deletes exactly those rows (prompts unless --assume-yes)

Was geschrieben wird: 15 Kontakte über 3 Themen hinweg, E-Mail-Vorlagen und gespeicherte Blöcke, eine versendete Kampagne mit Statistiken je Empfänger, eine Automatisierung, ein Webhook, transaktionale E-Mail-Historie sowie eine verifizierte Versanddomain demo.localhost. Was nicht geschrieben wird: Benutzerkonten oder Postfächer. Das nur für die Entwicklung gedachte /seed/demo legt Dummy-Anmeldungen von Teammitgliedern an, deren Passwort-Hashes in den Fixtures des Repositorys veröffentlicht sind — die haben auf einer Instanz, die auch andere erreichen können, nichts verloren, deshalb lässt die Sample-Data-Auswahl die Loader accounts und mailboxes aus (apps/api/convex/seedDemo/pipeline.tsSAMPLE_DATA_MODULES). Ebenfalls ausgelassen werden die Volumen-Rollups für Gmail-Massenversender, die sich auf einer echten Installation wie eine reale Domain lesen würden, die in der Compliance-Ansicht knapp unter dem Schwellwert liegt.

Der Datensatz ist konstruktionsbedingt inert. /seed/demo schreibt seine Automatisierung als active und seinen Webhook als aktiviert; der Sample-Data-Pfad führt dieselben Loader mit { inert: true } aus, was die Automatisierung als paused (ohne activatedAt) und den Webhook als deaktiviert schreibt. Das ist wichtig, weil keines der beiden Abonnements zum Auslösezeitpunkt weiß, dass es aus einem Seed stammt: Die Automatisierung wird durch contact_created ausgelöst, und der Webhook-Pool greift jedes aktive Abonnement auf — eine aktive Beispielzeile würde also Ihrer nächsten echten Anmeldung eine Demo-Werbemail schicken und die Daten dieser Person an eine Fixture-URL posten. Schalten Sie beides selbst ein, wenn Sie zusehen möchten, wie sie laufen.

Die Entfernung ist exakt und kein Tabellen-Wipe: Jede Zeile, die ein Loader einfügt, trägt seedTag: 'demo', die Entfernung löscht nur getaggte Zeilen und prüft das Tag innerhalb der Löschtransaktion erneut. Ein Beispielkontakt, den Sie bearbeitet haben, ist weiterhin getaggt und verschwindet ebenfalls; ein Kontakt, den Sie angelegt haben, ist nicht getaggt und bleibt. Das Löschen eines Beispielkontakts oder -themas löst dieselbe Kaskade aus wie ein dauerhaftes Löschen eines Kontakts, sodass eigene Zeilen, die daran hängen (eine Aktivität, ein Automatisierungslauf, eine Mitgliedschaft), mitgehen statt verwaist zurückzubleiben. Da das Tag keinen Index hat, scannt die Entfernung seitenweise aus einer Action heraus statt in einer einzigen Mutation, damit sie auf einer Instanz, die inzwischen echte Daten angesammelt hat, innerhalb des Leselimits pro Transaktion bleibt; ist eine Tabelle so groß, dass der Scan sein Seitenlimit erreicht, enthält die Antwort truncated: true und das CLI weist darauf hin — führen Sie es erneut aus, um fortzufahren.

quickstart führt denselben Installationsschritt aus, wenn Sie „mit Beispieldaten erkunden“ wählen, sodass eine frische Installation nie OWLAT_DEV_MODE benötigt, um befüllt auszusehen.

seed und reset

seed sendet ein POST an /seed/demo; reset an /dev/reset. Beide Endpunkte sind hinter OWLAT_DEV_MODE fail-closed abgesichert (siehe den Hinweis zum Dev-Modus) und benötigen den Header X-Instance-Secret. seed --reset löscht seed-getaggte Zeilen, bevor neu eingefügt wird. reset löscht alle Benutzer, die Organisation und jede geseedete Zeile, sodass der nächste Besuch von http://localhost:3000 auf /auth/register umleitet — verwenden Sie es nur auf Wegwerf-Instanzen.

owlat seed            # idempotent insert
owlat seed --reset    # wipe seed rows, then re-seed
owlat reset           # full wipe (prompts unless --assume-yes)

feature und pack

feature schaltet ein Flag aus dem Katalog der Feature-Flags um und erzeugt docker-compose.override.yml neu; Abhängigkeitskaskaden werden angewendet und gemeldet. pack schaltet eine ganze Gruppe auf einmal um. Die drei Pakete sind:

PaketFlags, die es steuert
emailClientinbox, chat, postbox
marketingcampaigns, automations, transactional
aiai, ai.agent, ai.autonomy, ai.knowledge, ai.knowledge.autoLink, ai.knowledge.graphRetrieval, ai.knowledge.analytics, ai.assistant, ai.visualizations
owlat feature ai on        # enable a single flag
owlat pack marketing on    # enable campaigns + automations + transactional

Beide geben die neuen aktiven Compose-Profile aus und erinnern Sie daran, owlat restart auszuführen, damit die Änderung wirksam wird.

env

Setzt eine einzelne Variable in .env. Der Schlüssel muss ein großgeschriebener, shell-sicherer Bezeichner sein; Werte für *_KEY, *_SECRET und *_PASSWORD werden in der Ausgabe maskiert.

owlat env LLM_API_KEY sk-...

env --show listet stattdessen die Variablen auf, die Ihr aktueller Feature-Flag-Zustand erfordert — dieselbe Menge erforderlicher Variablen, die doctor prüft, abzüglich der Docker-/Container-Prüfungen. Jede Zeile zeigt, ob die Variable gesetzt ist (Werte von *_KEY / *_SECRET / *_PASSWORD maskiert) und welches Flag (oder der providerabhängige Versandpfad) sie erfordert, sodass Sie auf einen Blick sehen, was vor dem Deployment noch fehlt.

owlat env --show
  KEY                           SET / VALUE      REQUIRED BY
────────────────────────────────────────────────────────────
✗ GOOGLE_SAFE_BROWSING_API_KEY  (unset)          scan.urls
✓ LLM_PROVIDER                  openrouter       ai
✓ MTA_API_KEY                   ********…        send path (EMAIL_PROVIDER=mta)

Es ist schreibgeschützt und beendet sich immer mit 0 — verwenden Sie doctor, wenn Sie ein Pass/Fail-Gate benötigen.

env aktualisiert nur .env

env schreibt die Compose-Datei .env, die die Container-Prozesse konfiguriert (web, convex, mta). Der Wert wird nicht in die Convex-Function-Runtime gepusht. Variablen, die von Convex-Functions gelesen werden (die Runtime-Env-Schlüssel), müssen zusätzlich mit convex env set gesetzt werden — führen Sie den Deploy-Schritt erneut aus oder setzen Sie sie über den convex-deploy-Container, damit die Functions sie sehen.

doctor

Führt eine Checkliste gegen die Installation aus und beendet sich bei jedem Fehlschlag mit einem Wert ungleich null:

  1. .env existiert und lässt sich parsen.
  2. Jede vom aktiven Feature-Set benötigte Umgebungsvariable ist befüllt.
  3. docker-compose.override.yml existiert.
  4. Mindestens ein Compose-Service läuft (docker compose ps).
owlat doctor

Lifecycle-Kommandos des Wrappers

Der installierte Wrapper /usr/local/bin/owlat (scripts/owlat) stellt zusätzlich Day-2-Ops-Kommandos bereit, die reine docker compose-Zeremonie sind — diese gibt es nur im Wrapper und ohne owlat-setup-Entsprechung:

KommandoWas es tut
startdocker compose up -d (aktiviert die Profile für eingeschaltete Features).
stopdocker compose down.
restart [service]docker compose restart [service].
statusdocker compose ps.
logs [service]docker compose logs -f [service].
shell <service>docker compose exec <service> sh (standardmäßig der web-Service).
versionGibt die laufende Stack-Version aus (aus dem web-Container gelesen, mit Rückfall auf die lokale .env / package.json).

Der Wrapper stellt außerdem backup, restore, backup-schedule und upgrade für Backups und In-App-Updates bereit.

Convex-Runtime-Umgebungsvariablen pushen (convex-deploy)

Convex-Functions lesen ihre Konfiguration aus dem Deployment, nicht aus der Compose-.env. Die Compose-.env konfiguriert nur die Container-Prozesse; sie erreicht die Convex-Function-Sandbox nicht. Ein Deploy besteht daher aus drei hostseitigen Schritten (apps/setup-cli/src/lib/convexDeploy.ts), die allesamt reine docker compose-Aufrufe sind:

Den Admin-Key erzeugen

generateConvexAdminKey() führt docker compose exec convex ./generate_admin_key.sh aus. Der Schlüssel wird vom laufenden Backend ausgestellt und kann nicht clientseitig fabriziert werden — eine Zufallszeichenkette wird abgelehnt. Der erzeugte Schlüssel wird als CONVEX_ADMIN_KEY in .env geschrieben, damit sich die nächsten beiden Schritte per Compose-Interpolation authentifizieren können.

Functions deployen

deployConvexFunctions() führt das einmalige Deploy-Profil aus und pusht die Functions, das Schema und die http.route-Handler aus apps/api:

docker compose --profile deploy run --rm convex-deploy

Runtime-Umgebungsvariablen pushen

setConvexEnvVars() führt convex env set für jede befüllte Runtime-Variable aus, wiederum über den convex-deploy-Container (der das CLI bereits pinnt und CONVEX_SELF_HOSTED_URL + CONVEX_SELF_HOSTED_ADMIN_KEY erhält). Die Werte werden als argv an die Container-Shell übergeben und über Positionsparameter konsumiert, sodass Geheimnisse niemals von einer Host-Shell interpoliert werden.

Die Liste der Schlüssel, die gepusht werden, ist CONVEX_RUNTIME_ENV_KEYS, definiert in packages/shared/src/convexRuntimeEnv.ts und re-exportiert aus apps/setup-cli/src/lib/convexDeploy.ts, synchron gehalten mit der EnvKey-Union in apps/api/convex/lib/env.ts. Sie umfasst Auth- und Instanzgeheimnisse, Site-URLs, E-Mail-/MTA-Standardwerte, Provider-Schlüssel (Resend, SES), die LLM-Konfiguration, den Vector Store, Analytics sowie die Webhook-Geheimnisse der eingehenden Kanäle. Reine Compose-Variablen (Ports, Image-Versionen, NUXT_PUBLIC_*, REDIS_*) sind bewusst ausgeschlossen — sie gehören nie ins Deployment.

Der Cloud-Port vs. der Site-Proxy

Convex bedient das Sync-Protokoll und das eingebaute /version auf dem Cloud-Port (3210), aber die http.route-Handler der Anwendung — /seed/admin, /seed/demo, /dev/reset, Tracking, Webhooks, Abmeldung — leben auf dem Site-Proxy (3211) und existieren erst nach dem Deploy der Functions. Das CLI fragt 3210 ab, während es auf den Start des Backends wartet, und danach 3211, sobald die Functions deployt sind. Ein POST auf /seed/* an 3210 liefert stillschweigend 404.

Setup-Assistent: Web vs. Terminal

owlat setup (apps/setup-cli/src/commands/setup.ts) ist der Konfigurationsassistent für den Erststart. Er schreibt ausschließlich .env und das Compose-Override — er deployt nichts und legt Ihr Admin-Konto nicht an. (config ist derselbe Assistent, erneut geöffnet für eine bestehende Installation.)

Es gibt zwei Wege:

  • Web-Assistent (Standard, wenn ein Browser verfügbar ist) — bereitet .env für den Setup-Modus vor und verweist Sie auf http://localhost:3000/setup. Er erzeugt außerdem ein einmaliges Setup-Token, schreibt es in die root-eigene .env (OWLAT_SETUP_TOKEN) und gibt es auf der Konsole aus — kopieren Sie es in den letzten Schritt des Assistenten. Die /api/setup/*-Endpunkte laufen unauthentifiziert (es gibt noch kein Admin-Konto), verlangen dieses Token daher in einem X-Setup-Token-Header, der mit einem laufzeitkonstanten Vergleich geprüft wird; ein fehlendes oder falsches Token ergibt 401. Ohne dieses Token könnte der erste Aufrufer, der den Assistenten erreicht, das Plattform-Admin-Konto anlegen und .env umschreiben. Für SSH-Installationen ohne lokalen Browser verwenden Sie stattdessen den Terminal-Assistenten (siehe unten). Im letzten Schritt „Launch“ legt sein Endpunkt /api/setup/apply den Admin an (POST /seed/admin) und pusht die Umgebungsvariablen der Function-Runtime (EMAIL_PROVIDER, RESEND_API_KEY / AWS_SES_* / SMTP_RELAY_* und den Rest von CONVEX_RUNTIME_ENV_KEYS) über die Admin-API in das Convex-Deployment — das HTTP-Äquivalent zu convex env set, da der schreibgeschützte Web-Container den convex-deploy-Schritt nicht ausführen kann. So erreicht die Wahl des E-Mail-Providers tatsächlich den Versandcode und nicht nur .env. Der Provider-Schritt ist eine ehrliche Entscheidung auf Instanzebene — eine eigene MTA betreiben, Amazon SES oder ein SMTP-Relay (mit Presets für Mailgun/Postmark/SendGrid/Brevo oder einem eigenen Server), dazu Resend — und er validiert die Zugangsdaten, bevor es weitergeht (eine Live-API-Prüfung für Resend, ein echter SMTP-Handshake + AUTH für ein Relay).
  • Terminal-Assistent (--terminal) — ein vollständiges TUI, das dieselben Fragen abdeckt: Deployment-Modus, Feature-Auswahl, Versandprovider (eigene MTA / Amazon SES / SMTP-Relay / Resend), KI-Provider (OpenRouter / OpenAI / eigener OpenAI-kompatibler Dienst), optionale Integrationen (Google Safe Browsing, PostHog), das Admin-Konto und — für die selbst gehostete MTA — Ihre EHLO- und Bounce-Domains.

Der Assistent erzeugt alle benötigten Geheimnisse automatisch (siehe unten), spiegelt den aufgelösten Flag-Zustand nach .owlat-flags.json (damit doctor, feature und pack dieselbe Basis teilen) und schließt mit dem Hinweis ab, owlat quickstart auszuführen, um den Stack tatsächlich hochzufahren.

Web-Assistent innerhalb des Installers

Der Einzeiler erzwingt --terminal, weil der browserbasierte Assistent nicht innerhalb des containerisierten Installers laufen kann. Wählen Sie den Web-Assistenten nur, wenn Sie owlat setup direkt auf einem Host mit Browser ausführen.

Erzeugte Geheimnisse

ensureSecrets() (apps/setup-cli/src/lib/secrets.ts) füllt jedes fehlende Geheimnis auf und bewahrt dabei Änderungen des Betreibers:

VariableFormat
BETTER_AUTH_SECRET48 Zeichen langes URL-sicheres Token
INSTANCE_SECRET64 Hex-Zeichen (32 Byte) — siehe Hinweis unten
UNSUBSCRIBE_SECRET48 Zeichen langes URL-sicheres Token
MTA_API_KEYmta_ + 40 Zeichen langes Token
MTA_WEBHOOK_SECRETwhsec_ + 40 Zeichen langes Token
REDIS_PASSWORD32 Zeichen langes Token

CONVEX_ADMIN_KEY wird hier bewusst nicht erzeugt — er muss während des Deploy-Schritts vom laufenden Backend ausgestellt werden.

INSTANCE_SECRET muss hexadezimal sein

Das selbst gehostete Convex-Backend dekodiert INSTANCE_SECRET beim Start als Hex und stürzt ab (Couldn't hexdecode key), wenn es kein gültiges Hex ist. Der Assistent erzeugt 64 Hex-Zeichen; wenn Sie den Wert von Hand setzen, orientieren Sie sich am openssl rand -hex 32 des alten Installers.

Admin-Bootstrap

bootstrap-org (und quickstart) legen den ersten Admin an, indem sie POST /seed/admin aufrufen (apps/api/convex/seedAdmin.ts).

POST /seed/admin
X-Instance-Secret: <INSTANCE_SECRET>
Content-Type: application/json

{ "email": "admin@example.com", "name": "Admin", "passwordHash": "<scrypt-hash>" }

Der Handler:

  • Verlangt einen passenden X-Instance-Secret-Header (laufzeitsicherer Vergleich mit dem INSTANCE_SECRET des Deployments); eine Abweichung ergibt 401.
  • Ist einmalig: Existiert bereits irgendein Benutzer, liefert er 409.
  • Legt bei Erfolg (201) den BetterAuth-Benutzer, das Credential-Konto (mit gespeichertem scrypt-Passwort-Hash), die Singleton-Organisation (Slug aus dem lokalen Teil der E-Mail-Adresse abgeleitet), eine owner-Mitgliedschaft, das Benutzerprofil und die Instanzeinstellungen an.
StatusBedeutung
201Admin + Organisation angelegt
400Ungültiges JSON oder fehlendes email / name / passwordHash
401Fehlendes oder falsches X-Instance-Secret
409Es existiert bereits ein Benutzer (der Endpunkt ist einmalig)

Das CLI hasht das Passwort vor dem Senden: bootstrap-org leitet einen BetterAuth-kompatiblen scrypt-Hash ab (apps/setup-cli/src/lib/passwordHash.ts), damit die geseedete Zugangsinformation zu einer normalen Anmeldung passt. Sie senden das Klartextpasswort nie an das Backend.

Endpunkte nur für die Entwicklung

/seed/demo und /dev/reset sind durch OWLAT_DEV_MODE geschützt (apps/api/convex/devShortcuts/_guard.ts) und fail-closed: Sofern OWLAT_DEV_MODE in der Convex-Function-Runtime nicht wahr ist, liefern sie 403. Lokale dev-Installationen haben es standardmäßig an; ein produktives Self-Hosting lässt es aus, und nichts im Installationsablauf schaltet es ein — Demo-Inhalte für eine echte Installation laufen über sample-data, das per Instanzgeheimnis authentifiziert und nicht dev-gesteuert ist. /seed/admin selbst ist nicht dev-gesteuert — es ist der produktive Pfad für den Admin-Bootstrap, geschützt durch das Instanzgeheimnis und die Einmaligkeitsprüfung.

Verwandt