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.
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
| Variable | Wirkung |
|---|---|
OWLAT_REPO | Zu klonende Git-URL (Standard https://github.com/wolvesdotink/owlat.git) |
OWLAT_REF | Zu 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_BRANCH | Wird nur als Fallback berücksichtigt, wenn OWLAT_REF nicht gesetzt ist; es fällt nicht auf main zurück |
OWLAT_INSTALL_DIR | Wohin geklont wird (Standard /opt/owlat; wird mit sudo angelegt und Ihnen per chown übertragen, wenn das übergeordnete Verzeichnis nicht beschreibbar ist) |
OWLAT_ASSUME_YES | 1 übergibt --assume-yes an den Assistenten (CI / Ansible) |
OWLAT_CONFIG_FILE | Pfad zu einer Antwortdatei, wird als --config <path> übergeben |
OWLAT_LEGACY_WIZARD | 1 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:
- Plausibilitätsprüfungen — bestätigt, dass Sie sich im Monorepo befinden (unter
--owlat-direxistiert eineturbo.json) und dass der Docker-Daemon erreichbar ist. - Konfiguration — fehlt
.envoderdocker-compose.override.yml, wird zunächst der Setup-Assistent ausgeführt; andernfalls wird die vorhandene Konfiguration weiterverwendet. - Modus-Abfrage —
populated(Admin + Demodaten, der Standard),blank(kein Admin, keine Daten — um den echten Registrierungsablauf/auth/registerzu testen) odercustom(Entscheidung pro Schritt). docker compose up -d— startet den Self-Hosting-Stack.- Auf Convex warten — pollt
/versionauf dem Convex-Cloud-Port (3210). Die HTTP-Routen der Anwendung existieren erst nach dem Deploy der Functions, deshalb wird hier der Cloud-Port abgefragt. - Backend deployen — siehe Convex-Runtime-Umgebungsvariablen pushen. Dieser Schritt erzeugt den Admin-Key, deployt die Functions und pusht die Runtime-Umgebungsvariablen.
- Auf HTTP-Routen warten — pollt
/api/v1/healthauf dem Convex-Site-Proxy (3211), wo die frisch deploytenhttp.route-Handler (/seed/*,/dev/*, Tracking, Webhooks) leben. - Admin-Bootstrap (außer bei
blank) — siehe Admin-Bootstrap. - Beispieldaten (außer bei
blankoder--no-seed) —POST /sample-data/install. Ein lokalesdev-Deployment, bei demOWLAT_DEV_MODE=truebereits 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. - Zusammenfassung — gibt die URL der Web-App (
http://localhost:3000), die Convex-URL und die Admin-E-Mail-Adresse aus.
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
| Flag | Zweck |
|---|---|
--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-seed | Befüllung mit Demodaten überspringen |
--assume-yes, -y | Standardwerte ü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.
| Kommando | Was es tut |
|---|---|
quickstart | Deploy von Anfang bis Ende (Konfiguration → up → Deploy → Bootstrap → Seed). |
setup | Konfigurationsassistent 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-org | Legt 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. |
reset | Setzt 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 --show | Listet die Umgebungsvariablen auf, die der aktuelle Feature-Flag-Zustand erfordert, ob sie jeweils gesetzt sind (Geheimnisse maskiert) und welches Flag sie erfordert. |
doctor | Diagnostiziert 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.ts → SAMPLE_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:
| Paket | Flags, die es steuert |
|---|---|
emailClient | inbox, chat, postbox |
marketing | campaigns, automations, transactional |
ai | ai, 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 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:
.envexistiert und lässt sich parsen.- Jede vom aktiven Feature-Set benötigte Umgebungsvariable ist befüllt.
docker-compose.override.ymlexistiert.- 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:
| Kommando | Was es tut |
|---|---|
start | docker compose up -d (aktiviert die Profile für eingeschaltete Features). |
stop | docker compose down. |
restart [service] | docker compose restart [service]. |
status | docker compose ps. |
logs [service] | docker compose logs -f [service]. |
shell <service> | docker compose exec <service> sh (standardmäßig der web-Service). |
version | Gibt 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.
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
.envfür den Setup-Modus vor und verweist Sie aufhttp://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 einemX-Setup-Token-Header, der mit einem laufzeitkonstanten Vergleich geprüft wird; ein fehlendes oder falsches Token ergibt401. Ohne dieses Token könnte der erste Aufrufer, der den Assistenten erreicht, das Plattform-Admin-Konto anlegen und.envumschreiben. Für SSH-Installationen ohne lokalen Browser verwenden Sie stattdessen den Terminal-Assistenten (siehe unten). Im letzten Schritt „Launch“ legt sein Endpunkt/api/setup/applyden Admin an (POST /seed/admin) und pusht die Umgebungsvariablen der Function-Runtime (EMAIL_PROVIDER,RESEND_API_KEY/AWS_SES_*/SMTP_RELAY_*und den Rest vonCONVEX_RUNTIME_ENV_KEYS) über die Admin-API in das Convex-Deployment — das HTTP-Äquivalent zuconvex env set, da der schreibgeschützte Web-Container denconvex-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.
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:
| Variable | Format |
|---|---|
BETTER_AUTH_SECRET | 48 Zeichen langes URL-sicheres Token |
INSTANCE_SECRET | 64 Hex-Zeichen (32 Byte) — siehe Hinweis unten |
UNSUBSCRIBE_SECRET | 48 Zeichen langes URL-sicheres Token |
MTA_API_KEY | mta_ + 40 Zeichen langes Token |
MTA_WEBHOOK_SECRET | whsec_ + 40 Zeichen langes Token |
REDIS_PASSWORD | 32 Zeichen langes Token |
CONVEX_ADMIN_KEY wird hier bewusst nicht erzeugt — er muss während des Deploy-Schritts vom laufenden Backend ausgestellt werden.
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 demINSTANCE_SECRETdes Deployments); eine Abweichung ergibt401. - 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), eineowner-Mitgliedschaft, das Benutzerprofil und die Instanzeinstellungen an.
| Status | Bedeutung |
|---|---|
201 | Admin + Organisation angelegt |
400 | Ungültiges JSON oder fehlendes email / name / passwordHash |
401 | Fehlendes oder falsches X-Instance-Secret |
409 | Es 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.