2026.09.35 — 2026-09-24
Breaking
Run on PostgreSQL 18 (c254b4d5)
The compose files now run postgres:18, and the data volume is mounted at /var/lib/postgresql, where the PostgreSQL 18 image keeps its version-specific data directory (/var/lib/postgresql/18/docker). effective_io_concurrency is lowered from 200 to 64 because PostgreSQL 18 turns it into real asynchronous I/O rather than a prefetch hint.
Added
Plugin SDK on Maven Central and npm, with a template (07289ec9)
- io.github.sudoitir:artemis-studio is published to Maven Central as the plugin API (provided scope); @PluginApi types are the supported surface, guarded by japicmp. PluginVerifier runs Studio's upload check in a plugin's own build.
- @artemis-studio/plugin-sdk is published to npm with a Vite preset that shares Studio's React, Mantine and TanStack instances.
- examples/plugin-template ("Notes") is a working plugin with a table, API, assistant tool, settings and a screen; CI installs, updates and rolls it back against a real Studio.
- The runnable jar in the image is now artemis-studio-exec.jar.
Plugin screens load into Studio, with an Admin → Plugins tab (17a1462c)
Studio's UI is now a Module Federation host. At startup it loads the screens of every active plugin; a plugin that fails to load or throws stays inside its own boundary and says why, and built-ins are unaffected. Open pages offer "Plugins changed — Reload" when plugins change.
Admin → Plugins: drop a .jar to inspect it, read what it will be able to do and what happens on confirm (including any database SQL), confirm by typing its id, re-authenticate, and follow its progress. A details drawer covers history, rollback, disable, uninstall and purge, and a header indicator tracks work in the background.
Install, update and remove plugins from Admin → Plugins (3bf3d7cb)
Adds the plugin administration API at /api/v1/admin/plugins: upload a jar (raw body, 50 MB, 5 per hour per user), inspect it, review what it will do, then activate, update, roll back, disable, uninstall or purge it.
- Only users on the installer list can change plugins. The list is checked on every request, no role grants it, and the last installer cannot be removed. The first local administrator (or the users named in ARTEMIS_STUDIO_PLUGINS_INITIAL_INSTALLERS) is seeded as an installer.
- Every change except an upload needs a sign-in within the last five minutes: a password re-entry, or an OIDC round trip with prompt=login that must return the same subject. A stale session answers 403 reauthentication-required. API-token and MCP callers are refused.
- When a plugin needs a restart and Studio runs under a supervisor (the compose restart policy or Kubernetes), Studio shuts down cleanly and exits so it comes back with the plugin loaded (ADR-0104). The compose files set ARTEMIS_STUDIO_PLUGINS_RESTART_SUPERVISED=true and cap metaspace at 256m.
- ARTEMIS_STUDIO_PLUGINS_UPLOAD_ENABLED=false turns installing off.
- Unloaded plugin versions are now released from memory; four JVM-wide caches in Spring and Spring AI no longer pin them.
Group Settings into tabs (7d55ec8f)
Settings now opens as a list of tabs on the left, grouped under Yours, Studio, This cluster and Plugins, instead of one long page of stacked sections. The open tab is in the address (?tab=), so a link or a reload lands on the same section. Arrow keys move through the tabs; Enter opens one and moves focus to its heading.
Fixed
Stop reporting unrelated failures as notification delivery errors (775de9de)
Any internal IllegalStateException reached the client as 502 "Notification delivery failed", whatever had actually gone wrong. Only a notification channel refusing a test message is reported that way now; other internal errors are reported as the server errors they are.
Security
Re-check the plugin id where it becomes a schema name (206444b4)
The id reaching DROP SCHEMA / pg_class queries was already validated at upload and quoted, but only indirectly (via the stored install row). The schema-name builder now rejects anything outside [a-z0-9-] itself, so every SQL use is guarded at the point it is built. Resolves the CodeQL "query built from user-controlled sources" alert on PR #66.
Issue a new session id on every sign-in (8e74ed99)
A session that existed before sign-in now gets a new identifier when the user authenticates, through local login or an identity provider. An id planted in or observed from a browser before login can no longer be used to ride the authenticated session (session fixation). Local login is a controller, so the filter chain's own fixation protection never ran for it.
Nothing to do on upgrade; existing sessions stay valid.
Build
Run on PostgreSQL 18 (c254b4d5)
The compose files now run postgres:18, and the data volume is mounted at /var/lib/postgresql, where the PostgreSQL 18 image keeps its version-specific data directory (/var/lib/postgresql/18/docker). effective_io_concurrency is lowered from 200 to 64 because PostgreSQL 18 turns it into real asynchronous I/O rather than a prefetch hint.