Changelog: Anaphora release notes
All notable changes to Anaphora are documented here.
Legend
- 🚨 Security fix: security vulnerability patches
- 🚀 New feature: new functionality
- 🧐 Enhancement: improvements to existing features, platform and quality work
- 🐞 Fix: bug fixes
[0.18.0] - 2026-10-09: Safer sign-in
A security update for the sign-in. It carries no database migration: the upgrade replaces the image, and you can go back to 0.17.0 the same way.
What you need to do
- Most installations: nothing.
- You sign in with single sign-on only, and have no local administrator? Create one in Settings. It is your
way in when single sign-on is off, for example after a mistake in an
OIDC_*variable or a change of licence. Without it, the first-time setup page opens again while single sign-on is off. - The start log or the upgrade script says that no one can administer Anaphora yet? Open the address it shows and
create the first administrator now.
ADMIN_USERNAMEandADMIN_PASSWORDapply only to the very first start of an empty installation, so setting them later does not close that page.
🚨 Security
- Settings that come from your environment variables are locked. The settings page cannot change them.
- The first-time setup page creates local user accounts, and nothing else. It cannot change any other setting.
- The OIDC client secret from your environment stays out of the database, and the settings page does not show it.
- Connections that stay open (WebSockets) get the same sign-in checks as normal page loads, and they close when the user signs out. Anaphora itself does not use them.
- Security fixes in the image:
perl-base5.36.0-7+deb12u4 (CVE-2026-13221 and six more),handlebars4.7.10 (CVE-2026-106445, CVE-2026-106446),shell-quote1.12.0 (CVE-2026-102422),sharp0.35.5 (GHSA-wq5f-xc86-pv6w) andsource-map-js1.2.2 (CVE-2026-93749).
🧐 Enhancements
- Single sign-on administrators skip the first-time setup page. When
OIDC_SYSTEM_GROUPSnames your administrators, they sign in with single sign-on from the first start, and the setup page does not open. See System groups.
🐞 Fixes
- The first-time setup page no longer turns off OIDC sign-in.
- A first start that fails leaves nothing half-saved. The next start begins clean.
The sign-in service in this release is Authfish 1.0.190.
[0.17.0] - 2026-10-04: Observer keys, AI triage, and single sign-on admins
This release carries three database migrations. AddApiKeys adds a table.
SamlClockSkewChecked and OidcSettingsExplicit come with Authfish 1.0.166,
the sign-in service, and change saved SAML and OIDC settings (see below).
Anaphora now runs Authfish 1.0.183. Some of the licence, LDAP and sign-in
changes change what an existing install does, or stop a login or a logout that
worked before. Read
After the upgrade from 0.16.0
before you upgrade.
🚀 New
- Kibana AI Triage, a built-in template that reasons before it pages. It counts the 5xx errors of the last hour and of the hour before, counts the traffic, takes the overview dashboard, and asks the AI provider for a severity from 0 to 10, with the rules an on-call engineer applies: load is not a failure, a silent source is. Below 7 the run stops and nobody is notified. At 7 and above a second AI action writes the briefing that goes with the report. When the space has exactly one AI provider, the new job uses it. With several or none, the form asks which. See AI providers.
- A monitor reads job health with a key of its own. Settings >
Application > API Keys creates an observer key. The monitor sends it
in the header
Authorization: Bearer <key>to/guest/api/healthand reads every job and delivery interface by name, with its schedule, its recent runs and the delivery counts of the last 24 hours. Error texts stay out: they can quote captured data. Before, the names needed the password of a system user, stored in the monitor. The key is shown once; Anaphora keeps only a hash. The list shows when each key was last used, and revokes a key. A revoked or mistyped key gets 401, so the monitor raises an alert instead of reading the anonymous answer, where every job looks the same. A key in the query string is not read: query strings end up in proxy logs. See Self-monitoring. - A ready curl test for an observer key. Settings > Application > API Keys shows a curl command that calls the health API with the key, right after the key is created, and one that asks for the key without putting it in the command or the shell history. A wrong key makes curl fail with 401 instead of printing the anonymous answer. Application Settings also fits narrow screens now.
- Single sign-on users can manage the settings. A user who signs in with
OIDC, SAML or LDAP gets the system role (the Settings menu) when one of
their groups is in the system groups of that method:
OIDC_SYSTEM_GROUPSfor OIDC from the environment, or System groups in Settings > System > Auth > OIDC, SAML or LDAP. Names match whole and with case. Use a name that is unique at the provider (an LDAP group DN, a Keycloak realm role or full group path). Before, only a local user could have the role. A change applies at the next request of the user. SAML users of a provider that sends one attribute per role now get every role, not only the first, and LDAP users get each name of a group that has several: check the space permissions that match these roles. See System groups.
🧐 Enhancements
- Every health answer carries
statusandcounts. The listing of the system user now has them too, so a monitor keeps every field it reads when it moves from no credentials to a key. - A licence that expires applies Free within the hour. Free has its limits (two jobs, two delivery interfaces, SMTP only, one AI provider). The rows past these limits stay readable.
- A licence that ends leaves only the administrators. Free gives every user
the system role. An install that runs on Pro or Enterprise (a trial too) with
this release or a later one now lets only the administrators in when it goes
to Free: the other users are signed out, their sessions end (also a session
from before a restart), and the login page tells them why. An administrator
logs in and renews the licence in Settings > System > Activation
key, and can give the System role to the users who must keep working (the
role stays editable on Free). Anaphora records the paid key in
storage/paid-edition-seen: delete that file and restart to go back to Free for every user. A new install on Free works as before, and so does an install whose paid key ended before this release. - An upgrade from Free gives each user the role saved for them. Free gives every user the system role. It now applies that role at each request and saves nothing: Settings shows the role of each user, and an upgrade applies it. A user added on Free has the role User and no space roles: give them their roles after the upgrade. An install that ran on Free before this release has every local user saved as System (the first start saved it too), and nothing can tell those users apart from real administrators: after the move to Pro or Enterprise, set the role of each user in Settings. Until then, these users also pass the rule above that lets only the administrators in.
- LDAP settings that cannot work are refused at start, and the settings
page shows why: a user filter without
{{username}}, a filter that does not parse, a group filter with no user in it, a CA certificate that Node cannot read. Check the LDAP settings before you upgrade: an install that runs on one of these has no LDAP login after the upgrade. - An LDAP login that hangs ends after 10 seconds, and a refused LDAP login
shows a reference. Give it to your administrator: every log line of that login
carries
[login <reference>], with the reason (wrong password, disabled, expired, locked, wrong service-account password, directory down).
🚨 Security
- A login from another site is refused (403).
- The limit on failed logins holds. This release closes ways around it.
- A request path that a browser never sends is refused (400). Authfish
answers 400 to a path with
.or..segments (also percent-encoded) or a\, before a page or the app sees it. Browsers resolve such paths themselves. A script that builds URLs by hand must send the resolved path. - A space permission by user name no longer matches an LDAP mail address
(
mail,proxyAddresses,otherMailbox). It matches the login name and the unique names of the entry (such assAMAccountNameanduserPrincipalName). A permission pattern that is not a valid regular expression is refused on save. - A regex space permission can match the whole name. A new button next to
.*sets the mode:^$for the whole name,~for any part (the only mode before). A new permission matches the whole name. A permission saved before keeps matching any part, and the log names it at each start: check the names that it must match before you set it to^$. An expression that runs longer than 100 ms on a name matches nobody until a restart, and the log names it. Do not roll back to an older Anaphora after you change a permission: older versions drop the mode, and the permission matches any part again. See How a permission matches. - OIDC checks the TLS certificate of the issuer at every address. An
httpsissuer URL (OIDC_INTERNAL_ISSUERwhen set, elseOIDC_ISSUER) on an IPv4 address,localhost, a*.localhostname or[::1]now needs a certificate that names that address and a trusted CA, or OIDC sign-in stops: the start log saysOIDC issuer discovery failed:and the reason, and the upgrade script shows it.OIDC_TLS_CA_CERTgives the CA as PEM and keeps the check on: use it if you can.OIDC_TLS_INSECURE_SKIP_VERIFY=trueturns the check off: use it only on a network you trust.OIDC_CLOCK_TOLERANCE_SECONDSlets the ID and logout tokens be that many seconds off the clock of the provider (default 0). A bad value in one of these leaves OIDC off. OIDC set up in the settings page needs no action: the upgrade turns the switch on where the old rule skipped the check, and the start log warns about it. See The TLS certificate of the issuer. A correction to the 0.16.0 notes: Authfish keeps a copy of the OIDC settings from the environment in its database, the client secret included. - SAML and OIDC sign-in follow the rules of ReadonlyREST (Authfish 1.0.166).
Some of these changes can stop a login or a logout that worked before:
- SAML checks the Audience. An identity provider that sends another
Audience, or none, fails every login: set
extraConfig.audience. - A SAML login that starts at the portal of the identity provider is
refused.
extraConfig.validateInResponseTo: neveraccepts it again. - A logout that the SAML provider starts must be signed. Unsigned, it leaves the Anaphora session open. Keycloak: turn on "Sign documents" in the client.
- A saved SAML clock skew of
-1(no time check, the old default) becomes0. A server clock behind the clock of the provider then fails with "SAML assertion not yet valid": sync the clocks, or setaccepted_clock_skew_ms. - A user whose provider sends exactly one group now gets that role. Before, the user got no role.
- A SAML assertion or an OIDC profile without the username attribute is refused.
- OIDC logout goes to the logout endpoint that the provider publishes,
unless
OIDC_INTERNAL_ISSUERis set (then it keeps the Keycloak logout path onOIDC_ISSUER). A logout at the provider ends the Anaphora session too when the back-channel logout URL of the provider is<PUBLIC_URL>/auth/logout-oidc/backchannel.
- SAML checks the Audience. An identity provider that sends another
Audience, or none, fails every login: set
- The session cookie is
Securebehind a TLS proxy. When the reverse proxy says that the request came over https (X-Forwarded-Proto), the cookie isSecure. Over plain http it stays as before. You do not set anything. The start log now warns when the certificate of anldaps://server is not checked (Authfish 1.0.167). - Security updates. Next.js 16.3.8 closes a critical remote code
execution in
next/og(GHSA-vcvr-r3jv-pc5j);brace-expansioncloses two denial-of-service advisories (CVE-2026-102276, CVE-2026-102278);moment2.31.0 (CVE-2026-17495),serialize-javascript7.1.2 (GHSA-gfhx-hw2g-v5hg) andfast-uri3.1.8 (GHSA-hrr3-gc8f-f4qj); the image moves to the Debian snapshot of 1 October 2026, which bringslibexpat12.5.0-1+deb12u4 (six CVEs) andxz-utils5.4.1-1+deb12u2.
🐞 Fixes
- An
ifcan test a number that an AI action returns. The form refused it as an invalid variable. The answer must be a bare number, or hold exactly one number ("Severity: 7", "7/10"). Any other answer fails the run, so a branch never runs on an answer that nobody can read. Before, a wordy answer compared as not-a-number and an empty one as 0. - A failed sign-in at the provider lands on the login page. An expired Keycloak login answered 500. The login page now says why. A session that is already signed in goes on to the app. No error shows a stack trace (Authfish 1.0.165).
- The login page works behind a proxy with small header limits. It set a 5 KB cookie with its branding and themes. nginx answered 502 on login after the 0.16.0 upgrade, and browsers drop cookies over 4 KB. The page now reads that configuration from the server (Authfish 1.0.164).
- The upgrade script shows its question before it waits. On Debian and
Ubuntu the question appeared only after the answer was typed, so the script
looked hung. It now ends with
Upgrade complete: Anaphora runs <image>. - A save that the licence refuses says why. Copying, cloning or adding past a Free limit showed "Application error" or a message with no text. It now names the limit.
- A database that is busy for a moment keeps the licence. One failed read of the stored activation key at the hourly check applied Free, which signed every user out, and the next check applied the paid edition again, which signed them out a second time. The licence now stays as it was until a read works.
- The licence end date names its time zone. The banner, the licence emails
and the alerts give the end of a licence in UTC, with the time: "October 14,
2026 at 23:30 UTC". Before, the emails gave the day in the time zone of the
server (
TZ), so an install withTZset can now see another day in them. The banner also stopped failing to load ("Hydration failed", React error 418) for a user far from UTC near midnight. The times in the Runs and API key tables and in a Kibana time range stay in the time zone of your browser. They show just after the page loads.
[0.16.0] - 2026-09-24: An API for agents, OIDC, and private report links
This release carries nine database migrations (AddReportTokenToRuns,
AddAiUsageAndBudgets, HealEmptyReportTemplates, LinkOrphanedRetryRuns,
DropStaleLogins, MoveBackgroundImagesToTable, HealBooleanRunValues,
KeepStoredReplacementsAsRegex, ClearStaleWebhookBodyFlags).
The previous version refuses to start on the upgraded database, so a rollback
needs the database from before. Anaphora now keeps that copy itself: before it
applies a migration it writes one to storage/backups/. To upgrade, use the
upgrade script that comes in the new image. It tries the new version on a copy
of your database first, and it can roll back. See Upgrading Anaphora.
What you see after the upgrade:
- Every user is signed out once and logs in again.
- The log may say that some local users "still carry the legacy sha512 password hash". Their passwords still work. Re-save them under Settings to store the new kind.
- Jobs set to keep their runs forever still keep them. To free disk space, set a retention on the job. The hourly clean-up then removes the older runs and their report files.
- An upgrade from a version older than 0.9.5 gives a job that never had a retention setting six months. On every install, a job made from a default template now gets six months too.
- The hourly clean-up also removes report folders that no run owns any more, for example those that "Delete ALL" left in older versions. It removes only folders older than one day, and it removes none while the database holds no run.
HealEmptyReportTemplates rewrites stored report templates in place and
cannot be reversed. It does two things, both described under Fixes: it gives a
template with no page one default page, and it trims a row that holds more
cells than its column count says. Two behaviour changes come with it: a job
or template write that carries no page is now refused, and a legacy JSON
import that carries one is refused in "safe" mode; pick "autofix" to have it
repaired on the way in.
Five more migrations rewrite stored data in place. None changes what a job does:
DropStaleLoginsremoves the user name and password that a capture kept after its authentication was set to "none". Nothing read them; they were still in the database and in every export.MoveBackgroundImagesToTablemoves the report background images out of the storage row into a table of their own (see Fixes).HealBooleanRunValuesstores 1 or 0 where a run holdstrueorfalse(see Fixes).KeepStoredReplacementsAsRegexmarks every text replacement saved before this release as a regular expression, which is how it always ran. The "Use regex" switch now works for new replacements (see Fixes).ClearStaleWebhookBodyFlagsclears the "user-defined body" flag on webhooks that are not a JSON POST, and copies the JSON body template of each job on such a webhook into its message, so the job sends what it sent.
🚀 New
-
A safe upgrade, in the open. The image carries an upgrade script.
checkstarts the new version on a copy of your database, with no network and with deliveries off, and tells you in plain words what it found: the database changes it applies, the jobs it reads, and any job it cannot read.applyshows its plan and asks before it stops Anaphora. It backs up the database, switches the version and checks that it answers. If the new version does not start, it puts the backup back.rollbackrestores a backup. Each docker command is printed before it runs,--dry-runruns none, and the whole session goes to a log file. -
Anaphora backs up its database before an upgrade changes it. The copy goes to the
backupsfolder next to the database, encrypted with the same key. The log names it and says how to restore it. The three newest are kept. If the copy cannot be written, no migration runs and the database stays as it was. -
AI actions send the whole context correctly. Four defects are fixed. The first two change what a dashboard job sends, and so what it costs: read this before you upgrade if you use AI actions on dashboards.
- Tiles that share a title are no longer merged. A dashboard tile is identified by the platform's own id, or, when it has none, by a hash of its title, so two panels both called "Error rate" got the same id. On a 12-tile page, 12 tiles were captured and 8 reached the model; the other four were silently swallowed, and a template that pointed at one of them was pointing at an ambiguous thing. Every tile now gets its own id. The first tile to claim an id keeps it, so references that resolve today keep resolving.
- A dashboard unticked in an AI action's context table is left out. The tick box stores the id of the capture action, while each tile carries its own; nothing matched, so unticking a dashboard removed nothing and all of it was sent anyway. A single snapshot always worked.
- Accessibility trees are no longer encoded twice. Every quote and line break in them was escaped a second time on the way out: 11% of those characters on the dashboard above were escaping the model had to read and you had to pay for.
- Chrome's internal bookkeeping is no longer sent. Every node of an
accessibility tree carried a
loaderId(a per-navigation nonce) and abackendNodeId(an internal handle). On the dashboard above that was 288 copies of a 32-character nonce: 40% of the accessibility payload, about 3,400 tokens on a single call, meaning nothing to the model.
Net effect on that 12-tile dashboard: the request is 45% smaller than before while carrying twelve tiles instead of eight. A job that sends one snapshot shrinks slightly. Nothing you configured changed.
-
AI actions can now say what they need. An action sent every earlier snapshot and variable, with an opt-out list; a dashboard job therefore sent twelve accessibility trees to answer a question about one of them. The "Context" section of an AI action now has two modes. "Everything so far" is what every action did, and still does until you switch it: every earlier row is sent unless you untick it, and so is anything you add to the job later. "Only ticked rows" sends the rows you tick and nothing else, so a row you add later stays out until you tick it. Switching modes moves no tick. Under the table, "Send the snapshots as images" sends the ticked snapshots as pictures to a provider whose vendor reads them (OpenAI does, DeepSeek does not, and the box says which), and "Answer length cap" limits the reply in tokens. Both are off unless you turn them on.
-
AI token budgets. Every AI provider now shows what it spent in the last 24 hours and in the last 7 days, and you can give each window a budget in thousands of tokens. When a budget is reached, the job that made the call is paused and every further AI call on that provider is refused until the window rolls over or you raise the budget. The two columns turn green, yellow and red as the budget fills, and a paused job says on the jobs list that a budget paused it, so you can tell it apart from one you switched off yourself. One button on the provider list switches the jobs that budget paused back on, and only those. Optionally the operator is notified over the channel that already carries the health alerts, when a budget is reached and, if you want it, earlier at a percentage you choose. Usage counts every call: scheduled runs, manual runs, tests, previews and the Test button on the provider form.
-
The Jobs, Templates, Delivery Interfaces and AI Providers lists share one action bar: the create button, a search box, a Filter menu with one checkbox group per column and a Clear button, with the bulk actions in the same place once rows are selected. The funnel and magnifier icons in the column headers, the per-column search popovers and the trailing Clear column are gone. The lists paginate with a page-size chooser and a "1-10 of 42" total, long names and URLs are cut with a tooltip that holds the full text, and a search or filter change goes back to the first page.
-
Anaphora explains itself to an AI agent that works in your browser (Claude in Chrome and the like). Every page links to
/llms.txt, a guide to what a job is, how a capture is built, how a report is templated and delivered, and how to do all of it through a JSON API instead of the editor. The API lives under/api/spaces/{space}/…(jobs, blueprints, delivery interfaces, AI providers, runs), with/api/agent/contextfor "who am I and what may I do" and/llms/openapi.jsonfor the description. It uses your signed-in session and the same access and edition checks as the pages. -
Sign-in through OpenID Connect can be set from the environment:
OIDC_ISSUER,OIDC_CLIENT_IDandOIDC_CLIENT_SECRETswitch it on, andOIDC_INTERNAL_ISSUERnames an issuer address that is reachable from inside your deployment when the public one is not. Authfish, the sign-in service, keeps a copy of these settings in its database, the client secret included. Enterprise only. -
An AI provider can be set from the environment too:
AI_PROVIDER,AI_MODELandAI_API_KEY, plusAI_ENDPOINTfor an OpenAI-compatible service andAI_NAMEfor the name you see. The provider follows the environment on every start. -
The dark theme sits on one hue: form fields, cards and the footer are steps of the sidebar's navy, the active menu item is a tint of the primary colour, and errors are a soft coral instead of a hard red.
🚨 Security
-
Password guessing is limited. A user name that fails 10 times from one address in 15 minutes, or an address that fails 100 times, gets 429 until the window ends; the basic-auth API routes pass the 429 on with
Retry-After. Behind a reverse proxy, setAF_TRUSTED_PROXIESto the proxy's address, or every browser shares the proxy's address and the start-up log warns. See Failed sign-in limits. -
The capture extension talks only to the Anaphora you trust. The extension (1.2) answers only the addresses you trust, hands a recording only to the Anaphora that started it (one started from the extension popup goes to any trusted Anaphora), and takes only messages the page sends to itself. Download the extension again from the job editor and load it once more. On first use, open the extension popup on your Anaphora page and click "Trust this Anaphora and reload"; the options page lists the trusted addresses. Recordings are not masked: a replay needs the typed login.
-
/guest/api/healthtells an anonymous caller only colours and counts. Without credentials it answers 200 withstatus(the worst colour),countsper colour, and one{ healthStatus }entry per job and per interface, worst first. A monitor that checks the status code, countsjobsor readsjobs[*].healthStatuskeeps working. The full listing now needs the system role: sendAuthorization: Basicwith the credentials the export uses. Other credentials (a proxy gate's, for example) get the summary. -
A basic-auth call no longer leaves a session behind. The session of a call to the guest export or import route ends when the call ends.
-
The API refuses calls from other origins, sibling subdomains included. It accepts a browser call only from the app's own origin:
Sec-Fetch-Site: same-siteis refused, and a browser that sends noSec-Fetch-Sitemust send anOriginorRefereron the app's host. A script that sends none of these headers (curl, the documented import and export calls) is not affected. -
Job login passwords reach fewer places. The runs, reports and home pages, the runs API and the Debug Files zip send a job with its capture password redacted, and the log line of each capture action no longer carries it. Container logs from before the upgrade can hold job login passwords in clear: delete them, or change those passwords.
-
The log names a leftover
storage/storage.json. Installs from before the database era keep this file: a plain-text copy of the old storage, outside the encrypted database, that can hold job passwords in clear. Anaphora reads it only to fill an empty database, so while your data is in the database nothing reads it; but a new, empty database gets its stale data. Anaphora does not delete it. When the file is still there, each start and each import now log a warning. An import of an old export writes the file again. Back the file up elsewhere if you want to keep it, then delete it. -
A password in a capture URL is refused. A job or template write with a URL such as
https://user:secret@kibana.example.comis refused, with a message that points to the Authentication field. A job saved before keeps running; its readers, its logs and its run errors show the password as********. Move the login to Authentication when you next edit it. -
The sign-in secrets are encrypted with your database key. A save in the Authfish settings encrypts the OIDC client secret, the LDAP bind password and the SAML decryption key with
DB_ENCRYPTION_KEY. Secrets saved before the upgrade still work. After that save, two things need care. A change of the key (PRAGMA rekey) needs the secrets entered again. A rollback needs the database from before the upgrade, which the upgrade script restores. -
More sign-in secrets are stored encrypted, and fewer reach the logs. The LDAP client TLS key and its passphrase, a
client_secretin the OIDC "extraConfig", andprivateKeyanddecryptionPvkin the SAML "extraConfig" are encrypted on save. Container logs from an install that ran LDAP at the trace log level can hold passwords in clear: delete them. A value saved in clear before keeps working. In the Authfish settings, show a hidden text such as the TLS key before you edit it. -
The image no longer carries a default HTTPS key pair. Anaphora never turned on the HTTPS server of Authfish, so no install used it.
-
Only a system user sees the secrets of the settings. Only a system user gets the authentication configuration, including a past version of it, and only a system user can list and end sessions or use the password and secret tools of the settings. Change the session secret and the identity-provider secrets after you upgrade if accounts you do not trust can sign in.
-
A stored activation key is no longer lost by chance. A rare start could delete a valid stored key.
-
A report template can no longer read files of the server. The
{% include %},{% render %}and{% layout %}tags are gone. A template that uses one is refused as a syntax error: by the editors, by every job and template write, by the JSON API and by imports. No template in Anaphora needs them. -
A template or an equation can no longer stall or crash the server. Both run in a separate process with its own memory (256 MB) and a wall clock: 10 seconds for all the text blocks of a report, 5 seconds for an equation. When the limit is reached, only that process stops: the block shows why, and the equation fails its action. The job editor renders its blocks and its preview in a Web Worker, one at a time, each with a 6-second clock, so a slow template no longer freezes the browser tab. A template also has at most 5 000 Liquid tags (each line of a
{% liquid %}tag counts as one), a render returns at most 20 million characters, and so do all the text blocks of a report together. The separate process keeps the time zone and the language of the server, so report dates stay in your zone. At most two per Puppeteer worker (WORKER_COUNT) run at once; the others wait. An equation that arrives through the JSON API is checked in a separate process too; the job form checks it in the browser. -
"Add job from template" reads a template of its own space only, as the edit page does.
-
The lists and menus of the Overview, Jobs, Runs and Reports pages, and the JSON reads of a member with read-only access (
GET .../jobs,.../jobs/{id},.../blueprints,.../blueprints/{id}), carry the captures without their login passwords. A writer still gets them as stored through the API and the editor, which need them. -
Suspend and Activate from the Jobs list write the state and nothing else.
-
Report links are private. Every new run gets a secret token; the links in a delivered email, Slack message or webhook carry it, and the report files (PDF, HTML, images) open for that token or for a signed-in member of the run's space, and answer "not found" to anyone else. Runs made before the upgrade keep the links that were already sent, until they expire or are deleted.
-
The edition limits are checked on the server, for every call and import. The three-actions-per-job limit of the free edition and the rule that a job starts with a navigation are checked on save too.
-
Copying a delivery interface, an AI provider or a blueprint into another space needs admin access to the space it comes from, and copying a job needs write access there. Cloning or copying an AI provider works again.
-
The delivery-interface list and the health-monitoring form no longer receive credentials; the pages get the names and types they show.
-
The container no longer carries
sudo. It starts as root, prepares the storage and report volumes, and drops to the unprivilegedpptruser(uid 996) with no capabilities and no way to regain them. A container started with--userskips that step and stops with thechownto run when its volumes are not writable. -
Local passwords are stored as salted scrypt hashes, and a login attempt costs the same whether the account exists or not. Existing accounts keep working; a password is re-hashed the next time it is set. A fresh install gets a random session secret, and an install that saved the old placeholder is repaired on start.
-
The two processes refuse every internal call when their shared secret is not set.
-
The database key is never printed in a log, a key with a quote in it no longer breaks the connection, and an instance running on the default
DB_ENCRYPTION_KEYsays so at start, once. -
Two leftover routes are removed: a test route and the unlinked
/debugpage. If a webhook delivery interface points at the test route (/guest/api/test/webhook), point it at a real receiver: a webhook that answers "not found" now fails the delivery. -
The snooze and unsubscribe pages check a link as strictly as their actions do.
-
A demo user entry with two fields no longer prints its password in the log.
-
A report can no longer reach your internal network. The report renderer refuses loopback, private, link-local and other internal addresses, and fails the render when a host that looked public answers from an internal address. A report that shows a logo from an intranet server needs that host in
REPORT_ALLOWED_HOSTS. Behind an HTTP proxy, the proxy must refuse internal addresses. See Installation. Captures are not affected: they can still open internal dashboards. -
The internal token of the renderer goes with the files of its own report only. Reports also run no script:
/contentserves them in a sandbox. -
A job opens http and https pages only. A URL with another scheme is refused on save; a stored one fails its run.
-
Basic-auth credentials stay with their host. The capture sends them only to the host of the navigation.
-
A job delivers only through a delivery interface of its own space. A job that names an interface of another space is refused on save, and a stored one fails that delivery with a message.
-
Every page of a space checks the membership. A member removed from a space loses access at once.
-
The live-update stream needs a login.
/scheduler/eventsneeds a session, takes at most 1000 streams, and pings every 30 seconds. -
The server decides every new id. A copy of a delivery interface, a template or an AI provider gets a new id on the server, and edits and deletes act on the rows of their own space only. A copy of an interface you may not copy is refused with the reason, instead of giving the job the dummy interface in silence.
-
The limits of the free edition hold on the server too. A create past a limit is refused, with the licence message.
-
The snooze and unsubscribe pages get the job name only. A snooze link with no recipient is refused.
-
An action id must be a UUID. Stored runs still read.
-
An upload is capped at 20 MB and must be a PNG, JPEG, GIF, WebP or SVG image.
-
The database import refuses calls from other sites, and
/llms.txtno longer repeats text from its address. -
The published image is reproducible: the base image, every Debian package, Chromium and the build tooling are pinned to exact versions, so two builds of the same commit give the same image. The vulnerability scan also covers dependency updates that touch only the lock file, and every dependency update goes through the full image, browser and end-to-end gates before it can merge.
libpcre2in the base image carried two high-severity advisories; the patched build is installed.
🐞 Fixes
- Long captures keep their retries. A capture that took more than five minutes lost its retry: the scheduler stopped waiting for the answer.
- A capture that runs past its 30-minute limit is stopped. Before, the queue freed its slot while the capture went on, so hung captures piled up. The run is stored as failed and retried.
- A failure after the capture is a failed run. A full disk, a delivery that throws or a PDF that times out used to leave no run at all; now the run shows the error and is retried.
- A Kibana page that never shows Kibana fails after five minutes. A login page or an error page used to produce a report of that page. A missing panel or a spinner that does not stop only warns, because they differ between Kibana versions.
- A report that reached nobody is retried. A report withheld on purpose, or delivered to some of its recipients, is not sent again.
- Mail goes out one message at a time over one connection. Parallel connections made a server that allows few of them refuse most recipients. After a server failure the other recipients are marked failed at once instead of each waiting for its own timeout, and every SMTP timeout is now at most one minute.
- Retries are listed under the run that failed first. Each attempt was stored as a separate run: "Attempts" never counted, and deleting the failed run left its retries and their files behind. The upgrade re-attaches the retries already stored.
- Deleting a job removes its report files. Before, the files stayed on disk for good, because the clean-up finds them through the runs.
- Start-up names the runs it missed. When Anaphora was down at a job's firing, the log says so. The job runs at its next firing, not late.
- No false alarm about zombie processes. Under
init: truein compose, the process manager logged that "zombie reaping won't work". It did work; the message is gone. - A typo in a text block no longer takes the job editor down.
{{ hitswith no closing braces, or an{% if %}with no{% endif %}, threw while the Compose tab drew the block. The whole job page closed and lost the unsaved changes of every section, and the job could not open Compose again. The block now shows the error in its place, and a syntax error cannot be saved. The text editor keeps Ok disabled. The message, rich message and JSON body editors of a delivery refuse it. A job or template write through the JSON API or an import is refused with the field's path. A template that fails only on example values (a colour filter on a captured text) can still be saved. - Quotes and comparisons work in a text block. The text editor stores
",',<and>as HTML entities, so{{ v | default: "n/a" }}and{% if n > 5 %}never rendered. They are decoded inside the Liquid tags before the template is read. - A report whose text block does not render is not delivered. Before, the whole report was replaced by "There was an error building the report" and that page went to the recipients. Now the report is built with the error in place of the broken block and kept on the run, so you can open it and see where the template breaks; no recipient, bucket or webhook gets it. The Jobs list shows the job as Not delivered, job health counts the run as failed (so the health alert fires), and every delivery of the run names the page, row and column of each broken block; the job log carries the error itself. The delivery interfaces are not blamed: their health ignores such a run. The run does not count as sent, so a throttled job sends the next run with the fixed template. A Test withholds the same way and says so. A report that could not be built at all is withheld the same way, and the run fails. For a job with no delivery interface, the Runs page names the row "No delivery interface" instead of "Unknown delivery config".
- A failed delivery shows on the job. When a mail server, Slack or a
webhook refused the report, the Runs page said "Delivery issue", but the
Jobs list showed the job as Success and job health stayed green, so no
health alert fired. The Jobs list now shows Delivery issue. Job health
counts a run whose report reached nobody as failed, as it counts a failed
capture, and a run whose report reached some interfaces or recipients but
not all as partly delivered: one address that refuses the mail keeps the job
yellow, not red. In
/guest/api/health, thestateof a recent run can now bepartial. If you monitor that endpoint: the first health check after the upgrade rates the last five runs of each job under this rule, so it can turn a job yellow or red, and send the health mail once, for a failed delivery that is days old. - A renderer failure is retried. When the server could not start the separate process that renders the report (out of memory, out of processes), the report was withheld as if a text block were wrong. The run now fails, and the job's retries run it again.
- The S3 copy matches the PDF. The HTML copy for S3 rendered the text blocks a second time, and could fail on a block the PDF showed; it now shows the blocks the run rendered. With "PDF and HTML", nothing is uploaded when the HTML copy cannot be built; before, the bucket got the PDF alone.
- The S3 HTML copy shows a snapshot that was not taken. A snapshot inside a condition that did not hold made the HTML uploaded to S3 the error page; it now shows the "Snapshot not caught" placeholder, as the PDF does.
- A webhook that refuses the report is no longer a green delivery. The webhook call ignored the answer. A rotated token, a moved URL or a receiver that was down was recorded as a successful delivery, of a report the webhook never took. A webhook that answers anything but 2xx now fails the delivery, and the run shows the status and the answer the webhook gave. The Test button, the health monitor, the licence alert and the AI budget alert read the same call, so they report the failure too.
- A report with no page is no longer possible. A template whose page list was empty rendered nothing and every run of the job died in the PDF step, with no message that named the cause. An install upgraded from the legacy storage carried such rows; the migration gives each one a default page, keeping the row's own colours and font, and a write that would create one is refused with a message that names the field. A template that already has no page can still be hidden, renamed or cloned: the page is repaired on the way through rather than the write being refused.
- Grafana 12 and 13 dashboards are captured again. The capture waited for every panel to draw, but Grafana draws a panel only when it comes into view, and it now scrolls the dashboard inside the page: the panels below the fold never drew, and the capture failed after a minute. Each panel is now brought into view in turn. A whole-dashboard snapshot held only the first screen; it now holds every panel. A panel that shows "No data" no longer holds the capture, and a dashboard that does not exist fails at once with "The Grafana dashboard was not found".
- Kibana hit counts over 999 are read whole. Kibana prints the count grouped by thousands ("1,470,592"), and Anaphora kept only the first group (here, 1). Every alert or conditional report that compared hits above 999 compared the wrong value. The count is now read whole, in any grouping style.
- A cron the scheduler cannot run no longer takes Anaphora down. A
pattern such as
0 */25 * * *passed the form but crashed the scheduler at start, and the scheduler serves the login page. Such a pattern is now refused on save, and a job that has one shows "Never runs". - One job that cannot be read no longer takes the pages down. A stored job row that does not parse broke the overview, the lists and the scheduler start. Now it is left out and named: the Jobs page shows it with a Delete action, and its runs are kept.
- A snooze link only snoozes. An empty snooze duration was stored as an unsubscribe. A snooze now lasts 5 minutes to 8 days, and the button stays off until you pick a duration. The unsubscribe link still unsubscribes.
- The Runs page shows what a run ended as. The Status and Report filters read the first attempt, so a run whose retry succeeded was listed under Error. They read the latest attempt now, as the row does. The Duration column was always empty, sorting by job name reset every filter, and the Attempts filter did nothing: all three work. A malformed filter or time range in the address no longer crashes the page, and deleting the last rows of the last page moves to the page before instead of "No data".
- Lists in reports have their bullets and numbers again. They were gone from every text block since the editor upgrade, in the delivered report and in the previews.
- A job with a missing or broken URL says so. It failed with an internal browser message. It now fails with "Found no valid URL to navigate to" before the navigation starts.
- A test run that fails after the capture is recorded. It showed only as a stream error, with no run to open.
- Every page with a card logged a rendering mismatch in the browser console. It is gone.
- The nightly database compaction (02:00) can no longer start during a database restore.
- Cells you removed from a row stay removed. Setting a row from three columns back to one hid the last two cells but left them in storage, and the report followed the hidden count. The row now holds exactly the cells you see, the migration trims the rows that drifted, and the editor and the delivered report can no longer disagree about how many cells a row has. Dragging a block to the side of a row no longer loses it when the row's stored count was wrong, and "delete cell" can no longer empty a row.
- A colour is checked before it is used. A colour must now be
#rrggbb,rgb(),rgba(),hsl(),hsla()or a colour name; anything else is refused on save, and a value already stored is ignored when the report renders. - A refused template save says what was wrong. Hiding, renaming, cloning or bulk-editing a template answered with a generic failure, and the Visible switch moved without saving anything. Each of those now shows the field and the reason.
- A legacy JSON import in "autofix" mode repairs the whole file. A file carrying both an old field shape and a template with no page needed two repair passes and got one, so the import was refused with the very message the autofix exists to remove. A refused import now also names the blueprint and the field instead of showing a generic error.
- The licence-expiry alert reaches you. The warning is sent once, and it was marked as sent whether or not the mail went out. A check during a restart, with no delivery interface, or with an S3 interface (which carries no message) used up the only warning there was. Now the warning counts as sent only after a real delivery. An address that bounces no longer stops the addresses after it. An alert that cannot be delivered at all gives up after three tries, instead of calling the mail relay at every monitor check.
- A job that failed on schedule still retries after you press "Run now". The scheduler read the newest run of any type. A manual run, started to look at a failed scheduled one, hid that run's pending retries. A restart or an edit then dropped them, and the job waited silently for its next cron slot.
- The job monitor keeps reporting. A stalled health check could block the monitor (and the licence alert with it) for the life of the process. The call now times out.
- Migration timestamps stay put on a host that is not on UTC. A datetime
read during a migration was parsed as local time, so on a host with
TZset (for exampleEurope/Luxembourg) values shifted by the offset on the way through. Note for installs upgraded before this release: theai_providertable'screated_atandupdated_atmay hold values shifted that way. No feature reads those two columns and there is no backfill: a shifted value cannot be told apart from a correct one. - Cloning selected templates clones those templates only. Before, every built-in template came along as a copy, and a copy of an edited built-in took the built-in's place in the list. Stray "(copy)" templates from an earlier clone stay in the list; select and delete them.
- A bulk action the licence refuses (a clone or a copy over the free cap) says so in a message and keeps the selection; before, nothing happened on screen. A bulk clone or delete on Jobs and Templates keeps the search, the filters and the sort order, as it does on the other lists.
- A second delivery interface with the same name becomes "Name (1)"; before, the renaming never fired and duplicates were kept. Editing a built-in blueprint no longer renames it to "Name (1)" on every save, and a copy into another space keeps its name.
- A rounded Kibana time range (
now-7d/d) reaches the capture unchanged; the rounding was dropped. - A snooze for a whole delivery interface shows on the page, is not offered twice, and can be revoked. Revoking one always failed with "Cannot snooze an unsubscribed recipient".
- A failed Enter step reports its real cause. The error handler threw on its own and hid it.
- The AI action reads the links of the captured page as links. Each one was a copy of the whole page with an address attached.
- A password with a colon in it reaches the authentication layer whole.
- Every capture step left a 60-second timer running after it finished, so a job with many steps kept the process busy long after the run.
- A scheduler request whose handler failed hung with no answer; it answers 500 now. The browser extension's wait for a page to settle could wait forever on a page that never went quiet.
- The disabled slider handle in the dark theme painted black.
- A missing email header printed "undefined" instead of nothing.
- The AI provider set by the environment repairs a broken row instead of stopping at it.
- Mail sent through Mailgun now carries the sender as
Name <address>, the form Mailgun documents; it went out asName addressbefore. - No write is lost any more. Two database writes that overlapped could run in one transaction, and a rollback of one took the other with it while both reported success. Every write now waits its turn.
- A calculate that gives true or false no longer breaks the Runs page. Such a run could not be read back, so the runs, reports, overview and jobs pages of its space failed until the run was removed by hand. The value is stored as 1 or 0, the migration repairs the runs already stored, and a run is checked before it is stored.
- A text replacement with "Use regex" off replaces the text as typed.
Every search ran as a regular expression, so
.matched any character and(net)kept its brackets. Replacements saved before the upgrade keep working as they did (see the migrations above). - Capture fixes. An
ifon a variable with no value is false, not a compare with an empty text. A clonedifblock gets new ids, so its snapshots no longer share one image. An Enter step can be followed by a navigation. A ROR or Grafana path that contains "login" (a space calledlogin-audit, a dashboard calledlogin-attempts) no longer fails every run. A scroll that runs out of time goes on instead of failing. One dialog handler per page, not one per navigation. A GrafanaredirectTowith%in it is decoded once. - Delivery fixes. Two S3 uploads at 08:00 and 20:00 no longer write one
object: the key uses a 24-hour clock. A form-encoded webhook message is
encoded once. A receiver that decoded it twice to work around the old
bug now gets the plain value. A webhook message keeps
$&and$1as written. Your ownContent-Typeheader wins over Anaphora's in any letter case. PDF attachment names no longer contain colons, which Windows refuses. Rich-text mails keep the editor's alignment, fonts and paragraph spacing. - Operator alerts never go to a webhook with a custom JSON body. The health report, licence alert and AI budget alert sent plain text to such a webhook; they now refuse it with a warning, as the picker does.
- Background images no longer slow down Anaphora. Every read of the settings parsed every uploaded image: about 220 ms per read after ten uploads. The images now live in a table of their own.
- A job copied to another space starts at once, not after a restart. Copies and jobs made from a template get their own delivery ids, so a snooze or an unsubscribe on the original no longer silences the copy.
- Snooze and unsubscribe links survive a
PUBLIC_URLchange. Links already in mailboxes keep working whilePUBLIC_URLstays the same. - The scheduler keeps running. A restore longer than five minutes no longer answers "Import failed" halfway. A scheduled task that fails no longer stops the scheduler. A job deleted during its run is logged, not retried.
- Two imports at once are refused. A second import while one runs answers 409; before, the two shared a rollback file and the live database could be lost. An import that runs past one hour answers 504 and the log says when the restore ends.
- A space whose name has a dash or a capital letter keeps the page when you switch spaces.
- A deleted delivery interface no longer crashes the Deliver tab, and one that a template still uses cannot be deleted.
- A run whose author name holds a backslash (
DOMAIN\alice) no longer crashes the Runs page or the Deliver tab. - Server actions work behind a proxy that rewrites the
Hostheader. - The SAML and OIDC settings pages, and the Calculate editor, no longer crash in the light theme.
- The capture extension loads 0.2 MB into a page instead of 1.1 MB: its interface loads only in the recording window.
- The demo seed's alert throttle is 3 hours, not 3 milliseconds.
🧐 Dependencies
- The libraries behind the AI actions (OpenAI client 7), Mailgun (14), SMTP mail (Nodemailer 10) and the cron descriptions (cronstrue 3) moved to their current majors. Each was read against how Anaphora calls it, and each has a test that runs the real library: the AI client against an OpenAI-compatible server, SMTP against a real mailbox, and Mailgun's attachment bytes read back off the wire. Nothing changes in what you see or configure.
- The database layer runs on TypeORM 1 and on SQLite 3.53 with SQLite3 Multiple Ciphers 2.4. Existing databases open unchanged and no schema migration runs for this. The SQLite engine now arrives prebuilt for every supported platform, so an install compiles nothing.
- The authentication layer (Authfish 1.0.163) moves with it: the same TypeORM and SQLite driver, the same Node 24, the YAML library's next major, and a stricter command line for the activation-key tool, where an extra argument is an error instead of being ignored.
- Two majors were declined on purpose:
iframe-resizer5 moved from MIT to GPL-3.0, and zod 4 is a migration of the schema module, tracked separately.
🧐 Platform
- The runtime moves from Node 22 to Node 24 LTS, the current long-term support line, maintained until April 2028. No configuration change on your side.
- The runtime image is a fifth smaller: about 210 MB less to pull.
🧐 Quality
- The capture engine, the scheduler's run lifecycle, report delivery and the browser connectors are under test. The suite grew from about 880 to more than 2300 tests, and the scheduler suites pass in every time zone.
- Dead files, dead exports and import cycles fail the build; every workspace is linted with the same rules, which found and fixed twelve unhandled failures.
[0.15.0] - 2026-09-08: Dark mode
🚀 New
- A dark theme, in the note of Solarized Dark and tinted to match the navy sidebar. Choose Light, Dark or System under Theme, at the bottom of the sidebar; System follows your operating system and switches with it. The choice is stored per browser, in a cookie, once you make it.
- Reports stay on white paper in both themes: the template page, the text editor and the preview show what your recipients will read.
- The login page and the Authentication panel under Settings > System follow the same theme and the same palette as the rest of the app.
- A demo dataset for a fresh instance: with
SEED_DEMO=1an empty database fills with jobs, runs, delivery interfaces and settings on start, andDEMO_USERSadds local users next to the admin. Meant for preview deployments; a database that holds a job is left alone.
🐞 Fixes
- Saving a form could answer "Error occurred while trying to proxy" with a 500. The auth layer forwarded a non-JSON request body as an empty placeholder object instead of the real body, and the app rejected it. It now forwards non-JSON bodies unchanged.
- Delete ALL on the runs page is disabled while the list is empty; it answered an error before.
- The footer background one step lighter than the page now applies; it was written in a place the page's own styles overrode.
[0.14.0] - 2026-09-05: Provider keys and equation cost
🚨 Security
- Testing an AI provider checks your access. The connection test and the model list check your access to the space that owns the stored provider, and answer an identifier you may not read the same way as one that does not exist.
- Sending a test message through a delivery interface requires admin access to the space.
🐞 Fixes
- Reading a job no longer runs its equations. The previous release moved one of the two checks that do this; a second one remained, because working out whether a step returns a number or text meant running the equation. The scheduler reads every job when it starts, so one saved equation could hold the server for half a minute on every read of that job. Equations now run when you save a job, and when a run reaches the step.
- The limit on equation size now also covers the operations whose cost grows faster than their result: matrix multiplication, matrix powers, determinants and inverses. An equation whose every value was inside the size limit could still occupy the server for tens of seconds.
🧐 Quality
- Publishing a release image retries a failed upload inside a time budget that fits, so the third attempt can actually run.
[0.13.0] - 2026-09-04: Browser-callable data access
🚨 Security
- Every browser-callable read and write of jobs, runs and AI providers checks your access to the space first.
🐞 Fixes
- A saved equation can no longer stop the scheduler from starting. Reading a job ran its equations, so one job whose equation the new limits no longer accept ended the process, and with it every schedule in the install. Equations are checked when you save a job, not when the server reads one.
- Free-edition limits now apply to installs whose activation key spells the edition in capitals, which is what the key issuer produces. Those installs ran without the free limits until now. If you hold more than two jobs or two delivery interfaces on a free key, the interface asks you to upgrade before you add another, and an existing job with more than three capture actions cannot be saved again until you upgrade.
🧐 Quality
- The equation worker and the space checks are now proven inside the container image that customers run, not only in unit tests. The arm64 image is started and checked before it is published.
[0.12.0] - 2026-09-04: Access control
🚨 Security
- Every action on jobs, blueprints, delivery interfaces and AI providers checks your access to the space it names, and so does the start of a run.
- The runs page and the job editor no longer send delivery interface credentials to the browser. Names and types are all the page needs.
- Exporting and importing the database, and changing application settings, require the System role.
- Report file paths reject a user name or id that could point outside the reports folder.
- Equations in a
calculatestep run with a time and memory limit, cannot reach the functions that load code, and cannot allocate matrices of more than a million cells. An equation can no longer stall or exhaust the server, at run time or while a saved job is read back. - Replaced the
query-stringpackage withqs, which clears thedecode-uri-componentadvisory. uuid11 andqs6.16 close the two remaining runtime dependency alerts; a stale lockfile that carried twelve more alerts is gone. Weekly automated dependency updates now open pull requests instead of alerts.
🧐 Platform
- The runtime moves from Node 20, which reached end of life in April 2026, to Node 22 LTS. No configuration change on your side.
- Preview builds now carry version numbers above the last release, so
preview-latestis never older thanlatest.
🐞 Fixes
- PDF attachment names use a 24-hour clock. Two runs twelve hours apart no longer share a file name.
- The equation editor now reports errors on
-,/,<,>,%,==and!=; before, an equation such astitle - 2saved without a warning and failed at run time. - Typing an assignment (
hits = "x") into the equation editor no longer breaks validation of the equations you type after it.
🧐 Quality
- The automated test suite grew to more than four hundred tests. They guard the run pipeline, report delivery, report file layout, equation validation, and every access check listed above. A coverage gate fails a build that lowers test coverage.
[0.11.0] - 2026-09-03: Security and reliability
🚨 Security
- Cleared every critical and high dependency vulnerability, in the app
and in the developer tooling.
npm auditnow reports zero high or critical advisories. - Untrusted HTML in job variables can no longer run on the app origin. Report previews render captured content in isolation.
🧐 Reliability and performance
- The health endpoint answers in milliseconds. It took ~8 seconds on installations with a large run history.
- The hourly clean-up no longer loads the whole run history into memory.
- The database closes cleanly on container shutdown, so an unlucky restart can no longer corrupt state.
- Headless-browser zombie processes are reaped. Long-running containers stay healthy instead of slowly filling the process table.
- Deleted runs now reclaim their report files from disk, and old runs are removed by a default retention policy.
🧐 Platform
- Upgraded to Next.js 16 and React 19.
- A large new automated test suite now guards report rendering, the customer upgrade path, exports, health monitoring, and the unsubscribe/resubscribe flow on every change.
[0.10.0] - 2026-03-04
🚀 Enhancement: Create delivery interfaces directly from job configuration
Enables creating delivery interfaces inside job configuration without navigation
When setting up a job's delivery configuration, you can now create new delivery interfaces directly within the job configuration flow. You no longer need to go to the separate Delivery Interfaces page during setup.
🚀 Feature: License expiration warning
Display warning when license is close to expiration and send notification
When the Anaphora license is nearing its expiration date, a warning message is now displayed in the Anaphora Web UI.
If health alerts are configured, a notification will also be sent to the defined channel. This enables administrators to renew the license in time and prevent a service interruption.
🐞 Fix: Kibana Discover hits variable could not be used in reports
Fixed issue where Kibana Discover hits variable was not available in report composition
The Kibana Discover hits variable, which returns the number of results from a Discover query, was not available during report composition. This issue is fixed. You can now use the variable to include hit counts dynamically in reports.
🚨 Security fix: Removal of YAML import/export for system settings
Removed YAML import/export of system settings to prevent corrupted data
The YAML import/export functionality for system settings is removed. This capability is no longer required because of
the new Import/Export feature available under /settings/data.
The updated approach supports exporting and importing all relevant data at once (including settings, jobs, delivery interfaces, and more). It also handles version changes correctly. This prevents data corruption and makes sure that exports created in older versions stay compatible with newer versions of Anaphora.
🧐 Enhancement: Storage optimization
Optimized database to reduce storage usage
We optimized storage in the Anaphora database. This reduces disk usage for large datasets.
- Deletions will now free up space more effectively
- Removal of unnecessary internal data will immediately reduce storage usage
🧐 Enhancement: Performance improvements
Optimized loading and saving times for Anaphora instances with large numbers of runs
We improved the Anaphora database to handle large datasets more efficiently. This results in faster loading and saving times.
[0.9.0] - 2026-01-15
🚀 New feature: Grafana connector
Capture and report on Grafana dashboards
The new Grafana connector lets Anaphora capture Grafana dashboards out of the box. It supports:
- Configurable authentication using the Grafana credentials
- Capture entire dashboards with automatic waiting for panels to load
- Capture individual panels for granular reporting