2026.09.65 — 2026-09-30
Breaking
Harden key rotation against stale replicas and old request-reply rows (51d5c1ef)
- Drop request-reply originals sealed in the old format, and count and sweep only well-formed blobs, so a malformed value never fails the status view or a rotation.
- Refuse to seal when the current key version was not confirmed from the database within 30 s, and settle a rotation 45 s after its start by the database clock.
- Reload an unknown key version at most every 30 s, and reuse one Vault session that is revoked on shutdown.
- Read the OIDC client secret of Vault from its own optional path, and report stored secrets whose key version the provider lacks (missingVersions).
- Fail startup on a configured OIDC client secret whenever the provider is not env.
- Cache the status counts for 10 s, keep key material out of Keyring.toString, and remove the unused secret_key_state.provider column.
Redact credentials in logs, audit parameters, errors and exports (91729c3f)
Credential-like values (password, secret, token, api key, authorization and private key assignments, Bearer/Basic values, URL user-info, PEM private keys and Studio API tokens) are now masked with [redacted] at four points:
- logs: a logback-spring.xml conversion rule masks the message and stack trace;
- audit: every AuditParamsFilter bean now applies, in order, and a new filter masks credential-named parameters and credential-shaped strings;
- errors: Problems masks the title and detail of every problem it builds, and the MCP error helpers and the SQL stream do the same;
- export: the broker.xml export passes through the redactor.
Envelope-encrypt every stored secret (7c77f0e2)
Every stored secret is now sealed with its own data key, and the data key is wrapped by a key-encryption key (KEK) that carries a version. The sealed value is one self-describing blob kept in a single
sealedcolumn in each store: broker_credential, notification_channel, plugin_secret, message_index and thesealedkey of rr_event.detail. Row binding through the additional authenticated data is unchanged.Keys come from a KeyProvider chosen by
artemis-studio.secrets.provider:env(the default) orfile(a directory ofkek-<n>files). The current KEK version is stored in the new secret_key_state table, so every replica wraps with the same version whatever order it restarts in. Startup fails, naming the provider and the version, when the keyring is missing, empty, the wrong length or lacks the stored version.For upgraders: existing ciphertext is not migrated. The upgrade drops it and these must be entered again: cluster and bridge credentials, notification channel secrets, plugin secrets. Governance originals sealed in message_index and in request-reply events can no longer be opened and those rows stay masked.
ARTEMIS_STUDIO_SECRET_KEYkeeps working as one base64 key (version 1) and also accepts1=<b64>,2=<b64>for several versions.
Added
Warn when a key version still in use is missing from the provider (7c52ce7d)
Settings → Security now names any key version that still protects stored secrets but is no longer in the key provider, and says to restore it.
Clearer key rotation status and guidance (aed3c346)
The rotation confirmation now counts only the secrets still under a version below the target, and says it finishes re-wrapping when no newer key exists. After a successful rotation the section names older key versions that protect no secrets and can be removed from the provider. The dialog now closes when the rotation starts after a step-up as well, and loading shows a placeholder.
Guide key rotation with a version timeline and live progress (d6b40fe9)
The Security section now lists every key version with its state (current, newer, older and still in use, older and safe to remove from the provider), says what to do next in plain words, and shows a progress bar and elapsed time while a rotation runs.
Show key provider and rotate the key-encryption key from Settings → Security (50d97557)
Settings gains a Security section under Studio. It shows the key provider, the current and available key versions, how many stored secrets sit under each version, and the last rotation with its progress and, on failure, its cause. Rotate key needs settings-write, asks for confirmation and a fresh sign-in, and stays visible with the reason when it cannot run. Progress refreshes every two seconds while a rotation runs.
Read keys from HashiCorp Vault or Kubernetes Secrets (e972ea9e)
Select the provider with artemis-studio.secrets.provider=vault or kubernetes (env stays the default, file is unchanged). A provider that cannot deliver a valid keyring fails startup with a message naming the provider.
vault (KV version 2; every version that is not deleted or destroyed and has a base64
kekfield is a key version): artemis-studio.secrets.vault.uri artemis-studio.secrets.vault.mount (default secret) artemis-studio.secrets.vault.path artemis-studio.secrets.vault.authentication (token | approle | kubernetes, default token) artemis-studio.secrets.vault.token artemis-studio.secrets.vault.role-id, .secret-id artemis-studio.secrets.vault.kubernetes-role artemis-studio.secrets.vault.kubernetes-token-path (default /var/run/secrets/kubernetes.io/serviceaccount/token)kubernetes (one Secret read through the API server with the pod's service-account token; keys
kek-<n>andoidc-client-secret): artemis-studio.secrets.kubernetes.secret-name artemis-studio.secrets.kubernetes.namespace (default: the pod's namespace file) artemis-studio.secrets.kubernetes.api-url (default https://kubernetes.default.svc) artemis-studio.secrets.kubernetes.token-path, .ca-path (defaults are the in-cluster service-account paths)The provider also supplies the OIDC client secret (
oidc-client-secret). When it holds one, it becomes the client secret of every OIDC registration. A client secret also set in spring.security.oauth2.client.registration.* stops startup unless the provider is env, so the secret has exactly one source.Adds spring-vault-core, version managed by the Spring Cloud BOM.
Rotate the key-encryption key online (1c189f1f)
An administrator can now rotate the key-encryption key without downtime. New secrets are wrapped with the new key from the moment the rotation starts, and a background sweep re-wraps every stored secret (cluster and bridge credentials, notification channel secrets, plugin secrets, governed message originals and request-reply originals) until none is left under an older key. The sweep resumes after a restart, runs on one replica only, and also catches a replica that still wrote under the old key.
To rotate:
- Add a new key version to the secret provider and keep the old one until the rotation succeeds.
- Start the rotation from Settings -> Security, or with
POST /api/v1/settings/secrets/rotations(needssettings:writeand a recent sign-in). - Follow progress on
GET /api/v1/settings/secrets, which shows the provider, the current and available key versions, how many secrets each version protects and the last rotation. It never returns key material.
A start is refused with 409 when the provider holds no newer key version or a rotation is already running. Every start, refused or not, is audited as SECRET_ROTATION_START. A secret that cannot be re-wrapped fails the rotation, naming the store and row but never a value.
Envelope-encrypt every stored secret (7c77f0e2)
Every stored secret is now sealed with its own data key, and the data key is wrapped by a key-encryption key (KEK) that carries a version. The sealed value is one self-describing blob kept in a single
sealedcolumn in each store: broker_credential, notification_channel, plugin_secret, message_index and thesealedkey of rr_event.detail. Row binding through the additional authenticated data is unchanged.Keys come from a KeyProvider chosen by
artemis-studio.secrets.provider:env(the default) orfile(a directory ofkek-<n>files). The current KEK version is stored in the new secret_key_state table, so every replica wraps with the same version whatever order it restarts in. Startup fails, naming the provider and the version, when the keyring is missing, empty, the wrong length or lacks the stored version.For upgraders: existing ciphertext is not migrated. The upgrade drops it and these must be entered again: cluster and bridge credentials, notification channel secrets, plugin secrets. Governance originals sealed in message_index and in request-reply events can no longer be opened and those rows stay masked.
ARTEMIS_STUDIO_SECRET_KEYkeeps working as one base64 key (version 1) and also accepts1=<b64>,2=<b64>for several versions.
Fixed
Confirm the key version when sealing instead of waiting for the refresh job (0a74abf7)
A seal whose current key version was read more than 10 seconds ago now reads it again from the database first, and refuses only when that fails. Sealing no longer depends on the background refresh job running.
Make key rotation states read correctly (9f2a2a3e)
A rotation that has re-wrapped everything now says it is confirming that no replica still writes under the old key, neutral notices are no longer green, and the first-run notice says a rotation can start when a newer key exists.
Print the one-time administrator password outside the redacted log (7d937469)
Log redaction masked the generated password in the first-start banner, so a fresh install could not sign in. The banner is now printed on standard output, unredacted, exactly once;
logs studio | grep -A4 'Created administrator'works as before.Harden key rotation against stale replicas and old request-reply rows (51d5c1ef)
- Drop request-reply originals sealed in the old format, and count and sweep only well-formed blobs, so a malformed value never fails the status view or a rotation.
- Refuse to seal when the current key version was not confirmed from the database within 30 s, and settle a rotation 45 s after its start by the database clock.
- Reload an unknown key version at most every 30 s, and reuse one Vault session that is revoked on shutdown.
- Read the OIDC client secret of Vault from its own optional path, and report stored secrets whose key version the provider lacks (missingVersions).
- Fail startup on a configured OIDC client secret whenever the provider is not env.
- Cache the status counts for 10 s, keep key material out of Keyring.toString, and remove the unused secret_key_state.provider column.
Keep an unused old key until the rotation succeeds (6bd5ff02)
The Security settings no longer call an unused older key version safe to remove while a rotation is still running.
Make the key rotation sweep converge cheaply and completely (12fa48cd)
A rotation now succeeds only when no secret is left under an older key version and thirty seconds have passed since it started, three times the interval at which each replica reads the current version. A replica that had not yet learned the new version can no longer write an old-version secret after the rotation has already reported success.
The sweep also reads each stored secret once per pass instead of rescanning the table for every batch: it walks each store in primary-key order, and the remaining count is taken once at the end of a pass.
Let a failed key rotation be started again (3488b4c9)
A rotation that failed or was interrupted left secrets under an older key version, and a new one was refused with 409 because the provider held no newer key. Starting is now also allowed when some stored secret is still wrapped under a version below the current one: the new rotation re-wraps those secrets to the current version and leaves the current version unchanged. The 409 remains for the case where there is no newer key and nothing is left under an older version.
Security
Keep private-key redaction linear on repeated markers (1a81718f)
Close redaction gaps in alert errors, log patterns and slow inputs (9ee99473)
Operators gain: webhook URLs (Slack, Teams, webhook, PagerDuty) no longer appear in delivery errors, which are stored and served by the API. Every log pattern word, custom logging.pattern.*, the log file and structured JSON logs are redacted, and Boot's own logging configuration is kept. Redaction runs in linear time on hostile input.
Redact credentials in logs, audit parameters, errors and exports (91729c3f)
Credential-like values (password, secret, token, api key, authorization and private key assignments, Bearer/Basic values, URL user-info, PEM private keys and Studio API tokens) are now masked with [redacted] at four points:
- logs: a logback-spring.xml conversion rule masks the message and stack trace;
- audit: every AuditParamsFilter bean now applies, in order, and a new filter masks credential-named parameters and credential-shaped strings;
- errors: Problems masks the title and detail of every problem it builds, and the MCP error helpers and the SQL stream do the same;
- export: the broker.xml export passes through the redactor.