Skip to main content
Version: 3.1

Release v3.1.0

Per-app databases land as the default storage model: every AppInstance gets its own SQLite file in an exclusive data folder, the manual databaseId setting is gone for new installs, and existing deployments migrate with a one-click button on the AppInstance's Databases tab. The legacy form keeps working unchanged. The release also introduces Operaide Code (a Go-based CLI coding agent), a system-admin backup workflow, a redesigned App Instance detail page, a new App Store layout, and a redesigned global layout. The Operaide Chat app gains file attachments, privacy mode, retention controls, and a language switcher.

Upgrade notes

Read this before upgrading from 3.0.x.

  • Existing AppInstances need a one-time Agent DB import: AppInstances that use aktorDatabase({id: aktorSetting('<database-id>')}) keep working unchanged. To move them onto the new per-app layout, open the AppInstance, switch to the new Databases tab and click Import from Agent DB. The button copies the legacy Agent DB onto the AppInstance's default.sqlite atomically.
  • App source migration to aktorAppDatabase: New apps should use aktorAppDatabase({}) from @operaide/database. The bundled skill op-migrate-to-app-database walks operaide-code users through the source-code transformation.
  • ROOT_URL trailing slash: The server now refuses to start when ROOT_URL is set explicitly with a trailing slash. Meteor's own default trailing slash is still accepted. Check your ROOT_URL env var on deploys that set it explicitly.
  • Per-user NPM registry tokens: The shared platform-wide NPM registry token is replaced by per-user keys. Users who publish or install apps through the registry need to (re)create their personal token. See the on-prem handbook section.
  • Backup status page is feature-gated: The system-admin Backups page is hidden unless ENABLE_BACKUP_STATUS is set under Meteor.settings.public.featureFlags. The host-side outer-backup and outer-restore scripts work independently of the flag.
  • Dev and stable channels: npm run deploy now uploads as a dev-channel build with a -dev pre-release suffix. The uploader (operaide-upload-code) appends -dev automatically; your package.json stays unchanged. npm publish rejects -dev versions. Existing stable AppInstances run unchanged; new deploys of the same code create a separate dev-channel App Instance lineage.
  • Polling runs per App Instance: A polling reaktor now runs once per App Instance, each with its own start/stop state and interval. The v1 REST endpoints /api/v1/polling/{reaktorId}/{start,stop,status} are removed. Use the App-scoped paths /api/v2/orgs/{orgId}/apps/{instanceName}/polling/{reaktorName}/{start,stop,status}. The legacy PollingReaktors collection is dropped on first boot; manual start/stop state from before the upgrade is not carried over, and jobs start in their autoStart default. A reaktor only polls when it is installed as part of an App Instance.

For Users

Per-app database

  • Per-app database: Newly installed apps automatically get their own exclusive SQLite database in the AppInstance's data folder. The manual databaseId setting is gone for new installs - instances are ready to use out of the box.
  • Databases tab on each AppInstance: New tab showing the per-app database's status, size, last-modified time, on-disk path, and sync URL. Buttons for Export to Remote (push to another Operaide instance), Import from Remote (pull from another instance), and Import from Agent DB (one-click local copy of a legacy Agent database onto the AppInstance, used during migration).
  • File downloads in chats: File URLs of the form /databases/{orgId}:{instanceName}/files/{fileId} are served directly from the per-app database. Chat-history URLs of the form /databases/{legacyId}/files/{fileId} from before the per-app rollout keep resolving through the unchanged legacy handler.
  • Per-app database sync between instances: The Databases tab supports chunked export/import of a per-app database via sync URL plus API key, the same machinery as the existing Agent DB sync but per AppInstance.

Operaide Chat

  • File attachments: PDF, image, Word, and Excel files can now be attached to a chat message.
  • Privacy mode: Internet-traffic warnings make explicit when a chat leaves the organisation network.
  • Retention and cleanup: Admins can configure chat retention, run cleanup runs, and grant delete-rights.
  • Language switcher: Toggle the chat UI between German and English.
  • User personalization: The system prompt is augmented with information about the current user.
  • Welcome screen recent templates: The welcome screen shows recently used templates for quick re-entry.
  • "Back to Operaide": The "Log out" link is now labelled "Back to Operaide" to match what it actually does.
  • Model selector label field: Configurable display labels for chat models.

App Instance and App Store UI

  • App Instance detail redesign: New sidebar with reaktor splitter, sticky save bar, danger zone, dedicated Databases tab, redesigned tracing tab. Settings page lets you edit display label and description. The save button activates only when there are actual changes; a Discard Changes button reverts.
  • App Store redesign: New layout with sidebar, version picker, and package.json label. The app name input is split into company and app-name fields. Cleaner route names. Install errors are surfaced in the UI.
  • Launch links and back navigation: The header logo links home, and detail pages have a sidebar back navigation.

