Bundles
Ein Bundle ist eine versionierte Datei, die eine funktionierende Konfiguration aus einer Instanz herauslöst und in eine andere einsetzt: einen Agenten, einen MCP-Connector, eine App oder eine Wissenssammlung. Der Export erzeugt ein YAML- oder JSON-Dokument, der Import legt daraus die Ressource an und merkt sich, was er geschrieben hat — damit eine spätere Version per Drei-Wege-Merge eingespielt werden kann, ohne lokale Änderungen zu überschreiben.
Export und Import funktionieren und werden unterstützt, sind aber noch nicht stabil:
- Das Dateiformat kann sich zwischen Releases ändern. Ein Bundle ist zum Umziehen und Bereitstellen gedacht, nicht als Langzeit-Backup. Exportieren Sie neu, statt alte Dateien aufzubewahren.
- Es gibt keine Registry. Bundles sind Dateien, die Sie selbst von einer Instanz zur anderen bewegen.
- Aktualisieren gibt es nur über die API. Trifft eine neue Version auf eine lokale Änderung am selben Feld, meldet die Route den Konflikt und überschreibt nichts — dieser Fall braucht noch eine Oberfläche.
- Deinstallieren löscht. Die vom Bundle erstellten Ressourcen werden entfernt; erhalten bleibt nur, was Sie seit der Installation lokal geändert haben.
Nicht alle Routen sind gleich geschützt:
GET /api/agents/{id}/bundleundGET /api/widgets/{public_id}/bundleerreicht jede angemeldete Person, die die passende Berechtigung hält — genau wie die daneben liegenden RoutenGET /api/agents/{id}/exportbzw. den übrigen App-Katalog. Der Handler prüftagent:create/agent:update/agent:deletebeziehungsweiseapp:manage; der App-Export ist zusätzlich auf die eigene Sichtbarkeit begrenzt, eine fremde private App lässt sich also nicht exportieren.GET /api/knowledge-sets/{id}/bundleverlangtknowledgeset:manage— dieselbe Berechtigung wie die übrigen Routen einer Wissenssammlung. Eine Sammlung, die ein Agent oder ein Thread für sich selbst angelegt hat, lässt sich ebenso wenig exportieren wie eine gelöschte.GET /api/tool-references/{id}/bundleund die fünf/api/bundles/*-Routen (Installieren, Aktualisieren, Auflisten, Drift, Deinstallieren) sind Instanz-Administratoren vorbehalten.
Was ein Bundle niemals enthält
Das ist die tragende Eigenschaft des Formats, nicht ein Nebeneffekt:
env-Werte eines Agenten werden geleert. Nur Name und Beschreibung reisen mit.- MCP-Header-Werte müssen
${VAR}-Platzhalter sein (optional mit einem vorangestellten Auth-Schema wieBearer). Alles andere wird geleert und im Ergebnis als Warnung gemeldet. - Reservierte Felder werden verworfen:
system,systemAccessFeature,systemAccessAdminOnly,default,alias,tokenLimits. - Ein Wissens-Bundle enthält keine Dokumente. Es beschreibt die Quellen, aus denen sich eine Wissenssammlung speist; der Bestand selbst reist weiterhin über Export und Import einer Wissenssammlung.
- Was eine Wissensquelle an die exportierende Organisation bindet, reist
nicht mit: Anmeldenamen (
usernamebei WebDAV, Nextcloud und SMB,userbei SFTP), die Windows-Domäne (domain), die OneDrive-Freigabelinks und der S3-endpoint. Der Endpoint bestimmt, welcher Objektspeicher angesprochen wird — bliebe er stehen, läse eine umgehängte Bucket-Angabe weiterhin vom Server der exportierenden Organisation. Alle diese Felder werden auf der Zielinstanz neu gesetzt. - Zugangsdaten, Threads, Läufe, Erinnerungen, Team- und Workspace-Daten sind gar nicht erst Teil der projizierten Struktur.
Beim Import werden die meisten dieser Regeln erneut angewandt — ein von Hand
geschriebenes Bundle kann sie also nicht umgehen: reservierte Felder werden erneut
verworfen, und ein MCP-Header, der einen literalen Wert statt eines
${VAR}-Platzhalters trägt, wird erneut geleert und gemeldet.
Eine Ausnahme: env-Werte eines Agenten werden beim Import nicht erneut
geleert. Der Export leert sie immer; ein von Hand geschriebenes Bundle, das einen
env-Wert mitbringt, wird jedoch mit diesem Wert installiert. Beim Aktualisieren
gilt das nicht — dort bleibt der auf dieser Instanz gesetzte Wert stehen, denn
env-Werte gelten als instanzlokal.
Endpunkte
| Methode | Pfad | Zweck |
|---|---|---|
GET | /api/agents/{id}/bundle | Agent exportieren |
GET | /api/tool-references/{id}/bundle | MCP-Connector exportieren |
GET | /api/widgets/{public_id}/bundle | App exportieren (neueste nicht archivierte Version) |
GET | /api/knowledge-sets/{id}/bundle | Wissenssammlung exportieren (nur Quellen, kein Bestand) |
GET | /api/assistants/{assistant_id}/projects/{project_id}/bundle | Projekt als zusammengesetztes Bundle exportieren (siehe Projekt-Bundles) |
POST | /api/bundles/install | Bundle installieren |
POST | /api/bundles/installations/{id}/update | Installation auf eine neue Version heben |
GET | /api/bundles/installations | Installationen auflisten |
GET | /api/bundles/installations/{id}/drift | Lokale Änderungen melden |
DELETE | /api/bundles/installations/{id} | Deinstallieren |
Export akzeptiert ?format=yaml (Standard) oder ?format=json sowie ?id= und
?version=, um die Bundle-Identität zu setzen. Ohne Angabe entsteht
local/<name> in Version 0.0.0.
Beispiel: Agent
Die Ausgabe von GET /api/agents/{id}/bundle, gekürzt um leere Felder: der
Agenten-Payload enthält zusätzlich rund fünfzehn Felder mit null bzw. "",
die hier der Lesbarkeit halber weggelassen sind. Jedes gezeigte Feld entspricht
exakt der tatsächlichen Ausgabe und ist per Test dagegen abgesichert. metadata
enthält beim echten Export zusätzlich einen provenance-Block (Instanz, Benutzer,
Zeitpunkt); ohne ?id=/?version= lauten die Vorgaben local/<name> und 0.0.0. Beachten
Sie value: "" —
der gesetzte env-Wert wurde beim Export geleert.
apiVersion: bundle.basepeak.ai/v1
kind: Bundle
metadata:
id: bp/bauamt-assistent
version: 1.0.0
spec:
type: agent
payload:
name: Bauamt Assistent
description: Beantwortet Fragen zu Bauanträgen.
prompt: Du bist ein hilfsbereiter Assistent der Bauverwaltung.
model: m1abc
tools:
- office-bundle
env:
- name: REGION_CODE
description: Regionalschlüssel
value: ""
existing: false
requires:
- kind: model
ref: m1abc
policy: substitute
- kind: tool
ref: office-bundle
policy: strip
Beispiel: MCP-Connector
Bearer ${MCP_HEADER_0} bleibt erhalten; der Header X-Leaked trug einen
literalen Wert und wurde geleert.
apiVersion: bundle.basepeak.ai/v1
kind: Bundle
metadata:
id: bp/nextcloud-connector
version: 1.0.0
spec:
type: connector
payload:
name: nextcloud-mcp
reference: mcp://nextcloud-mcp
bundle: true
toolMetadata:
name: Nextcloud
mcpServer:
url: https://cloud.example.test/mcp
headers:
- name: Authorization
value: Bearer ${MCP_HEADER_0}
- name: X-Leaked
allowedTools:
- list_files
- read_file
Ein Connector mit mcpServer.auth (universeller OAuth-Adapter) wird beim Import
abgelehnt — er würde sonst konfiguriert aussehen und sich stillschweigend nicht
authentifizieren können.
Beispiel: App
document ist ein eingebettetes JSON-Objekt, keine Zeichenkette.
apiVersion: bundle.basepeak.ai/v1
kind: Bundle
metadata:
id: bp/antragsstatus
version: 1.0.0
spec:
type: app
payload:
name: Antragsstatus
description: Zeigt den Status eines Antrags.
document:
root: r
elements:
r:
type: Card
state: {}
Beispiel: Wissen
Ein Wissens-Bundle beschreibt Quellen, nicht Inhalte. Bundes- und
Landesrecht reisen unverändert mit — jede Instanz zieht dieselben Gesetze aus
derselben Quelle, und genau das macht ein Fachpaket überhaupt erst
installierbar. Eine mandantenspezifische Adresse steht dagegen als
substitute unter spec.requires: Bucket (s3:), Host (sftp:), Server
(smb:), Basis-URL (webdav:, nextcloud:) — und der Host eines
Ratsinformationssystems (ris:).
Ein RIS gehört genau einer Kommune. Würde es unverändert mitreisen, zöge die installierende Verwaltung die Ratsdokumente der exportierenden in ihre eigene Wissenssammlung — öffentlich abrufbar und trotzdem falsch. Deshalb ist der RIS-Host substituierbar wie ein Bucket.
Die URL-Liste einer Website-Quelle (websiteCrawlingConfig.urls) reist dagegen
unverändert mit: eine Liste lässt sich nicht als eine einzelne Referenz
abbilden. Sie steht offen im Payload — prüfen Sie sie nach dem Import,
sonst crawlt Ihre Instanz weiter die Website der exportierenden Organisation.
apiVersion: bundle.basepeak.ai/v1
kind: Bundle
metadata:
id: bp/bauamt-wissen
version: 1.0.0
spec:
payload:
dataDescription: Bebauungspläne, Satzungen und einschlägige Gesetze.
flowBlueprint: bpai
name: Bauamt-Wissen
sources:
gesetze-1:
gesetzeConfig:
selectedLaws:
- baugb
- bauo-nrw
syncSchedule: 0 3 * * *
s3-1:
s3Config:
bucket: acme-docs
prefix: satzungen/
region: eu-central-1
requires:
- kind: knowledgeSource
policy: substitute
ref: s3:acme-docs
type: knowledge
Installiert wird das mit einer Zuordnung derselben Art:
curl -X POST https://<instanz>/api/bundles/install \
-H 'Content-Type: application/json' \
-d '{"bundle": "<inhalt der yaml-datei>",
"substitutions": {"s3:acme-docs": "s3:dormagen-docs"}}'
Ersetzt wird nur die Adresse selbst. prefix, region, port, dir und
share reisen unverändert mit und lassen sich nach der Installation
wie jede andere Einstellung ändern — dieselbe Ordnerstruktur in einem anderen
Bucket ist der Normalfall, und eine Zuordnung für jedes einzelne Feld zu
verlangen, würde die Installation mit Eingaben aufhalten, die niemand ändern
wollte.
Die Schlüssel (gesetze-1, s3-1) identifizieren eine Quelle über Instanzen
hinweg: Eine spätere Version wird Quelle für Quelle damit zusammengeführt.
Ein echter Export vergibt sie als <typ>-<kurzer Hash>. Eine Quelle, die Sie
nach der Installation selbst hinzufügen, trägt keinen solchen Schlüssel: Sie
gehört Ihnen, gilt nicht als lokale Abweichung, und ein Update rührt sie nicht
an. Deinstallieren wird dann abgelehnt — das Löschen der Sammlung würde
Ihre eigene Quelle mitnehmen, also entfernen Sie sie vorher oder behalten Sie
die Sammlung.
Abhängigkeiten: strip, block, substitute
Jede externe Referenz steht unter spec.requires mit einer Regel:
strip— fehlt sie, wird sie verworfen und als Warnung gemeldet.block— fehlt sie, schlägt die Installation fehl.substitute— die Administration muss beim Import eine lokale Entsprechung angeben. Modelle, Modell-Provider, die Tools einer App und die Adressen mandantenspezifischer Wissensquellen sind so eingestuft, weil sie instanzspezifisch sind.
Für eine Wissensquelle (kind: knowledgeSource) prüft die Instanz nur die
Form der Zuordnung — Typ und nicht-leere Adresse. Ob ein Bucket existiert und
von hier aus lesbar ist, beantwortet erst die erste Synchronisation; eine Prüfung
an dieser Stelle könnte immer nur „ja“ sagen.
curl -X POST https://<instanz>/api/bundles/install \
-H 'Content-Type: application/json' \
-d '{"bundle": "<inhalt der yaml-datei>",
"substitutions": {"m1abc": "m1xyz"}}'
Aktualisieren, Drift, Deinstallieren
Eine Installation merkt sich die exakten Bytes, die sie geschrieben hat. Beim Update wird daraus ein Drei-Wege-Merge:
- Wurde lokal nichts geändert, greift die neue Version durch.
- Lokale Änderungen an unberührten Feldern bleiben erhalten.
- Bei echtem Konflikt wird nichts geschrieben und
409mit der Konfliktliste geantwortet. MitonConflict: "keep-mine"oder"take-theirs"wird er aufgelöst.
GET .../drift meldet lokale Abweichungen als JSON-Pointer-Pfade. DELETE
entfernt, was die Installation unverändert besitzt, lässt lokal geänderte
Ressourcen stehen und meldet sie. Apps werden archiviert statt gelöscht, damit
laufende Instanzen weiterlaufen.
In der Oberfläche
Alle Endpunkte sind auch im Administrationsbereich erreichbar.
Exportieren — jeweils dort, wo die Ressource verwaltet wird:
| Typ | Ort |
|---|---|
| Agent | Agenten-Detailseite → Export → Als Bundle exportieren (mit Abhängigkeiten) |
| MCP-Connector | Werkzeuge → Connector auswählen → Als Bundle exportieren |
| App | Apps → Zeilenaktion Als Bundle exportieren |
| Wissen | Wissenssammlungen → Zeilenaktion Als Bundle exportieren |
Der Agenten-Export bietet weiterhin die bisherigen Manifest-Formate (YAML/JSON) an.
Manifest und Bundle sind verschiedene Artefakte: nur ein Bundle lässt sich über
POST /api/bundles/install installieren.
Wurde beim Export etwas geleert oder entfernt, erscheint ein Hinweisdialog. Das ist die einzige Stelle, an der Sie davon erfahren — die Bundle-Datei selbst sieht unauffällig aus.
Der Installationsdialog nimmt noch keine substitutions entgegen. Ein Bundle,
das eine mandantenspezifische Adresse mitbringt — jede S3-, SFTP-, WebDAV-,
Nextcloud-, SMB- oder RIS-Quelle, und ebenso ein Agent mit Modellbezug —
installieren Sie deshalb über POST /api/bundles/install wie oben gezeigt. Ein
Bundle, das nur öffentliche Quellen enthält, lässt sich über die Oberfläche
installieren.
Installieren und verwalten — unter Bundles (nur für Instanz-Administratoren): installieren, installierte Bundles auflisten, lokale Änderungen (Drift) erkennen und deinstallieren. Beim Deinstallieren werden die erstellten Ressourcen entfernt, sofern sie seit der Installation unverändert sind; lokal geänderte bleiben stehen und werden gemeldet.
Auf Instanzen mit aktiviertem Papierkorb wird eine Wissenssammlung dabei
dorthin verschoben statt sofort gelöscht — ohne Papierkorb ist das Löschen
endgültig, samt der bereits eingelesenen Dokumente —
und solange ein Agent oder ein Thread sie noch verwendet, wird das
Deinstallieren abgelehnt — derselbe Schutz, den auch
DELETE /api/knowledge-sets/{id} durchsetzt.
Grenzen dieser Phase
Über die oben genannten Einschränkungen hinaus:
agent,app,connector,knowledgeundproject.toolConfigunddataProtectionwerden gelesen, aber beim Import abgelehnt.- Verschachtelte Bundles (
spec.includes) sind nur innerhalb einesproject-Bundles erlaubt — dort tragen sie dessen Agent, Wissenssammlungen, Connectoren und App. Details unter Projekt-Bundles. - Eine Datei ohne
metadata.idlässt sich installieren, aber nicht aktualisieren.