Skip to main content
Version: 3.1

App Store

The App Store is the distribution and runtime layer for Operaide Apps. As a developer, it is how your code gets from npm publish to a running App Instance — and how other developers discover and build on your work.

The App Store is the package registry, deployment pipeline, and runtime host rolled into one. If you want to understand how your code is structured and executed, see Aktor Framework.

Architecture Overview

The App Store connects four key entities:

  • AppDefinition — the immutable package stored in the registry. One entry per packageName@version. Contains the TypeScript source files, package.json metadata, and integrity hashes.
  • App Instance — a named, per-organization installation of an AppDefinition. Multiple instances of the same package can coexist with different configurations. An instance runs on the development or the production channel; see Development and Production App Instances.
  • Deployment — a configured, running instance of a single ReaktorDefinition. Each App Instance owns one Deployment per Reaktor registered by the App.
  • ReaktorDefinition — the async function your main.ts registers via registerReaktorDefinition().

Package Format

Every App is a standard npm package. The naming convention is mandatory:

operaide-app-<name>     # must match ^operaide-app-[a-z0-9-]+$

Minimal package.json:

{
"name": "operaide-app-my-workflow",
"version": "1.0.0",
"description": "My custom workflow",
"main": "src/main.ts",
"peerDependencies": {
"@operaide/aktor": "^0.5.7"
},
"operaide": {
"label": "My Workflow",
"category": "custom",
"quality": "reference"
}
}

The peerDependencies on @operaide/aktor controls compatibility: the platform checks this semver range against its own version and blocks installation if they don't match.

The operaide field carries metadata for the App Store UI:

FieldValuesPurpose
labelfree textDisplay name in the UI; falls back to package name if unset
categorycore, custom, ...Grouping in the UI
qualityreference, template, experimental, needs-review, uncatalogedSignals maturity level
notesfree textShown in the details drawer

Publishing

Two paths to get your App into the App Store:

Point your .npmrc at the Operaide registry and publish like any npm package:

# .npmrc
registry=https://your-operaide-instance/api/v1/registry
//your-operaide-instance/api/v1/registry/:_authToken=${OPERAIDE_DEPLOY_API_KEY}
npm publish

The API key must carry the NPM Registry Publisher role (or a role that implies it, such as App Developer). Keys without it get 403 Forbidden on publish. Issue a reader-only key with NPM Registry Reader for pull-only CI that runs npm install but never publishes.

The App appears in the App Store immediately after publishing.

Dependencies from external registries

If your App depends on libraries hosted on a private npm registry (e.g. shared operaide-* libraries on another Operaide instance, a Verdaccio server, or GitHub Packages), the Operaide instance must be configured to forward requests to that upstream. This is an administrator task — see Upstream NPM Registry for the required environment variables (NPM_UPSTREAM_REGISTRY_URL, NPM_UPSTREAM_REGISTRY_AUTH_TOKEN).

As a developer, you do not need to configure .npmrc for upstream resolution. When you npm publish your App, the platform resolves dependencies from the configured upstream at install time. If a dependency cannot be found, the App installation fails with a missing-package error — ask your administrator to verify the upstream registry configuration.

Via Direct Deploy

For faster iteration during development:

npm run deploy              # one-shot upload
npm run deploy-watch # watches for changes, re-deploys automatically

This sends your source files directly to POST /api/v1/deploy-reaktor, bypassing the npm packaging step. Useful during active development, but npm publish is the canonical distribution path.

Version Channels

Operaide separates builds by a semver pre-release identifier on the package version:

ChannelPre-release identifierEntry pointReach
Stablenone, -rc.*, -beta.*npm publishApp Store, installable by other orgs
Dev-dev, -dev.*npm run deployYour organization only, not in registry
Builtin-builtinOperaide releaseApps shipped with the platform image

npm run deploy adds the -dev suffix automatically. Your package.json keeps its plain version like 0.6.0; the uploader patches the in-flight tarball to 0.6.0-dev before sending. The file on disk stays unchanged so npm publish keeps reading the same package.json as a stable release.

Apps bundled with the Operaide image arrive as builtin-channel versions such as 0.6.0-builtin. If you publish your own build of such an app as plain 0.6.0, your build wins: semver ranks 0.6.0 above 0.6.0-builtin, so default resolution picks your published version while the builtin one stays installable.

Mismatched channels are rejected at the API:

  • npm publish rejects any version with -dev (400). Use a plain version or a stable pre-release such as -rc.1 or -beta.2.
  • npm run deploy requires -dev. The uploader handles this; CI scripts that call POST /api/v1/deploy-reaktor directly must send a -dev version themselves.
  • The builtin identifier is reserved for the platform image. Both npm publish and npm run deploy reject it (400).

