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'sdefault.sqliteatomically. - App source migration to
aktorAppDatabase: New apps should useaktorAppDatabase({})from@operaide/database. The bundled skillop-migrate-to-app-databasewalks operaide-code users through the source-code transformation. ROOT_URLtrailing slash: The server now refuses to start whenROOT_URLis set explicitly with a trailing slash. Meteor's own default trailing slash is still accepted. Check yourROOT_URLenv 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_STATUSis set underMeteor.settings.public.featureFlags. The host-sideouter-backupandouter-restorescripts work independently of the flag. - Dev and stable channels:
npm run deploynow uploads as a dev-channel build with a-devpre-release suffix. The uploader (operaide-upload-code) appends-devautomatically; your package.json stays unchanged.npm publishrejects-devversions. 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 legacyPollingReaktorscollection is dropped on first boot; manual start/stop state from before the upgrade is not carried over, and jobs start in theirautoStartdefault. 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
databaseIdsetting 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.jsonlabel. 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
noEmailLoginsetting: Deployments that authenticate exclusively through SSO can hide the email/password login form.
Backups (system administrators)
- Backup status page: Gated by
ENABLE_BACKUP_STATUSunderMeteor.settings.public.featureFlags. Shows recent backup runs and a transferred badge. Pure-function summary tests cover the rendered status logic. - Host wrappers:
outer-backupandouter-restorescripts run on the host.inner-backup,inner-restore, andrestore-initrun 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}.sqlitein the same folder. - Sync primitives extracted:
DatabaseSyncResolver,registerDatabaseSyncEndpoints,DatabaseTransferClient,TransferRegistry, anddatabaseFileUtilsmoved out of the legacy Agent DB code into a standaloneimports/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'snext(), so old chat-history URLs keep working.parseSyncUrlis 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 viafs.copyFileplus temp + rename. Org-scoped permission check.- Playwright E2E coverage: New
AppDatabaseFileDownload.pw.ts(7 cases) andAppDatabaseLegacyImport.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:
/deployand/publishwith progress indicators. - Search subagent: Dedicated search subagent and conversation compaction for large repositories.
- Studio integration: Studio exposes a manual
config.tomlrefresh 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.meteorSubscribeand 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.yamlbuiltin is added. - Per-request org resolution: Studio resolves
orgIdper request instead of from the cookie. Theia sessions are torn down on org switch. - Pool user home sync:
/etc/passwdhome is synced with the assigned pool user home.
Deploy and registry
npm run deploysyncs 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 deployrequires-dev(the uploader adds it automatically);npm publishrejects-devand 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
loadInstalledAppused 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'sregisterDefinitionoverwrites 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: setOPERAIDE_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}/startand.../stopflip job state;GET .../statusreturns 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 globalPollingReaktorscollection, 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_URLtrailing slash validation: The server refuses to start whenROOT_URLis 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.registerPollingReaktorkeeps its signature; polling now runs per App Instance and the v1/api/v1/polling/...REST endpoints are removed (see Upgrade notes).autoStartseeds 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. Optionalnameselects an additional named database ({name}.sqlite, validated against[a-zA-Z0-9_-]{1,64}). Withoutnamethe call resolves todefault.sqlite. Requires an AppInstance context; for standalone reaktors or tests useaktorDatabase({id})oraktorInMemoryDatabase().appFileUrl({fileId, filename?, dbName?})(@operaide/aktor): builds a platform-served download URL for a file held in the per-app database.filenameis cosmetic (browser status bar, Save-As default);dbNametargets a named database. Apps no longer stitch/databases/...paths themselves.getCurrentAppInstance()on theExecutionContext: returns{ orgId, instanceName, dataDir }for the running AppInstance. Reaktor setup createsdataDireagerly.requireCurrentUser(): Identity helper for chat-style apps. Resolves the current user from the session rather than the request body.operaide-upload-codeauto-suffixes-dev: The uploader appends-devto package.json's version before uploading if no pre-release section is present. The file on disk stays unchanged sonpm publishkeeps 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 useaktorAppDatabase({}). The bundled skillop-migrate-to-app-databasewalks operaide-code users through the source-code transformation. The data half - copying the legacy SQLite onto the AppInstance'sdefault.sqlite- is the new Import from Agent DB button on the Databases tab.
Handbook
- New page App Database Files: the
filestable schema, storing files viaaktorStoreFileWithContent, building URLs withappFileUrl, 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-databasebundled 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.