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-sdkaus 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.
Der legal-Block
| 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;
criticalim 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
- Widget, Konfiguration und Vorschau — die Widget-Definition im Feld
widgets. - Datenzugriff, Rechte und Bedingungen —
dataAccess,permissions,unlocks,connectsundlegalim Detail. - Signieren und der Weg in den Katalog — was aus
signingbeim Release und am Gerät folgt.