Verentis

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

entrystring

Optional. 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.

splashstring

Optional 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.

launchobject

Optional. 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-rootstring

Optional. Where the app-record (manifest node + .verentis provenance) is anchored. Defaults to /applications/{metadata.name}. See install.

scopestring

Visibility 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.

sandboxobject

The iframe sandbox flags. See Sandbox.

oauthobject

OAuth 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.

displayobject

Default 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
patternstring
Required

The MIME type or wildcard to match (e.g. application/json, image/*, text/*).

modestring

How the app handles the type: view or edit.

priorityint

Higher wins when multiple apps match a file. Use high values for exact matches, low for wildcards.

json-schemaobject

Optional. 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
idstring

Stable identifier for the entry point.

display-name / description / iconstring

Presentation in the UI.

routestring

Path within your app for this entry point.

entrystring

Optional per-entry-point URL override.

splashstring

Optional splash URL override for this entry point. Relative values resolve against this entry point's resolved iframe URL.

orderint

Sort order among entry points.

mime-typesobject[]

MIME claims specific to this entry point.

displayobject

Overrides 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
enabledbool

Makes 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-pointstring

Optional ID from spec.entry-points. When omitted, the workspace launches spec.entry.

pinnablebool

Allows 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
fromstring
Required

Path or glob relative to the package payload root. Must match at least one file. * matches within a path segment, ** across segments, ? a single character.

tostring
Required

Workspace-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.

modestring

seed 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).

mimestring

Optional 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.

phaseint

Optional 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"
idstring

Identifier for the schema.

sourcestring

Where 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 / versionstring

Human-friendly metadata.

sandbox

Controls the iframe sandbox attribute. Defaults are conservative — enable only what you need.

allow-scriptsbool

Allow JavaScript to run. Default true.

allow-same-originbool

Treat the framed content as same-origin. Default false; enable only if your app genuinely needs it.

allow-popupsbool

Allow the app to open popups. Default false.

allow-formsbool

Allow 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-typestring

public 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.
keystring
Required

Short key, stored namespaced as {metadata.name}.{key} in the workspace settings store.

envstring

Environment variable name (relevant when the same declaration powers an engine run).

secretbool

Secret values never reach the browser: your iframe app can see that a secret is configured (hasValue) but not read it. Defaults to false.

requiredbool

Required settings prompt during marketplace install. Defaults to false.

display-namestring

Label shown in the workspace settings UI.

typestring

Value 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.

defaultany

Default 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

fullscreenbool

When 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.

Next