Modules

Module source layers, manifest, and runtime APIs.

Clapilot modules are filesystem packages with a manifest, frontend entry, optional API handler, optional background workers, and optional migrations.

Source layers and precedence

Modules are discovered from three source layers (discovery logic: src/lib/module-store/local-modules.ts):

  • workspace: <workspace>/modules (default /app/workspace/modules)
  • managed: <state dir>/modules — the state dir resolves from CLAPILOTAICORE_STATE_DIR / CLAPILOT_STATE_DIR / CLAPILOT_HOME, with OPENCLAW_HOME retained as a legacy fallback
  • bundled: CLAPILOT_BUNDLED_MODULES_DIR (legacy OPENCLAW_BUNDLED_MODULES_DIR), default /app/bundled-modules, plus a host-local fallback of <repo>/bundled-modules while running from the repo

Discovery scans each source in parallel and keeps a five-second process-local, single-flight snapshot with direct slug/module-key maps. Install, deactivate, delete, icon, and menu-visibility mutations invalidate the snapshot immediately. The browser independently deduplicates concurrent full-inventory requests from the sidebar and active-module guard.

When the same slug exists in several layers, the effective entry is picked in this order:

  1. a fixed: true bundled module always wins (and cannot be shadowed by a workspace copy)
  2. otherwise the higher manifest version wins
  3. at equal versions the bundled copy is preferred, then the newer manifest mtime, then source priority workspace > managed > bundled
workspace workspace/modules managed state dir/modules bundled /app/bundled-modules resolve per slug fixed > version > layer effective module /modules/<slug>

Bundled install state is instance-wide: explicit overrides live in module_installs, while app_settings.module_install_policy selects minimal for new instances or legacy_all for grandfathered instances. The legacy <workspace>/.clapilot-bundled-modules.json disabled list remains synchronized and is used only when the policy is legacy_all and no DB override exists. Fixed bundled modules ignore all overrides and are always installed. Workspace and managed entries are always installed.

The minimal new-instance set is notizen, excel-canvas, word-canvas, and agents; wiki is additionally present because it is fixed. Migration 214_module_installs.sql assigns legacy_all only to instances that already contain users when the migration first runs, preserving their former effective module set.

Manifest contract

module.json example:

{
  "slug": "my-module",
  "name": "My Module",
  "description": "What this module does",
  "version": "1.0.0",
  "entry": "index.html",
  "renderer": "iframe",
  "workers": [],
  "icon": "boxes",
  "categories": ["productivity", "documents"],
  "hiddenInMenu": false,
  "fixed": false,
  "allowOutsideRead": false,
  "outsideReadRoots": []
}

Validation enforces slug format, semver, safe entry path, and icon normalization.

  • renderer selects how /modules/<slug> renders the module: iframe (default) loads the module's entry HTML in a sandboxed iframe; react maps the slug to a first-party React component in src/components/modules/<slug>-module.tsx (wiring: src/app/(app)/modules/[slug]/page.tsx). Only known first-party slugs have React components; third-party modules use the iframe renderer.
  • workers lists optional background worker entry files inside the module (for example workers/post-publisher.mjs in social-media).
  • hiddenInMenu: true keeps the module active and routable under /modules/<slug>, but removes its automatic left-sidebar menu entry. Admins can toggle this from the local module list without uninstalling the module (POST /api/module-store/set-menu-visibility).
  • fixed: true is reserved for Clapilot-owned bundled modules that must always be active (currently wiki). Fixed bundled modules are read from the bundled source, cannot be installed into the workspace copy, cannot be deactivated, and hide install/remove actions in Module Store.
  • allowOutsideRead / outsideReadRoots let a module's API handler read paths outside its own directory (empty roots list = unrestricted read; used by file-explorer).

Beyond the manifest, some modules are visibility-gated in code (src/lib/module-store/developer-mode-modules.ts): agent-orchestrator and terminal are only visible when Developer mode is enabled (terminal additionally refuses to run without it), and video-studio is admin-only.

