# Eine App für die GenAI-Plattform bauen
So baust du heute ein App-Paket, das die Plattform selbst ausrollen und verdrahten kann: ein Git-Repository mit einem Terraform-Modul und einem Ordner `genai-cli/`. Darin steht, was die App braucht (Redis, LiteLLM, Datenbank, Admin-Zugang, Logo) – den Rest erledigt der Deployment-Workflow.
Stand: 13.10.2026
> [!NOTE] Was dieses Dokument ist
> Die Anleitung beschreibt das Format, das die Plattform **heute** einliest. Eine Einreich-API gibt es noch nicht; neue Apps kommen über das Plattform-Team in den Katalog ([Übergabe](#uebergabe)). Ein einfacheres Paketformat ist in Arbeit – wenn es kommt, wird diese Seite fortgeschrieben, nicht ersetzt.
> [!TIP] Für KI-Assistenten
> Diese Seite gibt es auch als reines Markdown ([app-entwicklung.md](app-entwicklung.md)) und als Einstieg [llms.txt](llms.txt). Die harten Regeln stehen kompakt im Abschnitt [Regeln für KI-Assistenten](#ki-regeln). Eine lauffähige Vorlage liegt unter [vorlage/](vorlage/).
## Auf einen Blick {#ueberblick}
Eine App ist heute ein **Modul-Repository** in der GitLab-Gruppe `genai-plattform/applications`. Die Plattform liest daraus den Ordner `genai-cli/`, erzeugt pro Umgebung die Terragrunt-Dateien und rollt aus.
1 · DuModul-Repo mit genai-cli/, SemVer-Tag
→
2 · Plattform-Teamimportiert das Repo in den App-Katalog
→
3 · BestellungApp wird für eine Umgebung gewählt, Abhängigkeiten kommen mit
→
4 · WorkflowSecrets erzeugen, Dateien rendern, Terragrunt ausführen
Was die Plattform für dich übernimmt:
- **Abhängigkeiten** – brauchst du Redis, wird Redis mit ausgerollt und vor deiner App angewendet.
- **Zugangsdaten** – Redis-Passwort, LiteLLM-Schlüssel, Datenbank, S3 und eigene Secrets werden erzeugt, verschlüsselt (SOPS) abgelegt und deiner App als Eingabe übergeben.
- **Domain, TLS, Ingress-Klasse, Pull-Secrets** – kommen aus der Umgebung.
- **Admin-Zugang** – die Umgebung hat einen Admin (E-Mail + Passwort), den deine App übernehmen kann.
- **Katalog-Kachel** – Name, Beschreibung und Logo erscheinen im Katalog und im AI Hub.
- **Pflicht-Labels** – `genai.govdigital.de/*` setzt die Plattform selbst.
## Aufbau des Repositorys {#repo}
meine-app/
├── main.tf # Terraform-Modul: meist ein helm_release
├── variables.tf # Eingaben, die die Plattform liefert
├── outputs.tf
├── terraform.tf # required_providers, keine provider-Blöcke
├── chart/ # eigenes Helm-Chart (optional, oder Chart aus Repo)
└── genai-cli/ # ← das liest die Plattform
├── terragrunt.hcl.genai # Pflicht: wie das Modul in der Umgebung aufgerufen wird
├── metadata.json # Name, Beschreibung, Katalogtexte
├── logo.svg # oder logo.png
├── config.domains.hcl.genai # öffentliche Adresse
├── config.hosts.hcl.genai # Adresse im Cluster
├── config.secrets.hcl.genai # eigene Secrets, Admin-Login
├── config.litellm.hcl.genai # LiteLLM-Schlüssel
├── config.databases.hcl.genai # verwaltetes Postgres
├── config.s3_buckets.hcl.genai # verwalteter S3-Bucket
└── config.versions.hcl.genai # App- und Chart-Version
| Datei | Pflicht | Wofür |
|---|---|---|
| `terragrunt.hcl.genai` | ja | Aufruf des Moduls: Quelle, Abhängigkeiten, Eingaben. Ohne sie ist das Repo nicht importierbar. |
| `metadata.json` | praktisch ja | Name, Beschreibung, Maintainer, Katalogtexte, Versionsquellen. |
| `logo.svg` / `logo.png` | für Partner ja | Kachel im Katalog und AI Hub. Ohne Logo sehen Partner die App nicht. |
| `config.domains.hcl.genai` | für Web-Apps | Öffentliche Adresse. Ohne sie keine Kachel im AI Hub. |
| `config.hosts.hcl.genai` | empfohlen | Interne Adresse, damit andere Apps dich erreichen. |
| `config.secrets.hcl.genai` | bei Bedarf | Secrets, die die Plattform erzeugen soll (z. B. Admin-Passwort). |
| `config.litellm.hcl.genai` | wenn die App ein LLM nutzt | Eigener LiteLLM-Schlüssel. |
| `config.databases.hcl.genai` | wenn die App Postgres braucht | Verbindungsdaten der verwalteten Datenbank. |
| `config.s3_buckets.hcl.genai` | wenn die App S3 braucht | Verbindungsdaten des Buckets. |
| `config.versions.hcl.genai` | empfohlen | App- und Chart-Version an einer Stelle. |
Jede `.genai`-Datei darf auch ohne Endung heißen (`terragrunt.hcl`); die Plattform sucht zuerst die Variante mit `.genai`.
## Schnellstart mit der Vorlage {#schnellstart}
Schnellster Weg: im [Baukasten](#baukasten) Schlüssel und Bausteine wählen und das fertige Repo als ZIP laden. Von Hand geht es so:
1. Den Ordner [vorlage/](vorlage/) in ein neues Repo kopieren.
2. Überall `meine-app` und `meine_app` durch deinen Schlüssel ersetzen ([Regeln](#schluessel)).
3. Nicht benötigte `config.*`-Dateien löschen und die passenden Zeilen in `terragrunt.hcl.genai` entfernen.
4. `main.tf` auf deine App anpassen, Logo austauschen, `metadata.json` ausfüllen.
5. Lokal prüfen ([Lokal prüfen](#lokal)), SemVer-Tag setzen, an das Plattform-Team übergeben ([Übergabe](#uebergabe)).
```bash title="Vorlage übernehmen"
cp -R docs/app-entwicklung/vorlage ../meine-wissensdatenbank
cd ../meine-wissensdatenbank
grep -rl 'meine[-_]app' . | xargs sed -i '' -e 's/meine-app/wissensdatenbank/g' -e 's/meine_app/wissensdatenbank/g'
./pruefen.sh
```
## Der Schlüssel der App {#schluessel}
Der Schlüssel ist der feste Name der App, zum Beispiel `open-webui`. Er lässt sich nach dem Import nicht mehr ändern.
- Nur Kleinbuchstaben, Ziffern und Bindestrich, höchstens 40 Zeichen, nicht mit `-` beginnen oder enden.
- Reserviert: `apps`, `import`, `new`.
- Der Import nimmt den einzigen Schlüssel aus `config.hosts`/`config.domains`, sonst den Repo-Namen ohne `terraform-`/`-terraform`.
Die Plattform schreibt den Schlüssel in zwei Formen. Das ist die häufigste Fehlerquelle:
| Wo | Schreibweise | Beispiel |
|---|---|---|
| Katalog, Ordner in der Umgebung, `dependencies` | mit Bindestrich | `open-webui` |
| Schlüssel in allen `config.*`-Dateien und in `inputs.*.` | mit Unterstrich | `open_webui` |
| Abschnitt in `config.secrets.hcl.genai` und `secrets.` | mit Unterstrich | `secrets.open_webui.admin_password` |
## `terragrunt.hcl.genai` {#terragrunt}
Die Datei wird pro Umgebung in zwei Schritten verarbeitet:
1. **Jinja** (`{{ … }}`) – beim Erzeugen der Umgebung. Verfügbar sind `source` (die Modulquelle mit Tag) und jeder Wert aus den Katalog-Standardwerten (`defaultValues`). Immer mit `default(…)` schreiben.
2. **Terragrunt/HCL** (`${ … }`, `try(…)`) – beim Ausrollen. Hier liest du alles, was die Umgebung bereitstellt, aus `local.common_vars.inputs`.
```hcl title="genai-cli/terragrunt.hcl.genai"
include "root" {
path = find_in_parent_folders("root.hcl")
}
# Provider kommen von der Plattform. kubernetes.hcl ignoriert die Pflicht-Labels.
include "kubernetes" {
path = "${get_terragrunt_dir()}/../providers/kubernetes.hcl"
}
include "helm" {
path = "${get_terragrunt_dir()}/../providers/helm.hcl"
}
terraform {
source = "{{ source | default('../../../../../terraform-meine-app') }}"
}
# Was vor dieser App angewendet sein muss. Daraus wird im Katalog "dependsOn".
dependencies {
paths = ["../redis", "../../applications/litellm"]
}
locals {
common_vars = read_terragrunt_config(find_in_parent_folders("terragrunt.hcl"))
}
inputs = merge(
local.common_vars.inputs,
{
release_name = "meine-app"
domain = try(local.common_vars.inputs.domains.meine_app, "")
replicas = {{ replicas | default(1) }}
# Redis – vom Redis-Modul der Umgebung
redis_host = try(local.common_vars.inputs.databases.redis.host, "")
redis_port = try(local.common_vars.inputs.databases.redis.port, "6379")
redis_password = try(local.common_vars.inputs.databases.redis.password, "")
# LiteLLM – OpenAI-kompatibel, eigener Schlüssel aus config.litellm.hcl.genai
openai_base_url = "http://${try(local.common_vars.inputs.hosts.litellm, "localhost")}:4000/v1"
openai_api_key = try(local.common_vars.inputs.litellm.meine_app.key, "")
# Admin – der Admin der Umgebung
admin_email = try(local.common_vars.inputs.default_user_email, "")
admin_password = try(local.common_vars.inputs.default_user_password, "")
}
)
```
> [!IMPORTANT] Immer `try(…, default)`
> Zugangsdaten für Datenbank und S3 stehen erst nach dem ersten Secret-Abgleich in der Umgebung. Ein nackter Zugriff wie `inputs.databases.meine_app.password` bricht dann schon den Plan ab. `try()` hält ihn lauffähig.
### Was die Umgebung bereitstellt {#inputs}
Alles unter `local.common_vars.inputs`. Weil `inputs = merge(local.common_vars.inputs, {...})` alles weiterreicht, kann dein Modul diese Namen auch direkt als Terraform-Variable deklarieren. Was es nicht deklariert, ignoriert Terraform.
| Eingabe | Inhalt |
|---|---|
| `namespace`, `release_name_prefix` | Kubernetes-Namespace der Umgebung und Präfix für Release-Namen |
| `domain`, `environment`, `organisation` | Basis-Domain, Umgebungsname, Organisation |
| `ingress_class_name`, `cluster_issuer_name`, `certificates` | Ingress-Klasse, cert-manager-Issuer, Zertifikate |
| `storage_class` | Storage-Klasse für Volumes |
| `image_pull_secrets`, `registry_credentials` | Namen der Pull-Secrets, Registry-Zugänge |
| `domains.`, `hosts.` | öffentliche und interne Adressen aller Apps der Umgebung |
| `databases.`, `s3_buckets.` | Verbindungsdaten (auch `databases.redis`) |
| `litellm..key` | LiteLLM-Schlüssel je App |
| `secrets..` | eigene Secrets aus `config.secrets.hcl.genai` |
| `versions.`, `helm_chart_versions.` | Versionen aus `config.versions.hcl.genai` |
| `default_user_email`, `default_user_first_name`, `default_user_password` | Admin der Umgebung |
| `smtp_host`, `smtp_port`, `smtp_from`, `smtp_from_name`, `smtp_username`, `smtp_password` | Mailversand, leer wenn die Umgebung keinen hat |
| `http_proxy`, `https_proxy`, `no_proxy` | Proxy, meist leer |
| `models` | Modell-Liste der Umgebung |
### Regeln für das Terraform-Modul {#modul}
- **Keine `provider`-Blöcke und kein `backend`.** Beides erzeugt die Plattform. Eine Datei `providers.tf` wird beim Ausrollen leer überschrieben.
- **Helm-Provider `~> 2.0`.** Die erzeugte Provider-Konfiguration nutzt die Syntax von Version 2.
- **`k8s_config_path` und `k8s_context` nicht deklarieren** – die Plattform deklariert sie selbst.
- **Nichts außerhalb des Namespace** `var.namespace` anlegen; Cluster-Ressourcen gehören nicht in ein App-Modul.
- **Keine eigenen `genai.govdigital.de/*`-Labels** und nie Selektoren oder Pod-Template-Labels dafür ändern.
- **Release- und Workload-Namen enthalten den Schlüssel** (`${release_name_prefix}meine-app`). Daran ordnet das Monitoring Pods der App zu.
## Abhängigkeiten (`dependsOn`) {#abhaengigkeiten}
Du erklärst eine Abhängigkeit an **einer** Stelle: im `dependencies`-Block von `terragrunt.hcl.genai`. Daraus leitet der Import das Katalogfeld `dependsOn` ab.
```hcl title="Abhängigkeit auf Redis und LiteLLM"
dependencies {
paths = ["../redis", "../../applications/litellm"]
}
```
Was dann passiert:
1. **Beim Bestellen** wählt die Plattform jede Abhängigkeit automatisch mit, auch über mehrere Stufen (deine App → LiteLLM → Redis, Presidio).
2. **Beim Erzeugen** bekommt die Abhängigkeit ihre Secrets (z. B. das Redis-Passwort) und schreibt ihre Verbindungsdaten in die Umgebung.
3. **Beim Ausrollen** wendet Terragrunt die Abhängigkeit vor deiner App an.
4. **Deine App** liest die Verbindungsdaten über `local.common_vars.inputs` – nicht über Terragrunt-Outputs.
Regeln:
- Der letzte Pfadteil ist der **Katalogschlüssel** der Abhängigkeit (`redis`, `litellm`). Ist er nicht im Katalog, blockiert der Import. Eine eigene Abhängigkeit (z. B. eine zweite App von dir) muss deshalb zuerst importiert werden.
- `../name` heißt „gleiche Stufe“, `../../applications/name` „andere Stufe“. Liegt die Abhängigkeit woanders, korrigiert die Plattform den Pfad selbst.
- Verweist deine Datei auf `../../…`, landet die App in der Stufe `ai-applications` (nach `applications`). Das ist richtig für Apps, die LiteLLM nutzen.
- Postgres ist **keine** Abhängigkeit, sondern eine verwaltete Datenbank ([Datenbank und S3](#datenbank)).
| Abhängigkeit | Pfad | Was deine App bekommt |
|---|---|---|
| Redis | `../redis` | `inputs.databases.redis.{host,port,password}` |
| LiteLLM | `../../applications/litellm` | `inputs.hosts.litellm`, `inputs.domains.litellm`, eigener Schlüssel über `config.litellm.hcl.genai` |
| Presidio | `../presidio` | `inputs.hosts.presidio_analyzer`, `inputs.hosts.presidio_anonymizer` |
| eine andere App | `../` | was diese App in `config.hosts`/`config.domains` veröffentlicht |
> [!WARNING] Nachinstallieren in eine laufende Umgebung
> Bestellung und Assistent ziehen Abhängigkeiten automatisch mit. Fügt ein Operator eine App einzeln einer bestehenden Umgebung hinzu, passiert das heute nicht – die Abhängigkeit muss dort schon laufen.
## Redis anbinden {#redis}
1. Abhängigkeit eintragen: `"../redis"` im `dependencies`-Block.
2. Verbindungsdaten lesen. Das Redis-Modul der Umgebung veröffentlicht sie unter `databases.redis`:
```hcl title="terragrunt.hcl.genai – Redis"
redis_host = try(local.common_vars.inputs.databases.redis.host, "")
redis_port = try(local.common_vars.inputs.databases.redis.port, "6379")
redis_password = try(local.common_vars.inputs.databases.redis.password, "")
```
3. Im Modul daraus die URL bauen, die deine App erwartet:
```hcl title="main.tf – Redis-URL für die App"
locals {
redis_url = "redis://:${var.redis_password}@${var.redis_host}:${var.redis_port}/0"
}
```
- `host` ist der Service im Namespace (`redis-master.`); wer einen voll qualifizierten Namen braucht, hängt `.svc.cluster.local` an.
- Das Passwort erzeugt die Plattform, sobald Redis in der Umgebung ist. Du musst nichts dafür deklarieren.
- Teile dir Redis mit anderen Apps: nimm eine eigene Datenbanknummer oder ein Schlüsselpräfix.
> [!NOTE] Ältere Schreibweise
> Ältere Module lesen `inputs.hosts.redis` und `inputs.redis_password`. Beides funktioniert noch; für neue Apps gilt `inputs.databases.redis.*`.
## LiteLLM anbinden {#litellm}
Deine App spricht mit LiteLLM wie mit der OpenAI-API. Sie bekommt einen eigenen Schlüssel mit eigenem Team, damit Nutzung und Kosten getrennt sichtbar sind.
1. Abhängigkeit eintragen: `"../../applications/litellm"`.
2. Schlüssel anmelden mit `config.litellm.hcl.genai`:
```hcl title="genai-cli/config.litellm.hcl.genai"
include "global" {
path = find_in_parent_folders("global.hcl")
expose = true
}
locals {
litellm = {
meine_app = {
team_alias = "meine-app-team"
key_alias = "meine-app-api-key"
create_api_key = true
models = [] # leer = alle Modelle der Umgebung
metadata = { service = "meine-app" }
key = try(include.global.locals.secrets.litellm.keys.meine_app, "")
}
}
}
```
3. Schlüssel und Adresse übergeben:
```hcl title="terragrunt.hcl.genai – LiteLLM"
openai_base_url = "http://${try(local.common_vars.inputs.hosts.litellm, "localhost")}:4000/v1"
openai_api_key = try(local.common_vars.inputs.litellm.meine_app.key, "")
```
- Die Zeile `key = try(include.global.locals.secrets.litellm.keys., "")` ist Pflicht und muss genau so heißen: an ihr erkennt die Plattform, dass sie einen `sk-…`-Schlüssel erzeugen und in LiteLLM anlegen soll.
- Nimm die interne Adresse (`hosts.litellm`, Port 4000). Die öffentliche (`https://${inputs.domains.litellm}`) geht auch, läuft aber über den Ingress.
- Welche Modelle es gibt, steht in `inputs.models`. Modellnamen besser als Katalog-Standardwert (`{{ model_chat | default('…') }}`) statt fest im Code.
## Datenbank und S3 {#datenbank}
Postgres und S3 sind **verwaltet**: die Plattform legt Datenbank bzw. Bucket in der Infrastruktur-Stufe an und schreibt die Zugangsdaten in die Secrets der Umgebung. Deine App bekommt sie über zwei Dateien.
```hcl title="genai-cli/config.databases.hcl.genai"
locals {
databases = {
meine_app = {
host = try(include.global.locals.secrets.database.meine_app.host, "localhost")
port = try(include.global.locals.secrets.database.meine_app.port, "5432")
name = try(include.global.locals.secrets.database.meine_app.name, "${include.global.locals.environment_name}-meine-app")
username = try(include.global.locals.secrets.database.meine_app.username, "${include.global.locals.environment_name}_meine_app")
password = try(include.global.locals.secrets.database.meine_app.password, "")
}
}
}
```
```hcl title="genai-cli/config.s3_buckets.hcl.genai"
locals {
s3_buckets = {
meine_app = {
bucket = try(include.global.locals.secrets.s3_buckets.meine_app.bucket, "meine-app")
endpoint = try(include.global.locals.secrets.s3_buckets.meine_app.endpoint, "")
region = try(include.global.locals.secrets.s3_buckets.meine_app.region, "eu01")
force_path_style = false
username = try(include.global.locals.secrets.s3_buckets.meine_app.access_key, "")
secret_key = try(include.global.locals.secrets.s3_buckets.meine_app.secret_key, "")
}
}
}
```
In `terragrunt.hcl.genai` liest du dann `inputs.databases.meine_app.{host,port,name,username,password}` und `inputs.s3_buckets.meine_app.{bucket,endpoint,region,username,secret_key}`.
- Der Import erkennt die Datenbank nur, wenn `config.databases` auf `secrets.database.` verweist. Dann trägt er `requires.databases` im Katalog ein.
- Im S3-Abschnitt heißt der Access-Key **`username`** – so erwartet es die Bucket-Bereitstellung. Lies ihn als `inputs.s3_buckets..username`.
- Eine App, die Postgres selbst mitbringt (eigener StatefulSet), braucht beides nicht – dann ist das Passwort ein eigenes Secret ([Eigene Secrets](#secrets)).
## Admin-Zugang und eigene Secrets {#secrets}
### Der Admin der Umgebung
Jede Umgebung hat einen Admin: E-Mail, Vorname, Passwort. Der Partner gibt die E-Mail bei der Bestellung an, das Passwort erzeugt die Plattform. Übernimm diesen Admin, statt einen eigenen zu erfinden – dann meldet sich der Partner überall gleich an.
```hcl title="terragrunt.hcl.genai – Admin übernehmen"
admin_email = try(local.common_vars.inputs.default_user_email, "")
admin_first_name = try(local.common_vars.inputs.default_user_first_name, "Administrator")
admin_password = try(local.common_vars.inputs.default_user_password, "")
```
Dein Modul legt damit beim ersten Start den Admin an (Bootstrap-Variablen, Init-Job oder Helm-Hook – je nach App).
### Eigene Secrets erzeugen lassen
Braucht deine App weitere Secrets (Signaturschlüssel, Admin-Benutzername, interne Passwörter), meldest du sie in `config.secrets.hcl.genai` an. Die Plattform erzeugt fehlende Werte beim Anlegen der Umgebung und überschreibt nie einen vorhandenen.
```hcl title="genai-cli/config.secrets.hcl.genai"
locals {
required_secrets = {
meine_app = {
admin_username = {
type = "static"
default = "admin"
description = "Login-Name des Admins"
}
admin_password = {
type = "password"
length = 32
description = "Passwort des Admins"
}
session_secret = {
type = "hex"
length = 32
description = "Signiert Sitzungen"
}
}
}
}
```
Lesen in `terragrunt.hcl.genai`:
```hcl title="terragrunt.hcl.genai – eigene Secrets"
admin_username = try(local.common_vars.inputs.secrets.meine_app.admin_username, "admin")
admin_password = try(local.common_vars.inputs.secrets.meine_app.admin_password, "")
session_secret = try(local.common_vars.inputs.secrets.meine_app.session_secret, "")
```
| `type` | erzeugt | Felder |
|---|---|---|
| `password` | Buchstaben und Ziffern | `length` (Standard 32) |
| `hex` | Hex-Zeichenkette, 2 Zeichen je Byte | `length` in Bytes (Standard 32) |
| `fernet` | Fernet-Schlüssel (44 Zeichen) | – |
| `prefixed` | `-` | `prefix`, `length` (Standard 24) |
| `static` | genau `default` | `default` – ohne `default` bleibt der Wert leer |
> [!WARNING] `admin_password` erbt das Admin-Passwort
> Heißt ein Secret `admin_password`, bekommt es das Passwort des Admins der Umgebung statt eines Zufallswerts. Das ist gewollt (ein Login für alle Apps) und **vorläufig**: es entfällt, sobald Konten zentral über den AI Hub vergeben werden. Willst du ein unabhängiges Passwort, nenne es anders.
- Zugangsdaten nie in `metadata.json`, Katalog-Standardwerte oder Klartext-Dateien schreiben. Secrets gehören in `config.secrets.hcl.genai`.
- Ein Secret, das der Partner selbst setzen muss (z. B. ein externer API-Schlüssel), meldest du als `static` ohne `default` an und beschreibst es in `description`. Das Plattform-Team trägt es dann ein.
## Adressen: Domain und Host {#adressen}
```hcl title="genai-cli/config.domains.hcl.genai"
locals {
domains = {
meine_app = "meine-app-${include.global.locals.domain}"
}
}
```
```hcl title="genai-cli/config.hosts.hcl.genai"
locals {
hosts = {
meine_app = "${include.global.locals.release_name_prefix}meine-app.${include.global.locals.k8s_namespace}"
}
}
```
- `domains.` ist die öffentliche Adresse. Dein Ingress nutzt sie zusammen mit `ingress_class_name` und dem Issuer `cluster_issuer_name`.
- `hosts.` muss zum Namen deines Kubernetes-Service passen. Andere Apps erreichen dich darüber.
- `include.global.locals.*` darfst du in allen `config.*`-Dateien nutzen; fehlt der `include "global"`-Block, ergänzt ihn die Plattform.
## `metadata.json` und Katalogtexte {#metadata}
```json title="genai-cli/metadata.json"
{
"name": "Meine App",
"description": "Ein Satz, was die App tut.",
"maintainers": ["team-meine-app"],
"catalog": {
"label": "Meine App",
"summary": "Ein Satz für die Katalog-Kachel, ohne Zeilenumbruch.",
"details": "Ein bis drei Absätze für die Detailseite.\n\nNur Klartext, kein Markdown.",
"vendor": "Hersteller (Open Source)",
"category": "ui",
"role": "application",
"defaultValues": {
"replicas": 1,
"model_chat": "stackit-openai-gpt-oss-120b"
}
},
"versions": {
"meine_app": {
"description": "Image der App",
"source": { "type": "registry", "host": "registry.onstackit.cloud", "repo": "govdigital/meine-app", "tagPrefix": "v", "limit": 50 }
}
}
}
```
| Feld | Bedeutung |
|---|---|
| `name`, `description`, `maintainers` | Grunddaten; `description` ohne `catalog.summary` wird zur Kurzbeschreibung |
| `catalog.label` | Anzeigename |
| `catalog.summary` | ein Satz, eine Zeile – Pflicht für Partner |
| `catalog.details` | Klartext-Absätze; keine Zeichen `<` `>` `*` `#` und kein Backtick – Pflicht für Partner |
| `catalog.vendor` | Hersteller – Pflicht für Partner |
| `catalog.category` | `core`, `observability`, `ui`, `tooling` oder `data` – Pflicht, ohne Kategorie lehnt der Katalog den Eintrag ab |
| `catalog.role` | `application` (Nutzer sehen sie) oder `service` (Baustein für andere Apps, z. B. Redis) |
| `catalog.tier` | `applications` oder `ai-applications`; sonst aus den Abhängigkeitspfaden abgeleitet |
| `catalog.defaultValues` | Jinja-Werte für `terragrunt.hcl.genai`; Platzhalter `${env}`, `${org}`, `${envName}` werden ersetzt |
| `versions..source` | woher die Konsole verfügbare Versionen liest: `registry`, `github-release` oder `gitlab-release` mit `host`, `repo`, `tagPrefix`, `stripPrefix`, `limit` |
| `helmChartVersions.` | dasselbe für die Chart-Version |
## Logo {#logo}
Lege das Logo in `genai-cli/` ab. Die Plattform sucht in dieser Reihenfolge: `logo.svg`, `logo.png`, `logo.jpg`, `logo.jpeg`.
| Regel | Wert |
|---|---|
| Formate | SVG (bevorzugt), PNG, JPEG |
| Größe | höchstens 1 MiB im Repo |
| Rasterbilder | quadratisch, am besten 256 × 256 px; größere werden auf 256 px verkleinert |
| SVG | höchstens 128 KiB, kein `