[{"data":1,"prerenderedAt":2409},["ShallowReactive",2],{"search-de":3,"content-de-developer\u002Fsetup-cli":4,"surround-de-\u002Fdeveloper\u002Fsetup-cli":2400},[],{"id":5,"title":6,"body":7,"description":2393,"extension":2394,"meta":2395,"navigation":393,"path":2396,"seo":2397,"stem":2398,"__hash__":2399},"content_de\u002F3.developer\u002F36.setup-cli.md","Setup-CLI & Installer",{"type":8,"value":9,"toc":2358},"minimark",[10,33,38,47,80,86,187,199,224,228,352,355,421,428,438,450,453,610,624,628,726,764,768,782,932,934,951,975,978,1026,1068,1097,1127,1138,1146,1150,1194,1231,1235,1253,1338,1374,1381,1384,1400,1418,1438,1452,1460,1469,1495,1497,1500,1522,1534,1538,1553,1658,1674,1678,1698,1791,1820,1865,1869,1887,1890,1982,1999,2011,2015,2024,2104,2112,2129,2132,2146,2177,2180,2213,2275,2285,2289,2332,2336,2340,2345,2349,2354],[11,12,13,14,18,19,22,23,26,27,32],"p",{},"Owlat liefert eine einzige Self-Hosting-Toolchain: einen Einzeiler-Installer (",[15,16,17],"code",{},"install.sh","), der einen Host aufsetzt, und das ",[15,20,21],{},"owlat-setup","-CLI (gebaut aus ",[15,24,25],{},"apps\u002Fsetup-cli\u002F","), 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 ",[28,29,31],"a",{"href":30},"\u002Fdeveloper\u002Fself-hosting","Self-Hosting","; diese Seite dokumentiert die einzelnen Kommandos, die jene Anleitungen aufrufen.",[34,35,37],"h2",{"id":36},"der-einzeiler-installer","Der Einzeiler-Installer",[11,39,40,42,43,46],{},[15,41,17],{}," wird unter ",[15,44,45],{},"https:\u002F\u002Fget.owlat.app"," ausgeliefert. Auf einem frischen Linux-VPS mit installiertem Docker und Docker Compose v2:",[48,49,54],"pre",{"className":50,"code":51,"language":52,"meta":53,"style":53},"language-sh shiki shiki-themes github-light github-dark-dimmed","curl -fsSL https:\u002F\u002Fget.owlat.app | bash\n","sh","",[15,55,56],{"__ignoreMap":53},[57,58,61,65,69,73,77],"span",{"class":59,"line":60},"line",1,[57,62,64],{"class":63},"sOLd2","curl",[57,66,68],{"class":67},"sviXB"," -fsSL",[57,70,72],{"class":71},"s-HuK"," https:\u002F\u002Fget.owlat.app",[57,74,76],{"class":75},"s7YZ4"," |",[57,78,79],{"class":63}," bash\n",[11,81,82,83,85],{},"Das Skript (",[15,84,17],{},") erledigt vier Dinge und übergibt dann an das CLI:",[87,88,89,94,115,119,146,154,172,176],"steps",{},[90,91,93],"h3",{"id":92},"preflight","Preflight",[11,95,96,97,99,100,99,103,106,107,110,111,114],{},"Prüft ",[15,98,64],{},", ",[15,101,102],{},"git",[15,104,105],{},"docker",", ob der Docker-Daemon erreichbar ist (",[15,108,109],{},"docker info",") und ob ",[15,112,113],{},"docker compose version"," funktioniert (Compose v2 ist erforderlich).",[90,116,118],{"id":117},"klonen-oder-erkennen","Klonen oder erkennen",[11,120,121,122,125,126,129,130,133,134,137,138,141,142,145],{},"Wenn Sie sich bereits in einem Klon befinden (es wird nach ",[15,123,124],{},"scripts\u002Fsetup.sh"," + ",[15,127,128],{},"docker-compose.yml"," gesucht), wird dieser verwendet. Andernfalls wird ",[15,131,132],{},"github.com\u002Fwolvesdotink\u002Fowlat"," flach nach ",[15,135,136],{},"\u002Fopt\u002Fowlat"," geklont (der Standardwert von ",[15,139,140],{},"OWLAT_INSTALL_DIR","). Ist das übergeordnete Verzeichnis des Installationsverzeichnisses nicht beschreibbar, wird es mit ",[15,143,144],{},"sudo"," angelegt und Ihnen per chown übertragen. Ein vorhandener Klon wird gefetcht und (detached) auf die Ziel-Ref ausgecheckt.",[90,147,149,150,153],{"id":148},"das-owlat-cli-installieren","Das ",[15,151,152],{},"owlat","-CLI installieren",[11,155,156,157,160,161,164,165,167,168,171],{},"Verlinkt ",[15,158,159],{},"scripts\u002Fowlat"," per Symlink nach ",[15,162,163],{},"\u002Fusr\u002Flocal\u002Fbin\u002Fowlat"," (bei Bedarf mit ",[15,166,144],{},"), sodass Sie anschließend ",[15,169,170],{},"owlat \u003Ccommand>"," von überall ausführen können.",[90,173,175],{"id":174},"den-assistenten-starten","Den Assistenten starten",[11,177,178,179,182,183,186],{},"Führt den containerisierten Quickstart aus: ",[15,180,181],{},"scripts\u002Fowlat quickstart --terminal",". Das Flag ",[15,184,185],{},"--terminal"," wird erzwungen, weil der browserbasierte Web-Assistent nicht innerhalb des containerisierten Installers laufen kann.",[11,188,189,190,193,194,198],{},"Da der Assistent im Docker-Image ",[15,191,192],{},"wolvesdotink\u002Fsetup"," läuft, benötigt der Host nur Docker und Compose — ",[195,196,197],"strong",{},"kein Bun und kein Node",".",[200,201,204],"callout",{"title":202,"type":203},"Bestehende MTA-Installationen erfordern eine explizite FBL-Umstellung","warning",[11,205,206,207,210,211,215,216,219,220,223],{},"Ein frisches Setup schreibt das Owned-v2-Beschwerdeprotokoll mit einer\n",[15,208,209],{},"fresh-install","-Bestätigung. Ein erneuter Setup-Lauf gegen eine bestehende Umgebung\nerfindet diesen Zustand niemals. Bevor Sie eine ältere MTA aktualisieren, folgen Sie\nder koordinierten Ruhigstellungs- und Drain-Prozedur unter ",[28,212,214],{"href":213},"\u002Fdeveloper\u002Fmta-system#fbl-deduplication-upgrade","FBL-Deduplizierungs-Upgrade","\nund setzen Sie anschließend ",[15,217,218],{},"FBL_DEDUP_PROTOCOL=owned-v2"," und\n",[15,221,222],{},"FBL_DEDUP_CUTOVER_ACK=quiesced-v1-intake"," explizit.",[90,225,227],{"id":226},"umgebungsvariablen-des-installers","Umgebungsvariablen des Installers",[229,230,231,244],"table",{},[232,233,234],"thead",{},[235,236,237,241],"tr",{},[238,239,240],"th",{},"Variable",[238,242,243],{},"Wirkung",[245,246,247,262,279,295,307,324,338],"tbody",{},[235,248,249,255],{},[250,251,252],"td",{},[15,253,254],{},"OWLAT_REPO",[250,256,257,258,261],{},"Zu klonende Git-URL (Standard ",[15,259,260],{},"https:\u002F\u002Fgithub.com\u002Fwolvesdotink\u002Fowlat.git",")",[235,263,264,269],{},[250,265,266],{},[15,267,268],{},"OWLAT_REF",[250,270,271,272,275,276,278],{},"Zu installierende Tag\u002FBranch\u002FRef (Standard: neuestes Release-Tag; fällt nur dann auf ",[15,273,274],{},"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 ",[15,277,274],{}," zu installieren.)",[235,280,281,286],{},[250,282,283],{},[15,284,285],{},"OWLAT_BRANCH",[250,287,288,289,291,292,294],{},"Wird nur als Fallback berücksichtigt, wenn ",[15,290,268],{}," nicht gesetzt ist; es fällt nicht auf ",[15,293,274],{}," zurück",[235,296,297,301],{},[250,298,299],{},[15,300,140],{},[250,302,303,304,306],{},"Wohin geklont wird (Standard ",[15,305,136],{},"; wird mit sudo angelegt und Ihnen per chown übertragen, wenn das übergeordnete Verzeichnis nicht beschreibbar ist)",[235,308,309,314],{},[250,310,311],{},[15,312,313],{},"OWLAT_ASSUME_YES",[250,315,316,319,320,323],{},[15,317,318],{},"1"," übergibt ",[15,321,322],{},"--assume-yes"," an den Assistenten (CI \u002F Ansible)",[235,325,326,331],{},[250,327,328],{},[15,329,330],{},"OWLAT_CONFIG_FILE",[250,332,333,334,337],{},"Pfad zu einer Antwortdatei, wird als ",[15,335,336],{},"--config \u003Cpath>"," übergeben",[235,339,340,345],{},[250,341,342],{},[15,343,344],{},"OWLAT_LEGACY_WIZARD",[250,346,347,349,350,261],{},[15,348,318],{}," startet stattdessen den alten Bash-Assistenten (",[15,351,124],{},[11,353,354],{},"Nicht-interaktive Beispiele:",[48,356,358],{"className":50,"code":357,"language":52,"meta":53,"style":53},"# Accept all defaults\nOWLAT_ASSUME_YES=1 curl -fsSL https:\u002F\u002Fget.owlat.app | bash\n\n# Drive from a config file (CI \u002F Ansible)\nOWLAT_CONFIG_FILE=\u002Fpath\u002Fto\u002Fanswers.env curl -fsSL https:\u002F\u002Fget.owlat.app | bash\n",[15,359,360,366,388,395,401],{"__ignoreMap":53},[57,361,362],{"class":59,"line":60},[57,363,365],{"class":364},"sDN9O","# Accept all defaults\n",[57,367,369,372,375,377,380,382,384,386],{"class":59,"line":368},2,[57,370,313],{"class":371},"sYgZi",[57,373,374],{"class":75},"=",[57,376,318],{"class":71},[57,378,379],{"class":63}," curl",[57,381,68],{"class":67},[57,383,72],{"class":71},[57,385,76],{"class":75},[57,387,79],{"class":63},[57,389,391],{"class":59,"line":390},3,[57,392,394],{"emptyLinePlaceholder":393},true,"\n",[57,396,398],{"class":59,"line":397},4,[57,399,400],{"class":364},"# Drive from a config file (CI \u002F Ansible)\n",[57,402,404,406,408,411,413,415,417,419],{"class":59,"line":403},5,[57,405,330],{"class":371},[57,407,374],{"class":75},[57,409,410],{"class":71},"\u002Fpath\u002Fto\u002Fanswers.env",[57,412,379],{"class":63},[57,414,68],{"class":67},[57,416,72],{"class":71},[57,418,76],{"class":75},[57,420,79],{"class":63},[34,422,424,427],{"id":423},"owlat-quickstart-deploy-von-anfang-bis-ende",[15,425,426],{},"owlat quickstart"," — Deploy von Anfang bis Ende",[11,429,430,433,434,437],{},[15,431,432],{},"quickstart"," (",[15,435,436],{},"apps\u002Fsetup-cli\u002Fsrc\u002Fcommands\u002Fquickstart.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.",[48,439,441],{"className":50,"code":440,"language":52,"meta":53,"style":53},"owlat quickstart\n",[15,442,443],{"__ignoreMap":53},[57,444,445,447],{"class":59,"line":60},[57,446,152],{"class":63},[57,448,449],{"class":71}," quickstart\n",[11,451,452],{},"Der Ablauf:",[454,455,456,471,490,512,520,530,541,561,573,600],"ol",{},[457,458,459,462,463,466,467,470],"li",{},[195,460,461],{},"Plausibilitätsprüfungen"," — bestätigt, dass Sie sich im Monorepo befinden (unter ",[15,464,465],{},"--owlat-dir"," existiert eine ",[15,468,469],{},"turbo.json",") und dass der Docker-Daemon erreichbar ist.",[457,472,473,476,477,480,481,484,485,489],{},[195,474,475],{},"Konfiguration"," — fehlt ",[15,478,479],{},".env"," oder ",[15,482,483],{},"docker-compose.override.yml",", wird zunächst der ",[28,486,488],{"href":487},"#setup-wizard-web-vs-terminal","Setup-Assistent"," ausgeführt; andernfalls wird die vorhandene Konfiguration weiterverwendet.",[457,491,492,495,496,499,500,503,504,507,508,511],{},[195,493,494],{},"Modus-Abfrage"," — ",[15,497,498],{},"populated"," (Admin + Demodaten, der Standard), ",[15,501,502],{},"blank"," (kein Admin, keine Daten — um den echten Registrierungsablauf ",[15,505,506],{},"\u002Fauth\u002Fregister"," zu testen) oder ",[15,509,510],{},"custom"," (Entscheidung pro Schritt).",[457,513,514,519],{},[195,515,516],{},[15,517,518],{},"docker compose up -d"," — startet den Self-Hosting-Stack.",[457,521,522,525,526,529],{},[195,523,524],{},"Auf Convex warten"," — pollt ",[15,527,528],{},"\u002Fversion"," 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.",[457,531,532,535,536,540],{},[195,533,534],{},"Backend deployen"," — siehe ",[28,537,539],{"href":538},"#pushing-convex-runtime-env-vars-convex-deploy","Convex-Runtime-Umgebungsvariablen pushen",". Dieser Schritt erzeugt den Admin-Key, deployt die Functions und pusht die Runtime-Umgebungsvariablen.",[457,542,543,525,546,549,550,553,554,99,557,560],{},[195,544,545],{},"Auf HTTP-Routen warten",[15,547,548],{},"\u002Fapi\u002Fv1\u002Fhealth"," auf dem Convex-Site-Proxy (3211), wo die frisch deployten ",[15,551,552],{},"http.route","-Handler (",[15,555,556],{},"\u002Fseed\u002F*",[15,558,559],{},"\u002Fdev\u002F*",", Tracking, Webhooks) leben.",[457,562,563,566,567,569,570,198],{},[195,564,565],{},"Admin-Bootstrap"," (außer bei ",[15,568,502],{},") — siehe ",[28,571,565],{"href":572},"#admin-bootstrap",[457,574,575,566,578,480,580,583,584,587,588,591,592,595,596,599],{},[195,576,577],{},"Beispieldaten",[15,579,502],{},[15,581,582],{},"--no-seed",") — ",[15,585,586],{},"POST \u002Fsample-data\u002Finstall",". Ein lokales ",[15,589,590],{},"dev","-Deployment, bei dem ",[15,593,594],{},"OWLAT_DEV_MODE=true"," bereits gesetzt ist, erhält stattdessen den umfangreicheren Datensatz ",[15,597,598],{},"\u002Fseed\u002Fdemo"," (Dummy-Anmeldungen von Teamkolleginnen und -kollegen + Postbox-Postfächer); kein Installationsablauf schaltet dieses Flag jemals ein.",[457,601,602,605,606,609],{},[195,603,604],{},"Zusammenfassung"," — gibt die URL der Web-App (",[15,607,608],{},"http:\u002F\u002Flocalhost:3000","), die Convex-URL und die Admin-E-Mail-Adresse aus.",[200,611,614],{"title":612,"type":613},"Warum ein frisches Backend den Deploy-Schritt braucht","info",[11,615,616,617,620,621,623],{},"Ein frisch gestartetes selbst gehostetes Convex-Backend ist ",[195,618,619],{},"leer",": Es bedient das Sync-Protokoll und ",[15,622,528],{}," 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.",[90,625,627],{"id":626},"quickstart-flags","Quickstart-Flags",[229,629,630,640],{},[232,631,632],{},[235,633,634,637],{},[238,635,636],{},"Flag",[238,638,639],{},"Zweck",[245,641,642,659,669,679,689,698,716],{},[235,643,644,649],{},[250,645,646],{},[15,647,648],{},"--mode \u003Cm>",[250,650,651,653,654,653,656,658],{},[15,652,498],{}," | ",[15,655,502],{},[15,657,510],{}," (überspringt die Modus-Abfrage)",[235,660,661,666],{},[250,662,663],{},[15,664,665],{},"--email \u003Ce>",[250,667,668],{},"Admin-E-Mail-Adresse für den Bootstrap",[235,670,671,676],{},[250,672,673],{},[15,674,675],{},"--name \u003Cn>",[250,677,678],{},"Anzeigename des Admins",[235,680,681,686],{},[250,682,683],{},[15,684,685],{},"--password \u003Cp>",[250,687,688],{},"Admin-Passwort (mindestens 12 Zeichen)",[235,690,691,695],{},[250,692,693],{},[15,694,582],{},[250,696,697],{},"Befüllung mit Demodaten überspringen",[235,699,700,707],{},[250,701,702,99,704],{},[15,703,322],{},[15,705,706],{},"-y",[250,708,709,710,712,713,261],{},"Standardwerte übernehmen (impliziert Modus ",[15,711,498],{},", Admin ",[15,714,715],{},"dev@example.com",[235,717,718,723],{},[250,719,720],{},[15,721,722],{},"--owlat-dir \u003Cdir>",[250,724,725],{},"Installationsverzeichnis (Standard: aufwärts bis zur Monorepo-Wurzel suchen)",[48,727,729],{"className":50,"code":728,"language":52,"meta":53,"style":53},"# Fully scripted populated install\nowlat quickstart -y --email admin@example.com --name \"Admin\" --password \"a-strong-password\"\n",[15,730,731,736],{"__ignoreMap":53},[57,732,733],{"class":59,"line":60},[57,734,735],{"class":364},"# Fully scripted populated install\n",[57,737,738,740,743,746,749,752,755,758,761],{"class":59,"line":368},[57,739,152],{"class":63},[57,741,742],{"class":71}," quickstart",[57,744,745],{"class":67}," -y",[57,747,748],{"class":67}," --email",[57,750,751],{"class":71}," admin@example.com",[57,753,754],{"class":67}," --name",[57,756,757],{"class":71}," \"Admin\"",[57,759,760],{"class":67}," --password",[57,762,763],{"class":71}," \"a-strong-password\"\n",[34,765,767],{"id":766},"kommandoreferenz","Kommandoreferenz",[11,769,770,771,773,774,777,778,781],{},"Führen Sie jedes Kommando als ",[15,772,170],{}," aus (nach dem Symlink des Installers) oder als ",[15,775,776],{},"bunx owlat-setup \u003Ccommand>"," aus einem Klon heraus. Mit ",[15,779,780],{},"--help"," erhalten Sie das vollständige Usage-Banner.",[229,783,784,794],{},[232,785,786],{},[235,787,788,791],{},[238,789,790],{},"Kommando",[238,792,793],{},"Was es tut",[245,795,796,809,823,833,843,853,867,877,887,897,909,919],{},[235,797,798,802],{},[250,799,800],{},[15,801,432],{},[250,803,804,805,808],{},"Deploy von Anfang bis Ende (Konfiguration → ",[15,806,807],{},"up"," → Deploy → Bootstrap → Seed).",[235,810,811,816],{},[250,812,813],{},[15,814,815],{},"setup",[250,817,818,819,125,821,198],{},"Konfigurationsassistent für den Erststart — schreibt ausschließlich ",[15,820,479],{},[15,822,483],{},[235,824,825,830],{},[250,826,827],{},[15,828,829],{},"config",[250,831,832],{},"Öffnet den Assistenten für eine bestehende Installation erneut (überspringt bereits gesetzte Felder).",[235,834,835,840],{},[250,836,837],{},[15,838,839],{},"bootstrap-org",[250,841,842],{},"Legt den ersten Admin-Benutzer + die Singleton-Organisation an.",[235,844,845,850],{},[250,846,847],{},[15,848,849],{},"sample-data \u003Cinstall|remove|status>",[250,851,852],{},"Optionale Demo-Inhalte für eine echte Installation und deren exakte Entfernung. Kein Dev-Modus erforderlich.",[235,854,855,860],{},[250,856,857],{},[15,858,859],{},"seed [--reset]",[250,861,862,863,866],{},"Vollständiger Demo-Seed nur für die Entwicklung (",[15,864,865],{},"OWLAT_DEV_MODE","), inklusive der Dummy-Anmeldungen von Teammitgliedern.",[235,868,869,874],{},[250,870,871],{},[15,872,873],{},"reset",[250,875,876],{},"Setzt die Instanz komplett auf leer zurück (um den Registrierungsablauf erneut zu testen).",[235,878,879,884],{},[250,880,881],{},[15,882,883],{},"feature \u003Ckey> \u003Con|off>",[250,885,886],{},"Schaltet ein einzelnes Feature-Flag um und erzeugt das Compose-Override neu.",[235,888,889,894],{},[250,890,891],{},[15,892,893],{},"pack \u003Ckey> \u003Con|off>",[250,895,896],{},"Schaltet alle Flags eines Feature-Pakets um.",[235,898,899,904],{},[250,900,901],{},[15,902,903],{},"env \u003CKEY> \u003CVALUE>",[250,905,906,907,198],{},"Setzt eine einzelne Umgebungsvariable in ",[15,908,479],{},[235,910,911,916],{},[250,912,913],{},[15,914,915],{},"env --show",[250,917,918],{},"Listet die Umgebungsvariablen auf, die der aktuelle Feature-Flag-Zustand erfordert, ob sie jeweils gesetzt sind (Geheimnisse maskiert) und welches Flag sie erfordert.",[235,920,921,926],{},[250,922,923],{},[15,924,925],{},"doctor",[250,927,928,929,931],{},"Diagnostiziert eine defekte Installation (",[15,930,479],{},", erforderliche Variablen, Override, Container-Zustand).",[90,933,839],{"id":839},[11,935,936,937,940,941,943,944,947,948,198],{},"Legt den ersten Admin-Benutzer und die Singleton-Organisation an, indem ",[15,938,939],{},"\u002Fseed\u002Fadmin"," per POST aufgerufen wird (siehe ",[28,942,565],{"href":572},"). 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 ",[15,945,946],{},"409"," (Admin existiert bereits) beendet es mit ",[15,949,950],{},"0",[48,952,954],{"className":50,"code":953,"language":52,"meta":53,"style":53},"owlat bootstrap-org --email admin@example.com --name \"Admin\" --password \"a-strong-password\"\n",[15,955,956],{"__ignoreMap":53},[57,957,958,960,963,965,967,969,971,973],{"class":59,"line":60},[57,959,152],{"class":63},[57,961,962],{"class":71}," bootstrap-org",[57,964,748],{"class":67},[57,966,751],{"class":71},[57,968,754],{"class":67},[57,970,757],{"class":71},[57,972,760],{"class":67},[57,974,763],{"class":71},[90,976,977],{"id":977},"sample-data",[11,979,980,981,984,985,99,988,991,992,99,995,991,998,1001,1002,1005,1006,1008,1009,1011,1012,1015,1016,1018,1019,1021,1022,1025],{},"Der Demo-Datensatz, den eine echte Installation tatsächlich verwenden kann. ",[15,982,983],{},"install"," sendet ein POST an ",[15,986,987],{},"\u002Fsample-data\u002Finstall",[15,989,990],{},"remove"," an ",[15,993,994],{},"\u002Fsample-data\u002Fremove",[15,996,997],{},"status",[15,999,1000],{},"\u002Fsample-data\u002Fstatus"," — alle drei authentifiziert über den Header ",[15,1003,1004],{},"X-Instance-Secret"," (das On-Box-Betreibergeheimnis aus ",[15,1007,479],{},", dasselbe, das auch ",[15,1010,939],{}," verwendet) und ",[195,1013,1014],{},"nicht"," durch ",[15,1017,865],{}," gesteuert. Diese Unterscheidung ist Absicht: Den Dev-Modus einzuschalten, um ",[15,1020,598],{}," zu erreichen, würde auch den Komplettlösch-Endpunkt ",[15,1023,1024],{},"\u002Fdev\u002Freset"," freischalten und das Rate-Limiting von BetterAuth auf einer Produktivinstanz aushebeln.",[48,1027,1029],{"className":50,"code":1028,"language":52,"meta":53,"style":53},"owlat sample-data install   # idempotent — re-running inserts nothing new\nowlat sample-data status    # how many sample rows are left\nowlat sample-data remove    # deletes exactly those rows (prompts unless --assume-yes)\n",[15,1030,1031,1044,1056],{"__ignoreMap":53},[57,1032,1033,1035,1038,1041],{"class":59,"line":60},[57,1034,152],{"class":63},[57,1036,1037],{"class":71}," sample-data",[57,1039,1040],{"class":71}," install",[57,1042,1043],{"class":364},"   # idempotent — re-running inserts nothing new\n",[57,1045,1046,1048,1050,1053],{"class":59,"line":368},[57,1047,152],{"class":63},[57,1049,1037],{"class":71},[57,1051,1052],{"class":71}," status",[57,1054,1055],{"class":364},"    # how many sample rows are left\n",[57,1057,1058,1060,1062,1065],{"class":59,"line":390},[57,1059,152],{"class":63},[57,1061,1037],{"class":71},[57,1063,1064],{"class":71}," remove",[57,1066,1067],{"class":364},"    # deletes exactly those rows (prompts unless --assume-yes)\n",[11,1069,1070,1071,1074,1075,1077,1078,1080,1081,1084,1085,1088,1089,1092,1093,1096],{},"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 ",[15,1072,1073],{},"demo.localhost",". Was ",[195,1076,1014],{}," geschrieben wird: Benutzerkonten oder Postfächer. Das nur für die Entwicklung gedachte ",[15,1079,598],{}," 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 ",[15,1082,1083],{},"accounts"," und ",[15,1086,1087],{},"mailboxes"," aus (",[15,1090,1091],{},"apps\u002Fapi\u002Fconvex\u002FseedDemo\u002Fpipeline.ts"," → ",[15,1094,1095],{},"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.",[11,1098,1099,1100,1103,1104,1106,1107,1110,1111,1114,1115,1118,1119,1122,1123,1126],{},"Der Datensatz ist ",[195,1101,1102],{},"konstruktionsbedingt inert",". ",[15,1105,598],{}," schreibt seine Automatisierung als ",[15,1108,1109],{},"active"," und seinen Webhook als aktiviert; der Sample-Data-Pfad führt dieselben Loader mit ",[15,1112,1113],{},"{ inert: true }"," aus, was die Automatisierung als ",[15,1116,1117],{},"paused"," (ohne ",[15,1120,1121],{},"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 ",[15,1124,1125],{},"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.",[11,1128,1129,1130,1133,1134,1137],{},"Die Entfernung ist exakt und kein Tabellen-Wipe: Jede Zeile, die ein Loader einfügt, trägt ",[15,1131,1132],{},"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 ",[15,1135,1136],{},"truncated: true"," und das CLI weist darauf hin — führen Sie es erneut aus, um fortzufahren.",[11,1139,1140,1142,1143,1145],{},[15,1141,432],{}," führt denselben Installationsschritt aus, wenn Sie „mit Beispieldaten erkunden“ wählen, sodass eine frische Installation nie ",[15,1144,865],{}," benötigt, um befüllt auszusehen.",[90,1147,1149],{"id":1148},"seed-und-reset","seed und reset",[11,1151,1152,984,1155,1157,1158,991,1160,1162,1163,1165,1166,1169,1170,1174,1175,1103,1177,1180,1181,1183,1184,1187,1188,1190,1191,1193],{},[15,1153,1154],{},"seed",[15,1156,598],{},"; ",[15,1159,873],{},[15,1161,1024],{},". Beide Endpunkte sind hinter ",[15,1164,865],{}," ",[195,1167,1168],{},"fail-closed"," abgesichert (siehe ",[28,1171,1173],{"href":1172},"#dev-only-endpoints","den Hinweis zum Dev-Modus",") und benötigen den Header ",[15,1176,1004],{},[15,1178,1179],{},"seed --reset"," löscht seed-getaggte Zeilen, bevor neu eingefügt wird. ",[15,1182,873],{}," löscht ",[195,1185,1186],{},"alle"," Benutzer, die Organisation und jede geseedete Zeile, sodass der nächste Besuch von ",[15,1189,608],{}," auf ",[15,1192,506],{}," umleitet — verwenden Sie es nur auf Wegwerf-Instanzen.",[48,1195,1197],{"className":50,"code":1196,"language":52,"meta":53,"style":53},"owlat seed            # idempotent insert\nowlat seed --reset    # wipe seed rows, then re-seed\nowlat reset           # full wipe (prompts unless --assume-yes)\n",[15,1198,1199,1209,1221],{"__ignoreMap":53},[57,1200,1201,1203,1206],{"class":59,"line":60},[57,1202,152],{"class":63},[57,1204,1205],{"class":71}," seed",[57,1207,1208],{"class":364},"            # idempotent insert\n",[57,1210,1211,1213,1215,1218],{"class":59,"line":368},[57,1212,152],{"class":63},[57,1214,1205],{"class":71},[57,1216,1217],{"class":67}," --reset",[57,1219,1220],{"class":364},"    # wipe seed rows, then re-seed\n",[57,1222,1223,1225,1228],{"class":59,"line":390},[57,1224,152],{"class":63},[57,1226,1227],{"class":71}," reset",[57,1229,1230],{"class":364},"           # full wipe (prompts unless --assume-yes)\n",[90,1232,1234],{"id":1233},"feature-und-pack","feature und pack",[11,1236,1237,1240,1241,1245,1246,1248,1249,1252],{},[15,1238,1239],{},"feature"," schaltet ein Flag aus dem Katalog der ",[28,1242,1244],{"href":1243},"\u002Fdeveloper\u002Ffeature-flags","Feature-Flags"," um und erzeugt ",[15,1247,483],{}," neu; Abhängigkeitskaskaden werden angewendet und gemeldet. ",[15,1250,1251],{},"pack"," schaltet eine ganze Gruppe auf einmal um. Die drei Pakete sind:",[229,1254,1255,1265],{},[232,1256,1257],{},[235,1258,1259,1262],{},[238,1260,1261],{},"Paket",[238,1263,1264],{},"Flags, die es steuert",[245,1266,1267,1285,1303],{},[235,1268,1269,1274],{},[250,1270,1271],{},[15,1272,1273],{},"emailClient",[250,1275,1276,99,1279,99,1282],{},[15,1277,1278],{},"inbox",[15,1280,1281],{},"chat",[15,1283,1284],{},"postbox",[235,1286,1287,1292],{},[250,1288,1289],{},[15,1290,1291],{},"marketing",[250,1293,1294,99,1297,99,1300],{},[15,1295,1296],{},"campaigns",[15,1298,1299],{},"automations",[15,1301,1302],{},"transactional",[235,1304,1305,1310],{},[250,1306,1307],{},[15,1308,1309],{},"ai",[250,1311,1312,99,1314,99,1317,99,1320,99,1323,99,1326,99,1329,99,1332,99,1335],{},[15,1313,1309],{},[15,1315,1316],{},"ai.agent",[15,1318,1319],{},"ai.autonomy",[15,1321,1322],{},"ai.knowledge",[15,1324,1325],{},"ai.knowledge.autoLink",[15,1327,1328],{},"ai.knowledge.graphRetrieval",[15,1330,1331],{},"ai.knowledge.analytics",[15,1333,1334],{},"ai.assistant",[15,1336,1337],{},"ai.visualizations",[48,1339,1341],{"className":50,"code":1340,"language":52,"meta":53,"style":53},"owlat feature ai on        # enable a single flag\nowlat pack marketing on    # enable campaigns + automations + transactional\n",[15,1342,1343,1359],{"__ignoreMap":53},[57,1344,1345,1347,1350,1353,1356],{"class":59,"line":60},[57,1346,152],{"class":63},[57,1348,1349],{"class":71}," feature",[57,1351,1352],{"class":71}," ai",[57,1354,1355],{"class":71}," on",[57,1357,1358],{"class":364},"        # enable a single flag\n",[57,1360,1361,1363,1366,1369,1371],{"class":59,"line":368},[57,1362,152],{"class":63},[57,1364,1365],{"class":71}," pack",[57,1367,1368],{"class":71}," marketing",[57,1370,1355],{"class":71},[57,1372,1373],{"class":364},"    # enable campaigns + automations + transactional\n",[11,1375,1376,1377,1380],{},"Beide geben die neuen aktiven Compose-Profile aus und erinnern Sie daran, ",[15,1378,1379],{},"owlat restart"," auszuführen, damit die Änderung wirksam wird.",[90,1382,1383],{"id":1383},"env",[11,1385,1386,1387,1389,1390,99,1393,1084,1396,1399],{},"Setzt eine einzelne Variable in ",[15,1388,479],{},". Der Schlüssel muss ein großgeschriebener, shell-sicherer Bezeichner sein; Werte für ",[15,1391,1392],{},"*_KEY",[15,1394,1395],{},"*_SECRET",[15,1397,1398],{},"*_PASSWORD"," werden in der Ausgabe maskiert.",[48,1401,1403],{"className":50,"code":1402,"language":52,"meta":53,"style":53},"owlat env LLM_API_KEY sk-...\n",[15,1404,1405],{"__ignoreMap":53},[57,1406,1407,1409,1412,1415],{"class":59,"line":60},[57,1408,152],{"class":63},[57,1410,1411],{"class":71}," env",[57,1413,1414],{"class":71}," LLM_API_KEY",[57,1416,1417],{"class":71}," sk-...\n",[11,1419,1420,1422,1423,1426,1427,1429,1430,1432,1433,1432,1435,1437],{},[15,1421,915],{}," listet stattdessen die Variablen auf, die Ihr ",[195,1424,1425],{},"aktueller Feature-Flag-Zustand"," erfordert — dieselbe Menge erforderlicher Variablen, die ",[15,1428,925],{}," prüft, abzüglich der Docker-\u002FContainer-Prüfungen. Jede Zeile zeigt, ob die Variable gesetzt ist (Werte von ",[15,1431,1392],{}," \u002F ",[15,1434,1395],{},[15,1436,1398],{}," maskiert) und welches Flag (oder der providerabhängige Versandpfad) sie erfordert, sodass Sie auf einen Blick sehen, was vor dem Deployment noch fehlt.",[48,1439,1441],{"className":50,"code":1440,"language":52,"meta":53,"style":53},"owlat env --show\n",[15,1442,1443],{"__ignoreMap":53},[57,1444,1445,1447,1449],{"class":59,"line":60},[57,1446,152],{"class":63},[57,1448,1411],{"class":71},[57,1450,1451],{"class":67}," --show\n",[48,1453,1458],{"className":1454,"code":1456,"language":1457},[1455],"language-text","  KEY                           SET \u002F VALUE      REQUIRED BY\n────────────────────────────────────────────────────────────\n✗ GOOGLE_SAFE_BROWSING_API_KEY  (unset)          scan.urls\n✓ LLM_PROVIDER                  openrouter       ai\n✓ MTA_API_KEY                   ********…        send path (EMAIL_PROVIDER=mta)\n","text",[15,1459,1456],{"__ignoreMap":53},[11,1461,1462,1463,1465,1466,1468],{},"Es ist schreibgeschützt und beendet sich immer mit ",[15,1464,950],{}," — verwenden Sie ",[15,1467,925],{},", wenn Sie ein Pass\u002FFail-Gate benötigen.",[200,1470,1472],{"title":1471,"type":203},"env aktualisiert nur .env",[11,1473,1474,1476,1477,1479,1480,1482,1483,1486,1487,1490,1491,1494],{},[15,1475,1383],{}," schreibt die Compose-Datei ",[15,1478,479],{},", die die Container-Prozesse konfiguriert (web, convex, mta). Der Wert wird ",[195,1481,1014],{}," in die Convex-Function-Runtime gepusht. Variablen, die von Convex-Functions gelesen werden (die ",[28,1484,1485],{"href":538},"Runtime-Env-Schlüssel","), müssen zusätzlich mit ",[15,1488,1489],{},"convex env set"," gesetzt werden — führen Sie den Deploy-Schritt erneut aus oder setzen Sie sie über den ",[15,1492,1493],{},"convex-deploy","-Container, damit die Functions sie sehen.",[90,1496,925],{"id":925},[11,1498,1499],{},"Führt eine Checkliste gegen die Installation aus und beendet sich bei jedem Fehlschlag mit einem Wert ungleich null:",[454,1501,1502,1507,1510,1515],{},[457,1503,1504,1506],{},[15,1505,479],{}," existiert und lässt sich parsen.",[457,1508,1509],{},"Jede vom aktiven Feature-Set benötigte Umgebungsvariable ist befüllt.",[457,1511,1512,1514],{},[15,1513,483],{}," existiert.",[457,1516,1517,1518,1521],{},"Mindestens ein Compose-Service läuft (",[15,1519,1520],{},"docker compose ps",").",[48,1523,1525],{"className":50,"code":1524,"language":52,"meta":53,"style":53},"owlat doctor\n",[15,1526,1527],{"__ignoreMap":53},[57,1528,1529,1531],{"class":59,"line":60},[57,1530,152],{"class":63},[57,1532,1533],{"class":71}," doctor\n",[90,1535,1537],{"id":1536},"lifecycle-kommandos-des-wrappers","Lifecycle-Kommandos des Wrappers",[11,1539,1540,1541,433,1543,1545,1546,1549,1550,1552],{},"Der installierte Wrapper ",[15,1542,163],{},[15,1544,159],{},") stellt zusätzlich Day-2-Ops-Kommandos bereit, die reine ",[15,1547,1548],{},"docker compose","-Zeremonie sind — diese gibt es nur im Wrapper und ohne ",[15,1551,21],{},"-Entsprechung:",[229,1554,1555,1563],{},[232,1556,1557],{},[235,1558,1559,1561],{},[238,1560,790],{},[238,1562,793],{},[245,1564,1565,1577,1589,1601,1611,1623,1640],{},[235,1566,1567,1572],{},[250,1568,1569],{},[15,1570,1571],{},"start",[250,1573,1574,1576],{},[15,1575,518],{}," (aktiviert die Profile für eingeschaltete Features).",[235,1578,1579,1584],{},[250,1580,1581],{},[15,1582,1583],{},"stop",[250,1585,1586,198],{},[15,1587,1588],{},"docker compose down",[235,1590,1591,1596],{},[250,1592,1593],{},[15,1594,1595],{},"restart [service]",[250,1597,1598,198],{},[15,1599,1600],{},"docker compose restart [service]",[235,1602,1603,1607],{},[250,1604,1605],{},[15,1606,997],{},[250,1608,1609,198],{},[15,1610,1520],{},[235,1612,1613,1618],{},[250,1614,1615],{},[15,1616,1617],{},"logs [service]",[250,1619,1620,198],{},[15,1621,1622],{},"docker compose logs -f [service]",[235,1624,1625,1630],{},[250,1626,1627],{},[15,1628,1629],{},"shell \u003Cservice>",[250,1631,1632,1635,1636,1639],{},[15,1633,1634],{},"docker compose exec \u003Cservice> sh"," (standardmäßig der ",[15,1637,1638],{},"web","-Service).",[235,1641,1642,1647],{},[250,1643,1644],{},[15,1645,1646],{},"version",[250,1648,1649,1650,1652,1653,1432,1655,1521],{},"Gibt die laufende Stack-Version aus (aus dem ",[15,1651,1638],{},"-Container gelesen, mit Rückfall auf die lokale ",[15,1654,479],{},[15,1656,1657],{},"package.json",[11,1659,1660,1661,99,1664,99,1667,1084,1670,1673],{},"Der Wrapper stellt außerdem ",[15,1662,1663],{},"backup",[15,1665,1666],{},"restore",[15,1668,1669],{},"backup-schedule",[15,1671,1672],{},"upgrade"," für Backups und In-App-Updates bereit.",[34,1675,1677],{"id":1676},"convex-runtime-umgebungsvariablen-pushen-convex-deploy","Convex-Runtime-Umgebungsvariablen pushen (convex-deploy)",[11,1679,1680,1681,1684,1685,1687,1688,1690,1691,1694,1695,1697],{},"Convex-",[195,1682,1683],{},"Functions"," lesen ihre Konfiguration aus dem Deployment, nicht aus der Compose-",[15,1686,479],{},". Die Compose-",[15,1689,479],{}," konfiguriert nur die Container-Prozesse; sie erreicht die Convex-Function-Sandbox nicht. Ein Deploy besteht daher aus drei hostseitigen Schritten (",[15,1692,1693],{},"apps\u002Fsetup-cli\u002Fsrc\u002Flib\u002FconvexDeploy.ts","), die allesamt reine ",[15,1696,1548],{},"-Aufrufe sind:",[87,1699,1700,1704,1725,1729,1742,1769,1773],{},[90,1701,1703],{"id":1702},"den-admin-key-erzeugen","Den Admin-Key erzeugen",[11,1705,1706,1709,1710,1713,1714,1717,1718,1721,1722,1724],{},[15,1707,1708],{},"generateConvexAdminKey()"," führt ",[15,1711,1712],{},"docker compose exec convex .\u002Fgenerate_admin_key.sh"," aus. Der Schlüssel wird ",[195,1715,1716],{},"vom laufenden Backend ausgestellt"," und kann nicht clientseitig fabriziert werden — eine Zufallszeichenkette wird abgelehnt. Der erzeugte Schlüssel wird als ",[15,1719,1720],{},"CONVEX_ADMIN_KEY"," in ",[15,1723,479],{}," geschrieben, damit sich die nächsten beiden Schritte per Compose-Interpolation authentifizieren können.",[90,1726,1728],{"id":1727},"functions-deployen","Functions deployen",[11,1730,1731,1734,1735,1737,1738,1741],{},[15,1732,1733],{},"deployConvexFunctions()"," führt das einmalige Deploy-Profil aus und pusht die Functions, das Schema und die ",[15,1736,552],{},"-Handler aus ",[15,1739,1740],{},"apps\u002Fapi",":",[48,1743,1745],{"className":50,"code":1744,"language":52,"meta":53,"style":53},"docker compose --profile deploy run --rm convex-deploy\n",[15,1746,1747],{"__ignoreMap":53},[57,1748,1749,1751,1754,1757,1760,1763,1766],{"class":59,"line":60},[57,1750,105],{"class":63},[57,1752,1753],{"class":71}," compose",[57,1755,1756],{"class":67}," --profile",[57,1758,1759],{"class":71}," deploy",[57,1761,1762],{"class":71}," run",[57,1764,1765],{"class":67}," --rm",[57,1767,1768],{"class":71}," convex-deploy\n",[90,1770,1772],{"id":1771},"runtime-umgebungsvariablen-pushen","Runtime-Umgebungsvariablen pushen",[11,1774,1775,1709,1778,1780,1781,1783,1784,125,1787,1790],{},[15,1776,1777],{},"setConvexEnvVars()",[15,1779,1489],{}," für jede befüllte Runtime-Variable aus, wiederum über den ",[15,1782,1493],{},"-Container (der das CLI bereits pinnt und ",[15,1785,1786],{},"CONVEX_SELF_HOSTED_URL",[15,1788,1789],{},"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.",[11,1792,1793,1794,1797,1798,1801,1802,1804,1805,1808,1809,1812,1813,99,1816,1819],{},"Die Liste der Schlüssel, die gepusht werden, ist ",[15,1795,1796],{},"CONVEX_RUNTIME_ENV_KEYS",", definiert in ",[15,1799,1800],{},"packages\u002Fshared\u002Fsrc\u002FconvexRuntimeEnv.ts"," und re-exportiert aus ",[15,1803,1693],{},", synchron gehalten mit der ",[15,1806,1807],{},"EnvKey","-Union in ",[15,1810,1811],{},"apps\u002Fapi\u002Fconvex\u002Flib\u002Fenv.ts",". Sie umfasst Auth- und Instanzgeheimnisse, Site-URLs, E-Mail-\u002FMTA-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, ",[15,1814,1815],{},"NUXT_PUBLIC_*",[15,1817,1818],{},"REDIS_*",") sind bewusst ausgeschlossen — sie gehören nie ins Deployment.",[200,1821,1823],{"title":1822,"type":613},"Der Cloud-Port vs. der Site-Proxy",[11,1824,1825,1826,1828,1829,1832,1833,1836,1837,1839,1840,99,1842,99,1844,1846,1847,433,1850,1853,1854,1856,1857,1859,1860,991,1862,1864],{},"Convex bedient das Sync-Protokoll und das eingebaute ",[15,1827,528],{}," auf dem ",[195,1830,1831],{},"Cloud","-Port (",[15,1834,1835],{},"3210","), aber die ",[15,1838,552],{},"-Handler der Anwendung — ",[15,1841,939],{},[15,1843,598],{},[15,1845,1024],{},", Tracking, Webhooks, Abmeldung — leben auf dem ",[195,1848,1849],{},"Site-Proxy",[15,1851,1852],{},"3211",") und existieren erst nach dem Deploy der Functions. Das CLI fragt ",[15,1855,1835],{}," ab, während es auf den Start des Backends wartet, und danach ",[15,1858,1852],{},", sobald die Functions deployt sind. Ein POST auf ",[15,1861,556],{},[15,1863,1835],{}," liefert stillschweigend 404.",[34,1866,1868],{"id":1867},"setup-assistent-web-vs-terminal","Setup-Assistent: Web vs. Terminal",[11,1870,1871,433,1874,1877,1878,1165,1881,1883,1884,1886],{},[15,1872,1873],{},"owlat setup",[15,1875,1876],{},"apps\u002Fsetup-cli\u002Fsrc\u002Fcommands\u002Fsetup.ts",") ist der Konfigurationsassistent für den Erststart. Er schreibt ",[195,1879,1880],{},"ausschließlich",[15,1882,479],{}," und das Compose-Override — er deployt nichts und legt Ihr Admin-Konto nicht an. (",[15,1885,829],{}," ist derselbe Assistent, erneut geöffnet für eine bestehende Installation.)",[11,1888,1889],{},"Es gibt zwei Wege:",[1891,1892,1893,1970],"ul",{},[457,1894,1895,1898,1899,1901,1902,1905,1906,1909,1910,433,1912,1915,1916,1919,1920,1923,1924,1927,1928,1930,1931,1934,1935,1938,1939,1942,1943,99,1946,1432,1949,1432,1952,1955,1956,1960,1961,1963,1964,1966,1967,1969],{},[195,1896,1897],{},"Web-Assistent"," (Standard, wenn ein Browser verfügbar ist) — bereitet ",[15,1900,479],{}," für den Setup-Modus vor und verweist Sie auf ",[15,1903,1904],{},"http:\u002F\u002Flocalhost:3000\u002Fsetup",". Er erzeugt außerdem ein einmaliges ",[195,1907,1908],{},"Setup-Token",", schreibt es in die root-eigene ",[15,1911,479],{},[15,1913,1914],{},"OWLAT_SETUP_TOKEN",") und gibt es auf der Konsole aus — kopieren Sie es in den letzten Schritt des Assistenten. Die ",[15,1917,1918],{},"\u002Fapi\u002Fsetup\u002F*","-Endpunkte laufen unauthentifiziert (es gibt noch kein Admin-Konto), verlangen dieses Token daher in einem ",[15,1921,1922],{},"X-Setup-Token","-Header, der mit einem laufzeitkonstanten Vergleich geprüft wird; ein fehlendes oder falsches Token ergibt ",[15,1925,1926],{},"401",". Ohne dieses Token könnte der erste Aufrufer, der den Assistenten erreicht, das Plattform-Admin-Konto anlegen und ",[15,1929,479],{}," umschreiben. Für SSH-Installationen ohne lokalen Browser verwenden Sie stattdessen den Terminal-Assistenten (siehe unten). Im letzten Schritt „Launch“ legt sein Endpunkt ",[15,1932,1933],{},"\u002Fapi\u002Fsetup\u002Fapply"," den Admin an (",[15,1936,1937],{},"POST \u002Fseed\u002Fadmin",") ",[195,1940,1941],{},"und"," pusht die Umgebungsvariablen der Function-Runtime (",[15,1944,1945],{},"EMAIL_PROVIDER",[15,1947,1948],{},"RESEND_API_KEY",[15,1950,1951],{},"AWS_SES_*",[15,1953,1954],{},"SMTP_RELAY_*"," und den Rest von ",[28,1957,1958],{"href":538},[15,1959,1796],{},") über die Admin-API in das Convex-Deployment — das HTTP-Äquivalent zu ",[15,1962,1489],{},", da der schreibgeschützte Web-Container den ",[15,1965,1493],{},"-Schritt nicht ausführen kann. So erreicht die Wahl des E-Mail-Providers tatsächlich den Versandcode und nicht nur ",[15,1968,479],{},". Der Provider-Schritt ist eine ehrliche Entscheidung auf Instanzebene — eine eigene MTA betreiben, Amazon SES oder ein SMTP-Relay (mit Presets für Mailgun\u002FPostmark\u002FSendGrid\u002FBrevo 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).",[457,1971,1972,433,1975,1977,1978,1981],{},[195,1973,1974],{},"Terminal-Assistent",[15,1976,185],{},") — ein vollständiges TUI, das dieselben Fragen abdeckt: Deployment-Modus, ",[28,1979,1980],{"href":1243},"Feature-Auswahl",", Versandprovider (eigene MTA \u002F Amazon SES \u002F SMTP-Relay \u002F Resend), KI-Provider (OpenRouter \u002F OpenAI \u002F 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.",[11,1983,1984,1985,1988,1989,99,1991,1084,1993,1995,1996,1998],{},"Der Assistent erzeugt alle benötigten Geheimnisse automatisch (siehe unten), spiegelt den aufgelösten Flag-Zustand nach ",[15,1986,1987],{},".owlat-flags.json"," (damit ",[15,1990,925],{},[15,1992,1239],{},[15,1994,1251],{}," dieselbe Basis teilen) und schließt mit dem Hinweis ab, ",[15,1997,426],{}," auszuführen, um den Stack tatsächlich hochzufahren.",[200,2000,2002],{"title":2001,"type":613},"Web-Assistent innerhalb des Installers",[11,2003,2004,2005,2007,2008,2010],{},"Der Einzeiler erzwingt ",[15,2006,185],{},", weil der browserbasierte Assistent nicht innerhalb des containerisierten Installers laufen kann. Wählen Sie den Web-Assistenten nur, wenn Sie ",[15,2009,1873],{}," direkt auf einem Host mit Browser ausführen.",[90,2012,2014],{"id":2013},"erzeugte-geheimnisse","Erzeugte Geheimnisse",[11,2016,2017,433,2020,2023],{},[15,2018,2019],{},"ensureSecrets()",[15,2021,2022],{},"apps\u002Fsetup-cli\u002Fsrc\u002Flib\u002Fsecrets.ts",") füllt jedes fehlende Geheimnis auf und bewahrt dabei Änderungen des Betreibers:",[229,2025,2026,2035],{},[232,2027,2028],{},[235,2029,2030,2032],{},[238,2031,240],{},[238,2033,2034],{},"Format",[245,2036,2037,2047,2060,2069,2082,2094],{},[235,2038,2039,2044],{},[250,2040,2041],{},[15,2042,2043],{},"BETTER_AUTH_SECRET",[250,2045,2046],{},"48 Zeichen langes URL-sicheres Token",[235,2048,2049,2054],{},[250,2050,2051],{},[15,2052,2053],{},"INSTANCE_SECRET",[250,2055,2056,2059],{},[195,2057,2058],{},"64 Hex-Zeichen"," (32 Byte) — siehe Hinweis unten",[235,2061,2062,2067],{},[250,2063,2064],{},[15,2065,2066],{},"UNSUBSCRIBE_SECRET",[250,2068,2046],{},[235,2070,2071,2076],{},[250,2072,2073],{},[15,2074,2075],{},"MTA_API_KEY",[250,2077,2078,2081],{},[15,2079,2080],{},"mta_"," + 40 Zeichen langes Token",[235,2083,2084,2089],{},[250,2085,2086],{},[15,2087,2088],{},"MTA_WEBHOOK_SECRET",[250,2090,2091,2081],{},[15,2092,2093],{},"whsec_",[235,2095,2096,2101],{},[250,2097,2098],{},[15,2099,2100],{},"REDIS_PASSWORD",[250,2102,2103],{},"32 Zeichen langes Token",[11,2105,2106,2108,2109,2111],{},[15,2107,1720],{}," wird hier bewusst ",[195,2110,1014],{}," erzeugt — er muss während des Deploy-Schritts vom laufenden Backend ausgestellt werden.",[200,2113,2115],{"title":2114,"type":203},"INSTANCE_SECRET muss hexadezimal sein",[11,2116,2117,2118,2120,2121,2124,2125,2128],{},"Das selbst gehostete Convex-Backend dekodiert ",[15,2119,2053],{}," beim Start als Hex und stürzt ab (",[15,2122,2123],{},"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 ",[15,2126,2127],{},"openssl rand -hex 32"," des alten Installers.",[34,2130,565],{"id":2131},"admin-bootstrap",[11,2133,2134,2136,2137,2139,2140,2142,2143,1521],{},[15,2135,839],{}," (und ",[15,2138,432],{},") legen den ersten Admin an, indem sie ",[15,2141,1937],{}," aufrufen (",[15,2144,2145],{},"apps\u002Fapi\u002Fconvex\u002FseedAdmin.ts",[48,2147,2151],{"className":2148,"code":2149,"language":2150,"meta":53,"style":53},"language-http shiki shiki-themes github-light github-dark-dimmed","POST \u002Fseed\u002Fadmin\nX-Instance-Secret: \u003CINSTANCE_SECRET>\nContent-Type: application\u002Fjson\n\n{ \"email\": \"admin@example.com\", \"name\": \"Admin\", \"passwordHash\": \"\u003Cscrypt-hash>\" }\n","http",[15,2152,2153,2158,2163,2168,2172],{"__ignoreMap":53},[57,2154,2155],{"class":59,"line":60},[57,2156,2157],{},"POST \u002Fseed\u002Fadmin\n",[57,2159,2160],{"class":59,"line":368},[57,2161,2162],{},"X-Instance-Secret: \u003CINSTANCE_SECRET>\n",[57,2164,2165],{"class":59,"line":390},[57,2166,2167],{},"Content-Type: application\u002Fjson\n",[57,2169,2170],{"class":59,"line":397},[57,2171,394],{"emptyLinePlaceholder":393},[57,2173,2174],{"class":59,"line":403},[57,2175,2176],{},"{ \"email\": \"admin@example.com\", \"name\": \"Admin\", \"passwordHash\": \"\u003Cscrypt-hash>\" }\n",[11,2178,2179],{},"Der Handler:",[1891,2181,2182,2193,2202],{},[457,2183,2184,2185,2187,2188,2190,2191,198],{},"Verlangt einen passenden ",[15,2186,1004],{},"-Header (laufzeitsicherer Vergleich mit dem ",[15,2189,2053],{}," des Deployments); eine Abweichung ergibt ",[15,2192,1926],{},[457,2194,2195,2196,2199,2200,198],{},"Ist ",[195,2197,2198],{},"einmalig",": Existiert bereits irgendein Benutzer, liefert er ",[15,2201,946],{},[457,2203,2204,2205,2208,2209,2212],{},"Legt bei Erfolg (",[15,2206,2207],{},"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 ",[15,2210,2211],{},"owner","-Mitgliedschaft, das Benutzerprofil und die Instanzeinstellungen an.",[229,2214,2215,2225],{},[232,2216,2217],{},[235,2218,2219,2222],{},[238,2220,2221],{},"Status",[238,2223,2224],{},"Bedeutung",[245,2226,2227,2236,2255,2266],{},[235,2228,2229,2233],{},[250,2230,2231],{},[15,2232,2207],{},[250,2234,2235],{},"Admin + Organisation angelegt",[235,2237,2238,2243],{},[250,2239,2240],{},[15,2241,2242],{},"400",[250,2244,2245,2246,1432,2249,1432,2252],{},"Ungültiges JSON oder fehlendes ",[15,2247,2248],{},"email",[15,2250,2251],{},"name",[15,2253,2254],{},"passwordHash",[235,2256,2257,2261],{},[250,2258,2259],{},[15,2260,1926],{},[250,2262,2263,2264],{},"Fehlendes oder falsches ",[15,2265,1004],{},[235,2267,2268,2272],{},[250,2269,2270],{},[15,2271,946],{},[250,2273,2274],{},"Es existiert bereits ein Benutzer (der Endpunkt ist einmalig)",[11,2276,2277,2278,2280,2281,2284],{},"Das CLI hasht das Passwort vor dem Senden: ",[15,2279,839],{}," leitet einen BetterAuth-kompatiblen scrypt-Hash ab (",[15,2282,2283],{},"apps\u002Fsetup-cli\u002Fsrc\u002Flib\u002FpasswordHash.ts","), damit die geseedete Zugangsinformation zu einer normalen Anmeldung passt. Sie senden das Klartextpasswort nie an das Backend.",[90,2286,2288],{"id":2287},"endpunkte-nur-für-die-entwicklung","Endpunkte nur für die Entwicklung",[11,2290,2291,1084,2293,2295,2296,2298,2299,2302,2303,2305,2306,2308,2309,2312,2313,2315,2316,2319,2320,2325,2326,2328,2329,2331],{},[15,2292,598],{},[15,2294,1024],{}," sind durch ",[15,2297,865],{}," geschützt (",[15,2300,2301],{},"apps\u002Fapi\u002Fconvex\u002FdevShortcuts\u002F_guard.ts",") und ",[195,2304,1168],{},": Sofern ",[15,2307,865],{}," in der Convex-Function-Runtime nicht wahr ist, liefern sie ",[15,2310,2311],{},"403",". Lokale ",[15,2314,590],{},"-Installationen haben es standardmäßig an; ein produktives Self-Hosting lässt es aus, und ",[195,2317,2318],{},"nichts im Installationsablauf schaltet es ein"," — Demo-Inhalte für eine echte Installation laufen über ",[28,2321,2323],{"href":2322},"#sample-data",[15,2324,977],{},", das per Instanzgeheimnis authentifiziert und nicht dev-gesteuert ist. ",[15,2327,939],{}," selbst ist ",[195,2330,1014],{}," dev-gesteuert — es ist der produktive Pfad für den Admin-Bootstrap, geschützt durch das Instanzgeheimnis und die Einmaligkeitsprüfung.",[34,2333,2335],{"id":2334},"verwandt","Verwandt",[2337,2338],"link-card",{"description":2339,"title":31,"to":30},"Die erzählende Deploy-Anleitung — vom ersten Start bis zum laufenden Stack.",[2337,2341],{"description":2342,"title":2343,"to":2344},"Jede Umgebungsvariable, die Service-Topologie und alle Volumes.","Self-Hosting-Konfiguration","\u002Fdeveloper\u002Fself-hosting-config",[2337,2346],{"description":2347,"title":2348,"to":1243},"Der Flag-Katalog, die Pakete und die Kaskadenregeln hinter feature und pack.","Feature-Flags — Entwicklerreferenz",[2337,2350],{"description":2351,"title":2352,"to":2353},"Die vollständige Referenz der Umgebungsvariablen für Backend und Services.","Umgebungsvariablen","\u002Fdeveloper\u002Fenvironment-variables",[2355,2356,2357],"style",{},"html pre.shiki code .sOLd2, html code.shiki .sOLd2{--shiki-default:#6F42C1;--shiki-dark:#F69D50}html pre.shiki code .sviXB, html code.shiki .sviXB{--shiki-default:#005CC5;--shiki-dark:#6CB6FF}html pre.shiki code .s-HuK, html code.shiki .s-HuK{--shiki-default:#032F62;--shiki-dark:#96D0FF}html pre.shiki code .s7YZ4, html code.shiki .s7YZ4{--shiki-default:#D73A49;--shiki-dark:#F47067}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sDN9O, html code.shiki .sDN9O{--shiki-default:#6A737D;--shiki-dark:#768390}html pre.shiki code .sYgZi, html code.shiki .sYgZi{--shiki-default:#24292E;--shiki-dark:#ADBAC7}",{"title":53,"searchDepth":368,"depth":368,"links":2359},[2360,2368,2372,2381,2386,2389,2392],{"id":36,"depth":368,"text":37,"children":2361},[2362,2363,2364,2366,2367],{"id":92,"depth":390,"text":93},{"id":117,"depth":390,"text":118},{"id":148,"depth":390,"text":2365},"Das owlat-CLI installieren",{"id":174,"depth":390,"text":175},{"id":226,"depth":390,"text":227},{"id":423,"depth":368,"text":2369,"children":2370},"owlat quickstart — Deploy von Anfang bis Ende",[2371],{"id":626,"depth":390,"text":627},{"id":766,"depth":368,"text":767,"children":2373},[2374,2375,2376,2377,2378,2379,2380],{"id":839,"depth":390,"text":839},{"id":977,"depth":390,"text":977},{"id":1148,"depth":390,"text":1149},{"id":1233,"depth":390,"text":1234},{"id":1383,"depth":390,"text":1383},{"id":925,"depth":390,"text":925},{"id":1536,"depth":390,"text":1537},{"id":1676,"depth":368,"text":1677,"children":2382},[2383,2384,2385],{"id":1702,"depth":390,"text":1703},{"id":1727,"depth":390,"text":1728},{"id":1771,"depth":390,"text":1772},{"id":1867,"depth":368,"text":1868,"children":2387},[2388],{"id":2013,"depth":390,"text":2014},{"id":2131,"depth":368,"text":565,"children":2390},[2391],{"id":2287,"depth":390,"text":2288},{"id":2334,"depth":368,"text":2335},"Betreiberreferenz für das Owlat-Self-Hosting-Tooling: der install.sh-Einzeiler, das owlat-setup-CLI, der convex-deploy-Ablauf und der Admin-Bootstrap.","md",{},"\u002Fdeveloper\u002Fsetup-cli",{"title":6,"description":2393},"3.developer\u002F36.setup-cli","SYrJlUkIu0sRfnS-Mp4LzRcgWKVuVHtRO7YWlOtu1kA",[2401,2405],{"title":2402,"path":2403,"stem":2404,"children":-1},"Desktop-Installer","\u002Fdeveloper\u002Fself-hosting-desktop","3.developer\u002F35.self-hosting-desktop",{"title":2406,"path":2407,"stem":2408,"children":-1},"Externes Reputations-Feedback","\u002Fdeveloper\u002Fexternal-reputation-feedback","3.developer\u002F37.external-reputation-feedback",1786915110177]