Store categories

categories assigns the module to one or more store categories. Allowed keys (see src/lib/module-categories.ts):

  • productivity, communication, documents, legal, finance, marketing, media, automation, developer, insights, utilities

A module can appear in multiple categories at once. Unknown keys are dropped during manifest parsing and logged as a server warning ([module-store] Module "<slug>" declares unknown categories ...); local modules without categories fall back to utilities in the store UI. Category labels are localized (de/en/it) via moduleCategory.* translation keys.

Store icon image

If a module ships an icon.png (or icon.svg/icon.webp/icon.jpg/icon.jpeg) in its root directory, the store uses it as the app icon, served through /api/modules/<slug>/assets/<iconFile>. Without an icon file the store falls back to the matching navigation icon from public/icons/navigation/ or a generated gradient tile with the manifest icon glyph.

Runtime endpoints

  • assets: /api/modules/[slug]/assets/[...assetPath]
  • module API handler: /api/modules/[slug]/api/[...endpointPath]
  • storage API: /api/modules/[slug]/storage

Auth model for the module API handler:

  • web users: clapilot_session cookie
  • native runtime machine calls: Bearer token minted by /api/auth/agent/system-token

HTML assets get window.__CLAPILOT_MODULE_CONTEXT__ injection with apiBase, storageUrl, and assetsBase.

Injected HTML remains no-store. Other module assets use a private five-minute cache, one-hour stale-while-revalidate window, and a weak ETag based on file modification time and size.

All three endpoints resolve only the effective module inventory. An uninstalled bundled slug returns 404 with {"error":"module_not_installed"}. The module page and module-owned settings pages are likewise unavailable, and sidebar/settings entries are filtered out. Native agent-tool gating follows in the companion agent-runtime change.

Context publishing

Modules report page context so chat/live agents know what the user is looking at:

  • React-rendered modules pass a context object to the shell via their onContextChange prop.
  • Iframe modules postMessage their context to the parent window; the module page relays it.

The native runtime maps the active module slug to tool families and action rules via services/clapilot-agent/src/page-capabilities.mjs (for example tax-manager activates the tax_manager_* tools).

Lifecycle: publish and install

  1. discover local modules via Module Store
  2. publish signed archive to hub (admin)
  3. install from hub with integrity checks
  4. for bundled install, run migrations once and persist the instance-wide install override without copying the bundled source

API surface: /api/module-store/* (local, catalog, publish, install, install-bundled, deactivate-bundled, delete, set-icon, set-menu-visibility).

Store experience

/modules renders an App Store-style storefront for modules, skills, and widgets:

  • category chip filters plus full-text search across name, slug, description, and category labels
  • category sections with two-column app rows (icon, name, description, action pill) and "see all" expansion
  • install / update / uninstall actions: bundled cards show their DB-backed install state, hub modules install from the connected hub, and installed hub modules show an update pill when the hub has a newer version
  • an app detail dialog with description, categories, version/source/entry metadata, and admin management (icon, menu visibility, publish, deactivate/delete with inline confirmation)

Implementation: src/components/module-store-content.tsx, src/components/skill-store-content.tsx, src/components/mini-apps-content.tsx, shared UI kit in src/components/store/store-app-kit.tsx.

Bundled modules shipped by Clapilot

  • agent-orchestrator (hidden unless Developer mode is enabled)
  • agents (Spezial-Agenten overview)
  • athlete-brand-matching
  • book-appointment (Termine)
  • call-agent
  • canvas
  • cases
  • excel-canvas
  • file-explorer
  • news
  • notizen
  • social-media
  • tax-manager (Finanzen)
  • terminal (hidden unless Developer mode is enabled)
  • video-studio (admin-only)
  • website-canvas
  • wiki (fixed)
  • word-canvas

accounting (Buchhaltung) is a special case: it renders natively in React (src/components/modules/accounting-module.tsx) without a bundled-modules/ directory.

Detailed docs: