App Configuration
Every Lombok app is defined by a single config.json manifest at the root of
its package. The manifest is not a loose plugin descriptor — it is validated
against a strict schema (AppConfig) at install time, and unknown top-level keys
are rejected.
This page documents that manifest field by field. It is derived directly from the schema rather than hand-maintained prose, so it is meant to be read as a reference.
Source of truth
Section titled “Source of truth”The manifest shape is defined in the main Lombok repository:
packages/types/src/apps.types.ts—appConfigSchema(AppConfig), the authoritative top-level schema, plusicon,contributions,settings, andcontainerProfilesdefinitions.packages/types/src/task.types.ts—taskConfigSchemaandtaskTriggerConfigSchema(triggers, schedules, completion handlers).packages/demo-apps/simple-demo/config.json— a complete, valid example.
When this page and the schema disagree, the schema wins.
Top-level fields
Section titled “Top-level fields”AppConfig is a strict object: only the keys below are allowed.
| Field | Required | Type | Summary |
|---|---|---|---|
slug | yes | string | Stable app identifier. [a-z0-9]+, lowercase; cannot be platform or core. |
label | yes | string | Human-readable name. 1–128 characters. |
description | yes | string | Short description. 1–1024 characters. |
icon | yes | object | App icon. Builtin or custom (see Icon). |
requiresStorage | no | boolean | Declares the app needs storage access. |
permissions | no | object | Platform permissions the app requests (see Permissions). |
subscribedCoreEvents | no | string[] | Platform events the app subscribes to (core:<event>). |
triggers | no | array | Bind events, schedules, or user actions to tasks (see Triggers). |
tasks | no | array | Named units of work (see Tasks). |
runtimeWorkers | no | object | JS worker definitions, keyed by worker id (see Runtime workers). |
containerProfiles | no | object | Docker-backed execution profiles, keyed by profile id (see Container profiles). |
systemRequestRuntimeWorkers | no | object | Maps platform system requests (e.g. search) to workers. |
ui | no | object | Declares an embedded UI bundle. { enabled: true, csp? }. |
database | no | object | Declares Lombok-managed database support. { enabled: true }. |
contributions | no | object | UI/navigation surfaces and mobile views (see Contributions). |
settings | no | object | User/folder settings schemas (see Settings). |
Identity
Section titled “Identity”A stable, lowercase identifier matching ^[a-z0-9]+$. It is cosmetic and may be
renamed; the canonical app identifier the platform uses internally is
<slug>-<id>, where id is an immutable 8-hex-character suffix. The values
platform and core are reserved.
The display name shown in the Lombok UI (1–128 characters).
description
Section titled “description”A short explanation of the app (1–1024 characters).
The app icon is a discriminated union on source. There are two sources:
builtin (a name from the platform catalog) and custom (assets shipped in
your UI bundle).
Builtin — references a catalog icon by name:
{ "source": "builtin", "name": "box", "label": "My App" }Catalog v1 names: app, box, code, file, folder, settings,
sparkles, terminal. Each rendering client maps these to its own assets.
Custom (SVG) — tintable or original-color vector assets:
{ "source": "custom", "format": "svg", "rendering": "template", "assets": [{ "path": "assets/icon.svg", "appearance": "any" }]}rendering is template (tintable) or original.
Custom (PNG) — raster assets at one or more scales:
{ "source": "custom", "format": "png", "rendering": "original", "assets": [ { "path": "assets/logo-light@2x.png", "scale": 2, "appearance": "light" }, { "path": "assets/logo-dark@2x.png", "scale": 2, "appearance": "dark" } ]}Asset rules:
appearanceislight,dark, orany. An asset set may use either a singleanyasset or up to onelightand onedark— the two cannot be mixed.- PNG
scaleis1,2, or3, and at least one asset must be scale2or3. PNG only supportsrendering: "original". - Custom icons require
ui.enabled: true— their assets are resolved from the app’s UI bundle at/ui/<path>and validated against the bundle manifest.
Permissions
Section titled “Permissions”permissions requests platform capabilities, grouped by scope. Each group is an
array drawn from a fixed enum.
{ "permissions": { "core": ["READ_FOLDER_ACL"], "user": ["READ_USER", "READ_FOLDERS"], "folder": ["READ_OBJECTS", "WRITE_OBJECTS"] }}| Scope | Allowed values |
|---|---|
core | READ_FOLDER_ACL |
user | CREATE_FOLDERS, READ_FOLDERS, UPDATE_FOLDERS, DELETE_FOLDERS, READ_USER |
folder | READ_OBJECTS, WRITE_OBJECTS, WRITE_OBJECTS_METADATA, WRITE_FOLDER_METADATA, REINDEX_FOLDER |
Event subscriptions
Section titled “Event subscriptions”subscribedCoreEvents
Section titled “subscribedCoreEvents”An array of platform event identifiers the app listens for, each prefixed with
core:. Known core events:
core:object_addedcore:object_removedcore:object_updatedcore:folder_scannedcore:serverless_task_enqueuedcore:docker_task_enqueuedcore:new_user_registered
An app must subscribe here to any core: event it references from an event
trigger.
Triggers
Section titled “Triggers”triggers is an array binding an event, schedule, or user action to a task. Each
entry is discriminated by kind. All trigger kinds share these fields:
taskIdentifier(required) — thetasks[].identifierto run. Must exist.condition(optional) — a guard expression evaluated before the task runs.onComplete(optional) — completion handlers (see below).onProgress(optional) — progress handlers.
Event triggers (kind: "event")
Section titled “Event triggers (kind: "event")”{ "kind": "event", "eventIdentifier": "core:object_added", "taskIdentifier": "demo_object_added_worker_task", "dataTemplate": { "key": "{{event.data.key}}" }, "triggerKey": "on_upload"}eventIdentifier— a plain identifier or acore:-prefixed platform event. If it is acore:event, it must also appear insubscribedCoreEvents.dataTemplate(optional) — shapes the data passed to the task using{{...}}references.triggerKey(optional) — a stable handle; when set it must be unique per app.
Schedule triggers (kind: "schedule")
Section titled “Schedule triggers (kind: "schedule")”{ "kind": "schedule", "triggerKey": "hourly_job", "config": { "kind": "interval", "interval": 1, "unit": "hours" }, "taskIdentifier": "demo_scheduled_worker_task"}triggerKeyis required on schedules.configis one of two forms:- interval —
{ "kind": "interval", "interval": <int > 0>, "unit": "minutes" | "hours" | "days" } - cron —
{ "kind": "cron", "expression": "<5-field cron>", "timezone": "<IANA tz>" }. Sub-minute cron is rejected;timezonedefaults to UTC.
- interval —
User-action triggers (kind: "user_action")
Section titled “User-action triggers (kind: "user_action")”{ "kind": "user_action", "taskIdentifier": "demo_user_action_task", "scope": { "folder": { "folderId": "<uuid>" } }}scope optionally narrows the action to a user permission or a specific folder.
User-action triggers are config-only.
Completion and progress handlers
Section titled “Completion and progress handlers”onComplete chains another task when one finishes. It is recursive — a handler
can itself declare onComplete.
onProgress works similarly but fires on progress reports. Every
taskIdentifier referenced by a handler must resolve to a declared task.
tasks declares named units of work. Each task is a strict object:
{ "identifier": "demo_object_added_worker_task", "label": "Demo Object Added Worker", "description": "A task that runs for every newly added object.", "handler": { "type": "runtime", "identifier": "demo_object_added_worker" }}| Field | Required | Notes |
|---|---|---|
identifier | yes | [a-z_]+. Referenced by triggers and handlers. |
label | yes | 1–128 characters. |
description | yes | Free text. |
handler.type | yes | runtime or docker. |
handler.identifier | yes | Target of the handler. |
Handler resolution is validated against the rest of the manifest:
type: "runtime"—identifiermust be a key inruntimeWorkers.type: "docker"—identifieris<profile>:<job>, where<profile>is a key incontainerProfilesand<job>is a job defined within that profile.
Runtime workers
Section titled “Runtime workers”runtimeWorkers is a map of worker id ([a-z0-9_]+) to a worker definition.
These are JavaScript entrypoints that execute tasks and service requests.
"runtimeWorkers": { "demo_object_added_worker": { "entrypoint": "demo_object_added_worker/index.js", "description": "Runs for every newly added object.", "environmentVariables": { "MY_CUSTOM_ENV_VAR": "value" } }}| Field | Required | Notes |
|---|---|---|
entrypoint | yes | Relative path within the workers bundle. No leading / or ./, no .., no backslashes. Must exist in the bundle manifest at /workers/<entrypoint>. |
description | yes | Free text. |
label | no | Display label. |
environmentVariables | no | Map of string → string (non-secret values). |
systemRequestRuntimeWorkers
Section titled “systemRequestRuntimeWorkers”Maps built-in platform system requests to runtime workers. Currently:
"systemRequestRuntimeWorkers": { "performSearch": ["demo_search_worker"] }Each named worker must exist in runtimeWorkers.
Container profiles
Section titled “Container profiles”containerProfiles declares Docker-backed execution, keyed by profile id
([a-z_]+). Use this for work that needs a container image rather than a JS
worker. Each profile is a strict object.
| Field | Required | Notes |
|---|---|---|
image | yes | Container image reference. |
workers | yes | Array of worker definitions. |
resources | no | Resource hints: gpu, memoryMB, cpuCores. |
containerTarget | no | Default targeting strategy for the profile. |
A workers entry is discriminated by kind:
exec—{ "kind": "exec", "command": string[], "jobIdentifier": "...", "containerTarget"? }http—{ "kind": "http", "command": string[], "port": 1–65535, "jobs": [{ "identifier": "...", "containerTarget"? }] }
UI and database
Section titled “UI and database”Declares that the app ships an embedded UI bundle:
"ui": { "enabled": true, "csp": "default-src 'self'" }enabled must be the literal true. csp (optional) overrides the
Content-Security-Policy for the embedded UI.
database
Section titled “database”Declares Lombok-managed database support:
"database": { "enabled": true }enabled must be the literal true. See
App Database Access for usage.
Contributions
Section titled “Contributions”contributions declares the surfaces an app injects into the Lombok UI. It is a
strict object. The five link arrays are all required when contributions is
present (use [] for those you do not contribute); mobile is optional.
The link arrays are:
sidebarMenuLinksfolderSidebarViewsobjectSidebarViewsobjectDetailViewsfolderDetailViews
Each link entry is { path, label, icon? }, where path must start with / and
label is non-empty. mobile declares server-driven mobile views and is
substantial enough to document separately.
Settings
Section titled “Settings”settings defines structured, user- and folder-scoped configuration that
operators fill in after install. At least one of user or folder must be
present.
user/folderare JSON-Schema-07-style objects.secretKeyPattern(optional) marks keys whose values are sensitive so Lombok can treat them as secrets.
For the full settings property model and secret handling, see App Settings & Secrets.
Cross-field validation
Section titled “Cross-field validation”Beyond per-field rules, the manifest is checked for internal consistency at install time. The most important rules:
- every trigger
taskIdentifiermust resolve to a declared task - a
core:event trigger requires the matching entry insubscribedCoreEvents - runtime handlers must name existing runtime workers
- docker handlers must name existing container profiles and jobs
- custom icons require
ui.enabled: true - runtime worker entrypoints must exist in the workers bundle manifest