The platform UI marks dev-channel versions with a "DEV" tag and builtin versions with a "BUILT-IN" tag in the App Instances list and detail views.

Mixed-channel installs from earlier versions

Older npm run deploy flows uploaded apps with stable version strings such as 0.6.0. After the channel split, those App Instances stay on the stable channel and run unchanged. New deploys of the same code go to the dev channel as 0.6.0-dev and create a separate App Instance lineage. To consolidate, uninstall the old stable App Instance and re-deploy.

Migration: rotate the embedded token

Older Operaide releases baked a shared platform token into the .npmrc of every downloaded App. That token no longer authenticates against the registry, so a local App folder downloaded before this change fails npm install and npm run deploy until you replace it with a per-user key.

  1. Create a personal token: open your user profile at /profile/tokens, click New Token, set a label and expiry. Copy the value before closing the dialog. It is not shown again.

  2. Update .env: replace the OPERAIDE_DEPLOY_API_KEY value with the new token.

  3. Update .npmrc: replace the _authToken value with the same token:

    registry=https://your-operaide-instance/api/v1/registry/
    //your-operaide-instance/api/v1/registry/:_authToken="<your-token>"

Re-run npm install to confirm the registry accepts the new key, then npm run deploy to confirm the deploy endpoint accepts it.

A freshly downloaded App from the App Store already ships with both files filled in. This step is only needed for App folders downloaded before the rotation.

Code Loading and Sandbox

When an App is installed, the platform automatically transpiles your TypeScript and runs it in an isolated sandbox. Only a fixed set of modules is available — you cannot add arbitrary npm packages beyond this list:

CategoryModules
@operaide/*aktor, ai, document, database, vector, mail
npm packagesai, axios, zod, pdf-lib, @libsql/client, fast-xml-parser, @modelcontextprotocol/sdk
Node.js builtinsnode:buffer, node:crypto, node:path, node:stream, node:url, node:util
Other operaide-*Any published operaide-* package (as library dependency)

Your registerReaktorDefinition() calls are intercepted and prefixed with the App ID to avoid naming collisions across Apps.

App Settings

You can expose configuration to the person installing your App via registerAppSettings() with a Zod schema. These settings are editable per App Instance and available at runtime through getAppInstanceSettingsForReaktor().

App Instance Lifecycle

Installation

From the App Store UI, clicking Install opens a three-step wizard:

  1. Select App — version, instance name (URL-safe, unique per org), display label and description (prefilled from package.json, both editable)
  2. App Settings — form generated from your registerAppSettings() schema
  3. Deploy — creates the App Instance and all Deployments

For scripts, CI pipelines, or agents, the same installation is available via REST:

POST /api/v2/orgs/{orgId}/apps

The request body matches the wizard fields (packageName, version, instanceName, optional label, appSettings, reaktorSettings). The full JSON Schema is retrievable via GET /api/v2/apps/schema. Both endpoints require authentication and the permToCreateReaktorInstance permission (role appManager).

Runtime

Each Reaktor in an App Instance is exposed via REST:

GET|POST /api/v2/orgs/{orgId}/apps/{instanceName}/reaktors/{reaktorName}

App Instance status transitions: deployingactive (or error). Instances can be stopped and restarted.

Updating

methodUpdateInstanceVersion handles version upgrades by diffing old vs. new ReaktorDefinitions: it adds new Reaktors, removes deleted ones, and updates existing Deployments in place.

Uninstalling

Removes all Deployments, their settings, and the App Instance document. The AppDefinition in the registry remains untouched — other organizations or future installs can still use it.

Multiple Instances

You can install the same App multiple times with different names and configurations. This is useful for running the same workflow against different AI providers, datasets, or customer-facing configurations.

Core Apps vs. Custom Apps

Every Operaide installation ships with Core Apps (op-core-*) — reference implementations like Basic Chat, Knowledge Chat, and Web Search. These are automatically published to the registry on startup from the bundled operaide-packages/ directory.

As a developer, Core Apps serve as working examples. You can open any Core App in Studio (via the Open in Studio action) to read the source, understand patterns, and use them as a starting point for your own Apps.

Quick Reference

WhatWhere
App Store UIBottom menu → "App-Store"
App Instances listBottom menu → "App Instances"
Package namingoperaide-app-<name> (lowercase, hyphens only)
Runtime API/api/v2/orgs/{orgId}/apps/{instanceName}/reaktors/{reaktorName}

For CI/tooling use:

WhatEndpoint
Publish to registryPUT /api/v1/registry/:name
Direct deployPOST /api/v1/deploy-reaktor
Create App InstancePOST /api/v2/orgs/{orgId}/apps
Instance body schemaGET /api/v2/apps/schema
Download source (Studio)GET /api/v1/download-reaktor-code/:id