Zum Inhalt springen

Das Manifest

Jedes Feld des App-Manifests mit Pflicht, Format und Wirkung — von id und version bis legal, unlocks und connects.

7 Minuten LesezeitApps-SDKStand: 25.10.2026

Auf dieser Seite

Bevor Sie loslegen

Für wen
Entwickler, die ein Manifest schreiben oder ein bestehendes lesen.
Voraussetzung
das SDK @szenera/apps-sdk aus dem Quellbaum der Plattform (nicht öffentlich veröffentlicht).

Das Manifest ist die einzige verbindliche Beschreibung einer App: defineAppManifest im SDK, die Prüfung beim Laden und die Ablage im Katalog lesen exakt dieselbe Form. Die Tabellen nennen jedes Feld mit Pflicht, Format und Wirkung; Beispiele stehen darunter.

Die Felder

Feld Pflicht Format Wirkung
id ja Kleinbuchstaben mit Bindestrichen (^[a-z][a-z0-9]*(-[a-z0-9]+)*$); core ist reserviert und wird abgelehnt Kanonische Kennung der App; muss dem Katalog-Eintrag entsprechen.
version ja Semver (1.0.0) Die Fassung, die ein Release einfriert.
displayName ja 1–60 Zeichen Name im Store und im Portal.
description ja max. 300 Zeichen Kurzbeschreibung im Store.
category ja zeit · information · web-video · gestaltung · geschaeft Die eine Einordnung: Filter im Store und Abschnitt im App-Bereich des Szenen-Editors. Widgets tragen keine eigene.
tier Vorgabe standard standard · pro · premium · addon Klasse: standard trägt im Store keine Marke, pro die Marke Pro, addon die Marke Add-on und wird zugebucht; premium trägt heute keine App.
vertical nein pharmacy Branchen-Facette des Store; übergreifende Apps lassen es weg.
publisher nein { name: 1–80 Zeichen } Herausgeber, den das Store-Listing zeigt.
schemaName ja ^apps_[a-z][a-z0-9_]+$ Name der App-Datenbank: apps_<id>, Bindestriche der Kennung werden zu Unterstrichen (qr-code → apps_qr_code). Pflicht auch ohne Datenbank.
apiCompatibility ja { appsSdk: Semver-Bereich, apiVersion?: string, rendererHost?: Semver-Bereich } appsSdk gilt dem Server-Teil: Der Loader hält die laufende SDK-Version dagegen, passt sie nicht, lädt die App nicht. rendererHost nennt den Host-Vertrag der Anzeige, den runtime- und authoring-Bündel brauchen, und gehört in jedes Manifest mit Widget: Anzeige und Szenen-Editor laden ein Bündel nur, wenn ihre Vertragsfassung im Bereich liegt. Gesetzt wird es aus RENDERER_HOST_RANGE des SDK, nie von Hand, damit es mit dem Vertrag wandert.
widgets ja Liste von Widget-Definitionen Die Widgets der App — nach der Regel der Plattform höchstens eines, bei Tor-Apps keines. Wie ihr configSchema im Katalog herauskommt, steht unter Widgets konfigurieren.
resources nein Liste von Ressourcentypen (id, appId, slug, name, description, drei JSON-Schemas, listEndpoint, authoringPath) Datentypen, die die Organisation im Portal pflegt; slug ist das Adress-Segment des App-Bereichs.
permissions ja { tenant: [], platform: [] }, je Eintrag key, label, description, defaultRoles Rechte, die die Installation in den Rechte-Katalog der Organisation einträgt; platform mit leeren defaultRoles ist reine Betreiberarbeit.
models nein Liste von Modellnamen Allowlist der Modelle, die der Server-Teil in der App-Datenbank ansprechen darf.
critical Vorgabe false boolean Nur Berichtszweck in der Betriebsdiagnose; ob ein Ladefehler den Start abbricht, entscheidet die Server-Konfiguration, nicht das Manifest.
criticalForTenant Vorgabe false boolean Das Portal warnt vor der Deinstallation: Diese App gehört zur Grundausstattung.
signing Vorgabe optional optional · required Ohne Wirkung auf die Auslieferung: Das Gerät prüft die Signatur jeder Fassung immer. Der Wert bleibt, weil er in den Manifesten veröffentlichter Fassungen steht.
preview ja { iconUrl } Das Icon fürs Listing. Die Aufnahmen leitet der Store aus den Varianten der Widgets ab und liefert sie aus dem Artefakt der Fassung; das frühere Feld screenshots liest er nicht mehr, ein neues Manifest lässt es weg.
store nein tagline (≤ 140), longDescription (≤ 3000), highlights (≤ 8), setupSteps (≤ 8, optional mit resourceType) Ausführliche Store-Inhalte; das Portal rendert sie nur.
dataAccess nein { reads[], writes[], external[], retention } Die Auskunft vor der Installation; fehlt der Block, gilt die zurückhaltendste Lesart.
unlocks nein, max. 8 Schlüssel kleingeschrieben mit Bindestrichen, ≤ 40 Zeichen Portal-Funktionen, die die Installation freischaltet; jeder Schlüssel muss im Katalog der Plattform stehen.
connects nein, max. 4 Schlüssel kleingeschrieben mit Bindestrichen, ≤ 40 Zeichen Speicher-Anbieter, deren Verbindungs-Verwaltung die App auf ihre Seite holt.
legal nein siehe unten Bedingungen, die Zustimmung und Hinweis im Widget auslösen.
compatibility ja (im SDK) { hosts, orientations, offline }, siehe unten Wo die App läuft. Der Store zeigt es unter „Läuft auf“, die Zuweisung an ein Display warnt, wenn dessen Wirt fehlt. Im Format optional, damit Manifeste früherer Fassungen lesbar bleiben.
translations nein (für die mitgelieferten Apps: en-US Pflicht) je Sprache ein Block mit den Texten, siehe unten Die Texte in weiteren Sprachen. Ohne Block zeigt der Store Deutsch.

unlocks und connects werden ausgelassen statt leer gesetzt: Ein leeres Feld verschöbe den kanonischen Hash jeder bestehenden Fassung, ohne dass sich etwas geändert hat.

Der Block compatibility

defineAppManifest verlangt den Block im Typ und zur Laufzeit: Ohne ihn wirft es mit einem Satz, der die drei Felder nennt. Jede Liste nennt einen Wert höchstens einmal.

Feld Werte Bedeutung
hosts szenera-os · fire-tv · tizen · webos · web-display, mindestens einer Die Wirte, auf denen die App läuft: SzeneraOS auf dem eigenen Gerät, die App für Fire TV und Android, die Apps für Samsung Tizen und LG webOS, ein Browser ohne eigenen Wirt.
orientations landscape · portrait, mindestens eine Die Lagen des Bildschirms, quer und hochkant, in denen die App taugt.
offline full · last-known · none Was die App ohne Netz zeigt: full weiter alles, weil sie allein aus ihrer Konfiguration und dem Vorrat des Geräts rendert (im Store „statisch“), last-known den Stand, den der Server zuletzt in den Schnappschuss des Displays gelegt hat, none nichts, weil sie live aus dem Netz lädt.

Der Block translations

Die Texte des Manifests stehen auf Deutsch, der Pflichtsprache. Jede weitere Sprache steht unter translations als eigener Block, der nur Texte trägt: displayName und description, store (Einzeiler, Langtext, Funktionen und Einrichtungsschritte in der Reihenfolge der deutschen), widgets je Kennung mit Name, Beschreibung und variants je Ansicht, resources je Kennung, permissions je Schlüssel, dataAccess.external in der Reihenfolge der Dienste und legal.disclaimerSummary. Kennungen, Werte und Regeln stehen nie in einem Block.

Ein Block ist vollständig oder gar nicht da: defineAppManifest prüft ihn mit appManifestTranslationIssues() und wirft beim Bauen, mit der Stelle beim Namen. Der Store antwortet in der Sprache, die das Portal erbittet, und fällt auf Deutsch zurück. Englische Begriffe folgen den Portalen: display, nie screen, die Leinwand heißt video wall. pnpm app:check <id> prüft die Texte beider Sprachen nach der Hausregel, in der Zeile Texte.

Feld Pflicht Bedeutung
disclaimerKey ja Schlüssel der Fassung, auf die sich eine Zustimmung bezieht (<gruppe>.bedingungen.v1). Eine inhaltliche Änderung des Hinweises hebt ihn (v1 → v2).
disclaimerSummary ja, max. 500 Zeichen Kurzfassung in Klartext; Installationsdialog, Store-Seite und Widget-Fußzeile zeigen sie unverändert. Sie stellt die Fassung dar, eine neue Formulierung bei gleichem Schlüssel verlangt keine Zustimmung.
termsPath nein Pfad des Volltexts auf der öffentlichen Website (^\/[a-z0-9-]+(\/[a-z0-9-]+)*$, ≤ 200 Zeichen, z. B. /app-bedingungen/notdienst). Ein Pfad, keine Adresse — das Portal setzt seine Basis davor.
requireAcceptanceAt Vorgabe install install: Zustimmung bei der Installation und danach mit jeder neuen Fassung des Hinweises. upgrade: Zustimmung bei jeder neuen Fassung der App, auch bei gleichem Hinweis. never: keine Zustimmung.

Ein neuer disclaimerKey macht ein Release zustimmungspflichtig: Sobald die neue Fassung als stable veröffentlicht ist, steht die Installation auf Update wartet. Wer den Hinweis inhaltlich ändert, hebt deshalb den Schlüssel, im selben Arbeitsgang wie Kurzfassung, Volltext und Fassung der App. Eine neu formulierte Kurzfassung bei gleichem Schlüssel übernimmt die Plattform still. Eine Manifest-Änderung ohne Versionssprung erreicht bestehende Installationen nicht.

Was nicht im Manifest steht

  • Fetcher und Jobs. Sie deklariert eine App als Decorator an Klassen ihres Server-Teils (@Fetcher, @JobHandler, @WidgetResolver); die Plattform entdeckt sie beim Start. Das Manifest bleibt reines Datenformat.
  • Ob ein Ladefehler den Start abbricht. Das ist eine Eigenschaft der Installation und steht in der Server-Konfiguration des Betriebs; critical im Manifest ist nur Bericht.

Beispiel: das Manifest der Notdienst-App (gekürzt)

import { defineAppManifest, RENDERER_HOST_RANGE } from '@szenera/apps-sdk/manifest';

export const manifest = defineAppManifest({
  id: 'pharmacies-on-duty',
  version: '1.3.0',
  displayName: 'Notdienst',
  description:
    'Zeigt die Apotheken im Notdienst rund um Ihren Standort, regelmäßig aktualisiert für ganz Deutschland.',
  tier: 'pro',
  category: 'geschaeft',
  vertical: 'pharmacy',
  schemaName: 'apps_pharmacies_on_duty',
  apiCompatibility: { appsSdk: '^0.2.0', rendererHost: RENDERER_HOST_RANGE },
  widgets: [PharmaciesOnDutyDisplayWidget],
  resources: [
    { ...PharmaciesOnDutyLocationResource, slug: 'locations' },
    { ...PharmaciesOnDutyScheduleResource, slug: 'schedules' },
  ],
  permissions: {
    tenant: [
      {
        key: 'pharmacies-on-duty.location.read',
        label: 'Standorte lesen',
        description: 'Hinterlegte Apotheken-Standorte ansehen, für die Notdienstpläne geladen werden.',
        defaultRoles: ['OWNER', 'MANAGER', 'MEMBER'],
      },
      // … weitere Rechte für das Team …
    ],
    platform: [
      {
        key: 'pharmacies-on-duty.crawler.config',
        label: 'Crawler konfigurieren',
        description: 'Betreiber-Recht: Quellen, Abrufintervalle und Parser des Notdienst-Crawlers einstellen.',
        defaultRoles: [],
      },
    ],
  },
  publisher: { name: 'Szenera' },
  signing: 'required',
  legal: {
    disclaimerKey: 'notdienst.bedingungen.v1',
    disclaimerSummary: 'Notdienstangaben stammen aus öffentlichen Verzeichnissen … und werden ohne Gewähr angezeigt …',
    termsPath: '/app-bedingungen/notdienst',
    requireAcceptanceAt: 'install',
  },
  preview: { iconUrl: '/apps/pharmacies-on-duty/icon.svg' },
  dataAccess: {
    external: [
      {
        name: 'Öffentliche Notdienst-Verzeichnisse (u. a. aponet.de)',
        purpose: 'Wir rufen die Dienstpläne für Ihre Standorte ab und halten sie aktuell.',
        sends: 'Postleitzahl und Umkreis der Standorte, die Sie angelegt haben.',
      },
    ],
  },
  // Die Dienstpläne holt der Server, das Display behält den letzten Stand im Schnappschuss.
  compatibility: {
    hosts: ['szenera-os', 'fire-tv', 'tizen', 'webos', 'web-display'],
    orientations: ['landscape', 'portrait'],
    offline: 'last-known',
  },
});

Formprüfung und Querregeln

defineAppManifest prüft die Form gegen das Schema, wendet Vorgabewerte an (tier, critical, criticalForTenant, signing), verlangt den Block compatibility und wirft bei jedem Formfehler. Drei Regeln, die sich aus mehreren Feldern zugleich ergeben, prüft erst die Plattform beim Laden: Das appId jedes Widgets muss der id des Manifests gleichen, jede Ressource muss das Präfix der App tragen, und keine zwei Widgets dürfen dieselbe Kennung haben. Ein Manifest, das dem Schema widerspricht, weist die Release-Anlage mit Manifest.ValidationFailed (HTTP 422) ab.

Weiterführendes