Global layout

  • Full-width header, three sidebar modes, flush page header: Layout refresh across the application.
  • Theme-neutral signet logo: A single signet logo renders correctly across light and dark modes.

Reporting

  • Reaktor runs chart redesign: The reaktor runs chart on the reporting page is redesigned to match the clickdummy style.

Authentication

  • noEmailLogin setting: Deployments that authenticate exclusively through SSO can hide the email/password login form.

Backups (system administrators)

  • Backup status page: Gated by ENABLE_BACKUP_STATUS under Meteor.settings.public.featureFlags. Shows recent backup runs and a transferred badge. Pure-function summary tests cover the rendered status logic.
  • Host wrappers: outer-backup and outer-restore scripts run on the host. inner-backup, inner-restore, and restore-init run inside the container. Two Mongo dumps, app-assets, transfer.sh hook, rsync staging. See the new Backup and Restore section in the admin handbook.

For Developers

Per-app database internals

  • Per-app storage layout: An app's data lives under {DATA_ROOT}/app-data/{orgId}/{instanceName}/ and holds the AppInstance's database file(s). In container deployments that resolves to /data/app-data/....
  • Multiple databases per app: An app can hold additional named databases next to its default - filename {name}.sqlite in the same folder.
  • Sync primitives extracted: DatabaseSyncResolver, registerDatabaseSyncEndpoints, DatabaseTransferClient, TransferRegistry, and databaseFileUtils moved out of the legacy Agent DB code into a standalone imports/extensions/database-sync/ extension. The resolver pattern carries both Mongo-backed Agent databases and filesystem-native per-app databases on the same transport machinery.
  • Legacy Agent DB code parked: The Agent DB extension moved to imports/extensions/to-delete/agent-db/. It stays fully functional for existing deployments; the rename signals that it retires once all installations have migrated to per-app storage.
  • AppDatabaseFileWebAppHandler: New WebApp handler under /databases/. URL pattern /databases/{orgId}:{instanceName}[:{dbName}]/files/{fileId}[/{filename}]. URLs without the : separator fall through to the legacy handler via Connect's next(), so old chat-history URLs keep working.
  • parseSyncUrl is variant-tolerant: The App-DB import accepts both app-database-sync URLs and legacy Agent-DB sync URLs as source, so the same transfer mechanism that backs Import from Agent DB also drives cross-instance migrations from legacy to per-app.
  • methodImportAppDatabaseFromAgentDb: New Meteor method for the local copy. Atomic via fs.copyFile plus temp + rename. Org-scoped permission check.
  • Playwright E2E coverage: New AppDatabaseFileDownload.pw.ts (7 cases) and AppDatabaseLegacyImport.pw.ts (5 cases). Both use Cypress-mode helpers to seed SQLite files directly on disk.

Operaide Code (CLI coding agent)

  • Go-based CLI: A new coding agent ships with the platform. Wired into platform build, deploy, and Studio.
  • Slash commands: /deploy and /publish with progress indicators.
  • Search subagent: Dedicated search subagent and conversation compaction for large repositories.
  • Studio integration: Studio exposes a manual config.toml refresh action.
  • Handbook chapter: New Operaide Code chapter walks through setup and usage.

Platform APIs

  • REST endpoint to create app instances: New REST endpoint accepts a POST to create an AppInstance from outside Operaide. Handler extracted with unit-test coverage.
  • Meteor.settings.public.featureFlags: Deployment-wide feature flags drive optional behaviour platform-wide; the Backup status page is the first consumer.
  • Subscription routing: Reactive subscriptions route through MeteorSubscriptionImpl.meteorSubscribe and re-subscribe on user/org change.
  • Publication pattern: Publish handlers use the denies() pattern and never throw. Pattern docs in the platform handbook updated accordingly.

Studio and Theia plugins

  • VSIX cache for Theia plugins: Plugins are hosted on a VSIX cache to bypass open-vsx rate limits. Theia's own downloader is used so transitive plugin dependencies land in the tarball. The vscode.yaml builtin is added.
  • Per-request org resolution: Studio resolves orgId per request instead of from the cookie. Theia sessions are torn down on org switch.
  • Pool user home sync: /etc/passwd home is synced with the assigned pool user home.

