App manifest
App manifest reference
Field-by-field reference for the application manifest (*.app.yaml) spec.
This is the complete reference for an application manifest. For the shared envelope (api-version,
kind, metadata), see Manifests: the contract.
An Application is a named, versioned package of installed files that declares optional
contributions. A browser UI surface (spec.entry + entry-points + sandbox) is just one of
those contributions — and it is optional. An app can instead (or additionally) declare an
install map that places files anywhere in the workspace, where behaviour emerges from
each file's type: a seeded dashboard JSON launches the graphite viewer, a *.schedule.yaml runs on a
cron, a .py a user opens runs on the Python engine. See
Headless apps: a collection of files.
A complete example (an iframe app)
api-version: verentis.io/v1
kind: Application
metadata:
name: smart-editor
display-name: Smart Editor
description: A Monaco-powered editor for JSON, YAML, Python and more
icon: file-code
version: 0.1.0
author: Verentis
labels:
category: editor
spec:
entry: https://localhost:3302
scope: global
mime-types:
- pattern: application/json
mode: edit
priority: 200
- pattern: "text/*"
mode: edit
priority: 100
capabilities:
- file:read
- file:write
permissions:
- node.file.read
- node.node.create
- node.node.update
sandbox:
allow-scripts: true
allow-same-origin: true
allow-popups: false
allow-forms: true
oauth:
client-type: public
redirect-uris:
- https://localhost:3302/auth/callback
scopes:
- smart-editor.document.export
- smart-editor.settings.manage
display:
fullscreen: false
Headless apps: a collection of files
An app does not need an iframe. Omit spec.entry (and entry-points/sandbox) entirely and use
an install map to place a collection of files into the workspace. Their behaviour then
emerges from each file's type — no UI surface required.
The example below is a "scripts" app: it seeds a couple of Python programs, a manual-run report, and a daily schedule that runs server-side on the Python engine.
api-version: verentis.io/v1
kind: Application
metadata:
name: wholesale-seeds
display-name: Wholesale Seeds
description: Scheduled and on-demand Python jobs for the wholesale-seeds workspace
icon: sprout
version: 0.1.0
author: Verentis
spec:
# No `entry` — this app has no iframe UI surface.
# Where the app-record (manifest node + provenance) is anchored. Optional; defaults to
# /applications/{metadata.name}.
install-root: /applications/wholesale-seeds
# Declarative install map: where packaged files land in the workspace.
install:
- from: scripts/**/*.py
to: /scripts/
mode: managed # app-owned: replaced on upgrade, removed on uninstall
- from: automation/*.schedule.yaml
to: /automation/
mode: managed
- from: seed-data/*.csv
to: /data/
mode: seed # install once, never overwrite user-edited data
# A headless app still declares the scopes its scheduled/triggered runs execute with.
scopes:
- wholesale-seeds.default
spec
entrystringOptional. The HTTPS URL Verentis loads in the iframe. Your hosted app (or dev server) lives here. Omit it for a headless app that ships only installed files. Typically supplied per environment — see Environments & publishing.
splashstringOptional splash image URL shown while the iframe starts. Relative values resolve against the selected iframe entry URL; an entry point can override it.
entry-pointsobject[]Optional. Multiple named surfaces for the same app (e.g. an editor and a settings panel), each with its own route and MIME claims. See Entry points.
launchobjectOptional. Explicitly opts a whole-experience app into a file-independent workspace launch surface. Contextual file handlers and headless apps should omit it. See Standalone launch.
installobject[]Optional. A declarative install map placing packaged files into workspace paths, each with a
seed/managed lifecycle. See install.
install-rootstringOptional. Where the app-record (manifest node + .verentis provenance) is anchored. Defaults to
/applications/{metadata.name}. See install.
scopestringVisibility of the app. global makes it available across the workspace; co-located manifests scope an
app to nearby content.
mime-typesobject[]The content types this app claims and how. See MIME types.
file-typesobject[]Optional extension→MIME rules (same shape as engines) when the app also wants to influence type identification. See File types & the MIME registry.
capabilitiesstring[]High-level intent flags shown to users (e.g. file:read, file:write). Back them with matching
permissions.
permissionsstring[]The platform scopes the app requests (e.g. node.file.read, node.node.create). These bound the
tokens the app receives and are reviewed at consent.
sandboxobjectThe iframe sandbox flags. See Sandbox.
oauthobjectOAuth client configuration if the app needs its own identity. See OAuth.
scopesstring[]Custom scopes the app defines (e.g. smart-editor.document.export). Registered in the Fabric scope
catalog so other callers can be granted them.
displayobjectDefault display settings (e.g. fullscreen). Can be overridden per entry point.
settingsobject[]Workspace settings and secrets required by the app. See settings.
mime-types
mime-types:
- pattern: application/json
mode: edit
priority: 200
patternstringThe MIME type or wildcard to match (e.g. application/json, image/*, text/*).
modestringHow the app handles the type: view or edit.
priorityintHigher wins when multiple apps match a file. Use high values for exact matches, low for wildcards.
json-schemaobjectOptional. Pairs the pattern with a JSON Schema so custom/vendor content types are recognised by shape. See json-schema.
entry-points
For apps with more than one surface, declare multiple entry points. Each can claim its own MIME types and override display settings.
entry-points:
- id: editor
display-name: Editor
route: /
order: 1
mime-types:
- pattern: "text/*"
mode: edit
priority: 100
- id: settings
display-name: Settings
route: /settings
order: 2
display:
fullscreen: true
idstringStable identifier for the entry point.
display-name / description / iconstringPresentation in the UI.
routestringPath within your app for this entry point.
entrystringOptional per-entry-point URL override.
splashstringOptional splash URL override for this entry point. Relative values resolve against this entry point's resolved iframe URL.
orderintSort order among entry points.
mime-typesobject[]MIME claims specific to this entry point.
displayobjectOverrides the spec-level display for this entry point.
Standalone launch
Applications are not automatically shown in the workspace sidebar just because they expose an iframe. File viewers/editors stay contextual: they open only when a registered MIME type matches a file. Apps that provide a complete workspace experience can opt into a separate launch surface:
spec:
entry: https://app.example.com
launch:
enabled: true
pinnable: true
For an app with several entry points, select the one that acts as its workspace home:
spec:
entry: https://app.example.com
launch:
enabled: true
entry-point: home
pinnable: true
entry-points:
- id: home
route: /workspace
- id: editor
route: /editor
mime-types:
- pattern: application/vnd.example.document+json
mode: edit
priority: 100
enabledboolMakes the app launchable without a selected file. The resolved launch target must be an absolute
HTTP(S) iframe entry, either from spec.entry or the selected entry point.
entry-pointstringOptional ID from spec.entry-points. When omitted, the workspace launches spec.entry.
pinnableboolAllows users to pin the standalone app to their personal workspace sidebar. It has no effect unless
enabled is true and does not pin the app by default.
Headless apps and apps that only react to existing file types remain valid and installable; simply omit
spec.launch.
The workspace opens an enabled registration at /apps/<resource-registration-id>. The app still runs
through the normal iframe bridge, but its SDK context has surface: 'workspace', no file, and the
resolved launch entryPoint. pinnable only allows a user to add that route to their personal
sidebar; it never pins the app automatically. See
The SDK & bridge.
install
The install map declares where the app's packaged files land in the workspace VFS and how they are governed over the app's lifecycle. Each directive maps a source (relative to the package payload root) to a destination path.
install:
- from: dashboards/*.json # glob, relative to the package payload root
to: /dashboards/ # workspace-absolute; trailing '/' = directory
mode: seed # install once, never overwrite user-edited data
mime: application/vnd.graphite.dashboard.executive-summary+json # optional MIME override
- from: scripts/**/*.py
to: /scripts/
mode: managed # app-owned: replaced on upgrade, removed on uninstall
fromstringPath or glob relative to the package payload root. Must match at least one file. * matches within a
path segment, ** across segments, ? a single character.
tostringWorkspace-absolute destination. A trailing / (or a wildcard from) denotes a directory — the
source's basename / matched sub-path is appended; otherwise it is an exact file path. Must not escape
the workspace root.
modestringseed or managed. Default managed. managed files are app-owned: created and replaced on
upgrade, removed on uninstall. seed files install once and are never overwritten or removed —
use seed for initial data a user may edit (e.g. a starter dashboard).
mimestringOptional explicit MIME for the placed files. Otherwise the MIME is inferred from the extension and JSON-schema content resolution — this is what lets a seeded vendor-MIME JSON launch the right app.
phaseintOptional non-negative install phase. Lower phases are uploaded first; later phases can wait until the application registration created by an earlier phase is visible.
install-root anchors the app record
The manifest node and its .verentis provenance install at spec.install-root (default
/applications/{metadata.name}) so the app is registered, consented, and has an anchor for uninstall.
Only the payload scatters per the map. A manifest with no install map keeps the classic
behaviour: everything installs under the app's install path.
Seed vs. managed on upgrade & uninstall
Upgrade and uninstall act on managed files only. Re-installing a newer version replaces managed
files and removes ones no longer shipped; seed files (and any user edits to them) are left untouched.
Uninstall removes managed files and the app record, but keeps seed data.
json-schema
Attach to a mime-type to recognise custom/structured content:
mime-types:
- pattern: application/vnd.acme.diagram+json
mode: edit
priority: 200
json-schema:
id: acme-diagram
source: ./schemas/diagram.schema.json
title: Acme Diagram
version: "1.0"
idstringIdentifier for the schema.
sourcestringWhere the schema is fetched from. Prefer a manifest-relative path (./schemas/…) and bundle the
file in your package — it installs as a child of the manifest node. Absolute VFS paths and URLs also
work for loose manifests.
title / versionstringHuman-friendly metadata.
sandbox
Controls the iframe sandbox attribute. Defaults are conservative — enable only what you need.
allow-scriptsboolAllow JavaScript to run. Default true.
allow-same-originboolTreat the framed content as same-origin. Default false; enable only if your app genuinely needs it.
allow-popupsboolAllow the app to open popups. Default false.
allow-formsboolAllow form submission. Default true.
Keep the sandbox tight
allow-same-origin + allow-scripts together significantly widen what framed content can do. Only
request the flags your app needs — admins see these at consent.
oauth
If your app needs its own identity (to request tokens, define scopes, participate in app-to-app security), declare an OAuth client:
client-typestringpublic for browser apps (no client secret), or confidential for apps with a secure backend.
redirect-urisstring[]Allowed OAuth redirect (callback) URLs for your app. Usually supplied per environment — see Environments & publishing.
settings
Workspace settings and secrets your app requires. Declared entries are auto-registered as workspace
setting definitions — namespaced {metadata.name}.{key} — so admins can supply values in the
workspace settings UI, and installers are prompted for missing values during marketplace install.
spec:
settings:
- key: api-key
secret: true
required: true
display-name: Backend API key
description: Used by the app's backend integration.
- key: region
type: select
options: [eu, us]
default: eu
- key: signing-key
type: file
secret: true
display-name: Signing key
description: Select the local key file during installation.
keystringShort key, stored namespaced as {metadata.name}.{key} in the workspace settings store.
envstringEnvironment variable name (relevant when the same declaration powers an engine run).
secretboolSecret values never reach the browser: your iframe app can see that a secret is configured
(hasValue) but not read it. Defaults to false.
requiredboolRequired settings prompt during marketplace install. Defaults to false.
display-namestringLabel shown in the workspace settings UI.
typestringValue input type: string (default), number, boolean, select, or file. A file field reads
the selected local file in the install dialog and stores its bytes as a base64 string; the file itself
is not added to the package or workspace file system.
defaultanyDefault value used when the workspace hasn't configured one. Not allowed for secrets.
optionsstring[]Allowed values when type is select.
For a headless application that installs a kind: ScriptRun program, link the run manifest back to the
installed Application manifest. Execution verifies that the application's spec.install[] map places the
program path as managed application payload, then supplies its declared settings through engine.settings
and each declaration's env variable:
api-version: verentis.io/v1
kind: ScriptRun
metadata:
name: my-app
engine: python
runtime: server
entry-point: main.py
application-manifest: /applications/my-app
Interactive scripts are normally restricted to their source directory. If an installed headless app keeps credentials, inputs, or outputs in one separate workspace directory, declare that directory on the owning Application manifest:
metadata:
annotations:
verentis.io/execution-file-root: /data/integrations/my-app
The directory must already exist when execution starts. Interactive engine tokens are then bounded to
that directory instead of the script source directory; / is never accepted. Background service runs
continue to use the service identity's declared grants.
Read non-secret values in your app with the SDK's settings module (requires the
fabric.setting.read / fabric.setting.read-all permissions in spec.permissions):
const settings = client.settings.namespaced('my-app')
const region = await settings.getValue<string>('region', 'eu')
display
fullscreenboolWhen true, hides workspace chrome (breadcrumbs, header, toolbar) and renders the app full-screen; the
app provides its own navigation. Can be set per entry point.
Environments & publishing
The same app is usually published to several environments — your local dev server, sprint/UAT, and
production. In practice only two fields change between them: spec.entry (where the iframe is hosted)
and spec.oauth.redirect-uris (the matching callback). Everything else — scopes, permissions,
MIME claims, install map — is identical.
Rather than maintain a whole manifest per environment, keep one base manifest and factor the
environment-specific values into small overlay files under environments/:
my-app/
my-app.app.yaml # base — no entry / redirect-uris
environments/
local.yaml # entry + redirect for local dev
production.yaml # entry + redirect for production
schemas/ …
The base omits spec.entry and spec.oauth.redirect-uris (it keeps oauth.client-type and
everything else):
# my-app.app.yaml (base)
spec:
oauth:
client-type: public
# entry + redirect-uris live in environments/<env>.yaml
Each overlay supplies just the per-environment values:
# environments/local.yaml
spec:
entry: https://my-app.localtest.me:3300
oauth:
redirect-uris:
- https://my-app.localtest.me:3300/auth/callback
# environments/production.yaml
spec:
entry: https://my-app.apps.verentis.dev
oauth:
redirect-uris:
- https://my-app.apps.verentis.dev/auth/callback
Add sprint.yaml, uat.yaml, etc. the same way — the CLI accepts any environment name.
Packing an environment
verentis pack deep-merges the overlay onto the base before it packages the manifest:
verentis pack . --env production # merges environments/production.yaml
verentis pack . --overlay ./custom.yaml # explicit overlay file (wins over --env)
verentis validate . --env production # pre-flight the merged result
Merge semantics: nested objects merge recursively; arrays and scalars in the overlay replace
the base value (so redirect-uris is taken from the overlay, not appended). Packing without--env/--overlay produces a valid but entry-less (headless) manifest — always pass an environment
when you intend to deploy an iframe app.
Versioning in CI
Marketplace versions are unique per package, so a build pipeline needs a fresh version each publish.
Use --set-version to stamp one at pack time (the flag is --set-version, not --version, which the
CLI reserves for printing its own version):
verentis pack . --env production --set-version "1.2.${RUN_NUMBER}" --no-sign
Publishing per environment
Give each environment its own API key and publish from CI. A typical GitHub Actions job on main:
verentis pack . --env production --set-version "$VERSION" --publisher my-org --no-sign
verentis login --api-key "$VERENTIS_API_KEY" # targets production by default
verentis publish my-app-"$VERSION".vpkg
Store VERENTIS_API_KEY as a per-environment secret. The CLI publishes to production by default; to target
another environment (sprint/UAT), set the VERENTIS_API_URL env var (e.g. as a repo variable) — create the
matching environments/<env>.yaml and a workflow (or workflow_dispatch input) that selects it and its own key.