[{"data":1,"prerenderedAt":3280},["ShallowReactive",2],{"search-de":3,"content-de-developer\u002Fproviders":4,"surround-de-\u002Fdeveloper\u002Fproviders":3271},[],{"id":5,"title":6,"body":7,"description":3264,"extension":3265,"meta":3266,"navigation":395,"path":3267,"seo":3268,"stem":3269,"__hash__":3270},"content_de\u002F3.developer\u002F15.providers.md","Provider",{"type":8,"value":9,"toc":3244},"minimark",[10,19,22,45,80,85,92,102,135,161,164,177,191,198,205,409,418,469,472,559,587,603,606,636,664,673,678,681,1099,1110,1113,1200,1258,1318,1322,1331,1335,1444,1448,1543,1547,1557,1723,1734,1738,1748,1973,2038,2042,2051,2081,2117,2146,2152,2191,2195,2198,2205,2252,2270,2294,2298,2315,2323,2327,2361,2365,2376,2434,2820,2864,2871,2962,2995,3017,3039,3115,3119,3240],[11,12,13,14,18],"p",{},"Überall dort, wo Owlat mit einem externen System spricht — einem LLM, einem E-Mail-Delivery-Backend, einem Notification-Transport, einem Vector Store, einer Analytics-Senke —, führt der Weg über eine ",[15,16,17],"strong",{},"Provider-Abstraktion",". Die meisten sind Factories, die eine einzige Env-Variable lesen, um eine Implementierung auszuwählen, sie pro Prozess cachen und dem Rest der Codebase eine kleine, stabile Schnittstelle anbieten.",[11,20,21],{},"Der Sinn: Self-Hoster tauschen Provider ohne Code-Änderungen, und wir können neue hinzufügen, ohne jede Aufrufstelle anzufassen.",[11,23,24,25,28,29,32,33,37,38,40,41,44],{},"Zwei austauschbare Provider-Abstraktionen werden ausgeliefert: der ",[15,26,27],{},"LLM-Provider"," und der ",[15,30,31],{},"Send-Provider (E-Mail)",", beide vollständig vom Backend genutzt (Agent-Schritte, Knowledge-Extraktion, Übersetzung, jeder Versandpfad). Der LLM-Provider liest eine Env-Variable (",[34,35,36],"code",{},"LLM_PROVIDER",") und cacht den aufgelösten Client pro Prozess. Der ",[15,39,31],{}," ist die Ausnahme: Er verwendet eine statische Registry, die nach der Provider-Art (",[34,42,43],{},"kind",", pro Aufruf übergeben) geschlüsselt ist, statt eine Env-Variable zu lesen und die aufgelöste Instanz zu cachen. Auf diesen Unterschied wird unten eingegangen.",[46,47,50],"callout",{"title":48,"type":49},"Keine Provider-Abstraktion für Notifications, Vector Store oder Analytics","info",[11,51,52,53,56,57,60,61,64,65,68,69,72,73,72,76,79],{},"Frühere Fassungen dieser Seite beschrieben Factories für ",[15,54,55],{},"Notification-",", ",[15,58,59],{},"Vector-Store-"," und **Analytics-**Provider. Das waren ungenutzte spekulative Nahtstellen und sie wurden ",[15,62,63],{},"entfernt",". Die Runtime behandelt diese Belange stattdessen direkt: Benachrichtigungen sind Client-verwaltet, die Knowledge-Retrieval fragt den in Convex eingebauten Vector-Index ab, und Produkt-Analytics laufen über ",[34,66,67],{},"lib\u002Fposthog.ts",". Es gibt keine Env-Variablen ",[34,70,71],{},"NOTIFICATION_PROVIDER"," \u002F ",[34,74,75],{},"VECTOR_STORE",[34,77,78],{},"ANALYTICS_PROVIDER",".",[81,82,84],"h2",{"id":83},"aufbau-der-factories","Aufbau der Factories",[11,86,87,88,91],{},"Die beiden ausgelieferten Abstraktionen liegen unter ",[34,89,90],{},"apps\u002Fapi\u002Fconvex\u002Flib\u002F",":",[93,94,99],"pre",{"className":95,"code":97,"language":98},[96],"language-text","lib\u002F\n├── llmProvider.ts          ← LLM_PROVIDER — single module\n│                              exports getLLMProvider(task) \u002F getEmbeddingModel() \u002F getLLMConfig()\n├── sendProviders\u002F          ← registry keyed by SendProviderKind (not one env var)\n│   ├── types.ts            ← SendProviderModule\u003CK>, EmailSendAttempt, EmailErrorCode\n│   ├── index.ts            ← SEND_PROVIDERS registry + providerFor(kind)\n│   ├── dispatch.ts         ← sendProviderDispatch() — owns the retry loop\n│   ├── transports.ts       ← transport registry: id → configured instance\n│   ├── transportEnv.ts     ← per-instance configuration reads\n│   ├── routing.ts          ← resolveRoute() — per-org route selection\n│   ├── health.ts           ← providerHealth recording + reads\n│   ├── capability.ts       ← isSendProviderReady() — required env present?\n│   ├── fallbackEligibility.ts ← may this kind be the deliverability-fallback relay?\n│   ├── mta\u002Findex.ts        ← built-in MTA adapter\n│   ├── ses\u002Findex.ts        ← Amazon SES adapter\n│   ├── smtp\u002Findex.ts       ← generic SMTP-relay adapter (SMTP_RELAY_*)\n│   ├── resend\u002Findex.ts     ← Resend adapter\n│   ├── mandrill\u002Findex.ts   ← Mailchimp Transactional adapter (messages\u002Fsend-raw)\n│   └── strategies\u002F         ← single \u002F priority_failover \u002F workload_split \u002F adaptive_mix\n└── emailProviders\u002F         ← identity & domain verification only (NOT the send factory)\n    ├── domainVerification.ts\n    ├── mtaIdentity.ts\n    └── sesIdentity.ts\n","text",[34,100,97],{"__ignoreMap":101},"",[11,103,104,105,108,109,112,113,56,116,56,119,122,123,126,127,130,131,134],{},"Die Identität der Versanddomain (SPF-\u002FDKIM-Registrierung und -Verifizierung beim Provider) ist eine ",[15,106,107],{},"eigene"," Registry unter ",[34,110,111],{},"apps\u002Fapi\u002Fconvex\u002Fdomains\u002Fproviders\u002F",", geschlüsselt nach derselben Art: ",[34,114,115],{},"mta\u002F",[34,117,118],{},"ses\u002F",[34,120,121],{},"mandrill\u002F",", dazu ",[34,124,125],{},"plugin\u002F"," — ein host-eigener Adapter, der jeden gebundelten Plugin-Transport bedient, der eine ",[34,128,129],{},"domainIdentity"," beisteuert. Eine Core-Art taucht dort nur auf, wenn ihr Katalogeintrag ",[34,132,133],{},"domainVerification: 'api'"," deklariert, und ein Mapped-Type-Guard lässt den Build scheitern, wenn eine Art das deklariert, ohne einen Provider zu registrieren; bei einem Plugin-Transport wird dieses Wort aus der Contribution selbst abgeleitet, sodass das Versprechen und der Code, der es hält, eine einzige Deklaration sind.",[11,136,137,138,141,142,72,145,148,149,152,153,156,157,160],{},"Dieser Ordner beantwortet ",[15,139,140],{},"zwei"," Fragen mit zwei Registries. ",[34,143,144],{},"SENDING_DOMAIN_PROVIDERS",[34,146,147],{},"providerFor(kind)"," ist die PRIMÄRE — der Wert, den eine ",[34,150,151],{},"domains","-Zeile in ",[34,154,155],{},"providerType"," festhält und dessen Adapter Registrierung, DNS-Bundle, die Geschwister-Identitätszeile und den Return Path besitzt — und sie bleibt eine geschlossene Core-Union. ",[34,158,159],{},"relayIdentityProviderFor(kind)"," beantwortet die kleinere RELAY-Frage („kann diese Art eine Domain nachweisen, deren Lebenszyklus sie nicht besitzt?\"), gestellt vom Routing-Gate, vom Identity-Backfill, vom Alignment-Pre-Flight und vom Fälligkeits-Sweep; sie wird zur Build-Zeit aus jedem Core-Adapter komponiert, der alle drei Relay-Nahtstellen implementiert, plus einem Eintrag pro gebundelter Plugin-Identität. Die primäre zu erweitern würde den gesamten Lebenszyklus einer Domain durch Code laufen lassen, den dieses Repository nicht enthält.",[81,162,27],{"id":163},"llm-provider",[11,165,166,169,170,172,173,176],{},[15,167,168],{},"Env-Variable:"," ",[34,171,36],{}," (Standard: ",[34,174,175],{},"openai",")",[11,178,179,169,182,184,185,56,188],{},[15,180,181],{},"Unterstützte Werte:",[34,183,175],{}," (Standard), ",[34,186,187],{},"openrouter",[34,189,190],{},"ollama",[11,192,193,194,197],{},"Alle drei sprechen die Form der OpenAI Chat Completions (sodass sich alles OpenAI-Kompatible einklinken lässt — etwa ein selbst gehostetes vLLM oder LM Studio). Jeder Client wird mit ",[34,195,196],{},"createOpenAI"," aus dem Vercel AI SDK gebaut.",[11,199,200,201,204],{},"Die gesamte Abstraktion ist ein einziges Modul, ",[34,202,203],{},"apps\u002Fapi\u002Fconvex\u002Flib\u002FllmProvider.ts",", das eigenständige Funktionen exportiert:",[93,206,210],{"className":207,"code":208,"language":209,"meta":101,"style":101},"language-ts shiki shiki-themes github-light github-dark-dimmed","\u002F\u002F apps\u002Fapi\u002Fconvex\u002Flib\u002FllmProvider.ts\nexport function getLLMProvider(task: LLMTask): LanguageModel; \u002F\u002F returns AI SDK LanguageModel\nexport function getEmbeddingModel(): EmbeddingModel;\nexport function getLLMConfig(): {\n    provider: string;\n    modelFast: string;\n    modelCapable: string;\n    embeddingModel: string;\n    baseURL: string | undefined;\n    hasApiKey: boolean; \u002F\u002F snapshot — never the key itself — safe to log\n};\n\n\u002F\u002F task tiers — classify\u002Fextract\u002Fguard\u002Fsummarize → fast model\n\u002F\u002F              draft\u002Fplan                       → capable model\n","ts",[34,211,212,221,262,283,300,314,326,338,350,368,384,390,397,403],{"__ignoreMap":101},[213,214,217],"span",{"class":215,"line":216},"line",1,[213,218,220],{"class":219},"sDN9O","\u002F\u002F apps\u002Fapi\u002Fconvex\u002Flib\u002FllmProvider.ts\n",[213,222,224,228,231,235,239,243,245,249,251,253,256,259],{"class":215,"line":223},2,[213,225,227],{"class":226},"s7YZ4","export",[213,229,230],{"class":226}," function",[213,232,234],{"class":233},"sPO5f"," getLLMProvider",[213,236,238],{"class":237},"sYgZi","(",[213,240,242],{"class":241},"stnAF","task",[213,244,91],{"class":226},[213,246,248],{"class":247},"sOLd2"," LLMTask",[213,250,176],{"class":237},[213,252,91],{"class":226},[213,254,255],{"class":247}," LanguageModel",[213,257,258],{"class":237},"; ",[213,260,261],{"class":219},"\u002F\u002F returns AI SDK LanguageModel\n",[213,263,265,267,269,272,275,277,280],{"class":215,"line":264},3,[213,266,227],{"class":226},[213,268,230],{"class":226},[213,270,271],{"class":233}," getEmbeddingModel",[213,273,274],{"class":237},"()",[213,276,91],{"class":226},[213,278,279],{"class":247}," EmbeddingModel",[213,281,282],{"class":237},";\n",[213,284,286,288,290,293,295,297],{"class":215,"line":285},4,[213,287,227],{"class":226},[213,289,230],{"class":226},[213,291,292],{"class":233}," getLLMConfig",[213,294,274],{"class":237},[213,296,91],{"class":226},[213,298,299],{"class":237}," {\n",[213,301,303,306,308,312],{"class":215,"line":302},5,[213,304,305],{"class":241},"    provider",[213,307,91],{"class":226},[213,309,311],{"class":310},"sviXB"," string",[213,313,282],{"class":237},[213,315,317,320,322,324],{"class":215,"line":316},6,[213,318,319],{"class":241},"    modelFast",[213,321,91],{"class":226},[213,323,311],{"class":310},[213,325,282],{"class":237},[213,327,329,332,334,336],{"class":215,"line":328},7,[213,330,331],{"class":241},"    modelCapable",[213,333,91],{"class":226},[213,335,311],{"class":310},[213,337,282],{"class":237},[213,339,341,344,346,348],{"class":215,"line":340},8,[213,342,343],{"class":241},"    embeddingModel",[213,345,91],{"class":226},[213,347,311],{"class":310},[213,349,282],{"class":237},[213,351,353,356,358,360,363,366],{"class":215,"line":352},9,[213,354,355],{"class":241},"    baseURL",[213,357,91],{"class":226},[213,359,311],{"class":310},[213,361,362],{"class":226}," |",[213,364,365],{"class":310}," undefined",[213,367,282],{"class":237},[213,369,371,374,376,379,381],{"class":215,"line":370},10,[213,372,373],{"class":241},"    hasApiKey",[213,375,91],{"class":226},[213,377,378],{"class":310}," boolean",[213,380,258],{"class":237},[213,382,383],{"class":219},"\u002F\u002F snapshot — never the key itself — safe to log\n",[213,385,387],{"class":215,"line":386},11,[213,388,389],{"class":237},"};\n",[213,391,393],{"class":215,"line":392},12,[213,394,396],{"emptyLinePlaceholder":395},true,"\n",[213,398,400],{"class":215,"line":399},13,[213,401,402],{"class":219},"\u002F\u002F task tiers — classify\u002Fextract\u002Fguard\u002Fsummarize → fast model\n",[213,404,406],{"class":215,"line":405},14,[213,407,408],{"class":219},"\u002F\u002F              draft\u002Fplan                       → capable model\n",[11,410,411,412,415,416,91],{},"Um Claude-Modelle zu betreiben, richten Sie den OpenAI-Client auf einen OpenAI-kompatiblen Endpunkt — es gibt keinen nativen Anthropic-Provider, und ",[34,413,414],{},"anthropic"," ist kein anerkannter Wert für ",[34,417,36],{},[93,419,423],{"className":420,"code":421,"language":422,"meta":101,"style":101},"language-sh shiki shiki-themes github-light github-dark-dimmed","LLM_PROVIDER=openai\nLLM_BASE_URL=https:\u002F\u002Fapi.anthropic.com\u002Fv1\u002F   # or an OpenAI-compat shim\nLLM_MODEL_CAPABLE=claude-sonnet-4-6\nLLM_MODEL_FAST=claude-haiku-4-5-20251001\n","sh",[34,424,425,436,449,459],{"__ignoreMap":101},[213,426,427,429,432],{"class":215,"line":216},[213,428,36],{"class":237},[213,430,431],{"class":226},"=",[213,433,435],{"class":434},"s-HuK","openai\n",[213,437,438,441,443,446],{"class":215,"line":223},[213,439,440],{"class":237},"LLM_BASE_URL",[213,442,431],{"class":226},[213,444,445],{"class":434},"https:\u002F\u002Fapi.anthropic.com\u002Fv1\u002F",[213,447,448],{"class":219},"   # or an OpenAI-compat shim\n",[213,450,451,454,456],{"class":215,"line":264},[213,452,453],{"class":237},"LLM_MODEL_CAPABLE",[213,455,431],{"class":226},[213,457,458],{"class":434},"claude-sonnet-4-6\n",[213,460,461,464,466],{"class":215,"line":285},[213,462,463],{"class":237},"LLM_MODEL_FAST",[213,465,431],{"class":226},[213,467,468],{"class":434},"claude-haiku-4-5-20251001\n",[11,470,471],{},"Welche Env-Variablen erforderlich sind, hängt vom gewählten Provider ab:",[473,474,475,490],"table",{},[476,477,478],"thead",{},[479,480,481,484,487],"tr",{},[482,483,6],"th",{},[482,485,486],{},"Erforderliche Env",[482,488,489],{},"Optional",[491,492,493,520,541],"tbody",{},[479,494,495,500,509],{},[496,497,498],"td",{},[34,499,175],{},[496,501,502,505,506,176],{},[34,503,504],{},"LLM_API_KEY"," (oder ",[34,507,508],{},"OPENAI_API_KEY",[496,510,511,56,513,56,515,56,518],{},[34,512,463],{},[34,514,453],{},[34,516,517],{},"LLM_EMBEDDING_MODEL",[34,519,440],{},[479,521,522,526,533],{},[496,523,524],{},[34,525,187],{},[496,527,528,505,530,176],{},[34,529,504],{},[34,531,532],{},"OPENROUTER_API_KEY",[496,534,535,56,537,56,539],{},[34,536,463],{},[34,538,453],{},[34,540,440],{},[479,542,543,547,550],{},[496,544,545],{},[34,546,190],{},[496,548,549],{},"—",[496,551,552,554,555,558],{},[34,553,440],{}," (Standard ",[34,556,557],{},"http:\u002F\u002Follama:11434\u002Fv1","), Modell-Overrides",[11,560,561,562,56,564,566,567,569,570,573,574,576,577,579,580,582,583,586],{},"Alle OpenAI-kompatiblen Provider akzeptieren wahlweise ",[34,563,504],{},[34,565,532],{}," oder ",[34,568,508],{}," (die erste gesetzte gewinnt — siehe ",[34,571,572],{},"resolveApiKey()"," in ",[34,575,203],{},"). Die Standard-Base-URL für ",[34,578,190],{}," ist der Docker-Service-Hostname ",[34,581,557],{},", aufgelöst in ",[34,584,585],{},"resolveBaseURL()"," in derselben Datei.",[11,588,589,590,593,594,596,597,599,600,602],{},"Das Feature-Flag ",[34,591,592],{},"ai"," erfordert, dass ",[34,595,36],{}," und ",[34,598,504],{}," gesetzt sind. Die Admin-UI blockiert das Einschalten von ",[34,601,592],{},", solange sie fehlen.",[81,604,31],{"id":605},"send-provider-e-mail",[11,607,608,609,612,613,616,617,619,620,623,624,627,628,631,632,635],{},"Die versandseitige Abstraktion liegt unter ",[34,610,611],{},"apps\u002Fapi\u002Fconvex\u002Flib\u002FsendProviders\u002F"," (gemäß ADR-0020). Anders als die übrigen Factories wird sie ",[15,614,615],{},"nicht"," über das Lesen einer Env-Variable in einen gecachten Singleton ausgewählt — stattdessen ist sie eine statische Registry, geschlüsselt nach der Provider-Art (",[34,618,43],{},"), und die Art wird pro Versand aufgelöst (aus der Route-Konfiguration der Organisation, ersatzweise aus der Env-Variable ",[34,621,622],{},"EMAIL_PROVIDER",", und schließlich ",[15,625,626],{},"unkonfiguriert",", wenn keines von beidem einen Provider benennt). Die Auflösung ist ",[15,629,630],{},"fail-closed",": Es gibt keinen impliziten MTA-Standard, sodass ein Versand, der ohne konfigurierten Provider beim Dispatch ankommt, abgelehnt statt an einen Phantom-MTA übergeben wird. Die Versands-Einstiegspunkte prüfen zuerst die Capability ",[34,633,634],{},"isDeliveryConfigured"," (sodass ein Kampagnen- oder Transaktionsversand abgelehnt wird, bevor irgendeine Zeile geschrieben ist), womit dieser Routing-Fallback Defence-in-Depth ist.",[11,637,638,169,641,56,644,56,647,56,650,56,653,56,656,659,660,663],{},[15,639,640],{},"Unterstützte Arten:",[34,642,643],{},"mta",[34,645,646],{},"ses",[34,648,649],{},"resend",[34,651,652],{},"smtp",[34,654,655],{},"mandrill",[34,657,658],{},"emailit"," — in Katalogreihenfolge. ",[34,661,662],{},"SEND_TRANSPORT_KINDS",", Setup-Oberflächen, Laufzeit-Komposition und Conformance-Suites leiten sich alle aus dieser Deklaration ab. Kuratierter Picker-Text darf vertraute Provider zuerst anordnen; ein Eintrag ohne eigenen Text wird in Katalogreihenfolge angehängt.",[11,665,666,668,669,672],{},[34,667,643],{}," ist der eingebaute Sender, der mit dem Self-Hosting ausgeliefert wird (",[34,670,671],{},"apps\u002Fmta","). Die Alternativen sind natives Amazon SES, generische SMTP-Submission, Resend, Mailchimp Transactional und Emailit. Jede hat dieselbe komponierte Bundle-Form: Transport plus optional Feedback, Domain-Identität, Setup und Plattform-Hooks.",[674,675,677],"h3",{"id":676},"deklarierte-capabilities","Deklarierte Capabilities",[11,679,680],{},"Jeder Katalogeintrag beantwortet dreizehn Fragen über den Provider, und der Rest des Systems fragt den Katalog, statt eine Art beim Namen zu nennen:",[473,682,683,717],{},[476,684,685],{},[479,686,687,690,693,697,701,705,709,713],{},[482,688,689],{},"Feld",[482,691,692],{},"Bedeutung",[482,694,695],{},[34,696,643],{},[482,698,699],{},[34,700,646],{},[482,702,703],{},[34,704,649],{},[482,706,707],{},[34,708,652],{},[482,710,711],{},[34,712,655],{},[482,714,715],{},[34,716,658],{},[491,718,719,755,781,808,833,856,880,919,946,982,1018,1055,1077],{},[479,720,721,726,729,734,739,743,747,751],{},[496,722,723],{},[34,724,725],{},"tier",[496,727,728],{},"Integrationszuständigkeit",[496,730,731],{},[34,732,733],{},"own",[496,735,736],{},[34,737,738],{},"core",[496,740,741],{},[34,742,738],{},[496,744,745],{},[34,746,738],{},[496,748,749],{},[34,750,738],{},[496,752,753],{},[34,754,738],{},[479,756,757,762,765,768,771,774,777,779],{},[496,758,759],{},[34,760,761],{},"requiredEnvVars",[496,763,764],{},"Vorhandenseins-Gate",[496,766,767],{},"MTA-URL + Key",[496,769,770],{},"AWS-Keys",[496,772,773],{},"API-Key",[496,775,776],{},"Host\u002FUser\u002FPasswort",[496,778,773],{},[496,780,773],{},[479,782,783,788,791,794,797,800,803,806],{},[496,784,785],{},[34,786,787],{},"optionalEnvVars",[496,789,790],{},"Nicht gatende Verfeinerungen",[496,792,793],{},"TLS-Untergrenze, Webhook-Secret",[496,795,796],{},"Configuration Set",[496,798,799],{},"Webhook-Secret",[496,801,802],{},"Port\u002FTLS",[496,804,805],{},"Webhook-Key, Subaccount, IP-Pool",[496,807,799],{},[479,809,810,815,818,821,824,826,829,831],{},[496,811,812],{},[34,813,814],{},"credentialFields",[496,816,817],{},"Über Deskriptor gerendertes Formular",[496,819,820],{},"TLS-Untergrenze",[496,822,823],{},"Region + Keys",[496,825,773],{},[496,827,828],{},"Endpunkt + User\u002FPasswort",[496,830,773],{},[496,832,773],{},[479,834,835,840,843,845,847,849,852,854],{},[496,836,837],{},[34,838,839],{},"setupProbe",[496,841,842],{},"Credential-Prüfung vor dem Anwenden",[496,844,549],{},[496,846,549],{},[496,848,773],{},[496,850,851],{},"Verbindung",[496,853,549],{},[496,855,773],{},[479,857,858,863,866,869,871,873,876,878],{},[496,859,860],{},[34,861,862],{},"hasProviderFeedback",[496,864,865],{},"Meldet Provider-Ergebnisse",[496,867,868],{},"ja",[496,870,868],{},[496,872,868],{},[496,874,875],{},"nein",[496,877,868],{},[496,879,868],{},[479,881,882,887,890,895,901,906,908,914],{},[496,883,884],{},[34,885,886],{},"providerFeedback",[496,888,889],{},"Route und Zeremonie",[496,891,892],{},[34,893,894],{},"\u002Fwebhooks\u002Fmta",[496,896,897,900],{},[34,898,899],{},"\u002Fwebhooks\u002Fses",", SNS",[496,902,903],{},[34,904,905],{},"\u002Fwebhooks\u002Fresend",[496,907,549],{},[496,909,910,913],{},[34,911,912],{},"\u002Fwebhooks\u002Fmandrill",", signiert",[496,915,916,913],{},[34,917,918],{},"\u002Fwebhooks\u002Femailit",[479,920,921,926,929,931,933,935,940,944],{},[496,922,923],{},[34,924,925],{},"supportsCustomReturnPath",[496,927,928],{},"Trägt Owlats VERP-Envelope",[496,930,868],{},[496,932,875],{},[496,934,875],{},[496,936,937],{},[34,938,939],{},"probe",[496,941,942],{},[34,943,939],{},[496,945,875],{},[479,947,948,953,956,961,966,970,974,978],{},[496,949,950],{},[34,951,952],{},"domainVerification",[496,954,955],{},"Provider-Identity-API angebunden",[496,957,958],{},[34,959,960],{},"none",[496,962,963],{},[34,964,965],{},"api",[496,967,968],{},[34,969,960],{},[496,971,972],{},[34,973,960],{},[496,975,976],{},[34,977,965],{},[496,979,980],{},[34,981,960],{},[479,983,984,989,992,997,1002,1006,1010,1014],{},[496,985,986],{},[34,987,988],{},"acceptanceSemantics",[496,990,991],{},"Obhut oder unbekannter Timeout",[496,993,994],{},[34,995,996],{},"accepted",[496,998,999],{},[34,1000,1001],{},"unknown-on-timeout",[496,1003,1004],{},[34,1005,1001],{},[496,1007,1008],{},[34,1009,1001],{},[496,1011,1012],{},[34,1013,1001],{},[496,1015,1016],{},[34,1017,1001],{},[479,1019,1020,1025,1028,1033,1038,1042,1047,1051],{},[496,1021,1022],{},[34,1023,1024],{},"messageIdSource",[496,1026,1027],{},"Quelle der erfassten Message-ID",[496,1029,1030],{},[34,1031,1032],{},"idempotency-key",[496,1034,1035],{},[34,1036,1037],{},"provider",[496,1039,1040],{},[34,1041,1037],{},[496,1043,1044],{},[34,1045,1046],{},"composed",[496,1048,1049],{},[34,1050,1037],{},[496,1052,1053],{},[34,1054,1037],{},[479,1056,1057,1062,1065,1067,1069,1071,1073,1075],{},[496,1058,1059],{},[34,1060,1061],{},"deduplicatesOnIdempotencyKey",[496,1063,1064],{},"Provider-seitiges Zusammenfassen von Wiederholungen",[496,1066,868],{},[496,1068,875],{},[496,1070,868],{},[496,1072,875],{},[496,1074,875],{},[496,1076,875],{},[479,1078,1079,1084,1087,1089,1091,1093,1095,1097],{},[496,1080,1081],{},[34,1082,1083],{},"tagsFeedbackProvenance",[496,1085,1086],{},"Trägt Owlat-Provenienz",[496,1088,868],{},[496,1090,875],{},[496,1092,875],{},[496,1094,875],{},[496,1096,875],{},[496,1098,875],{},[11,1100,1101,1102,1105,1106,1109],{},"Jede Zelle oben ist der in ",[34,1103,1104],{},"packages\u002Fshared\u002Fsrc\u002FsendProviderCatalog.ts"," deklarierte Wert — die Tabelle gibt den Katalog wieder, sie definiert ihn nie. ",[34,1107,1108],{},"apps\u002Fdocs\u002F__tests__\u002FproviderCapabilityDocs.test.ts"," liest beides und schlägt fehl, wenn sie auseinanderdriften, sodass eine Zelle, die dem Code widerspricht, ein Testfehler ist und nichts, was eine lesende Person bemerken müsste. Der Test schlägt außerdem fehl bei einem Capability-Feld, das der Katalog deklariert und diese Tabelle auslässt — auch das nächste hinzugefügte Feld kann hier also nicht unsichtbar bleiben.",[11,1111,1112],{},"Fünf Konsequenzen sind erwähnenswert, weil sie früher fest verdrahtete Identitätsprüfungen waren:",[1114,1115,1116,1135,1149,1167,1184],"ul",{},[1117,1118,1119,1122,1123,1126,1127,1129,1130,1134],"li",{},[15,1120,1121],{},"Fallback-Eignung"," (",[34,1124,1125],{},"fallbackEligibility.ts",") — eine Art darf genau dann das Deliverability-Fallback-Relay sein, wenn sie konfiguriert und nicht ",[34,1128,643],{}," ist. Der MTA ist der Arm, ",[1131,1132,1133],"em",{},"von dem weg"," ein Fallback Traffic verschiebt; ihn auf sich selbst zu routen würde ein Reputationsproblem über genau den Transport lösen wollen, der es hat. Nichts nennt SES beim Namen.",[1117,1136,1137,1122,1140,1143,1144,56,1146,1148],{},[15,1138,1139],{},"Relay-Domain-Verifizierung",[34,1141,1142],{},"relayDomainVerification.ts",") — die From-Domain eines Relays gilt genau dann als verifiziert, wenn ein registrierter Sending-Domain-Provider dieser Art das bestätigt. Arten ohne einen solchen Provider (",[34,1145,649],{},[34,1147,652],{},") behalten eine ehrliche „nicht verifizierbar\"-Haltung und schlagen fail-closed fehl.",[1117,1150,1151,1122,1154,1157,1158,1161,1162,596,1164,1166],{},[15,1152,1153],{},"Governed Dispatch",[34,1155,1156],{},"delivery\u002FgovernedDispatch.ts",") — die Identitätsbindung vor dem Dispatch, die Message-ID-Substitution, das Verdikt ",[34,1159,1160],{},"acceptedForDelivery"," und das Replay einer mehrdeutigen Annahme lesen alle ",[34,1163,988],{},[34,1165,1024],{},". Diese Datei vergleicht überhaupt keine Provider-Art mehr mit einem Literal; ein Test sichert das ab.",[1117,1168,1169,1122,1172,1175,1176,1179,1180,1183],{},[15,1170,1171],{},"Redigierte Beschwerden",[34,1173,1174],{},"webhooks\u002FcomplaintDispatch.ts",") — eine Beschwerde nach RFC 5965 §3.2 trägt nur den Empfänger, sodass die Blocklist-Entscheidung daran hängt, ob der Report echte Produktions-Mail betrifft. Eine Quelle, die ihr Feedback taggt, muss ",[34,1177,1178],{},"deliveryDomain: 'production'"," zeigen; eine, die es nicht taggt, hat kein Tag zu zeigen und wird sofort unterdrückt. Früher stand dort ",[34,1181,1182],{},"providerType === 'ses'",", was die byte-identische Beschwerde jedes anderen ESP verwarf und die beschwerdeführende Person weiterhin anschreibbar ließ.",[1117,1185,1186,1122,1189,1192,1193,1195,1196,1199],{},[15,1187,1188],{},"System- und Auth-Mail",[34,1190,1191],{},"lib\u002FsystemMailOutcome.ts",") — ob ein mehrdeutiger Versand (ein Passwort-Reset, dessen Antwort verloren ging) wiederholt werden darf, wird aus ",[34,1194,1061],{}," gelesen. Früher war es die Liste ",[34,1197,1198],{},"provider === 'mta' || provider === 'resend'",", ausgeschrieben in einer Datei und wiederholt in einer anderen.",[46,1201,1204],{"title":1202,"type":1203},"`deduplicatesOnIdempotencyKey` ist nur ein halbes Versprechen","warning",[11,1205,1206,1207,1212,1213,1216,1217,1220,1221,1224,1225,1228,1229,1232,1233,1235,1236,1238,1239,56,1242,1245,1246,1249,1250,1252,1253,1255,1256,79],{},"Der Katalog sagt, dass eine Wiederholung sicher ist; was den Schlüssel, auf dem dedupliziert würde, tatsächlich mitführt, ist ",[15,1208,1209],{},[34,1210,1211],{},"buildSystemMailExtras"," der jeweiligen Art (",[34,1214,1215],{},"lib\u002FsendProviders\u002FsystemMailExtras.ts","). ",[34,1218,1219],{},"true"," zu deklarieren, ohne es zu implementieren, kompiliert — die Interface-Methode ist optional — und ",[34,1222,1223],{},"systemMailRetryDisposition"," meldet dann einen mehrdeutigen Passwort-Reset als ",[34,1226,1227],{},"safe_to_retry",", während der Schlüssel den Provider nie erreicht hat; der „Retry\" ist also eine zweite echte Passwort-Reset-Mail an einen Menschen. Beides ist gemeinsam erforderlich; ",[34,1230,1231],{},"lib\u002FsendProviders\u002F__tests__\u002FsystemMailExtras.test.ts"," ist das Gate. Ein **gebundelter Plugin-**Transport darf ",[34,1234,1219],{}," deklarieren — die Parität hat dieser Ebene einen eigenen Extras-Vertrag gegeben (",[34,1237,1211],{}," auf ",[34,1240,1241],{},"PluginSendTransportModule",[34,1243,1244],{},"packages\u002Fplugin-kit\u002Fsrc\u002FsendTransport.ts",") — und das Paar wird auf dieselbe Weise zusammengehalten, nur eine Schicht später: ",[34,1247,1248],{},"lib\u002FsendProviders\u002Findex.ts"," verweigert beim Modul-Load einen gebundelten Eintrag, der ",[34,1251,1219],{}," deklariert, dessen Modul aber kein ",[34,1254,1211],{}," exportiert — ein Boot-Fehler, der den Eintrag benennt, statt eines falschen ",[34,1257,1227],{},[46,1259,1261],{"title":1260,"type":1203},"`acceptanceSemantics: 'accepted'` hat Voraussetzungen außerhalb des Katalogs",[11,1262,1263,1264,1267,1268,1278,1279,1282,1283,1286,1287,1290,1291,1293,1294,1297,1298,1301,1302,1304,1305,1308,1309,1312,1313,72,1315,1317],{},"Es ist ",[15,1265,1266],{},"noch keine allgemeine Capability"," — nur der eigene MTA deklariert es, und es ist das eine Feld, bei dem der falsche Wert nicht kosmetisch ist. Drei Stellen schreiben den Obhut-Arm weiterhin als genau diese eine Art aus und müssen in derselben Änderung generalisiert werden, in der eine zweite Art es deklariert. ",[15,1269,1270,1271,573,1274,1277],{},"Lesen Sie den PREREQUISITES-Hinweis zu ",[34,1272,1273],{},"AcceptanceSemantics",[34,1275,1276],{},"packages\u002Fshared\u002Fsrc\u002FsendProviderCatalogTypes.ts",", bevor Sie es deklarieren",": Dieser Hinweis benennt alle drei Stellen und was kaputtgeht, wenn eine übersehen wird, und er ist bewusst die einzige Kopie, damit diese Seite nicht dagegen veralten kann. Es zu deklarieren ",[1131,1280,1281],{},"erfordert"," außerdem ",[34,1284,1285],{},"messageIdSource: 'idempotency-key'"," (und umgekehrt) — die Paarung ist eine Compile-Zeit-Union in ",[34,1288,1289],{},"sendProviderCatalogTypes.ts",", denn ein Replay ist nur sicher, wenn es die von uns geprägte ID trägt. Ein **gebundelter Plugin-**Transport kann keinen der beiden Werte überhaupt deklarieren und bekommt das zweimal gesagt: Das Vokabular des Kits selbst lässt beide aus (es gibt kein Feld ",[34,1292,988],{}," an einer ",[34,1295,1296],{},"sendTransports","-Contribution, und ",[34,1299,1300],{},"PluginSendTransportMessageIdSource"," hat kein Mitglied ",[34,1303,1032],{},"), sodass ",[34,1306,1307],{},"definePlugin"," das Manifest verweigert — und ",[34,1310,1311],{},"catalog.ts"," verweigert den komponierten Eintrag zur Kompositionszeit als Load-Zeit-Absicherung, ein Boot-Fehler, der den Eintrag benennt, bis jene Stellen generalisiert sind. Im Zweifel ist ",[34,1314,1001],{},[34,1316,1037],{}," das fail-closed-Paar, und es ist auch das, was eine fehlende Deklaration bedeutet.",[81,1319,1321],{"id":1320},"mailchimp-transactional-mandrill","Mailchimp Transactional (Mandrill)",[11,1323,1324,1325,1330],{},"Der Migrationsarm. Ein Team, das von Mailchimp kommt, fügt seinen bestehenden API-Key ein und sendet weiter auf der Reputation, die es bereits hat; der ausgelieferte Ramp-Controller lässt dann den Anteil des eigenen MTA Zelle für Zelle unter seinen Gates wachsen und fährt bei Bounce-, Beschwerde-, Engagement- oder Seed-Placement-Signalen automatisch zurück. Es gibt keinen Stichtag und kein manuelles Traffic-Verschieben — siehe ",[1326,1327,1329],"a",{"href":1328},"\u002Fdeveloper\u002Fdeliverability-infrastructure","Deliverability-Infrastruktur"," für die Maschinerie.",[674,1332,1334],{"id":1333},"setup","Setup",[1336,1337,1338,1365,1411],"ol",{},[1117,1339,1340,169,1343,1346,1347,1350,1351,1354,1355,1358,1359,596,1362,79],{},[15,1341,1342],{},"API-Key.",[34,1344,1345],{},"MANDRILL_API_KEY"," aus Mailchimp Transactional → Settings → API keys. Setzen Sie ",[34,1348,1349],{},"EMAIL_PROVIDER=mandrill",", um alles darüber zu routen, oder fügen Sie ihn einer ",[34,1352,1353],{},"providerRoutes","-Zeile hinzu, um ihn neben dem eingebauten MTA zu betreiben. Benannte Instanzen (",[34,1356,1357],{},"mandrill#eu",") funktionieren genau wie bei jeder anderen Art, über ",[34,1360,1361],{},"SEND_TRANSPORT_INSTANCES",[34,1363,1364],{},"MANDRILL_API_KEY__EU",[1117,1366,1367,1370,1371,1374,1375,56,1378,56,1381,56,1384,56,1387,56,1390,56,1393,1396,1397,596,1400,169,1403,1406,1407,1410],{},[15,1368,1369],{},"Webhook."," Legen Sie unter Settings → Webhooks einen Webhook auf ",[34,1372,1373],{},"\u003CCONVEX_SITE_URL>\u002Fwebhooks\u002Fmandrill"," an und aktivieren Sie genau ",[34,1376,1377],{},"send",[34,1379,1380],{},"deferral",[34,1382,1383],{},"hard_bounce",[34,1385,1386],{},"soft_bounce",[34,1388,1389],{},"spam",[34,1391,1392],{},"unsub",[34,1394,1395],{},"reject",". Lassen Sie ",[34,1398,1399],{},"open",[34,1401,1402],{},"click",[15,1404,1405],{},"aus",": Owlat erfasst Öffnungen und Klicks auf jedem Transport first-party, und das Engagement-Ramp-Gate kann die beiden Arme nur vergleichen, weil beide mit demselben Instrument gemessen werden. Kopieren Sie den Signaturschlüssel, den Mandrill einmalig anzeigt, in ",[34,1408,1409],{},"MANDRILL_WEBHOOK_KEY",". Die Signaturprüfung ist HMAC-SHA1 über die exakte Webhook-URL plus die alphabetisch sortierten POST-Parameter — ein Redirect oder ein Unterschied beim abschließenden Slash lässt sie scheitern.",[1117,1412,1413,1416,1417,1419,1420,1423,1424,1427,1428,1431,1432,1435,1436,1439,1440,1443],{},[15,1414,1415],{},"Domain-Verifizierung."," Mandrill signiert die Mail jedes Accounts mit einem gemeinsamen, account-unabhängigen Schlüssel unter dem Selector ",[34,1418,655],{},", sodass das DNS aus dem Domainnamen ",[1131,1421,1422],{},"abgeleitet"," statt pro Identität geprägt wird: ein SPF-Record, der ",[34,1425,1426],{},"spf.mandrillapp.com"," einschließt, und ein TXT-Record unter ",[34,1429,1430],{},"mandrill._domainkey",". ",[15,1433,1434],{},"Ein dritter Schritt wird leicht übersehen"," — Mandrill will außerdem einen Eigentumsnachweis (einen TXT-Record ",[34,1437,1438],{},"mandrill_verify.\u003Ckey>"," oder eine Bestätigung im eigenen Dashboard bei Accounts, die kein Token anbieten). Eine Domain mit makellosem SPF und DKIM, aber ohne Eigentumsnachweis, wird von Mandrill weiterhin mit ",[34,1441,1442],{},"reject_reason: unsigned"," abgelehnt, deshalb behandelt Owlat sie nicht als verifiziert. Delivery → Versanddomains rendert alle drei mit Mandrills eigenem Verdikt-Text.",[674,1445,1447],{"id":1446},"capabilities-ehrlich-betrachtet","Capabilities, ehrlich betrachtet",[1114,1449,1450,1476,1505,1529],{},[1117,1451,1452,1455,1456,1459,1460,1463,1464,1467,1468,1471,1472,1475],{},[15,1453,1454],{},"Kein eigener Return Path."," Mandrill akzeptiert pro Nachricht ein ",[34,1457,1458],{},"return_path_domain",", prägt aber seinen eigenen Bounce-Local-Part, sodass Owlats VERP-Envelope-Sender das nicht überlebt. Der Katalog deklariert ",[34,1461,1462],{},"supportsCustomReturnPath: 'probe'"," der Einheitlichkeit halber, und die Probe-Maschinerie legt es auf ",[34,1465,1466],{},"unsupported"," mit dem Grund ",[34,1469,1470],{},"no_envelope_control"," fest. Die praktische Konsequenz ist eng und sollte klar benannt werden: Bounces, die ",[1131,1473,1474],{},"nur"," am SMTP-Envelope ankommen, können von uns keinem konkreten Versand zugeordnet werden — Mandrills Webhook-Feedback deckt den Rest ab — und die Messung der Zelle wird als degradiert markiert statt blockiert. Nichts hindert am Versand.",[1117,1477,1478,1481,1482,1485,1486,1489,1490,1493,1494,1497,1498,1500,1501,1504],{},[15,1479,1480],{},"Provider-Feedback: ja."," Jedes Ereignis aus der obigen Liste bewegt die passende ",[34,1483,1484],{},"emailSends","- \u002F ",[34,1487,1488],{},"transactionalSends","-Zeile über ",[34,1491,1492],{},"providerMessageId"," (Mandrills ",[34,1495,1496],{},"_id","), und ",[34,1499,1395],{},"-Ereignisse spiegeln Mandrills eigene Blacklist in ",[34,1502,1503],{},"blockedEmails",", damit der eigene Arm nie eine Adresse anschreibt, die dem Referenzarm stillschweigend erspart blieb. Genau dieser Spiegel sorgt dafür, dass eine gemessene Migration beide Arme auf derselben Population vergleicht.",[1117,1506,1507,1512,1513,1516,1517,1520,1521,1524,1525,1528],{},[15,1508,1509,1510,79],{},"Domain-Verifizierung: ",[34,1511,965],{}," Registrierung über ",[34,1514,1515],{},"senders\u002Fadd-domain",", stündliche Neuprüfung über ",[34,1518,1519],{},"senders\u002Fcheck-domain",", gespeichert in der generischen Tabelle ",[34,1522,1523],{},"sendingDomainRelayIdentities",". Der Nachweis läuft nach ",[15,1526,1527],{},"7 Tagen"," ab (gegenüber 30 bei SES): Die Prüfung ist ein einziger billiger HTTP-Aufruf, und ein so billiger Nachweis hat keine Entschuldigung, einen Monat alt zu sein, bevor das Routing ihm nicht mehr traut. Eine verifizierte, aber veraltete Identität liest sich als „wird erneut geprüft\", nie als „verifiziert\".",[1117,1530,1531,1534,1535,1538,1539,1542],{},[15,1532,1533],{},"Kein Idempotency Key."," Mandrills API hat keinen, sodass eine in einen Timeout gelaufene Anfrage angenommen worden sein kann oder auch nicht. Der Adapter gibt ",[34,1536,1537],{},"AMBIGUOUS_TIMEOUT"," mit ",[34,1540,1541],{},"acceptanceUnknown"," zurück, statt es erneut zu versuchen; der Wiedereintritts-Snapshot des Governed Dispatch und die Ein-Versuch-Disziplin des Workpools sind es, die eine Doppelzustellung verhindern.",[674,1544,1546],{"id":1545},"registry-lookup","Registry & Lookup",[11,1548,1549,1552,1553,1556],{},[34,1550,1551],{},"providers\u002Fcomposition.ts"," komponiert jeden First-Party- und gebundelten Provider. ",[34,1554,1555],{},"sendProviders\u002Findex.ts"," stellt die Kompatibilitäts-Registry und die Lookup-Helfer aus dieser einen Sicht bereit:",[93,1558,1560],{"className":207,"code":1559,"language":209,"meta":101,"style":101},"export const SEND_PROVIDER_BUNDLES = composeProviderBundles(assigned);\nexport const SEND_PROVIDERS = Object.fromEntries(\n    SEND_PROVIDER_BUNDLES.filter(({ descriptor }) => isCoreSendProviderKind(descriptor.kind))\n);\n\nexport function providerFor\u003CK extends SendProviderKind>(kind: K): SendProviderModule\u003CK>;\nexport function isSendProviderKind(kind: string | undefined | null): kind is SendProviderKind;\n",[34,1561,1562,1581,1601,1629,1634,1638,1683],{"__ignoreMap":101},[213,1563,1564,1566,1569,1572,1575,1578],{"class":215,"line":216},[213,1565,227],{"class":226},[213,1567,1568],{"class":226}," const",[213,1570,1571],{"class":310}," SEND_PROVIDER_BUNDLES",[213,1573,1574],{"class":226}," =",[213,1576,1577],{"class":233}," composeProviderBundles",[213,1579,1580],{"class":237},"(assigned);\n",[213,1582,1583,1585,1587,1590,1592,1595,1598],{"class":215,"line":223},[213,1584,227],{"class":226},[213,1586,1568],{"class":226},[213,1588,1589],{"class":310}," SEND_PROVIDERS",[213,1591,1574],{"class":226},[213,1593,1594],{"class":237}," Object.",[213,1596,1597],{"class":233},"fromEntries",[213,1599,1600],{"class":237},"(\n",[213,1602,1603,1606,1608,1611,1614,1617,1620,1623,1626],{"class":215,"line":264},[213,1604,1605],{"class":310},"    SEND_PROVIDER_BUNDLES",[213,1607,79],{"class":237},[213,1609,1610],{"class":233},"filter",[213,1612,1613],{"class":237},"(({ ",[213,1615,1616],{"class":241},"descriptor",[213,1618,1619],{"class":237}," }) ",[213,1621,1622],{"class":226},"=>",[213,1624,1625],{"class":233}," isCoreSendProviderKind",[213,1627,1628],{"class":237},"(descriptor.kind))\n",[213,1630,1631],{"class":215,"line":285},[213,1632,1633],{"class":237},");\n",[213,1635,1636],{"class":215,"line":302},[213,1637,396],{"emptyLinePlaceholder":395},[213,1639,1640,1642,1644,1647,1650,1653,1656,1659,1662,1664,1666,1669,1671,1673,1676,1678,1680],{"class":215,"line":316},[213,1641,227],{"class":226},[213,1643,230],{"class":226},[213,1645,1646],{"class":233}," providerFor",[213,1648,1649],{"class":237},"\u003C",[213,1651,1652],{"class":247},"K",[213,1654,1655],{"class":226}," extends",[213,1657,1658],{"class":247}," SendProviderKind",[213,1660,1661],{"class":237},">(",[213,1663,43],{"class":241},[213,1665,91],{"class":226},[213,1667,1668],{"class":247}," K",[213,1670,176],{"class":237},[213,1672,91],{"class":226},[213,1674,1675],{"class":247}," SendProviderModule",[213,1677,1649],{"class":237},[213,1679,1652],{"class":247},[213,1681,1682],{"class":237},">;\n",[213,1684,1685,1687,1689,1692,1694,1696,1698,1700,1702,1704,1706,1709,1711,1713,1716,1719,1721],{"class":215,"line":328},[213,1686,227],{"class":226},[213,1688,230],{"class":226},[213,1690,1691],{"class":233}," isSendProviderKind",[213,1693,238],{"class":237},[213,1695,43],{"class":241},[213,1697,91],{"class":226},[213,1699,311],{"class":310},[213,1701,362],{"class":226},[213,1703,365],{"class":310},[213,1705,362],{"class":226},[213,1707,1708],{"class":310}," null",[213,1710,176],{"class":237},[213,1712,91],{"class":226},[213,1714,1715],{"class":241}," kind",[213,1717,1718],{"class":226}," is",[213,1720,1658],{"class":247},[213,1722,282],{"class":237},[11,1724,1725,1726,1729,1730,1733],{},"Eine Mapped-Type-Prüfung im ",[34,1727,1728],{},"satisfies","-Stil zur Compile-Zeit fixiert jeden Registry-Wert auf ",[34,1731,1732],{},"SendProviderModule\u003CthatKind>",", sodass eine fehlende Methode den Build scheitern lässt.",[674,1735,1737],{"id":1736},"adapter-interface","Adapter-Interface",[11,1739,1740,1741,1744,1745,91],{},"Jeder Adapter implementiert ",[34,1742,1743],{},"SendProviderModule\u003CK>"," aus ",[34,1746,1747],{},"sendProviders\u002Ftypes.ts",[93,1749,1751],{"className":207,"code":1750,"language":209,"meta":101,"style":101},"export interface SendProviderModule\u003CK extends SendProviderKind> {\n    readonly kind: K;\n    readonly retryDelays: readonly number[]; \u002F\u002F backoff schedule; dispatch owns the loop\n    sendEmail(params: EmailSendParams, extras?: ExtrasFor\u003CK>): Promise\u003CEmailSendAttempt>;\n    categorizeError(message: string, httpStatus?: number): EmailErrorCode;\n}\n\n\u002F\u002F Single-attempt result — no internal retry:\nexport type EmailSendAttempt =\n    | { success: true; id: string }\n    | { success: false; errorMessage: string; errorCode: EmailErrorCode };\n",[34,1752,1753,1773,1786,1807,1852,1884,1889,1893,1898,1911,1939],{"__ignoreMap":101},[213,1754,1755,1757,1760,1762,1764,1766,1768,1770],{"class":215,"line":216},[213,1756,227],{"class":226},[213,1758,1759],{"class":226}," interface",[213,1761,1675],{"class":247},[213,1763,1649],{"class":237},[213,1765,1652],{"class":247},[213,1767,1655],{"class":226},[213,1769,1658],{"class":247},[213,1771,1772],{"class":237},"> {\n",[213,1774,1775,1778,1780,1782,1784],{"class":215,"line":223},[213,1776,1777],{"class":226},"    readonly",[213,1779,1715],{"class":241},[213,1781,91],{"class":226},[213,1783,1668],{"class":247},[213,1785,282],{"class":237},[213,1787,1788,1790,1793,1795,1798,1801,1804],{"class":215,"line":264},[213,1789,1777],{"class":226},[213,1791,1792],{"class":241}," retryDelays",[213,1794,91],{"class":226},[213,1796,1797],{"class":226}," readonly",[213,1799,1800],{"class":310}," number",[213,1802,1803],{"class":237},"[]; ",[213,1805,1806],{"class":219},"\u002F\u002F backoff schedule; dispatch owns the loop\n",[213,1808,1809,1812,1814,1817,1819,1822,1824,1827,1830,1833,1835,1837,1840,1842,1845,1847,1850],{"class":215,"line":285},[213,1810,1811],{"class":233},"    sendEmail",[213,1813,238],{"class":237},[213,1815,1816],{"class":241},"params",[213,1818,91],{"class":226},[213,1820,1821],{"class":247}," EmailSendParams",[213,1823,56],{"class":237},[213,1825,1826],{"class":241},"extras",[213,1828,1829],{"class":226},"?:",[213,1831,1832],{"class":247}," ExtrasFor",[213,1834,1649],{"class":237},[213,1836,1652],{"class":247},[213,1838,1839],{"class":237},">)",[213,1841,91],{"class":226},[213,1843,1844],{"class":247}," Promise",[213,1846,1649],{"class":237},[213,1848,1849],{"class":247},"EmailSendAttempt",[213,1851,1682],{"class":237},[213,1853,1854,1857,1859,1862,1864,1866,1868,1871,1873,1875,1877,1879,1882],{"class":215,"line":302},[213,1855,1856],{"class":233},"    categorizeError",[213,1858,238],{"class":237},[213,1860,1861],{"class":241},"message",[213,1863,91],{"class":226},[213,1865,311],{"class":310},[213,1867,56],{"class":237},[213,1869,1870],{"class":241},"httpStatus",[213,1872,1829],{"class":226},[213,1874,1800],{"class":310},[213,1876,176],{"class":237},[213,1878,91],{"class":226},[213,1880,1881],{"class":247}," EmailErrorCode",[213,1883,282],{"class":237},[213,1885,1886],{"class":215,"line":316},[213,1887,1888],{"class":237},"}\n",[213,1890,1891],{"class":215,"line":328},[213,1892,396],{"emptyLinePlaceholder":395},[213,1894,1895],{"class":215,"line":340},[213,1896,1897],{"class":219},"\u002F\u002F Single-attempt result — no internal retry:\n",[213,1899,1900,1902,1905,1908],{"class":215,"line":352},[213,1901,227],{"class":226},[213,1903,1904],{"class":226}," type",[213,1906,1907],{"class":247}," EmailSendAttempt",[213,1909,1910],{"class":226}," =\n",[213,1912,1913,1916,1919,1922,1924,1927,1929,1932,1934,1936],{"class":215,"line":370},[213,1914,1915],{"class":226},"    |",[213,1917,1918],{"class":237}," { ",[213,1920,1921],{"class":241},"success",[213,1923,91],{"class":226},[213,1925,1926],{"class":310}," true",[213,1928,258],{"class":237},[213,1930,1931],{"class":241},"id",[213,1933,91],{"class":226},[213,1935,311],{"class":310},[213,1937,1938],{"class":237}," }\n",[213,1940,1941,1943,1945,1947,1949,1952,1954,1957,1959,1961,1963,1966,1968,1970],{"class":215,"line":386},[213,1942,1915],{"class":226},[213,1944,1918],{"class":237},[213,1946,1921],{"class":241},[213,1948,91],{"class":226},[213,1950,1951],{"class":310}," false",[213,1953,258],{"class":237},[213,1955,1956],{"class":241},"errorMessage",[213,1958,91],{"class":226},[213,1960,311],{"class":310},[213,1962,258],{"class":237},[213,1964,1965],{"class":241},"errorCode",[213,1967,91],{"class":226},[213,1969,1881],{"class":247},[213,1971,1972],{"class":237}," };\n",[11,1974,1975,1978,1979,1981,1982,1985,1986,1989,1990,56,1993,56,1996,56,1999,56,2002,56,2005,56,2008,56,2011,56,2014,56,2016,56,2019,2022,2023,596,2025,2027,2028,2030,2031,2033,2034,2037],{},[34,1976,1977],{},"sendEmail"," führt genau einen Versuch aus und wiederholt intern nie. ",[34,1980,1826],{}," ist ein typisiertes, providerspezifisches zweites Argument (",[34,1983,1984],{},"MtaExtras"," trägt Pool-\u002FSignatur-Metadaten plus den Governed-Routing-Kontext und das Lease oder den expliziten System-Intake-Pfad; Resend trägt einen Idempotency Key; SES nimmt keines). ",[34,1987,1988],{},"EmailErrorCode"," ist ein Enum (",[34,1991,1992],{},"RATE_LIMIT",[34,1994,1995],{},"SERVER_ERROR",[34,1997,1998],{},"ROUTING_DEFERRED",[34,2000,2001],{},"ROUTING_LEASE_UNREADABLE",[34,2003,2004],{},"INVALID_RECIPIENT",[34,2006,2007],{},"INVALID_SENDER",[34,2009,2010],{},"AUTH_FAILED",[34,2012,2013],{},"CONTENT_REJECTED",[34,2015,1537],{},[34,2017,2018],{},"SMTPUTF8_UNSUPPORTED",[34,2020,2021],{},"UNKNOWN","); Routing-Deferrals gehen zurück in ein beschränktes Scheduling mit frischer Route, während nur ",[34,2024,1992],{},[34,2026,1995],{}," Provider-Versuchs-Retries nutzen. Die beiden Routing-Codes planen identisch neu und unterscheiden sich nur in dem, was sie behaupten: ",[34,2029,1998],{}," ist der MTA, der diese Versandidentität ablehnt (gezählt gegen das Deferral-Gate der Ramp), ",[34,2032,2001],{}," ist der MTA, der einen selbst geschriebenen Lease-Datensatz nicht zurücklesen kann (unser eigener Speicher, als ",[34,2035,2036],{},"local"," zurückgestellt und nicht gezählt).",[674,2039,2041],{"id":2040},"dispatch","Dispatch",[11,2043,2044,2045,573,2048,91],{},"Der einzige öffentliche Einstiegspunkt ist ",[34,2046,2047],{},"sendProviderDispatch()",[34,2049,2050],{},"sendProviders\u002Fdispatch.ts",[93,2052,2054],{"className":207,"code":2053,"language":209,"meta":101,"style":101},"sendProviderDispatch(ctx, transportId, params, extras?): Promise\u003CDispatchResult>\n",[34,2055,2056],{"__ignoreMap":101},[213,2057,2058,2061,2064,2067,2069,2071,2073,2075,2078],{"class":215,"line":216},[213,2059,2060],{"class":233},"sendProviderDispatch",[213,2062,2063],{"class":237},"(ctx, transportId, params, extras",[213,2065,2066],{"class":226},"?",[213,2068,176],{"class":237},[213,2070,91],{"class":226},[213,2072,1844],{"class":310},[213,2074,1649],{"class":226},[213,2076,2077],{"class":237},"DispatchResult",[213,2079,2080],{"class":226},">\n",[11,2082,2083,2084,2087,2088,56,2090,56,2092,2094,2095,2097,2098,2100,2101,2104,2105,2108,2109,2112,2113,2116],{},"Die Dispatch-Einheit ist eine ",[15,2085,2086],{},"Transport-ID",", nicht eine nackte Provider-Art.\nJede Art hat eine Standardinstanz, deren ID DIE Art ist (",[34,2089,643],{},[34,2091,646],{},[34,2093,649],{},",\n",[34,2096,652],{},") und die die Variablen ohne Suffix liest. Zusätzliche Instanzen derselben\nArt werden in ",[34,2099,1361],{}," als ",[34,2102,2103],{},"\u003Ckind>#\u003CinstanceKey>"," deklariert\nund lesen dieselben Variablen unter einem Suffix ",[34,2106,2107],{},"__\u003CINSTANCEKEY>"," —\n",[34,2110,2111],{},"smtp#backup"," liest ",[34,2114,2115],{},"SMTP_RELAY_HOST__BACKUP"," — sodass ein Deployment ein warmes\nFallback-Relay behalten kann, während es ein zweites erprobt. Jeder Adapter löst\nseine eigene Konfiguration aus dem aufgelösten Transport-Datensatz auf; der\nDatensatz selbst trägt nur Variablen-NAMEN, nie Werte.",[11,2118,2119,2120,2123,2124,2127,2128,2130,2131,72,2133,2135,2136,2138,2139,1538,2142,2145],{},"Die Auflösung ist fail-closed. Eine ID, die fehlerhaft ist, eine unbekannte Art\nbenennt, eine Instanz benennt, die niemand deklariert hat, oder eine deklarierte\nInstanz benennt, deren Konfiguration entfernt wurde, wirft\n",[34,2121,2122],{},"SendTransportResolutionError"," vor jedem Versuch, jedem Autorisierungsaufruf und\njedem Health-Write — sie fällt nie auf einen anderen Transport zurück. Benannte\nInstanzen folgen der KONFIGURATION, nicht der Ebene: Eine Art kann sie haben,\nwenn sie eigene Variablen deklariert, die ein Instanz-Suffix erreicht.\nCore-Arten tun das immer. Eine plugin-beigesteuerte Art\n(",[34,2125,2126],{},"plugin.\u003CpluginId>.\u003ClocalId>",") tut es, wenn ihre ",[34,2129,1296],{},"-Contribution\n",[34,2132,761],{},[34,2134,787],{}," deklariert — der Host löst sie dann pro\nInstanz auf und übergibt dem Modul genau diese Werte. Ein Plugin-Transport, der\nkeine deklariert, wird aus der deploymentweiten Umgebung des Plugins selbst\nkonfiguriert, die das Suffix ",[34,2137,2107],{}," nicht erreicht, sodass\n",[34,2140,2141],{},"plugin.…#alt",[34,2143,2144],{},"instances_unsupported"," abgelehnt wird, statt mit den\nCredentials der Standard-Plugin-Instanz zu senden.",[11,2147,2148,2151],{},[34,2149,2150],{},"listSendTransports()"," zählt jeden Transport auf, über den dieses Deployment\ndispatchen kann, und nur diese: Eine deklarierte benannte Instanz, die\nunkonfiguriert ist oder zu einer Art gehört, die keine Instanzen haben kann,\nwird ausgelassen statt aufgeführt — so kann „aufzählen, dann dispatchen\" nie eine\nID wählen, die die Auflösung ablehnen würde.",[11,2153,2154,2155,2158,2159,2162,2163,2166,2167,2170,2171,2173,2174,2176,2177,2179,2180,2183,2184,2187,2188,79],{},"Der Helfer besitzt die Retry-Schleife (getrieben von ",[34,2156,2157],{},"retryDelays"," und\n",[34,2160,2161],{},"categorizeError"," des Moduls), und nach jedem terminalen Ausgang — Erfolg oder\naufgebrauchte Retries — erfasst er die Provider-Health, indem er\n",[34,2164,2165],{},"internal.lib.sendProviders.health.recordSendResult"," einplant. Health bleibt nach\nProvider-ART geschlüsselt (",[34,2168,2169],{},"providerHealth"," hält eine Zeile pro Art), sodass\nInstanzen einer Art sich eine Health-Zeile teilen. ",[34,2172,2077],{}," trägt den\nfinalen ",[34,2175,1849],{},", den verwendeten ",[34,2178,155],{},", die ",[34,2181,2182],{},"transportId",",\nüber die es lief, die gesamte ",[34,2185,2186],{},"latencyMs"," und die Anzahl der ",[34,2189,2190],{},"attempts",[674,2192,2194],{"id":2193},"routing-health","Routing & Health",[11,2196,2197],{},"Routen mit dem eingebauten MTA und einem aktivierten Relay können sich für eine\nDeliverability-Notluke entscheiden. Authentifizierte MTA-Snapshots werden pro\nEmpfänger-Provider angewendet: ein Gmail-spezifischer Breaker oder ein Signal\nanhaltender Deferrals verschiebt nur den Gmail-Traffic auf das Relay. Eine\npoolweite FCrDNS-Quarantäne oder eine kritische DNSBL-Erschöpfung gilt für jede\nProvider-Scheibe. Der Failback erfordert 15 gesunde Minuten und eine\nMindest-Fallback-Dauer von 30 Minuten, was Flattern verhindert.",[11,2199,2200,2201,2204],{},"Relay-Credentials sind kein DNS-Nachweis. Die From-Domain muss für genau diesen\nRelay-Pfad verifiziert sein. Das Aktivieren des SES-Fallbacks provisioniert per\nCursor für jede bestehende verifizierte MTA-Domain eine SES-Geschwisteridentität,\nund der Lebenszyklus der Versanddomains provisioniert künftige Domains, sobald\nsie verifiziert werden. Provider Routing zeigt den exakten Geschwister-DNS-Plan\nund dessen Live-DNS-\u002FSES-Status. Der Plan enthält einen zusammengeführten\nApex-SPF-Record (eigener MTA plus SES), SES-DKIM und ein dediziertes\n",[34,2202,2203],{},"ses-mail","-MAIL-FROM-Paar; das DMARC der primären Domain bleibt maßgeblich.\nResend und generische SMTP-Relay-Ziele bleiben fail-closed, bis sie einen\nproviderspezifischen Verifizierungsadapter bereitstellen. Kampagnenrouten können\nsich außerdem dafür entscheiden, Überlauf oberhalb der synchronisierten\nWarming-Obergrenze der eigenen IPs über das verifizierte Relay zu senden.\nRelay-Sends verändern nie die Zähler oder die Obergrenze der eigenen IPs.",[11,2206,2207,573,2210,2213,2214,2216,2217,2219,2220,2223,2224,56,2227,566,2230,2233,2234,2236,2237,1122,2240,2242,2243,2245,2246,169,2249,2251],{},[34,2208,2209],{},"resolveRoute()",[34,2211,2212],{},"sendProviders\u002Frouting.ts"," wählt die Provider-Art (",[34,2215,43],{},") für einen Versand aus der ",[34,2218,1353],{},"-Konfiguration einer Organisation und dispatcht an ein Strategiemodul unter ",[34,2221,2222],{},"sendProviders\u002Fstrategies\u002F"," — ",[34,2225,2226],{},"single",[34,2228,2229],{},"priority_failover",[34,2231,2232],{},"workload_split",". Es fällt auf die Env-Variable ",[34,2235,622],{}," zurück und gibt dann ",[34,2238,2239],{},"null",[15,2241,626],{},") zurück — nie ein Phantom-",[34,2244,643],{}," —, wenn es keine Konfiguration und keine aktivierten Provider gibt, die Strategie null zurückgibt ",[15,2247,2248],{},"und",[34,2250,622],{}," keinen Provider benennt.",[11,2253,2254,2255,2258,2259,2261,2262,2264,2265,2269],{},"Admins konfigurieren diese Routen pro Nachrichtentyp (transaktional, Kampagnen, Automationen) unter ",[15,2256,2257],{},"Settings → Technical → Provider Routing"," im Dashboard — Strategie wählen, Provider ordnen, Workload-Split-Gewichte setzen und optional einen IP-Pool anpinnen. Ohne konfigurierte Route nutzt jeder Versand den Wert von ",[34,2260,622],{},"; ohne Route und ohne ",[34,2263,622],{}," wird der Versand abgelehnt (kein Delivery-Provider konfiguriert). Siehe ",[1326,2266,2268],{"href":2267},"\u002Fguide\u002Foperating-modes","Betriebsmodi"," dazu, welche Deployment-Formen einen Provider benötigen.",[11,2271,2272,2275,2276,2279,2280,2279,2283,2286,2287,2289,2290,2293],{},[34,2273,2274],{},"sendProviders\u002Fhealth.ts"," erfasst rollierende Erfolgs-\u002FFehlerzählungen, durchschnittliche Latenz und einen Status ",[34,2277,2278],{},"healthy","\u002F",[34,2281,2282],{},"degraded",[34,2284,2285],{},"down"," pro Provider in der Tabelle ",[34,2288,2169],{},". Der Dispatch ist der einzige Schreiber; ",[34,2291,2292],{},"resolveRoute"," ist der einzige Leser des Snapshots über alle Provider.",[674,2295,2297],{"id":2296},"identität-domain-verifizierung","Identität & Domain-Verifizierung",[11,2299,2300,2301,2304,2305,56,2308,56,2311,2314],{},"Das ältere Verzeichnis ",[34,2302,2303],{},"lib\u002FemailProviders\u002F"," existiert weiterhin, enthält jetzt aber nur noch Helfer für Identität und Domain-Verifizierung — ",[34,2306,2307],{},"domainVerification.ts",[34,2309,2310],{},"mtaIdentity.ts",[34,2312,2313],{},"sesIdentity.ts"," — nicht die Send-Factory.",[11,2316,2317,2318,2322],{},"Siehe ",[1326,2319,2321],{"href":2320},"\u002Fdeveloper\u002Fenvironment-variables#email-sending","Umgebungsvariablen → E-Mail-Versand"," für die providerspezifischen Env-Variablen.",[81,2324,2326],{"id":2325},"einen-neuen-provider-hinzufügen","Einen neuen Provider hinzufügen",[11,2328,2329,2332,2333,2336,2337,2339,2340,2342,2343,2345,2346,2349,2350,573,2353,2356,2357,79],{},[15,2330,2331],{},"LLM:"," Weil jeder unterstützte Client die OpenAI-Form spricht, brauchen die meisten „neuen Provider\" überhaupt keinen Code — setzen Sie ",[34,2334,2335],{},"LLM_PROVIDER=openai"," und richten Sie ",[34,2338,440],{}," auf den OpenAI-kompatiblen Endpunkt. Für ein wirklich nicht OpenAI-kompatibles Backend würden Sie das eine Modul ",[34,2341,203],{}," direkt bearbeiten (z. B. einen Base-URL-Fall in ",[34,2344,585],{}," ergänzen und, falls ein anderer SDK-Client nötig ist, in ",[34,2347,2348],{},"getClient()"," verzweigen). Wenn der Provider neue Env-Variablen braucht, deklarieren Sie sie in ",[34,2351,2352],{},"FEATURE_FLAGS[\u003Cflag>].requiredEnvVars",[34,2354,2355],{},"packages\u002Fshared\u002Fsrc\u002FfeatureFlags.ts",", damit der Wizard danach fragt, und dokumentieren Sie sie unter ",[1326,2358,2360],{"href":2359},"\u002Fdeveloper\u002Fenvironment-variables","Umgebungsvariablen",[674,2362,2364],{"id":2363},"send-e-mail-die-provider-n1-checkliste","Send (E-Mail) — die Provider-N+1-Checkliste",[11,2366,2367,2368,2371,2372,2375],{},"Einen Sender hinzuzufügen bedeutete früher eine Suche über zwölf Dateien, zwei davon mit fest verdrahteten, nur auf ",[34,2369,2370],{},"'ses'"," bezogenen Gates. Das ist vorbei: Es ist ein Katalogeintrag plus die Module, die dieser Eintrag verspricht. Alles Folgende ist additiv, und ",[15,2373,2374],{},"nichts in Routing, Ramp-Steuerung, Messung oder Governed Dispatch ändert sich"," — die lesen alle deklarierte Capabilities (ADR-0055).",[11,2377,2378,2379,2382,2383,2386,2387,56,2389,56,2391,56,2393,56,2395,56,2397,2399,2400,2403,2404,2406,2407,2409,2410,2413,2414,2416,2417,2419,2420,2422,2423,2425,2426,2428,2429,2433],{},"Die Liste ist auf ",[15,2380,2381],{},"beiden Ebenen"," dieselbe, Artefakt für Artefakt, was der ganze Anspruch dieser Nahtstelle ist: Ein Provider ist ein Bundle, und wo das Bundle liegt, ist eine Packaging-Entscheidung und kein Unterschied darin, was es liefern muss. Eine ",[15,2384,2385],{},"Core-Art"," ist ein In-Repo-Bundle (",[34,2388,643],{},[34,2390,646],{},[34,2392,649],{},[34,2394,652],{},[34,2396,655],{},[34,2398,658],{},"); eine ",[15,2401,2402],{},"Plugin-Art"," ist ein gebundeltes Paket, das dasselbe Bundle über ",[34,2405,1296],{}," beisteuert, und seit der Parität ist sie der Standard für Provider N+1 — greifen Sie nur dann zur Core-Ebene, wenn der Provider etwas braucht, das die Plugin-Ebene nicht deklarieren kann, und das sind heute genau drei Dinge: ein Envelope-Sender, den wir signieren (",[34,2408,925],{}," anders als ",[34,2411,2412],{},"no",", und damit Bounce-Zuordnung pro Nachricht über VERP), Obhut-Semantik (",[34,2415,988],{}," und damit ",[34,2418,1285],{},") sowie ein host-seitiger ",[34,2421,839],{},". Jedes andere Feld ist entweder deklarierbar oder wird aus einer Hälfte ",[1131,2424,1422],{},", die das Bundle ohnehin mitbringt. (",[34,2427,1083],{}," ist auf dieser Ebene ebenfalls nicht deklarierbar, trifft aber immer nur auf unseren eigenen MTA zu und ist deshalb nie der Grund, warum ein Drittanbieter-Provider die Core-Ebene braucht.) Die Plugin-Spalte hier ist die Kurzfassung; ",[1326,2430,2432],{"href":2431},"\u002Fdeveloper\u002Fplugin-send-providers","Send-Provider (Plugins)"," ist der Leitfaden, gegen den diese Ebene geschrieben ist.",[473,2435,2436,2453],{},[476,2437,2438],{},[479,2439,2440,2443,2446,2448,2451],{},[482,2441,2442],{},"#",[482,2444,2445],{},"Artefakt",[482,2447,2385],{},[482,2449,2450],{},"Erforderlich?",[482,2452,2402],{},[491,2454,2455,2547,2591,2641,2697,2723,2763],{},[479,2456,2457,2460,2466,2526,2529],{},[496,2458,2459],{},"1",[496,2461,2462,2463],{},"Katalogeintrag mit Capabilities — ",[15,2464,2465],{},"das ist die Deklaration der Art",[496,2467,2468,2470,2471,2473,2474,2477,2478,56,2480,56,2482,56,2484,56,2486,56,2488,56,2490,56,2492,56,2494,56,2496,56,2498,2223,2500,56,2502,2504,2505,2508,2509,2512,2513,72,2515,72,2517,2520,2521,566,2523,2525],{},[34,2469,1104],{}," (der Eintrag; sein Feldvokabular ist ",[34,2472,1276],{},", seine Formular-Deskriptoren ",[34,2475,2476],{},"sendProviderCredentialFields.ts","): ",[34,2479,725],{},[34,2481,761],{},[34,2483,787],{},[34,2485,814],{},[34,2487,2157],{},[34,2489,925],{},[34,2491,862],{},[34,2493,952],{},[34,2495,988],{},[34,2497,1024],{},[34,2499,1061],{},[34,2501,725],{},[34,2503,814],{}," und die letzten ",[15,2506,2507],{},"vier"," sind für eine Core-Art erforderlich (",[34,2510,2511],{},"CoreSendProviderCatalogEntry","), und ein neues Relay will fast immer ",[34,2514,1001],{},[34,2516,1037],{},[34,2518,2519],{},"false"," (siehe die Warnungen oben, bevor Sie zu ",[34,2522,996],{},[34,2524,1219],{}," greifen)",[496,2527,2528],{},"immer",[496,2530,2531,2532,2534,2535,2537,2538,2541,2542,2546],{},"Die ",[34,2533,1296],{},"-Contribution im Paketmanifest, die dieselben Capability-Felder über ein engeres Vokabular trägt — manche werden aus den eigenen Hälften des Bundles ",[1131,2536,1422],{},", manche sind auf dieser Ebene ",[1131,2539,2540],{},"nicht deklarierbar",", und die Tabelle ",[1326,2543,2545],{"href":2544},"\u002Fdeveloper\u002Fplugin-send-providers#capability-semantics","Capability-Semantik"," im Leitfaden sagt, was was ist. Der Host komponiert den Eintrag zur Ladezeit auf den Katalog, es wird also keine Host-Datei bearbeitet",[479,2548,2549,2552,2555,2571,2573],{},[496,2550,2551],{},"2",[496,2553,2554],{},"Env-Keys",[496,2556,2557,169,2560,2563,2564,72,2567,2570],{},[34,2558,2559],{},"apps\u002Fapi\u002Fconvex\u002Flib\u002Fenv.ts",[34,2561,2562],{},"EnvKey","-Union + ",[34,2565,2566],{},".env.example",[34,2568,2569],{},".env.selfhost.example"," + die Env-Variablen-Referenz",[496,2572,2528],{},[496,2574,2575,72,2577,2579,2580,2583,2584,596,2587,2590],{},[34,2576,761],{},[34,2578,787],{}," an der Contribution, mit ",[34,2581,2582],{},"PLUGIN_","-Präfix und pro Instanz vom Host aufgelöst, der ihr Vorhandensein prüft — ",[34,2585,2586],{},"lib\u002Fenv.ts",[34,2588,2589],{},"lint:env"," bleiben unberührt",[479,2592,2593,2596,2599,2632,2634],{},[496,2594,2595],{},"3",[496,2597,2598],{},"Adaptermodul",[496,2600,2601,2604,2605,2608,2609,56,2611,2613,2614,596,2617,2619,2620,2623,2624,2627,2628,2631],{},[34,2602,2603],{},"lib\u002FsendProviders\u002F\u003Ckind>\u002Findex.ts",", das ",[34,2606,2607],{},"SendProviderModule\u003C'\u003Ckind>'>"," implementiert (",[34,2610,1977],{},[34,2612,2161],{},", optional ",[34,2615,2616],{},"buildDispatchExtras",[34,2618,1211],{}," — am Interface optional, aber ",[15,2621,2622],{},"erforderlich",", sobald der Eintrag ",[34,2625,2626],{},"deduplicatesOnIdempotencyKey: true"," deklariert, denn es ist das, was den Schlüssel trägt) + eine Zeile in der ",[34,2629,2630],{},"SEND_PROVIDERS","-Registry",[496,2633,2528],{},[496,2635,2636,2637,2640],{},"Der ",[34,2638,2639],{},"module","-Send-Export der Contribution: ein Versuch pro Aufruf, dasselbe typisierte Ergebnis, provenienzgeprüft und in der generierten Registry durch einen Mapped-Type-Guard fixiert",[479,2642,2643,2646,2649,2678,2683],{},[496,2644,2645],{},"4",[496,2647,2648],{},"Webhook-Adapter",[496,2650,2651,2654,2655,2658,2659,2662,2663,2666,2667,2670,2671,2673,2674,2677],{},[34,2652,2653],{},"webhooks\u002Fadapters\u002F\u003Ckind>.ts",", der einen rein parsenden ",[34,2656,2657],{},"InboundAdapter"," implementiert + eine Contribution in der komponierten Feedback-Registry (",[34,2660,2661],{},"providers\u002Ffeedback.ts",") + eine statische ",[34,2664,2665],{},"http.ts","-Route (",[34,2668,2669],{},"providerFeedbackWebhook('\u003Ckind>')"," — schreiben Sie den Pfad als Literal; diese URLs stehen in Provider-Konsolen, die hier niemand bearbeiten kann), am Eintrag als ",[34,2672,886],{}," zurückdeklariert (Pfad, Signaturschlüssel, Verifizierer-Vertrag und Setup-Panel). Die Delivery-Seite liest eine transport-geschlüsselte ",[34,2675,2676],{},"getProviderFeedbackStatus","-Query, sodass jede deklarierte Zeremonie den Schlüsselstatus und die Zeit des letzten Ereignisses des aktiven Providers erhält, ohne einen providerspezifischen Backend-Read hinzuzufügen",[496,2679,2680,2681],{},"falls ",[34,2682,862],{},[496,2684,2685,2686,2689,2690,2693,2694,2696],{},"Ein rein parsender ",[34,2687,2688],{},"webhook","-Modul-Export plus der Signaturvertrag, mit dem der Host ihn verifiziert; keine Route hinzuzufügen, denn das Feedback jedes gebundelten Transports kommt auf der einen gemeinsamen Route an (",[34,2691,2692],{},"\u002Fwebhooks\u002Fplugin\u002F\u003CpluginId>",", aus demselben Grund wie ein Core-Pfad in ",[34,2695,2665],{}," ausgeschrieben) und wird von dort über die komponierte Webhook-Registry verteilt",[479,2698,2699,2702,2705,2713,2717],{},[496,2700,2701],{},"5",[496,2703,2704],{},"Domain-Identity-Provider",[496,2706,2707,2710,2711],{},[34,2708,2709],{},"domains\u002Fproviders\u002F\u003Ckind>\u002F"," + ein Registry-Eintrag; die Zeilen landen in der gemeinsamen Tabelle ",[34,2712,1523],{},[496,2714,2680,2715],{},[34,2716,133],{},[496,2718,2719,2720,2722],{},"Ein ",[34,2721,129],{},"-Modul-Export, zur Kompositionszeit in dieselbe Registry eingetragen; seine Zeilen landen in derselben Tabelle unter der namespaced Art",[479,2724,2725,2728,2731,2747,2749],{},[496,2726,2727],{},"6",[496,2729,2730],{},"Doku",[496,2732,2733,2734,2738,2739,2742,2743,2746],{},"diese Seite — ein Abschnitt hier und eine Zeile, die Sie in der ",[1326,2735,2737],{"href":2736},"\u002Fdeveloper\u002Fenvironment-variables#provider-selection","Umgebungsvariablen-Referenz"," schreiben; es gibt keinen Generator, beide sind also handgeschrieben und beide sind durch ",[34,2740,2741],{},"apps\u002Fdocs\u002F__tests__\u002FproviderCatalogDocs.test.ts"," an den Eintrag ",[15,2744,2745],{},"gepinnt",", der fehlschlägt, wenn eine Art oder eine erforderliche Variable fehlt",[496,2748,2528],{},[496,2750,2531,2751,2754,2755,2758,2759,2762],{},[34,2752,2753],{},"README.md"," des Pakets und das in ",[34,2756,2757],{},"plugins.config.ts"," gelistete Paket — was es zugleich ",[1131,2760,2761],{},"gebundelt"," statt bloß geschrieben macht",[479,2764,2765,2768,2771,2795,2805],{},[496,2766,2767],{},"7",[496,2769,2770],{},"UI-Text-Overrides",[496,2772,2773,1122,2776,56,2779,2782,2783,1122,2786,2782,2789,1122,2792,176],{},[34,2774,2775],{},"apps\u002Fweb\u002Fapp\u002Futils\u002FtransportState.ts",[34,2777,2778],{},"TRANSPORT_LABEL_OVERRIDE",[34,2780,2781],{},"TRANSPORT_DESCRIPTION","), ",[34,2784,2785],{},"apps\u002Fweb\u002Fapp\u002Futils\u002FtransportDnsGuidance.ts",[34,2787,2788],{},"GUIDANCE",[34,2790,2791],{},"useRelayCredentialDraft.ts",[34,2793,2794],{},"TRANSPORT_PICKER_COPY",[496,2796,2797,2800,2801,2804],{},[15,2798,2799],{},"optional"," — jede dieser Tabellen ist ein ",[34,2802,2803],{},"Partial","-Override über einen aus dem Katalog abgeleiteten Fallback, sodass eine Art ohne Zeile mit dem Label des Eintrags, einem capability-abgeleiteten DNS-Absatz und einem neutralen Picker-Icon rendert",[496,2806,2807,2810,2811,2814,2815,72,2817,2819],{},[15,2808,2809],{},"Kein Äquivalent auf dieser Ebene."," Die Plugin-Codegen gibt den komponierten, rein datenbasierten Katalog nach ",[34,2812,2813],{},"apps\u002Fweb"," aus, sodass eine namespaced Art automatisch Picker, generisches Credential-Formular, Server-Allowlist und capability-abgeleitete DNS-Hinweise erreicht. Core-only-Texttabellen bleiben optionale Overrides; ein Plugin ohne Zeile nutzt sein Manifest-Label, das neutrale Picker-Icon und den aus ",[34,2816,725],{},[34,2818,952],{}," abgeleiteten Absatz",[11,2821,2822,2825,2826,2829,2830,2833,2834,2837,2838,1122,2840,2843,2844,596,2847,1122,2850,2853,2854,1122,2857,2860,2861,2863],{},[15,2823,2824],{},"Es gibt keinen Schritt „die Art deklarieren\"."," Die Core-Art-Union ist ",[34,2827,2828],{},"(typeof CORE_SEND_PROVIDER_CATALOG)[number]['kind']"," (und ",[34,2831,2832],{},"SendProviderKind"," im Backend ist diese Union plus die gebundelten Plugin-Arten), Schritt 1 ",[1131,2835,2836],{},"ist"," also die Deklaration: ",[34,2839,662],{},[34,2841,2842],{},"packages\u002Fshared\u002Fsrc\u002FtransportAlignment.ts",") re-exportiert sie, ",[34,2845,2846],{},"DELIVERY_PROVIDER_KINDS",[34,2848,2849],{},"getSendPathRequiredEnv",[34,2851,2852],{},"featureFlags.ts",") lesen sie, und ",[34,2855,2856],{},"PROVIDER_ENV_KEYS",[34,2858,2859],{},"setupSendingPresets.ts",") wird aus den ",[34,2862,814],{}," des Eintrags abgeleitet. Diese vier waren früher unabhängige Literale, an die man bei einem sechsten Provider denken musste; eines davon heute zu ergänzen, hieße die Duplizierung wieder einzuführen, die diese Nahtstelle beseitigt hat.",[11,2865,2866,2867,2870],{},"Fünf davon sind ",[15,2868,2869],{},"zur Compile-Zeit erzwungen",", was der Sinn der Nahtstelle ist:",[1114,2872,2873,2881,2902,2913,2947],{},[1117,2874,2875,573,2878,2880],{},[34,2876,2877],{},"_typecheck: { [K in CoreSendProviderKind]: SendProviderModule\u003CK> }",[34,2879,1555],{}," bricht den Build, wenn eine deklarierte Art keinen Adapter hat (Schritt 3).",[1117,2882,2883,573,2886,2889,2890,2893,2894,2897,2898,2901],{},[34,2884,2885],{},"_typecheck: { [K in FeedbackReportingSendProviderKind]: AnyInboundAdapter\u003CK> }",[34,2887,2888],{},"webhooks\u002Fadapters\u002Findex.ts"," bricht den Build, wenn eine Art ",[34,2891,2892],{},"hasProviderFeedback: true"," deklariert, ohne einen registrierten Feedback-Adapter — und, weil der Mapped Type jeden Schlüssel auf einen Adapter fixiert, dessen ",[34,2895,2896],{},"source"," genau dieser Schlüssel ist, auch bei einem unter der falschen Art abgelegten Adapter. ",[34,2899,2900],{},"_RegisteredFeedbackAdaptersAreDeclared"," in derselben Datei bricht ihn in der Gegenrichtung, nämlich bei einem Adapter, der für eine Art registriert ist, die laut Katalog nichts meldet (Schritt 4).",[1117,2903,2904,573,2907,2889,2910,2912],{},[34,2905,2906],{},"_ApiVerifiedKindsHaveDomainProviders",[34,2908,2909],{},"domains\u002Fproviders\u002Findex.ts",[34,2911,133],{}," deklariert, ohne einen registrierten Provider (Schritt 5).",[1117,2914,2915,2917,2918,2920,2921,72,2923,72,2925,72,2927,2929,2930,2933,2934,2936,2937,2940,2941,2943,2944,2946],{},[34,2916,2511],{}," — der Core-Eintragstyp in ",[34,2919,1276],{},", re-exportiert aus beiden Katalogmodulen — ist eine Union und kein Sack unabhängiger Felder: Er bricht den Build bei einem Core-Eintrag, der ",[34,2922,952],{},[34,2924,988],{},[34,2926,1024],{},[34,2928,1061],{}," auslässt oder der das Obhut-Paar in einer der beiden Richtungen aufspaltet — ",[34,2931,2932],{},"acceptanceSemantics: 'accepted'"," mit einer ID, die der Provider prägt (was einen mehrdeutigen Versand mit einem Idempotency Key beantworten würde, den der Provider nie gesehen hat), oder ",[34,2935,1285],{}," ohne die Obhut, die das Vorab-Binden dieser ID seine Voraussetzungen wert macht (Schritt 1). Dieselbe Union trägt das ",[15,2938,2939],{},"Feedback-Paar",": ",[34,2942,2892],{}," erfordert einen ",[34,2945,886],{},"-Kanal und ein Kanal erfordert das Boolean, sodass Sie weder einen Webhook deklarieren können, den die Messebene als „meldet nichts\" bewertet, noch eine Art mit Feedback und ohne Panel, ohne Endpunkt und ohne Route (Schritte 1 und 4).",[1117,2948,2949,596,2952,2955,2956,2958,2959,2961],{},[34,2950,2951],{},"bun run lint:env",[34,2953,2954],{},"apps\u002Fapi\u002Fscripts\u002Fcheck-env-docs.sh"," schlagen bei einem ",[34,2957,2562],{}," fehl, der außerhalb von ",[34,2960,2586],{}," gelesen oder undokumentiert gelassen wird (Schritt 2).",[11,2963,2964,2967,2968,2971,2972,2974,2975,2977,2978,2981,2982,2985,2986,2989,2990,1485,2992,2994],{},[15,2965,2966],{},"Schritt 7 ist bewusst NICHT erzwungen."," Die drei Web-Tabellen waren früher erschöpfende ",[34,2969,2970],{},"Record\u003CDeliveryProviderKind, …>","-Maps, sodass eine sechste Art ein Compile-Fehler in ",[34,2973,2813],{}," war — einer Datei, die das Bundle des Providers nichts angeht, was das Gegenteil von „einen Provider hinzuzufügen ist additiv\" ist. Heute sind es ",[34,2976,2803],{},"-Overrides: Eine Art ohne Zeile wird aus ihrem Katalogeintrag gerendert (",[34,2979,2980],{},"transportKindLabel"," fällt auf ",[34,2983,2984],{},"entry.label"," zurück, ",[34,2987,2988],{},"transportDnsGuidance"," auf den ",[34,2991,725],{},[34,2993,952],{},"-Absatz, der Picker auf das Label des Eintrags und ein neutrales Icon). Der generierte Web-Katalog gibt Plugin-Arten dieselben Fallbacks. Nichts bricht, und nichts bleibt leer — schreiben Sie also ein Core-Override, wenn diese Oberfläche jenen Provider anders benennen soll, und nicht, weil der Build Sie dazu zwingt.",[11,2996,2997,2998,3001,3002,3004,3005,3008,3009,3012,3013,79],{},"Der Unterschied zwischen den Ebenen bei Schritt 4 ist die Frage, ",[15,2999,3000],{},"wer verifiziert",", und die Antwort ist auf beiden dieselbe: der Host. Der ",[34,3003,2688],{},"-Export eines Plugins ist rein parsend und sieht nie ein unverifiziertes Byte, eine Contribution, deren Webhook keinen Signaturvertrag oder keine Replay-Vorkehrungen trägt, scheitert an der Manifest-Validierung, statt einen offenen Endpunkt auszuliefern, und das Feedback landet auf einer Route — ",[34,3006,3007],{},"POST \u002Fwebhooks\u002Fplugin\u002F\u003CpluginId>",", hinter der Autorisierungs-Nahtstelle für gehostete Contributions —, während eine Core-Art einen eigenen statischen Pfad ",[34,3010,3011],{},"\u002Fwebhooks\u002F\u003Ckind>"," behält. Zwei Routing-Formen, ein Sicherheitsniveau. Siehe ",[1326,3014,3016],{"href":3015},"\u002Fdeveloper\u002Fplugin-contributions#feedback-webhook","Plugin-Contributions → Feedback-Webhook",[11,3018,3019,3020,3023,3024,3026,3027,566,3029,3031,3032,56,3035,3038],{},"Ein gebundelter Plugin-Transport wird generiert statt geschrieben und erreicht diese Union deshalb nie — für ihn wird die eine Deklaration, deren Voraussetzungen außerhalb des Katalogs liegen, stattdessen zur ",[15,3021,3022],{},"Kompositionszeit"," verweigert: ",[34,3025,1311],{}," wirft beim Bauen des Katalogs, wenn ein Plugin-Eintrag ",[34,3028,2932],{},[34,3030,1285],{}," deklariert, benennt den Eintrag und verweist auf den PREREQUISITES-Hinweis. Die beiden fail-closed-Standardwerte und dieser Guard haben eigene Suites (",[34,3033,3034],{},"undeclaredSemanticsFailClosed",[34,3036,3037],{},"pluginCustodyGuard","), die einen generierten Katalog mocken, weil der ausgelieferte keine der beiden Formen erzeugen kann.",[11,3040,3041,3042,3045,3046,56,3049,56,3052,56,3055,56,3058,56,3061,56,3064,56,3067,56,3070,56,3073,56,3076,56,3079,3082,3083,3085,3086,3089,3090,3093,3094,3096,3097,3099,3100,3103,3104,3107,3108,3110,3111,3114],{},"Der Rest wird zur Laufzeit durch die gemeinsamen Conformance-Suites in ",[34,3043,3044],{},"lib\u002FsendProviders\u002F__tests__\u002F"," erzwungen (",[34,3047,3048],{},"registry",[34,3050,3051],{},"providers",[34,3053,3054],{},"selection",[34,3056,3057],{},"dispatch.integration",[34,3059,3060],{},"transportIdDispatch",[34,3062,3063],{},"twoTransportsSameKind",[34,3065,3066],{},"unknownTransportFailsClosed",[34,3068,3069],{},"transportSecrets",[34,3071,3072],{},"dispatchExtras",[34,3074,3075],{},"fallbackEligibility",[34,3077,3078],{},"systemMailExtras",[34,3080,3081],{},"feedbackRoutes","), die allesamt über jede Katalogart iterieren — eine neue Art tritt ihnen also dadurch bei, dass sie existiert, und nicht dadurch, dass jemand daran denkt, einen Fall zu ergänzen. ",[34,3084,3081],{}," ist die Suite, die Paketgrenzen überschreitet: Sie läuft den echten ",[34,3087,3088],{},"httpRouter"," ab und schlägt fehl, wenn ein deklarierter ",[34,3091,3092],{},"providerFeedback.webhookPath"," keine von ",[34,3095,2665],{}," bediente Route ist, oder wenn eine ",[34,3098,3011],{},"-Route für eine Art existiert, deren Eintrag keinen Kanal deklariert — was genau die Paarung ist, von der der von einer Betreiberin eingefügte Endpunkt abhängt (Schritt 4). Ihr Gegenstück ist ",[34,3101,3102],{},"webhooks\u002F__tests__\u002FadapterRegistry.test.ts",", das jeden deklarierten Pfad über den echten Router abruft und nachweist, dass die Antwort vom registrierten Adapter ",[1131,3105,3106],{},"dieser Art"," kam. Das ist die Hälfte, die weder der Mapped Type noch ",[34,3109,3081],{}," sehen kann: Die Guards beweisen, dass die Registry sauber geschlüsselt ist und die Route existiert, aber ",[34,3112,3113],{},"http.route({ path: '\u002Fwebhooks\u002Fses', handler: providerFeedbackWebhook('resend') })"," erfüllt beides und typecheckt.",[81,3116,3118],{"id":3117},"design-notizen","Design-Notizen",[1114,3120,3121,3148,3167,3198],{},[1117,3122,3123,3126,3127,573,3130,3133,3134,3137,3138,3141,3142,3144,3145,3147],{},[15,3124,3125],{},"Caching",": Der LLM-Client wird auf Modulebene gecacht (",[34,3128,3129],{},"cachedClient",[34,3131,3132],{},"lib\u002FllmProvider.ts","), sodass jeder Prozess ihn einmal auflöst. Es gibt kein exportiertes ",[34,3135,3136],{},"clear*Cache()"," — Tests setzen den Modulzustand mit ",[34,3139,3140],{},"vi.resetModules()"," zurück. Der Send-Provider hat überhaupt keinen Cache: Er nutzt die statische ",[34,3143,2630],{},"-Registry mit der pro Aufruf übergebenen Art (",[34,3146,43],{},").",[1117,3149,3150,2940,3153,3156,3157,3160,3161,3163,3164,3166],{},[15,3151,3152],{},"Werfen bei Unbekanntem",[34,3154,3155],{},"providerFor"," wirft einen sprechenden Fehler ",[34,3158,3159],{},"Unknown send provider: \u003Ckind>",", sodass ein Tippfehler laut scheitert, statt still zurückzufallen. (Ein nicht erkannter ",[34,3162,36],{},"-Wert — z. B. ",[34,3165,414],{}," — fällt still auf den Standard-OpenAI-Client ohne Base-URL durch.)",[1117,3168,3169,2940,3175,3177,3178,3181,3182,3185,3186,56,3188,56,3190,56,3192,3194,3195,3197],{},[15,3170,3171,3172],{},"Keine Secrets in ",[34,3173,3174],{},"getLLMConfig()",[34,3176,3174],{}," gibt einen Snapshot der aufgelösten Einstellungen mit ",[34,3179,3180],{},"hasApiKey: boolean"," statt des Schlüssels selbst zurück, sicher zum Loggen. (Der Send-Provider hat keinen Config-Getter — ",[34,3183,3184],{},"SendProviderModule"," exponiert nur ",[34,3187,43],{},[34,3189,2157],{},[34,3191,1977],{},[34,3193,2161],{}," und das optionale ",[34,3196,2616],{},", das vertraglich env-frei ist.)",[1117,3199,3200,3203,3204,3207,3208,3211,3212,3215,3216,72,3219,3222,3223,72,3225,3228,3229,3231,3232,3235,3236,3239],{},[15,3201,3202],{},"Embeddings",": Die Embedding-Ebene wird UNABHÄNGIG von der Sprachebene durch ",[34,3205,3206],{},"resolveEmbeddingModel(ctx)"," (über denselben Punkt ",[34,3209,3210],{},"resolveAiConfig(ctx)",") aufgelöst und ist STANDARDMÄSSIG LOKAL — ein lokaler OpenAI-kompatibler ",[34,3213,3214],{},"\u002Fembeddings","-Sidecar (",[34,3217,3218],{},"LOCAL_EMBEDDING_BASE_URL",[34,3220,3221],{},"LOCAL_EMBEDDING_MODEL","), damit Retrieval unter jedem Sprach-Provider funktioniert (inkl. Anthropic, das keine Embeddings-API hat). Optionale gehostete Embedder (",[34,3224,175],{},[34,3226,3227],{},"google",") sind Overrides mit eigenem verschlüsseltem Schlüssel; die Env-Variable ",[34,3230,517],{}," bleibt der Deployment-Fallback. Ein falsch konfigurierter gehosteter Embedder wirft zur Auflösungszeit einen umsetzbaren Fehler (nie einen stillen leeren Vektor); eine bekannte Dimensions-Abweichung gegenüber ",[34,3233,3234],{},"EMBEDDING_DIMENSIONS"," wirft zur Auflösungszeit, und ",[34,3237,3238],{},"assertEmbeddingDimension"," erzwingt die Breite zur Schreibzeit.",[3241,3242,3243],"style",{},"html pre.shiki code .sDN9O, html code.shiki .sDN9O{--shiki-default:#6A737D;--shiki-dark:#768390}html pre.shiki code .s7YZ4, html code.shiki .s7YZ4{--shiki-default:#D73A49;--shiki-dark:#F47067}html pre.shiki code .sPO5f, html code.shiki .sPO5f{--shiki-default:#6F42C1;--shiki-dark:#DCBDFB}html pre.shiki code .sYgZi, html code.shiki .sYgZi{--shiki-default:#24292E;--shiki-dark:#ADBAC7}html pre.shiki code .stnAF, html code.shiki .stnAF{--shiki-default:#E36209;--shiki-dark:#F69D50}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 .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 .s-HuK, html code.shiki .s-HuK{--shiki-default:#032F62;--shiki-dark:#96D0FF}",{"title":101,"searchDepth":223,"depth":223,"links":3245},[3246,3247,3248,3251,3260,3263],{"id":83,"depth":223,"text":84},{"id":163,"depth":223,"text":27},{"id":605,"depth":223,"text":31,"children":3249},[3250],{"id":676,"depth":264,"text":677},{"id":1320,"depth":223,"text":1321,"children":3252},[3253,3254,3255,3256,3257,3258,3259],{"id":1333,"depth":264,"text":1334},{"id":1446,"depth":264,"text":1447},{"id":1545,"depth":264,"text":1546},{"id":1736,"depth":264,"text":1737},{"id":2040,"depth":264,"text":2041},{"id":2193,"depth":264,"text":2194},{"id":2296,"depth":264,"text":2297},{"id":2325,"depth":223,"text":2326,"children":3261},[3262],{"id":2363,"depth":264,"text":2364},{"id":3117,"depth":223,"text":3118},"Austauschbare Provider-Abstraktionen für LLM, E-Mail-Versand, Benachrichtigungen, Vector Stores und Analytics, pro Deployment auswählbar, damit Self-Hoster Implementierungen ohne Code-Änderungen tauschen können.","md",{},"\u002Fdeveloper\u002Fproviders",{"title":6,"description":3264},"3.developer\u002F15.providers","-3Txp5MoIlnhKDt7-xw0OoeXr5iAwLmfpMuDMgI6ymo",[3272,3276],{"title":3273,"path":3274,"stem":3275,"children":-1},"Postbox-Architektur","\u002Fdeveloper\u002Fpostbox-architecture","3.developer\u002F14.postbox-architecture",{"title":3277,"path":3278,"stem":3279,"children":-1},"Kampagnen-Interna","\u002Fdeveloper\u002Fcampaign-internals","3.developer\u002F16.campaign-internals",1786915108598]