[{"data":1,"prerenderedAt":1901},["ShallowReactive",2],{"search-de":3,"content-de-developer\u002Fplatform-operations":4,"surround-de-\u002Fdeveloper\u002Fplatform-operations":1892},[],{"id":5,"title":6,"body":7,"description":1884,"extension":1885,"meta":1886,"navigation":1887,"path":1888,"seo":1889,"stem":1890,"__hash__":1891},"content_de\u002F3.developer\u002F20.platform-operations.md","Plattformbetrieb",{"type":8,"value":9,"toc":1856},"minimark",[10,19,32,56,61,72,164,171,244,249,252,314,332,336,415,448,452,477,484,568,585,589,603,727,731,738,796,800,811,821,902,919,978,984,988,1013,1026,1029,1120,1127,1131,1140,1144,1169,1229,1236,1240,1251,1338,1342,1360,1449,1462,1476,1483,1487,1491,1518,1592,1604,1624,1628,1634,1763,1779,1783,1801,1804,1825,1831,1835,1839,1842,1847,1852],[11,12,13,14,18],"p",{},"Diese Seite dokumentiert die operative Oberfläche, die ",[15,16,17],"em",{},"hinter"," dem Produkt sitzt: den Lebenszyklus des Abuse-Status, der das Senden freischaltet, die Rolle des Plattform-Admins und ihre Queries, die Warteschlange zur Inhaltsprüfung, den Walker für die Organisationslöschung, Systemzustand und In-App-Selbstupdate sowie die Dev-Mode-Endpunkte, Crons und Migrationen, die ein Deployment wartbar halten. Sie richtet sich an Betreiber und Mitwirkende, die an einer selbst gehosteten Owlat-Instanz arbeiten.",[20,21,24],"callout",{"title":22,"type":23},"Eine Organisation pro Deployment","info",[11,25,26,27,31],{},"Owlat ist auf eine Organisation pro Deployment ausgelegt. Die Data Plane gehört genau einer Organisation, und Abuse-Status, Einstellungen und Statistiken, die hier beschrieben werden, liegen allesamt in der Singleton-Zeile ",[28,29,30],"code",{},"instanceSettings",". Es gibt keinen mandantenfähigen Organisationswechsler.",[20,33,36],{"title":34,"type":35},"Kein Plattform-Admin-Dashboard in diesem Repository","warning",[11,37,38,39,43,44,47,48,51,52,55],{},"Dieses OSS-Repository liefert ",[40,41,42],"strong",{},"eine"," Seite aus, die auf Plattform-Admins beschränkt ist: ",[40,45,46],{},"Einstellungen → System & Updates"," (",[28,49,50],{},"\u002Fdashboard\u002Fadmin\u002Fsystem","). Die reichhaltigere Control-Plane-Oberfläche, die die untenstehenden Queries konsumiert hat, wurde in ein separates privates Repository ausgelagert. Behandeln Sie die hier dokumentierten Funktionen für Verzeichnis, Abuse-Status und Inhaltsprüfung als ",[40,53,54],{},"Backend-\u002FAPI-Oberfläche",", die Sie aus eigenem Tooling oder eigenen Skripten aufrufen — nicht als Dashboard im Produkt.",[57,58,60],"h2",{"id":59},"lebenszyklus-des-abuse-status-und-das-sende-gate","Lebenszyklus des Abuse-Status und das Sende-Gate",[11,62,63,64,67,68,71],{},"Jedes Deployment trägt genau einen Abuse-Status auf ",[28,65,66],{},"instanceSettings.abuseStatus",". Er steuert, ob die Instanz senden darf. Die vier Status, definiert in ",[28,69,70],{},"apps\u002Fapi\u002Fconvex\u002Forganizations\u002FabuseStatus.ts",", bilden eine Schweregradleiter:",[73,74,75,94],"table",{},[76,77,78],"thead",{},[79,80,81,85,88,91],"tr",{},[82,83,84],"th",{},"Status",[82,86,87],{},"Schweregrad",[82,89,90],{},"Versand",[82,92,93],{},"Bedeutung",[95,96,97,114,129,147],"tbody",{},[79,98,99,105,108,111],{},[100,101,102],"td",{},[28,103,104],{},"clean",[100,106,107],{},"0",[100,109,110],{},"Erlaubt",[100,112,113],{},"Normalbetrieb (zugleich der Standard, wenn nie ein Status geschrieben wurde)",[79,115,116,121,124,126],{},[100,117,118],{},[28,119,120],{},"warned",[100,122,123],{},"1",[100,125,110],{},[100,127,128],{},"Warnhinweis erteilt — weiterhin voll betriebsfähig",[79,130,131,136,139,144],{},[100,132,133],{},[28,134,135],{},"suspended",[100,137,138],{},"2",[100,140,141],{},[40,142,143],{},"Blockiert",[100,145,146],{},"Jeglicher Versand blockiert; das Konto bleibt zugänglich",[79,148,149,154,157,161],{},[100,150,151],{},[28,152,153],{},"banned",[100,155,156],{},"3",[100,158,159],{},[40,160,143],{},[100,162,163],{},"Konto vollständig deaktiviert; terminal für interne Schreiber",[11,165,166,167,170],{},"Lese- und Schreibhälfte sind bewusst in Geschwistermodule getrennt (ADR-0011, ",[28,168,169],{},"docs\u002Fadr\u002F0011-abuse-status-modules.md","):",[172,173,174,218],"ul",{},[175,176,177,47,180,183,184,187,188,191,192,195,196,198,199,201,202,204,205,208,209,212,213,201,215,217],"li",{},[40,178,179],{},"Abuse-Gate",[28,181,182],{},"apps\u002Fapi\u002Fconvex\u002Forganizations\u002FabuseGate.ts",") ist die Heimat des Sende-Prädikats. Es exportiert zwei Oberflächen: ",[28,185,186],{},"isSendingAllowed(status)",", das reine Prädikat, das die Live-Sendepfade aufrufen (der Transaktions-Dispatch-Pfad in ",[28,189,190],{},"transactional\u002Fdispatch.ts","), und ",[28,193,194],{},"requireSendingAllowed(ctx)",", ein Helfer für den Mutation-Kontext, der ",[28,197,30],{}," lädt und bei ",[28,200,135],{},"\u002F",[28,203,153],{}," wirft. Der Kampagnen-Sendepfad erzwingt dasselbe Prädikat inline über seine Pre-Flight-Query (",[28,206,207],{},"campaigns\u002Fpreflight.ts",", die ",[28,210,211],{},"abuseStatus"," direkt gegen ",[28,214,135],{},[28,216,153],{}," prüft), statt den Helfer aufzurufen. So oder so läuft das Gate immer — es steht nicht hinter einem Feature-Flag.",[175,219,220,47,223,225,226,229,230,232,233,236,237,236,240,243],{},[40,221,222],{},"Abuse-Status",[28,224,70],{},") ist der ",[15,227,228],{},"einzige"," Schreiber von ",[28,231,211],{}," und den zugehörigen Feldern (",[28,234,235],{},"abuseStatusReason",", ",[28,238,239],{},"abuseStatusChangedAt",[28,241,242],{},"abuseStatusChangedBy",").",[245,246,248],"h3",{"id":247},"übergangsregeln","Übergangsregeln",[11,250,251],{},"Es gibt zwei Schreib-Einstiegspunkte. Der Pfad für interne Schreiber erzwingt die Schweregradregeln; der Admin-Pfad umgeht sie.",[73,253,254,267],{},[76,255,256],{},[79,257,258,261,264],{},[82,259,260],{},"Einstiegspunkt",[82,262,263],{},"Genutzt von",[82,265,266],{},"Regeln",[95,268,269,292],{},[79,270,271,277,280],{},[100,272,273,276],{},[28,274,275],{},"transition"," (interne Mutation)",[100,278,279],{},"MTA-Circuit-Breaker, reputationsbasierte Auto-Durchsetzung",[100,281,282,284,285,288,289,291],{},[28,283,153],{}," ist terminal; Herabstufungen werden abgelehnt, ",[15,286,287],{},"außer"," hinunter auf ",[28,290,104],{}," (der Auto-Recover-Pfad); ein Versuch auf denselben Zustand wird protokolliert, aber nicht gepatcht",[79,293,294,299,305],{},[100,295,296,276],{},[28,297,298],{},"adminOverride",[100,300,301,302],{},"Die Plattform-Admin-Mutation ",[28,303,304],{},"setOrganizationStatus",[100,306,307,308,310,311,313],{},"Umgeht alle Schweregradregeln — ein Admin kann eine ",[28,309,153],{},"-Organisation zurück auf ",[28,312,104],{}," stufen, um einen Widerspruch aufzulösen",[11,315,316,317,320,321,324,325,327,328,331],{},"Beide Pfade schreiben bei ",[40,318,319],{},"jedem"," Aufruf eine Audit-Log-Zeile ",[28,322,323],{},"abuse_status_changed",", auch bei No-Ops auf denselben Zustand (damit „Circuit Breaker erneut ausgelöst, während bereits gewarnt“ beobachtbar ist). Eine fehlende ",[28,326,30],{},"-Zeile liefert ",[28,329,330],{},"{ ok: false, reason: 'no_settings_row' }",", statt zu werfen — ein Sonderfall in der Frühphase eines Deployments.",[245,333,335],{"id":334},"was-den-status-automatisch-umschaltet","Was den Status automatisch umschaltet",[73,337,338,351],{},[76,339,340],{},[79,341,342,345,348],{},[82,343,344],{},"Auslöser",[82,346,347],{},"Quelle",[82,349,350],{},"Zielstatus",[95,352,353,378,397],{},[79,354,355,361,374],{},[100,356,357,358],{},"Reputation erreicht Risiko ",[28,359,360],{},"critical",[100,362,363,366,367,370,371],{},[28,364,365],{},"analytics\u002FsendingReputation.ts"," → ",[28,368,369],{},"evaluateAutoEnforce"," (stündlicher Cron) → ",[28,372,373],{},"autoEnforceReputation",[100,375,376],{},[28,377,135],{},[79,379,380,385,393],{},[100,381,357,382],{},[28,383,384],{},"high",[100,386,387,366,389,370,391],{},[28,388,365],{},[28,390,369],{},[28,392,373],{},[100,394,395],{},[28,396,120],{},[79,398,399,402,411],{},[100,400,401],{},"MTA-Circuit-Breaker löst aus",[100,403,404,47,407,410],{},[28,405,406],{},"webhooks\u002Fdispatcher.ts",[28,408,409],{},"internal.circuit_breaker_tripped",")",[100,412,413],{},[28,414,120],{},[11,416,417,418,423,424,47,427,430,431,433,434,436,437,47,440,443,444,447],{},"Die Reputation wird beim Lesen abgeleitet (ein rollierendes 30-Tage-Fenster — siehe ",[419,420,422],"a",{"href":421},"\u002Fdeveloper\u002Fdeliverability-infrastructure","Deliverability-Infrastruktur","), doch die Auto-Durchsetzung wird bewusst vom heißen Lesepfad ferngehalten: Ein stündlicher Cron ",[28,425,426],{},"evaluate reputation auto-enforce",[28,428,429],{},"apps\u002Fapi\u002Fconvex\u002Fcrons.ts",") ruft ",[28,432,369],{}," auf, das das Org-Fenster zusammenfasst und ",[28,435,373],{}," einplant, wenn das Risiko hoch oder kritisch ist. Ein separater stündlicher Cron ",[28,438,439],{},"cleanup sending reputation",[28,441,442],{},"recalculateAll",") lässt Buckets älter als 60 Tage ausaltern. Weil Herabstufungen des Schweregrads abgelehnt werden, lockert ein Übergang ",[28,445,446],{},"critical → warned"," niemals stillschweigend eine bestehende Sperre.",[57,449,451],{"id":450},"plattform-admin-rolle-verzeichnis-und-operative-queries","Plattform-Admin-Rolle, Verzeichnis und operative Queries",[11,453,454,455,458,459,462,463,466,467,470,471,473,474,476],{},"Plattform-Admins sind Zeilen in der Tabelle ",[28,456,457],{},"platformAdmins",", geschlüsselt über die BetterAuth-Benutzer-ID. Es gibt zwei Rollen: ",[28,460,461],{},"admin"," und ",[28,464,465],{},"superadmin",". Ein Betreiber kann den ersten Admin von Hand anlegen mit ",[28,468,469],{},"convex run migrations\u002F0036_seed_platform_admin:run","; das gelingt nur, solange die Tabelle leer ist, und legt stets einen ",[28,472,465],{}," an. Danach verwalten ",[28,475,465],{},"s das Verzeichnis.",[11,478,479,480,483],{},"Die Absicherung ist in ",[28,481,482],{},"apps\u002Fapi\u002Fconvex\u002FplatformAdmin\u002FplatformAdmin.ts"," zentralisiert:",[73,485,486,499],{},[76,487,488],{},[79,489,490,493,496],{},[82,491,492],{},"Funktion",[82,494,495],{},"Art",[82,497,498],{},"Zweck",[95,500,501,524,537,553],{},[79,502,503,508,511],{},[100,504,505],{},[28,506,507],{},"requirePlatformAdmin(ctx)",[100,509,510],{},"Helfer",[100,512,513,514,517,518,520,521],{},"Wirft ",[28,515,516],{},"FORBIDDEN",", sofern der Aufrufer nicht in ",[28,519,457],{}," steht; liefert ",[28,522,523],{},"{ authUserId, email, role }",[79,525,526,531,534],{},[100,527,528],{},[28,529,530],{},"isPlatformAdmin",[100,532,533],{},"Öffentliche Query",[100,535,536],{},"Boolescher Navigationshelfer, sicher für anonyme Aufrufer",[79,538,539,544,547],{},[100,540,541],{},[28,542,543],{},"isPlatformAdminByUserId",[100,545,546],{},"Interne Query",[100,548,549,550],{},"Admin-Prüfung anhand der Benutzer-ID, für HTTP-\u002FAction-Kontexte ohne ",[28,551,552],{},"QueryCtx",[79,554,555,560,563],{},[100,556,557],{},[28,558,559],{},"migrations\u002F0036_seed_platform_admin:run",[100,561,562],{},"Von Hand ausgeführte interne Mutation",[100,564,565,566],{},"Optionaler einmaliger Bootstrap des ersten ",[28,567,465],{},[11,569,570,571,47,574,577,578,580,581,584],{},"Die Web-App sichert die einzige Admin-Seite mit der Route-Middleware ",[28,572,573],{},"platform-admin",[28,575,576],{},"apps\u002Fweb\u002Fapp\u002Fmiddleware\u002Fplatform-admin.ts",") ab, die ",[28,579,530],{}," aufruft und Nicht-Admins auf ",[28,582,583],{},"\u002Fdashboard"," umleitet.",[245,586,588],{"id":587},"operative-queries","Operative Queries",[11,590,591,592,595,596,599,600,602],{},"Alle diese liegen in ",[28,593,594],{},"apps\u002Fapi\u002Fconvex\u002FplatformAdmin\u002Fqueries.ts"," und beginnen mit ",[28,597,598],{},"requirePlatformAdmin",". Sie lesen die Singleton-Zeile ",[28,601,30],{}," sowie abgeleitete Reputations-\u002FStatistikwerte:",[73,604,605,615],{},[76,606,607],{},[79,608,609,612],{},[82,610,611],{},"Query",[82,613,614],{},"Liefert",[95,616,617,627,647,669,685,697,717],{},[79,618,619,624],{},[100,620,621],{},[28,622,623],{},"getPlatformStats",[100,625,626],{},"Kontaktanzahl, Abuse-Status, 30-Tage-Aggregate zu Versand\u002FZustellung\u002FBounces\u002FBeschwerden, Anmeldungen pro Tag",[79,628,629,634],{},[100,630,631],{},[28,632,633],{},"getOrganizationDetail",[100,635,636,637,201,640,201,643,646],{},"Einstellungen, Reputationszusammenfassung, Anzahl blockierter E-Mail-Adressen (",[28,638,639],{},"bounced",[28,641,642],{},"complained",[28,644,645],{},"manual","), jüngste Inhaltsscans und Kampagnen",[79,648,649,654],{},[100,650,651],{},[28,652,653],{},"listFlaggedOrganizations",[100,655,656,657,201,659,201,661,663,664,201,666,668],{},"Den Instanzeintrag, aber nur bei ",[28,658,120],{},[28,660,135],{},[28,662,153],{}," oder wenn das Reputationsrisiko ",[28,665,384],{},[28,667,360],{}," ist",[79,670,671,676],{},[100,672,673],{},[28,674,675],{},"listAllOrganizations",[100,677,678,679,201,682],{},"Den Instanzeintrag mit optionalem ",[28,680,681],{},"search",[28,683,684],{},"statusFilter",[79,686,687,692],{},[100,688,689],{},[28,690,691],{},"listAllUsers",[100,693,694,695],{},"Benutzerprofile mit optionalem ",[28,696,681],{},[79,698,699,704],{},[100,700,701],{},[28,702,703],{},"listRecentAbuse",[100,705,706,707,201,710,713,714],{},"Markierte Inhaltsscans (",[28,708,709],{},"suspicious",[28,711,712],{},"blocked",") sowie Kampagnen im Status ",[28,715,716],{},"pending_review",[79,718,719,724],{},[100,720,721],{},[28,722,723],{},"listPlatformAdmins",[100,725,726],{},"Das vollständige Admin-Verzeichnis",[245,728,730],{"id":729},"mutationen-für-verzeichnis-status","Mutationen für Verzeichnis + Status",[11,732,733,734,737],{},"Diese liegen in ",[28,735,736],{},"apps\u002Fapi\u002Fconvex\u002FplatformAdmin\u002Fmutations.ts",":",[73,739,740,750],{},[76,741,742],{},[79,743,744,747],{},[82,745,746],{},"Mutation",[82,748,749],{},"Wirkung",[95,751,752,770,783],{},[79,753,754,758],{},[100,755,756],{},[28,757,304],{},[100,759,760,761,763,764,766,767],{},"Setzt den Abuse-Status über ",[28,762,298],{}," (unter Umgehung der Schweregradregeln); schreibt sowohl die Audit-Zeile ",[28,765,323],{}," als auch die veraltete Zeile ",[28,768,769],{},"platform_admin.org_status_changed",[79,771,772,777],{},[100,773,774],{},[28,775,776],{},"addPlatformAdmin",[100,778,779,780,782],{},"Fügt einen Admin hinzu (nur ",[28,781,465],{},"; lehnt Duplikate ab)",[79,784,785,790],{},[100,786,787],{},[28,788,789],{},"removePlatformAdmin",[100,791,792,793,795],{},"Entfernt einen Admin (nur ",[28,794,465],{},"; man kann sich nicht selbst entfernen)",[57,797,799],{"id":798},"warteschlange-zur-inhaltsprüfung-ausstehende-inhalte-freigebenablehnen","Warteschlange zur Inhaltsprüfung (ausstehende Inhalte freigeben\u002Fablehnen)",[11,801,802,803,805,806,810],{},"Kampagnen und Transaktionsmails können im Status ",[28,804,716],{}," landen (etwa wenn das Content-Scanning sie markiert — siehe ",[419,807,809],{"href":808},"\u002Fdeveloper\u002Femail-security","E-Mail-Sicherheit","). Die Prüfoberfläche ist ausschließlich Plattform-Admins vorbehalten.",[11,812,813,816,817,820],{},[28,814,815],{},"getContentReviewQueue"," (in ",[28,818,819],{},"platformAdmin\u002Fqueries.ts",") liefert die ausstehenden Objekte samt ihrem jüngsten Inhaltsscan-Ergebnis sowie das Protokoll der kürzlich geprüften Objekte:",[822,823,828],"pre",{"className":824,"code":825,"language":826,"meta":827,"style":827},"language-ts shiki shiki-themes github-light github-dark-dimmed","{\n  pending: [{ type: 'campaign' | 'transactional', id, name, subject, scan: { score, level } | null, ... }],\n  pendingCount: number,\n  recentlyReviewed: [{ action, details, userId, createdAt }],\n}\n","ts","",[28,829,830,839,878,887,896],{"__ignoreMap":827},[831,832,835],"span",{"class":833,"line":834},"line",1,[831,836,838],{"class":837},"sYgZi","{\n",[831,840,842,846,849,853,857,860,863,866,870,872,875],{"class":833,"line":841},2,[831,843,845],{"class":844},"sOLd2","  pending",[831,847,848],{"class":837},": [{ type: ",[831,850,852],{"class":851},"s-HuK","'campaign'",[831,854,856],{"class":855},"s7YZ4"," |",[831,858,859],{"class":851}," 'transactional'",[831,861,862],{"class":837},", id, name, subject, scan: { score, level } ",[831,864,865],{"class":855},"|",[831,867,869],{"class":868},"sviXB"," null",[831,871,236],{"class":837},[831,873,874],{"class":855},"...",[831,876,877],{"class":837}," }],\n",[831,879,881,884],{"class":833,"line":880},3,[831,882,883],{"class":844},"  pendingCount",[831,885,886],{"class":837},": number,\n",[831,888,890,893],{"class":833,"line":889},4,[831,891,892],{"class":844},"  recentlyReviewed",[831,894,895],{"class":837},": [{ action, details, userId, createdAt }],\n",[831,897,899],{"class":833,"line":898},5,[831,900,901],{"class":837},"}\n",[11,903,904,905,908,909,911,912,915,916,737],{},"Die drei Prüf-Mutationen (in ",[28,906,907],{},"platformAdmin\u002Fmutations.ts",") verlangen allesamt ",[28,910,716],{}," als aktuellen Status und schreiben eine Audit-Zeile ",[28,913,914],{},"platform_admin.content_approved"," oder ",[28,917,918],{},"platform_admin.content_rejected",[73,920,921,930],{},[76,922,923],{},[79,924,925,927],{},[82,926,746],{},[82,928,929],{},"Wirkung auf die Ressource",[95,931,932,946,963],{},[79,933,934,939],{},[100,935,936],{},[28,937,938],{},"approveCampaign",[100,940,941,942,945],{},"Kampagne → ",[28,943,944],{},"draft"," (der Eigentümer kann sie dann versenden)",[79,947,948,953],{},[100,949,950],{},[28,951,952],{},"approveTransactional",[100,954,955,956,959,960,410],{},"Transaktionsmail → ",[28,957,958],{},"published"," (setzt ",[28,961,962],{},"publishedAt",[79,964,965,970],{},[100,966,967],{},[28,968,969],{},"rejectContent",[100,971,941,972,974,975,977],{},[28,973,944],{}," bzw. Transaktionsmail → ",[28,976,944],{},"; der Grund wird festgehalten",[11,979,980,981,983],{},"Beide Freigabepfade und der Ablehnungspfad holen die Ressource aus der Warteschlange; es gibt keinen separaten terminalen Zustand „blockiert“ für geprüfte Inhalte — eine Ablehnung führt zurück auf ",[28,982,944],{},", wobei der Grund im Audit-Log festgehalten wird.",[57,985,987],{"id":986},"walker-für-die-organisationslöschung","Walker für die Organisationslöschung",[11,989,990,991,816,994,997,998,1001,1002,1005,1006,1009,1010,243],{},"Die Organisation zu löschen ist ein harter, unumkehrbarer Wipe der gesamten Data Plane. Er ist dem Eigentümer vorbehalten: ",[28,992,993],{},"organizationSettings.remove",[28,995,996],{},"apps\u002Fapi\u002Fconvex\u002Forganizations\u002Fsettings.ts",") prüft ",[28,999,1000],{},"session.role === 'owner'"," und plant ",[28,1003,1004],{},"internal.organizations.deletion.walker.start"," ein. Der Walker liegt in ",[28,1007,1008],{},"apps\u002Fapi\u002Fconvex\u002Forganizations\u002Fdeletion\u002Fwalker.ts"," und setzt ADR-0025 um (",[28,1011,1012],{},"docs\u002Fadr\u002F0025-organization-deletion-module-family.md",[20,1014,1017],{"title":1015,"type":1016},"Unumkehrbar","danger",[11,1018,1019,1022,1023,1025],{},[28,1020,1021],{},"remove"," löscht jede Tabelle pro Organisation, einschließlich Storage-Blobs, Audit-Logs und schließlich der Singleton-Zeile ",[28,1024,30],{},", die die Existenz der Organisation trägt. Es gibt kein Soft-Delete und keine Wiederherstellung außer einem Datenbank-Restore.",[11,1027,1028],{},"Funktionsweise:",[172,1030,1031,1058,1096],{},[175,1032,1033,1036,1037,1040,1041,236,1044,236,1047,1050,1051,1054,1055,1057],{},[40,1034,1035],{},"Geordnete Kaskade."," ",[28,1038,1039],{},"STEPS"," ist eine geordnete Liste des vollständigen Tabellensatzes pro Organisation, ~90 Tabellen. Kinder werden vor Eltern gelöscht; Tabellen mit Storage-Bezug (z. B. ",[28,1042,1043],{},"mediaAssets",[28,1045,1046],{},"semanticFiles",[28,1048,1049],{},"mailMessages",") räumen ihre Blobs vor dem Löschen der Zeile ab; ",[28,1052,1053],{},"auditLogs"," steht an vorletzter Stelle (es sammelt während des Wipes weiterhin Einträge aus delegierten Lifecycle-Aufrufen an); ",[28,1056,30],{}," ist der abschließende Schritt.",[175,1059,1060,1063,1064,1067,1068,1071,1072,1075,1076,1079,1080,236,1083,236,1086,236,1089,236,1092,1095],{},[40,1061,1062],{},"Ein Modul pro Tabelle."," Jeder Eintrag hat ein Geschwister-Step-Modul unter ",[28,1065,1066],{},"deletion\u002Fsteps\u002F\u003Ctable>.ts",", das den Contract ",[28,1069,1070],{},"deleteBatch(ctx)"," aus ",[28,1073,1074],{},"steps\u002F_common.ts"," implementiert (Standard-Batchgröße 100). Einige Tabellen delegieren — so fegt der Step ",[28,1077,1078],{},"contacts"," fünf Kindtabellen mit (",[28,1081,1082],{},"contactTopics",[28,1084,1085],{},"contactPropertyValues",[28,1087,1088],{},"contactActivities",[28,1090,1091],{},"contactIdentities",[28,1093,1094],{},"contactRelationships","), die keine eigenständigen Steps sind.",[175,1097,1098,1036,1101,1104,1105,1108,1109,1112,1113,1115,1116,1119],{},[40,1099,1100],{},"Selbstgeplanter Hop.",[28,1102,1103],{},"runStep"," führt einen Batch aus, feuert sich selbst erneut, solange ",[28,1106,1107],{},"hasMore"," wahr ist, rückt auf ",[28,1110,1111],{},"nextTable"," vor, wenn ein Step leerläuft, und endet, wenn es keinen nächsten Step gibt. Das Argument ",[28,1114,73],{}," wird gegen die Literal-Union in ",[28,1117,1118],{},"_common.ts"," validiert, sodass ein Tippfehler ein Compile- und Boot-Zeit-Fehler ist statt eines stillen No-Ops.",[11,1121,1122,1123,1126],{},"Jede neue Tabelle pro Organisation, die dem Schema hinzugefügt wird, muss außerdem ein Literal in ",[28,1124,1125],{},"OrganizationDeletionTable"," und ein Geschwister-Step-Modul ergänzen, sonst lässt die Löschkaskade sie verwaist zurück.",[57,1128,1130],{"id":1129},"systemzustand-und-in-app-selbstupdate-updater-sidecar","Systemzustand und In-App-Selbstupdate (Updater-Sidecar)",[11,1132,1133,1134,47,1137,1139],{},"Die Seite ",[40,1135,1136],{},"System & Updates",[28,1138,50],{},", nur für Plattform-Admins) zeigt die aktuelle Version, den Container-Zustand, die GitHub-Release-Prüfung, den Update-Ablauf und die Update-Historie.",[245,1141,1143],{"id":1142},"update-prüfung","Update-Prüfung",[11,1145,1146,1149,1150,1153,1154,47,1157,1160,1161,1164,1165,1168],{},[28,1147,1148],{},"apps\u002Fapi\u002Fconvex\u002FsystemUpdates.ts"," fragt die GitHub-Releases-API ab (",[28,1151,1152],{},"https:\u002F\u002Fapi.github.com\u002Frepos\u002Fwolvesdotink\u002Fowlat\u002Freleases\u002Flatest","), cacht das Ergebnis in einer Singleton-Zeile ",[28,1155,1156],{},"systemUpdates",[28,1158,1159],{},"kind: 'latestCheck'",") mit einer TTL von 1 Stunde und berechnet ",[28,1162,1163],{},"updateAvailable",", indem es die gecachte neueste Version mit der laufenden ",[28,1166,1167],{},"OWLAT_VERSION"," vergleicht.",[73,1170,1171,1182],{},[76,1172,1173],{},[79,1174,1175,1177,1179],{},[82,1176,492],{},[82,1178,495],{},[82,1180,1181],{},"Hinweise",[95,1183,1184,1201,1213],{},[79,1185,1186,1191,1194],{},[100,1187,1188],{},[28,1189,1190],{},"checkForUpdates",[100,1192,1193],{},"Action",[100,1195,1196,1197,1200],{},"Admin-gesichert; respektiert den 1-Stunden-Cache, sofern nicht ",[28,1198,1199],{},"force: true","; liefert bei GitHub-Rate-Limit 403\u002F429 den Cache zurück",[79,1202,1203,1208,1210],{},[100,1204,1205],{},[28,1206,1207],{},"getLatestRelease",[100,1209,611],{},[100,1211,1212],{},"Liest die gecachte Zeile der letzten Prüfung",[79,1214,1215,1220,1222],{},[100,1216,1217],{},[28,1218,1219],{},"listUpdateHistory",[100,1221,611],{},[100,1223,1224,1225,1228],{},"Listet Zeilen mit ",[28,1226,1227],{},"kind: 'updateRun'"," (neueste zuerst; auf 200 begrenzt)",[11,1230,1231,1232,1235],{},"Ein lokaler Build, der ",[28,1233,1234],{},"OWLAT_VERSION=dev"," (oder einen Nicht-Semver-Wert) meldet, gilt immer als „kein Update verfügbar“, weil Owlat nicht feststellen kann, ob ein Dev-Build dem Release-Tag voraus oder hinterher ist.",[245,1237,1239],{"id":1238},"ein-update-anwenden","Ein Update anwenden",[11,1241,1242,1243,1246,1247,1250],{},"Ein Klick auf ",[40,1244,1245],{},"Jetzt aktualisieren"," postet an die Nitro-Route ",[28,1248,1249],{},"apps\u002Fweb\u002Fserver\u002Fapi\u002Fsystem\u002Fupdate.post.ts",", die:",[1252,1253,1254,1258,1265,1269,1284,1288,1306,1310,1320,1324],"steps",{},[245,1255,1257],{"id":1256},"den-aufrufer-verifizieren","Den Aufrufer verifizieren",[11,1259,1260,1261,1264],{},"Die Route prüft den Plattform-Admin-Status über das Session-Cookie erneut und verlangt, dass ",[28,1262,1263],{},"INSTANCE_SECRET"," konfiguriert ist.",[245,1266,1268],{"id":1267},"die-gepinnte-compose-datei-herunterladen-und-verifizieren","Die gepinnte Compose-Datei herunterladen und verifizieren",[11,1270,1271,1272,1275,1276,1279,1280,1283],{},"Sie holt ",[28,1273,1274],{},"docker-compose-\u003Cversion>.yml"," und das zugehörige ",[28,1277,1278],{},".sha256","-Manifest vom GitHub-Release, verifiziert den SHA-256-Hash und bestätigt, dass der Inhalt ",[28,1281,1282],{},"ghcr.io\u002Fwolvesdotink\u002Fweb:\u003Cversion>"," referenziert. Ein Hash-Mismatch bricht das Update ab.",[245,1285,1287],{"id":1286},"den-versuch-protokollieren","Den Versuch protokollieren",[11,1289,1290,1291,1294,1295,1298,1299,201,1302,1305],{},"Sie hält über ",[28,1292,1293],{},"recordUpdateStart"," eine ",[28,1296,1297],{},"updateRun","-Zeile fest und erfasst dabei ",[28,1300,1301],{},"versionFrom",[28,1303,1304],{},"versionTo",".",[245,1307,1309],{"id":1308},"an-den-updater-sidecar-übergeben","An den Updater-Sidecar übergeben",[11,1311,1312,1313,1316,1317,1305],{},"Sie POSTet das verifizierte Compose-Template mit dem Header ",[28,1314,1315],{},"X-Instance-Secret"," an ",[28,1318,1319],{},"http:\u002F\u002Fupdater:3200\u002Fupdate",[245,1321,1323],{"id":1322},"das-ergebnis-protokollieren","Das Ergebnis protokollieren",[11,1325,1326,1327,1330,1331,201,1334,1337],{},"Sie hält ",[28,1328,1329],{},"recordUpdateFinish"," mit ",[28,1332,1333],{},"success",[28,1335,1336],{},"failed"," und der Ausgabe des Schritts fest. Beide Protokoll-Mutationen geben zusätzlich eine strukturierte JSON-Logzeile für externe Log-Senken aus.",[245,1339,1341],{"id":1340},"der-updater-sidecar","Der Updater-Sidecar",[11,1343,1344,1345,1348,1349,1352,1353,1356,1357,1359],{},"Der Updater (",[28,1346,1347],{},"apps\u002Fupdater\u002Fsrc\u002Findex.ts",", Image ",[28,1350,1351],{},"ghcr.io\u002Fwolvesdotink\u002Fupdater",", lauscht auf Port 3200, ausschließlich in einem internen Docker-Netzwerk exponiert (kein Host-Port-Mapping), sodass ihn nur der Web-Container über ",[28,1354,1355],{},"http:\u002F\u002Fupdater:3200"," erreichen kann) ist die einzige Komponente, die Docker anfasst. Er authentifiziert jede Anfrage mit einem timing-sicheren Vergleich von ",[28,1358,1315],{}," und erreicht Docker über einen Socket-Proxy nach dem Least-Privilege-Prinzip (er kann Container pullen\u002Fneu erstellen\u002Fauflisten, aber nicht exec ausführen, bauen oder Volumes anfassen).",[73,1361,1362,1374],{},[76,1363,1364],{},[79,1365,1366,1369,1372],{},[82,1367,1368],{},"Endpunkt",[82,1370,1371],{},"Methode",[82,1373,498],{},[95,1375,1376,1404,1417,1433],{},[79,1377,1378,1383,1386],{},[100,1379,1380],{},[28,1381,1382],{},"\u002Fupdate",[100,1384,1385],{},"POST",[100,1387,1388,1389,1392,1393,1396,1397,1400,1401],{},"Das Compose-Template gegen eine Image-Allowlist sowie Regeln zu gefährlichen Mounts\u002FPrivileged-Mode validieren, es schreiben, ",[28,1390,1391],{},"docker compose pull",", einen einmaligen ",[28,1394,1395],{},"convex-deploy"," ausführen (Funktions-Deploy ",[15,1398,1399],{},"vor"," dem Neustart — ein fehlerhaftes Schema bricht hier ab und die alten Container bedienen weiter), dann ",[28,1402,1403],{},"docker compose up -d",[79,1405,1406,1411,1414],{},[100,1407,1408],{},[28,1409,1410],{},"\u002Fhealth",[100,1412,1413],{},"GET",[100,1415,1416],{},"Containerzustand, Image-Tag und Health pro Service (authentifizierungspflichtig, um Enumeration zu verhindern)",[79,1418,1419,1424,1426],{},[100,1420,1421],{},[28,1422,1423],{},"\u002Fconfigure-ip",[100,1425,1385],{},[100,1427,1428,1429,1432],{},"Eine Floating-IPv4 an ",[28,1430,1431],{},"eth0"," und den Kampagnen-IP-Pool an-\u002Fabhängen, dann den MTA neu starten",[79,1434,1435,1440,1442],{},[100,1436,1437],{},[28,1438,1439],{},"\u002Frotate-env",[100,1441,1385],{},[100,1443,1444,1445,1448],{},"Rotierte Secrets in ",[28,1446,1447],{},".env"," neu schreiben und Container erzwungen neu erstellen (verlangt alle Secret-Felder; strengstes Rate-Limit)",[11,1450,1451,1452,1454,1455,1458,1459,1461],{},"Die Seite System & Updates liest ",[28,1453,1410],{}," (über ",[28,1456,1457],{},"apps\u002Fweb\u002Fserver\u002Fapi\u002Finternal\u002Fupdater-health.get.ts"," weitergereicht), um die Tabelle zum Container-Zustand zu rendern. Die Reihenfolge ",[28,1460,1395],{}," vor Neustart ist die Sicherheitseigenschaft: Ein inkompatibles Schema lässt den Deploy-Schritt scheitern, und die laufenden Web-\u002FMTA-Container werden nie gegen ein halb deploytes Backend neu gestartet.",[20,1463,1465],{"title":1464,"type":35},"Vom Betreiber bereitgestellte Distribution",[11,1466,1467,1468,1471,1472,1305],{},"Der In-App-Updater wendet Images und Compose-Templates aus der GitHub-Release-Pipeline an; er ist der unterstützte Weg für Selbstupdates. Der Auto-Update-Schlüssel der ",[15,1469,1470],{},"Desktop","-App ist ein leerer Platzhalter, daher ist das Auto-Update auf dem Desktop nicht produktionsreif und die Desktop-Distribution stellt der Betreiber bereit — siehe ",[419,1473,1475],{"href":1474},"\u002Fguide\u002Fdesktop-app","Desktop-App",[11,1477,1478,1479,1305],{},"Das betreiberseitige Playbook für Updates (Rollback, Wiederherstellung, Automatisierung) finden Sie unter ",[419,1480,1482],{"href":1481},"\u002Fdeveloper\u002Fself-hosting-maintenance","Wartung & Updates",[57,1484,1486],{"id":1485},"dev-mode-endpunkte-crons-und-migrationen","Dev-Mode-Endpunkte, Crons und Migrationen",[245,1488,1490],{"id":1489},"dev-mode-endpunkte","Dev-Mode-Endpunkte",[11,1492,1493,1494,1497,1498,1501,1502,1505,1506,1509,1510,1513,1514,1517],{},"Für die lokale Entwicklung existiert eine kleine Menge destruktiver Abkürzungen. Sie sind ",[40,1495,1496],{},"fail-closed",": Der Guard in ",[28,1499,1500],{},"apps\u002Fapi\u002Fconvex\u002FdevShortcuts\u002F_guard.ts"," behandelt das Deployment als Produktion, sofern der Betreiber nicht ausdrücklich ",[28,1503,1504],{},"OWLAT_DEV_MODE=true"," in der Laufzeitumgebung des Convex-Backends setzt (",[28,1507,1508],{},"npx convex env set OWLAT_DEV_MODE true","). Die CLI-seitige Umgebungsvariable ",[28,1511,1512],{},"CONVEX_DEPLOYMENT"," wird ",[15,1515,1516],{},"nicht"," in die Function-Runtime propagiert und taugt daher nicht als Sicherheitsgrenze.",[73,1519,1520,1531],{},[76,1521,1522],{},[79,1523,1524,1526,1529],{},[82,1525,1368],{},[82,1527,1528],{},"Guard",[82,1530,498],{},[95,1532,1533,1555,1571],{},[79,1534,1535,1540,1548],{},[100,1536,1537],{},[28,1538,1539],{},"POST \u002Fdev\u002Freset",[100,1541,1542,1545,1546],{},[28,1543,1544],{},"OWLAT_DEV_MODE"," + ",[28,1547,1315],{},[100,1549,1550,1551,1554],{},"Die Instanz auf einen leeren Stand zurücksetzen (Mandantentabellen, BetterAuth-Tabellen und lokale Auth-Tabellen), damit der Signup-Flow ohne ",[28,1552,1553],{},"docker compose down -v"," erneut durchlaufen werden kann; idempotent",[79,1556,1557,1562,1568],{},[100,1558,1559],{},[28,1560,1561],{},"POST \u002Fseed\u002Fdemo",[100,1563,1564,1545,1566],{},[28,1565,1544],{},[28,1567,1315],{},[100,1569,1570],{},"Realistische Demo-Inhalte einspielen (idempotent)",[79,1572,1573,1578,1585],{},[100,1574,1575],{},[28,1576,1577],{},"forceVerifyDomain",[100,1579,1580,1545,1582],{},[28,1581,1544],{},[28,1583,1584],{},"organization:manage",[100,1586,1587,1588,1591],{},"Eine Domain mit synthetisierten DNS-Ergebnissen zwangsweise auf ",[28,1589,1590],{},"verified"," setzen und dabei Live-DNS-Abfragen umgehen",[20,1593,1595],{"title":1594,"type":1016},"OWLAT_DEV_MODE niemals in Produktion aktivieren",[11,1596,1597,1600,1601,1603],{},[28,1598,1599],{},"\u002Fdev\u002Freset"," löscht alles auf Mandantenseite — nicht nur Zeilen mit Seed-Tag. Lassen Sie ",[28,1602,1544],{}," auf jedem Deployment ungesetzt, das echte Daten hält.",[11,1605,1606,1607,47,1610,1613,1614,1616,1617,1620,1621,1305],{},"Der Wunsch nach Demo-Inhalten ist kein Grund, ihn zu aktivieren. ",[28,1608,1609],{},"POST \u002Fsample-data\u002F{install,remove,status}",[28,1611,1612],{},"apps\u002Fapi\u002Fconvex\u002FsampleData\u002F",") spielt denselben Datensatz abzüglich der Dummy-Anmeldungen und Postfächer ein, wird allein mit ",[28,1615,1315],{}," authentifiziert und entfernt exakt das, was es eingefügt hat (auf ",[28,1618,1619],{},"seedTag"," eingegrenzt, innerhalb der Löschtransaktion erneut geprüft). Steuern Sie ihn mit ",[28,1622,1623],{},"owlat sample-data \u003Cinstall|remove|status>",[245,1625,1627],{"id":1626},"crons","Crons",[11,1629,1630,1631,1633],{},"Geplante Jobs sind in ",[28,1632,429],{}," registriert. Operativ relevant sind unter anderem:",[73,1635,1636,1649],{},[76,1637,1638],{},[79,1639,1640,1643,1646],{},[82,1641,1642],{},"Cron",[82,1644,1645],{},"Intervall",[82,1647,1648],{},"Job",[95,1650,1651,1666,1679,1690,1701,1711,1721,1731,1742,1752],{},[79,1652,1653,1656,1659],{},[100,1654,1655],{},"process scheduled campaigns",[100,1657,1658],{},"1 Min.",[100,1660,1661,1662,1665],{},"Absichernder Versand für Kampagnen, deren ",[28,1663,1664],{},"scheduledAt"," verstrichen ist",[79,1667,1668,1671,1673],{},[100,1669,1670],{},"reconcile sending campaigns",[100,1672,1658],{},[100,1674,1675,1678],{},[28,1676,1677],{},"sending → sent"," fortschreiben, wenn keine eingereihten Sendungen mehr offen sind",[79,1680,1681,1684,1687],{},[100,1682,1683],{},"process account deletions",[100,1685,1686],{},"24 Std.",[100,1688,1689],{},"Konten nach Ablauf ihrer 30-tägigen Karenzzeit verarbeiten",[79,1691,1692,1695,1698],{},[100,1693,1694],{},"cleanup webhook logs",[100,1696,1697],{},"wöchentlich",[100,1699,1700],{},"Zustellprotokolle löschen, die älter als 30 Tage sind",[79,1702,1703,1705,1708],{},[100,1704,439],{},[100,1706,1707],{},"1 Std.",[100,1709,1710],{},"Reputations-Buckets älter als 60 Tage ausaltern lassen (beide Scopes)",[79,1712,1713,1716,1718],{},[100,1714,1715],{},"cleanup soft-deleted contacts",[100,1717,1686],{},[100,1719,1720],{},"Kontakte nach Ablauf ihrer 30-tägigen Aufbewahrung endgültig löschen",[79,1722,1723,1726,1728],{},[100,1724,1725],{},"knowledge graph maintenance",[100,1727,1686],{},[100,1729,1730],{},"Konfidenzabfall + Bereinigung abgelaufener Einträge",[79,1732,1733,1736,1739],{},[100,1734,1735],{},"channel health checks",[100,1737,1738],{},"5 Min.",[100,1740,1741],{},"Channel-Konnektivität prüfen",[79,1743,1744,1747,1749],{},[100,1745,1746],{},"agent metrics rollup",[100,1748,1738],{},[100,1750,1751],{},"Queue-Tiefe\u002FLatenz\u002FFehlerraten berechnen und Circuit Breaker auswerten",[79,1753,1754,1757,1760],{},[100,1755,1756],{},"report analytics",[100,1758,1759],{},"15 Min.",[100,1761,1762],{},"Instanz-Analytics an die Control Plane melden",[11,1764,1765,1766,236,1769,236,1772,236,1775,1778],{},"Die Crons zum Zählerabgleich (",[28,1767,1768],{},"reconcile contact counts",[28,1770,1771],{},"reconcile topic member counts",[28,1773,1774],{},"refresh segment counts",[28,1776,1777],{},"reconcile transactional send counts",") halten denormalisierte Zähler gegenüber jeglicher Drift aus Teilfehlern ehrlich.",[245,1780,1782],{"id":1781},"migrationen","Migrationen",[11,1784,1785,1786,1789,1790,1793,1794,1797,1798,1305],{},"Schema-\u002FDatenmigrationen sind einmalige interne Mutationen unter ",[28,1787,1788],{},"apps\u002Fapi\u002Fconvex\u002Fmigrations\u002F",", die jeweils eine ",[28,1791,1792],{},"run","-Mutation exportieren. Sie folgen dem Pre-Prod-Muster der „atomaren Breaking Change“: Jede Migration ist ",[40,1795,1796],{},"idempotent"," (ein erneuter Lauf ist ein No-Op, sobald jede Zeile umgeschrieben ist), und weil Deployments einorganisatorisch und zurücksetzbar sind, laufen die meisten synchron gegen ",[28,1799,1800],{},".collect()",[11,1802,1803],{},"Sie rufen eine Migration namentlich über die Convex-CLI auf, zum Beispiel:",[822,1805,1809],{"className":1806,"code":1807,"language":1808,"meta":827,"style":827},"language-sh shiki shiki-themes github-light github-dark-dimmed","npx convex run migrations\u002F0033_campaign_audience:run\n","sh",[28,1810,1811],{"__ignoreMap":827},[831,1812,1813,1816,1819,1822],{"class":833,"line":834},[831,1814,1815],{"class":844},"npx",[831,1817,1818],{"class":851}," convex",[831,1820,1821],{"class":851}," run",[831,1823,1824],{"class":851}," migrations\u002F0033_campaign_audience:run\n",[20,1826,1828],{"title":1827,"type":23},"In der Pre-Prod lieber zurücksetzen als nachziehen",[11,1829,1830],{},"Owlats Konvention sind saubere Breaking Changes plus ein Daten-Reset statt der Zeremonie zweiphasiger, rückwärtskompatibler Migrationen. Mehrere Migrationen halten fest, dass sie auf einem frisch eingespielten Deployment keine Altzeilen vorfinden und lediglich eine idempotente Absichtserklärung sind — sollte ein Deployment doch einmal Altzeilen tragen, die eine Migration nicht abbilden kann, wirft die Migration lautstark, statt Daten stillschweigend zu verwerfen.",[57,1832,1834],{"id":1833},"verwandte-seiten","Verwandte Seiten",[1836,1837],"link-card",{"description":1838,"title":1482,"to":1481},"Das Betreiber-Playbook für Updates, Rollback und Wiederherstellung nach einem fehlgeschlagenen Update.",[1836,1840],{"description":1841,"title":422,"to":421},"Wie die Sende-Reputation abgeleitet wird und wie Risikostufen die Auto-Durchsetzung bei Missbrauch steuern.",[1836,1843],{"description":1844,"title":1845,"to":1846},"Die Backend-Konventionen, auf denen diese Betriebsmodule aufbauen.","Convex-Backend","\u002Fdeveloper\u002Fconvex",[1836,1848],{"description":1849,"title":1850,"to":1851},"Wie Laufzeit-Features auf einem selbst gehosteten Deployment auf Docker-Profile abgebildet werden.","Feature-Flags","\u002Fdeveloper\u002Ffeature-flags",[1853,1854,1855],"style",{},"html pre.shiki code .sYgZi, html code.shiki .sYgZi{--shiki-default:#24292E;--shiki-dark:#ADBAC7}html pre.shiki code .sOLd2, html code.shiki .sOLd2{--shiki-default:#6F42C1;--shiki-dark:#F69D50}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 pre.shiki code .sviXB, html code.shiki .sviXB{--shiki-default:#005CC5;--shiki-dark:#6CB6FF}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);}",{"title":827,"searchDepth":841,"depth":841,"links":1857},[1858,1862,1866,1867,1868,1878,1883],{"id":59,"depth":841,"text":60,"children":1859},[1860,1861],{"id":247,"depth":880,"text":248},{"id":334,"depth":880,"text":335},{"id":450,"depth":841,"text":451,"children":1863},[1864,1865],{"id":587,"depth":880,"text":588},{"id":729,"depth":880,"text":730},{"id":798,"depth":841,"text":799},{"id":986,"depth":841,"text":987},{"id":1129,"depth":841,"text":1130,"children":1869},[1870,1871,1872,1873,1874,1875,1876,1877],{"id":1142,"depth":880,"text":1143},{"id":1238,"depth":880,"text":1239},{"id":1256,"depth":880,"text":1257},{"id":1267,"depth":880,"text":1268},{"id":1286,"depth":880,"text":1287},{"id":1308,"depth":880,"text":1309},{"id":1322,"depth":880,"text":1323},{"id":1340,"depth":880,"text":1341},{"id":1485,"depth":841,"text":1486,"children":1879},[1880,1881,1882],{"id":1489,"depth":880,"text":1490},{"id":1626,"depth":880,"text":1627},{"id":1781,"depth":880,"text":1782},{"id":1833,"depth":841,"text":1834},"Betreiberreferenz zu Abuse-Status und Sende-Gate, zum Plattform-Admin-Verzeichnis, zur Inhaltsprüfung, zur Organisationslöschung, zum In-App-Selbstupdate, zu Dev-Endpunkten, Crons und Migrationen.","md",{},true,"\u002Fdeveloper\u002Fplatform-operations",{"title":6,"description":1884},"3.developer\u002F20.platform-operations","P3ZK5koiGkoBSJK3bSzDCSm4HiWgLKjUHFSYbwaS9Gk",[1893,1897],{"title":1894,"path":1895,"stem":1896,"children":-1},"Architekturüberblick","\u002Fdeveloper\u002Farchitecture","3.developer\u002F2.architecture",{"title":1898,"path":1899,"stem":1900,"children":-1},"Sealed Mail: Nachrichtentexte im Ruhezustand","\u002Fdeveloper\u002Fsealed-mail-at-rest","3.developer\u002F21.sealed-mail-at-rest",1786915110259]