Deploy and registry

  • npm run deploy syncs new reaktors: New reaktors land in existing AppInstances on deploy.
  • NPM registry per-user tokens: Registry tokens are no longer sent to non-local tarball URLs. The shared platform token is replaced by per-user keys.
  • Dev vs stable channel separation: A semver pre-release identifier marks the channel. npm run deploy requires -dev (the uploader adds it automatically); npm publish rejects -dev and accepts plain versions plus -rc.* / -beta.*. The ReaktorDefinitionRegistry is now version-keyed so multiple versions of the same package coexist; V2 AppInstances pin to specific versions and look them up by channel.
  • V1 loadInstalledApp no longer wipes other versions: The legacy loadInstalledApp used to remove all versions of a package from the in-memory registry before re-registering its single version, which could blank out V2 AppInstances pinned to different versions across organizations. The package-level removal is gone; loadApp's registerDefinition overwrites the same-version entry in place.
  • AppInstance UI markers: A "DEV" tag marks dev-channel versions in the App Instance wizard, list, and overview. The list also shows an "X.Y.Z available" tag when the App Store has a newer published version.

Polling reaktors

  • One polling job per App Instance: A polling reaktor materializes a job keyed by (appInstanceId, reaktorName). Two App Instances of the same app poll independently, each under its own user identity and org context. This replaces the single global job per reaktor definition.
  • Scheduler dispatches over REST: A one-second tick calls the reaktor through POST /api/v2/orgs/{orgId}/apps/{instanceName}/polling/{reaktorName}/fire, the same execution path as a user call (settings, metrics, permissions, tracing). An internal token authenticates the dispatch: set OPERAIDE_POLLING_SCHEDULER_TOKEN, or one is generated at boot. A per-job in-flight guard prevents overlap when a run outlasts its interval.
  • Control endpoints: POST .../polling/{reaktorName}/start and .../stop flip job state; GET .../status returns state, interval, last run, and next run. Start records the caller as the job owner, and the scheduler runs the reaktor under that identity.
  • Polling needs an App Instance: A reaktor deployed standalone, without being installed as an App Instance, does not poll. The 2.x model of polling individual deployed reaktors is gone.
  • v1 removed: The /api/v1/polling/... endpoints, the global PollingReaktors collection, and the in-process scheduler are deleted. The collection is dropped on first boot. Single-node only for now; multi-node needs a database-level claim before dispatch.

Boot and configuration

  • ROOT_URL trailing slash validation: The server refuses to start when ROOT_URL is set explicitly with a trailing slash. Meteor's own default trailing slash is still accepted.

For Aktor Framework

Breaking changes

  • None for the Aktor API. Existing aktorDatabase({id}) calls keep working unchanged. registerPollingReaktor keeps its signature; polling now runs per App Instance and the v1 /api/v1/polling/... REST endpoints are removed (see Upgrade notes). autoStart seeds each job's initial state when an App Instance first materializes it and does not retro-apply on later version changes.

New features

  • aktorAppDatabase({name?}) (@operaide/database): returns the per-app database client for the running reaktor. Optional name selects an additional named database ({name}.sqlite, validated against [a-zA-Z0-9_-]{1,64}). Without name the call resolves to default.sqlite. Requires an AppInstance context; for standalone reaktors or tests use aktorDatabase({id}) or aktorInMemoryDatabase().
  • appFileUrl({fileId, filename?, dbName?}) (@operaide/aktor): builds a platform-served download URL for a file held in the per-app database. filename is cosmetic (browser status bar, Save-As default); dbName targets a named database. Apps no longer stitch /databases/... paths themselves.
  • getCurrentAppInstance() on the ExecutionContext: returns { orgId, instanceName, dataDir } for the running AppInstance. Reaktor setup creates dataDir eagerly.
  • requireCurrentUser(): Identity helper for chat-style apps. Resolves the current user from the session rather than the request body.
  • operaide-upload-code auto-suffixes -dev: The uploader appends -dev to package.json's version before uploading if no pre-release section is present. The file on disk stays unchanged so npm publish keeps reading the same stable version. A console hint ("Tagging dev build: X -> X-dev") shows the transformation.

Deprecations

  • aktorDatabase({id: aktorSetting('<database-id>')}) as the default pattern for app-bound reaktors: still works, but new apps should use aktorAppDatabase({}). The bundled skill op-migrate-to-app-database walks operaide-code users through the source-code transformation. The data half - copying the legacy SQLite onto the AppInstance's default.sqlite - is the new Import from Agent DB button on the Databases tab.

Handbook

  • New page App Database Files: the files table schema, storing files via aktorStoreFileWithContent, building URLs with appFileUrl, and the URL contract as a platform-internal detail.
  • Agent DB page rewritten: aktorAppDatabase({}) is the default everywhere; aktorDatabase({id}) stays documented as the exception for standalone reaktors and legacy databases.
  • New skill op-migrate-to-app-database bundled with operaide-code: documents both halves (source-code transformation + data copy via the new button).
  • New Operaide Code chapter covers setup and usage of the CLI coding agent.
  • New Backup and Restore section in the admin handbook documents the full backup workflow.