Skip to content

Artalk v2.10.0 Release Notes and Migration Guide

v2.10.0 is the next stable release after v2.9.1. It adds page voting, SSO token exchange, MySQL Cloud SQL TLS, and CGO-free SQLite builds, while reorganizing the UI client's lifecycle, configuration, and type system.

Read before upgrading

v2.10.0 contains intentional API and behavior changes. Upgrade the server and UI client together. Themes, plugins, direct HTTP API consumers, and projects importing Artalk TypeScript types should complete the migrations below first.

Upgrade checklist

  • Back up the database, configuration files, and custom frontend assets.
  • For binary deployments, verify the working directory and every relative file path; pass -w and an absolute -c path to retain the previous behavior.
  • Upgrade source-build and packaging environments to Go 1.26.5.
  • To change the application timezone, set timezone or ATK_TIMEZONE and fully restart Artalk.
  • Upgrade the server and UI client together to avoid mixing the old and new Vote APIs.
  • Review local-versus-remote UI configuration priority; local configuration is now preferred by default.
  • Search for useBackendConf, remoteConfModifier, two-argument ctx.inject(), assignments to ctx.conf / ctx.$root, and artalk.update(...).xxx() chains.
  • Rebuild themes and plugins that import Artalk types or internal UI services.
  • Use a modern Node.js toolchain; the Artalk repository development baseline is Node.js 22.19 or later.

Highlights

