Die offizielle Companion-Layer `@kirchdev/duxt-meilisearch` verarbeitet den gesamten
[`duxt:search:records`-Snapshot](/reference/hooks). URL-basierte IDs, normalisierter
Klartext, Quelle, Version und Sprache bleiben erhalten, auch bei generierten
Abschnitten. Meilisearch ist optional; die eingebaute Suche braucht keinen Dienst.

::package-managers{command="add @kirchdev/duxt-meilisearch"}
::

```ts [nuxt.config.ts]
export default defineNuxtConfig({
  extends: ['@kirchdev/duxt', '@kirchdev/duxt-meilisearch'],
  duxtMeilisearch: {
    host: process.env.MEILISEARCH_HOST,
    index: 'docs',
    searchKey: process.env.MEILISEARCH_SEARCH_KEY,
    syncKey: process.env.MEILISEARCH_SYNC_KEY,
    taskKey: process.env.MEILISEARCH_TASK_KEY,
    sync: {
      enabled: process.env.MEILISEARCH_SYNC_ENABLED === 'true',
      deployment: process.env.DUXT_DEPLOYMENT === 'production'
        ? 'production' : 'preview'
    }
  }
})
```

Aktiviere beide Produktionsoptionen ausschließlich im Veröffentlichungsjob.
`syncKey` allein erlaubt keine Schreibzugriffe. Entwicklung, Nuxt prepare und
erkannte PR- oder Vorschaukontexte überspringen den Sync. Andere CI-Systeme müssen
Vorschauen auf `deployment: 'preview'` abbilden. Bei Cloudflare Pages muss
`DUXT_PRODUCTION_BRANCH` außerdem mit `CF_PAGES_BRANCH` übereinstimmen.

Verwende einen eigenen Index `docs`. Der private Build-Schlüssel braucht
`indexes.create`, `indexes.get`, `indexes.delete`, `indexes.swap`,
`settings.update`, `documents.add` und `tasks.get` für `docs` und `docs__duxt_*`.
Die Layer erstellt einen eindeutigen Staging-Index, wartet auf Einstellungen und
Importaufträge, tauscht ihn atomar mit `docs`, wartet auf den erfolgreichen Tausch
und entfernt dann den alten Snapshot. Spätere Builds entfernen gelöschte Seiten;
ein leerer Snapshot leert den Index. Fehlgeschlagene Importe lassen den bisherigen
Index unverändert. Bei unklarem Tausch bleibt Staging erhalten, damit keine
möglicherweise veröffentlichten Daten gelöscht werden. Aufräumfehler werden
angezeigt. Serialisiere Veröffentlichungen, wenn die Build-Reihenfolge zählt.
Der erste Sync erstellt einen leeren Zielindex, sobald Staging bereit ist.

Der private Schlüssel gelangt nie in die Browserkonfiguration. Ohne `searchKey`
bleibt die eingebaute Suche aktiv, während der Sync trotzdem möglich ist. Für den
bestehenden Suchdialog brauchst du einen separaten Schlüssel mit **nur** der
Aktion `search` und **nur** dem konfigurierten Index `docs`. Der standardmäßige
Suchschlüssel gilt für alle Indizes und ist ungeeignet. Master- und Admin-Schlüssel
gehören niemals in die öffentliche Konfiguration. Die Layer lehnt die
Wiederverwendung eines bekannten Build-Schlüssels ab; die Rechte undurchsichtiger
Schlüssel kann sie nicht erkennen. Die sichere Vergabe liegt beim Betreiber.
Reine Browsersuche braucht keinen privaten Build-Schlüssel. Erlaube die Herkunft
deiner Website in der CORS-Konfiguration des Dienstes.

Die Suche behält je Artefakt die gewählte oder Standardversion und die
Sprach-Fallback-Kette. Eine übersetzte Seite verdrängt ihr Original; ohne
Übersetzung bleibt die Originalsprache auffindbar. Dafür liest die Layer ein
zwischengespeichertes Seitenpfadverzeichnis über Content und schließt abgedeckte
Seiten je Quelle, Version und Sprache vor Bewertung und Begrenzung aus. Das nur
zum Filtern verwendete Feld `page` enthält die URL ohne Anker; versteckte
Abschnitte verbrauchen keine Plätze im 1.000-Treffer-Fenster. Synchronisiere vor
der Browsersuche einmal, wenn ein früherer Provider-Build den Index erstellt hat.
Dienstfehler liefern keine Treffer und eine
Konsolenwarnung. HTTP-Anfragen haben 30 Sekunden Zeit, Build-Aufträge fünf
Minuten. `sync.taskTimeoutMs` erlaubt höchstens eine Stunde.

Das Paket hat seine eigene Release-Version und sein eigenes Changelog. duxt
bekommt weder SDK noch Schlüssel noch Dienstkonfiguration. Eigene Oberflächen
nutzen `@kirchdev/duxt-meilisearch/search`; Build-Skripte können
`@kirchdev/duxt-meilisearch/sync` mit denselben Produktionsoptionen nutzen.

Siehe Meilisearchs [atomaren Indextausch](https://specs.meilisearch.dev/specifications/text/0191-swap-indexes-api.html),
[asynchrone Aufträge](https://www.meilisearch.com/docs/reference/api/async-task-management/get-task)
und [eingeschränkte API-Schlüssel](https://www.meilisearch.com/docs/reference/api/keys/create-api-key).

Meilisearch 1.54.3 verbirgt den globalen `indexSwap`-Auftrag (`indexUid: null`)
vor indexbeschränkten Schlüsseln, selbst wenn beide Indizes erlaubt sind. Ergänze
bei einem eingeschränkten Schreibschlüssel einen privaten `taskKey` mit nur
`actions: ['tasks.get']` und `indexes: ['*']`. Er wird ausschließlich zum Abfragen
von Aufträgen genutzt und niemals veröffentlicht. Ohne ihn muss `syncKey` selbst
globale Aufträge lesen können. Ein verborgenes 404 bestätigt keine erfolgreiche
Veröffentlichung; Staging bleibt erhalten. Dieses Verhalten wurde am echten
Dienst überprüft.