UI and interaction

  • Added page voting and the ability to query the current visitor's vote state (#983, #998).
  • Added preferRemoteConf to make local-versus-remote UI configuration precedence explicit (#1000, #1011).
  • Introduced a dependency injection container and reorganized UI modules, lifecycle handling, and public types (#1006, #1007).
  • Added the Artalk UI ESLint plugin and updated it to v1.0.2 (#995, #1010).
  • Extended User-Agent detection for OpenHarmony and more browsers (#1087).
  • Added ugc to comment-link rel attributes (#1120).

Server and API

  • Added POST /api/v2/sso/exchange to exchange an access token from a trusted OIDC identity provider for an Artalk session token (#1134). It is disabled by default; configure only a trusted HTTPS issuer in production, and require /userinfo to return email_verified: true.
  • Unified the Vote API around a target type, target ID, and choice, and added a vote-status endpoint.
  • Improved configuration-file and data-directory discovery while preserving the located configuration before changing the data directory (#992).
  • Added mutual TLS configuration for MySQL on Google Cloud SQL (#1104).

Builds and runtime

  • SQLite now uses a pure-Go driver. Stable builds no longer require CGO or a separate GORELEASER_CROSS toolchain, so standard Go tooling can produce cross-platform artifacts directly.
  • Upgraded the Go toolchain to v1.26.5 and updated related dependencies, including the maintained v5 JWT implementation used for login sessions. Existing HS256 token claims and signatures remain wire-compatible, so this server upgrade does not itself force users to sign in again.
  • Updated the official container build and runtime bases to Alpine 3.24. The Go builder remains pinned to v1.26.5, and container data-directory and configuration paths are unchanged.
  • Updated the frontend and documentation toolchain, including Vite 8.1, Marked 18.0.7, and KaTeX 0.17. The @artalk/plugin-katex peer range still accepts KaTeX 0.16.45 through 0.17.x, so existing themes are not forced to replace KaTeX as part of this upgrade.
  • Added scripts for fetching the plugin registry and checking versions already published to npm.
  • npm packages use modern conditional exports; legacy Node.js 10 module resolution is no longer supported.

Documentation and localization

  • Added Turkish (tr-TR) and a Traditional Chinese configuration template.
  • Added a Japanese README.
  • Redesigned the landing page, added translations, and improved responsive font sizing.
  • Integrated Git Changelog into the documentation site.

Security and reliability

  • The backend JWT parser now uses github.com/golang-jwt/jwt/v5, explicitly allows only HS256, continues to validate expiration, and has regression coverage for legacy token format, signing algorithms, and expiration behavior.
  • The Markdown HTML sanitizer now uses the maintained DOMPurify instead of the unpatched, ReDoS-affected insane parser. Artalk retains its previous tag, attribute, class, style, and URL-scheme allowlists and adds malicious-markup and ReDoS regression tests.
  • The frontend lockfile pins patched transitive versions of fast-uri, brace-expansion, js-yaml, undici, Vite, and other build and development tooling; the complete workspace dependency audit reports no known vulnerabilities.
  • Google Cloud SQL TLS now loads the server CA, client certificate, and client key together, verifies the server certificate and hostname, and requires TLS 1.2 or later. All three certificate paths must be configured together so partial TLS configuration cannot be silently ignored.
  • The Vote API rejects malformed, non-positive, missing, or unknown targets. Vote records and target counters are updated in one database transaction and roll back together on failure.
  • Plugin URLs are filtered by the server: relative and trusted URLs may be sent to the client, while untrusted absolute URLs are removed.
  • Plugin-registry downloads require HTTPS and approved hosts and have bounded redirects, response size, and request time.
  • The npm version check no longer interpolates package names into a shell command and reports network or parsing errors as failures.
  • Process timezone initialization now happens once instead of mutating global timezone state during concurrent requests.

Breaking changes and migration

1. Local UI configuration is preferred by default

In v2.9.x, remote configuration overrode values passed to Artalk.init(). v2.10.0 prefers local configuration by default:

text
Artalk.init() local config > environment variables > Dashboard = configuration file

No additional option is needed for the new default:

js
Artalk.init({
  server: 'https://comments.example.com',
  site: 'My Site',
  locale: 'en', // the local value is retained
})

Explicitly enable remote-first behavior when required:

js
Artalk.init({
  server: 'https://comments.example.com',
  site: 'My Site',
  preferRemoteConf: true,
})

useBackendConf is deprecated and is always treated as true. Setting it to false no longer prevents the client from reading configuration that the backend must provide; it is not a replacement for preferRemoteConf.

2. remoteConfModifier has been removed

remoteConfModifier is no longer part of the public configuration. Migrate according to its purpose:

  • Put fixed overrides directly in Artalk.init(); local values win by default.
  • Use preferRemoteConf: true when Dashboard values should win.
  • Use fetchCommentsOnInit to control the initial comment request.
  • Listen for created and call update() for post-initialization changes.
js
const artalk = Artalk.init({
  server: 'https://comments.example.com',
  site: 'My Site',
  fetchCommentsOnInit: false,
})

artalk.on('created', () => {
  artalk.update({ locale: 'en' })
  artalk.reload()
})

There is no equivalent hook for arbitrarily modifying remote configuration before plugin initialization.

3. Configuration types distinguish resolved values from inputs

Config represents a fully resolved configuration. ConfigPartial is the recursively optional input accepted by Artalk.init(), update(), and plugin configuration updates.

ts
import type { Config, ConfigPartial } from 'artalk'

function createComments(conf: ConfigPartial) {
  return Artalk.init(conf)
}

function readResolvedConfig(conf: Config) {
  console.log(conf.server, conf.pagination.pageSize)
}

Code that used Partial<ArtalkConfig> for initialization should use ConfigPartial. ArtalkConfig remains importable but now represents the complete configuration.

ConfigPartial preserves functions, DOM elements, Date, RegExp, and array-element semantics instead of incorrectly expanding those values into recursively partial object fields.

4. DI registration and resolution are separate

The old two-argument ctx.inject(key, value) registration form has been removed. Register with provide() and resolve with inject():

ts
// v2.9.x
ctx.inject('example', createExample())
const example = ctx.get('example')

// v2.10.0
ctx.provide('example', () => createExample())
const example = ctx.inject('example')

provide() supports dependency lists and singleton / transient lifecycles. Custom TypeScript service keys should extend Services through declaration merging.

5. ctx.conf and ctx.$root cannot be replaced

Both remain readable, but assigning a new object or element throws an error with migration guidance:

ts
// Incorrect
ctx.conf = nextConf
ctx.$root = nextElement

// Correct
ctx.updateConf(nextConf)
const conf = ctx.getConf()
const root = ctx.getEl()

To change the root element, destroy the old instance and create a new instance with a different el.

6. Artalk.update() returns void

update() no longer returns the Artalk instance. Split chained calls:

js
// v2.9.x
artalk.update({ pageKey: '/next' }).reload()

// v2.10.0
artalk.update({ pageKey: '/next' })
artalk.reload()

7. Plugin initialization and the created event

Built-in, local, and network plugins now initialize after local and remote configuration have been merged. Network plugins no longer observe a temporary configuration from only one source.

created fires after every plugin has initialized, followed by mounted. Plugins that depended on the old order should perform pre-initialization work in the plugin function and use a created listener for work requiring the complete plugin environment.

8. Vote HTTP API and client methods

Old vote endpoint:

http
POST /api/v2/votes/comment_up/123

New endpoints:

http
GET  /api/v2/votes/comment/123
POST /api/v2/votes/comment/123/up

target_name must be comment or page; choice must be up or down.

The generated client methods changed accordingly:

ts
await api.votes.getVote('comment', 123)
await api.votes.createVote('comment', 123, 'up', { name, email })

The old endpoint and votes.vote() method do not have compatibility adapters.

9. SQLite now uses a pure-Go driver

The server SQLite driver has changed from the CGO implementation based on mattn/go-sqlite3 to the pure-Go implementation provided by github.com/libtnb/sqlite. Stable builds no longer require a local C compiler, CGO_ENABLED=1, or the cross-compilation toolchain selected through GORELEASER_CROSS_VERSION.

The SQLite on-disk format has not changed. Existing databases configured through db.file can be used directly, so standard deployments do not require a database configuration change. Back up the database before upgrading as usual; Artalk will run its normal schema migrations on the first v2.10.0 startup.

Projects that build Artalk from source or embed it as a Go module should note the following:

  • C compiler, CGO, and Goreleaser Cross configuration used only for SQLite can be removed.
  • Do not rely on mattn/go-sqlite3-specific build tags, DSN parameters, C APIs, or dynamic extension-loading behavior.
  • Re-run migration, concurrent read/write, and backup/restore tests when using custom SQLite compile options, extensions, or native backup integrations.

This change does not affect deployments using MySQL, PostgreSQL, or MSSQL.

10. Default configuration and data directories follow XDG rules

Without --workdir / -w, Artalk no longer always uses the invocation directory as its working directory. v2.10.0 uses the following discovery order:

  • Configuration files: the invocation directory, its data subdirectory, $XDG_CONFIG_HOME/artalk, then /etc/artalk.
  • Data directory: keep the previous working directory when ./data exists; otherwise use $XDG_DATA_HOME/artalk, then /var/lib/artalk, or create $XDG_DATA_HOME/artalk if neither exists.

The configuration file is located before an implicit data-directory change. Relative database, log, and upload paths in that configuration—as well as relative file arguments passed to commands such as import, export, and gen—are still resolved from the final working directory.

Existing deployments with a ./data directory keep the previous behavior. The official Docker image also passes an explicit working directory and configuration path, so it is not affected.

To preserve the previous path semantics for a binary deployment, specify both paths explicitly:

bash
artalk -w /srv/artalk -c /srv/artalk/artalk.yml server

Before upgrading, verify every relative path including db.file, log.filename, img_upload.path, IP-region databases, and keyword files to avoid creating an empty database or files in the newly selected data directory.

11. Source builds require Go 1.26.5

The minimum version in go.mod has increased from Go 1.22.7 to Go 1.26.5. This affects go install, source builds, distribution packaging, and downstream projects that include Artalk in a Go build.

Users of official binaries and Docker images do not need to install Go. Source-build environments must use Go 1.26.5. CI systems that disable automatic Go Toolchain downloads or operate offline must install the matching toolchain in advance.

12. The application timezone is fixed at process initialization

The timezone configuration and ATK_TIMEZONE environment variable remain available for an Artalk application timezone that is independent of the host system or container TZ setting:

yaml
timezone: America/New_York
bash
ATK_TIMEZONE=America/New_York artalk server

Timezone is process-global state. v2.10.0 sets it once during Artalk process initialization and does not support changing it dynamically at runtime. After changing it in the configuration file, environment, or Dashboard, fully restart the Artalk process or container; using Dashboard “Apply settings” alone is not sufficient to switch timezone.

13. marked 18 and modern package entry points

The default Artalk build continues to include Markdown support. ArtalkLite does not bundle marked; downstream consumers must provide a compatible marked@^18.0.3.

Artalk's use of Marked, Renderer, parse(), setOptions(), use(), and tokenizer extensions does not rely on v18-only APIs. Upstream parsing details changed between v14 and v18, however, so projects with custom renderers, tokenizers, or output snapshots should rerun their Markdown tests.

Node.js 10 and toolchains that rely on legacy package resolution are no longer supported. Artalk repository development and builds use Node.js 22.19 or later.

Retained compatibility entry points

The following entry points remain in v2.10.0 to give themes and third-party plugins time to migrate, but are marked deprecated:

Previous entry pointPreferred entry point
ContextApiContext
DataManagerApiDataManager
EditorApiEditor
ctx.datactx.getData() or ctx.inject('data')
ctx.userctx.getUser() or ctx.inject('user')
ctx.editorctx.inject('editor')
ctx.listctx.inject('list')
ctx.layerManagerctx.inject('layers')
ctx.checkerLauncherctx.inject('checkers')
ctx.sidebarLayerctx.inject('sidebar')
ctx.editorPlugsctx.inject('editorPlugs')
editor.$eleditor.getEl()
editor.getPlugs()editor.getPlugins()
editor.setReply()editor.setReplyComment()

These aliases are transitional. New code should use the preferred entry points directly. See TypeDoc for the complete public type surface.

Other changes

  • The comment reply button now uses a component implementation.
  • Sidebar and Auth UI have been updated for the new client types.
  • Added npm-published-version checking and plugin-registry generation workflows.
  • Pinned the Swagger TypeScript client generator and updated its command so development environments do not receive incompatible versions through npx.
  • Updated the GitHub Actions runtimes used for artifacts, caches, Docker, CodeQL, and Codecov while preserving workflow inputs and release-artifact layout.
  • Updated frontend, Go, and documentation dependencies and removed obsolete test projects and CI configuration.

See the complete commit comparison at v2.9.1...v2.10.0.

Contributors

Changelog