Flarum 2.0.0-rc.4 Released 🎉 — 2.0 stable is almost here
This is it — 2.0 stable is very, very close now. Today we're shipping 2.0.0-rc.4 (33 merged PRs since rc.3), and it's the clearest sign yet that the finish line is right in front of us. This isn't just maintenance: rc.4 brings a previously-premium extension into the open-source core, adds new realtime features, and closes out a security fix and a stack of bugs.
And here's the thing we can't say loudly enough: we are this close because of you. The forums running 2.0 in production, the people filing bug reports, the "I upgraded and everything's fine" notes and the "this broke for me" ones — that feedback, good and bad, is exactly what turns a release candidate into a stable release. Every report has sanded down another edge. The bug reports are getting rarer, the fixes smaller, the confidence higher. That's what the last stretch before 2.0 stable looks like, and we're nearly through it.
🚀 Production-ready for early adopters. The upgrade from rc.3 is a plain composer update. Back up your database, test on staging if you can, keep a rollback plan — the usual.
👀 This forum runs it. discuss.flarum.org is on the latest 2.x. So is the nightly demo, rebuilt daily. You're looking at 2.0 right now.
💬 Already upgraded? Tell us. Drop a note inSupport — every "running it in prod, all good" report builds confidence for the next forum about to take the leap.
⭐ The headlines
🗂️ Audit joins the open-source core
The big one: flarum/audit is now a first-party extension. It records moderation and administration actions — discussions, posts, users, settings, permissions, extension toggles, and more — to a tamper-resistant log you can browse from the admin panel, or (with permission) straight from a user's or discussion's page. It's searchable by action, actor, user, client type, discussion, and IP address.
This started life as Clark Winkelmann's KILOWHAT Audit extension, which the Flarum Foundation has brought in-house and reworked as a first-party extension — and the previous free/pro split is gone, so every feature is now included. New installs of Flarum 2.0 will come with Audit bundled in; on existing installs it's opt-in from the Extensions page.
It also ships with integrations out of the box — Tags, Flags, Lock, Sticky, Approval, Suspend and Nicknames, plus several FriendsOfFlarum extensions (Impersonate, Split, Merge Discussions, Ban IPs, User Bio, Username Request) — and extension developers can now log their own actions through a public extender. Read more in the announcement and the docs. (#4711)
💬 Ambient typing indicators — now on the tag list too
Realtime gains an ambient "someone's typing here" indicator on the discussion list — a subtle, coalesced presence dot that shows activity without flooding the client. In rc.4 it extends to the index sidebar tag list: a tag lights up while someone is typing in — or composing a new discussion in — one of its discussions, with restricted tags only ever shown to people who can see them. The typing indicator was also reworked to be reusable and movable so extensions can place it where it fits. (#4731, #4756, #4721)
🔔 "A new version is available" — without a manual reload
Ever deployed an update and left everyone already on the forum running stale JS/CSS until they happened to refresh? Not any more. When the forum's assets are rebuilt — a deployment, an extension toggle — browsing users are now prompted to reload to pick up the new version. It works on every forum via the API, and instantly (no interaction needed) on forums running Realtime. (#4752)
🔒 A realtime security fix
rc.4 closes an authorization gap in the Realtime WebSocket: client-originated events were rebroadcast to the channel named in the payload without checking the sender was subscribed to it. Realtime now only relays client- events to private/presence channels the sender is actually subscribed to. If you run Realtime, this is a good reason to upgrade. (#4738)
🛠️ Everything else in rc.4
🐛 Fixes
- Small avatars no longer vanish. Avatar uploads smaller than 100×100 were skipped entirely, leaving a database record pointing at a file that was never written (a 404). The base image is now always generated. (#4740)
- A
TypeError when creating a user with a nickname is fixed. (#4734)
- Custom tag slug drivers now work. The tag filter resolved slugs against the literal
slug column, so custom slug drivers returned no results; resolution now goes through the active driver. (#4739) A related fix ensures an unslugged tag's slug is serialized correctly to the API. (#4746)
- The loading indicator no longer flickers. A lazy-loaded chunk that arrives quickly used to flash a "Loading…" toast for a split second; it's now debounced so it only appears for genuinely slow loads. (#4750)
- Pusher boot crash fixed, and discussion-list updates restored. (#4730)
- Internal API requests are no longer subjected to JSON:API query validation meant for external callers. (#4728)
- The upgrade page now returns HTTP 503 instead of 200, so health checks and caches treat it correctly. (#4720)
- The admin Extensions page shows the correct database display name again. (#4747)
- Realtime notification toasts now show the correct toast, and stop being dropped. (#4713)
- Post mention notifications no longer jump on the page. (#4686)
- The initial page of posts embedded in the page load is no longer re-fetched. (#4703)
Flag::$reason is now correctly typed as nullable, so static analysis stops flagging legitimate code that leaves it null. (#4760)
♿ Accessibility
- Composer control buttons and the new discussion button now have accessible labels for screen readers. (#4723, #4749, #4758)
⚡ Performance
- The
editCredentials permission check no longer loads the target user's groups when it doesn't need to. (#4729)
- The admin bundle is smaller —
sortablejs is now lazy-loaded instead of shipping on every admin page. (#4754)
🧩 Under the hood
- The Audit lifecycle gained coverage: GDPR erasure requests are now audit-logged (#4732) and discussion approvals are recorded (#4748).
MariaDB was added to the frontend database-driver enum (#4701), and a circular optional-dependency in Audit was removed (#4712).
TagLinkButton was converted to TypeScript with an extensible linkItems list, giving extensions a clean way to add to a tag link. (#4755)
💙 This is a community release
As ever, a big slice of this cycle came from the community: 9 of the 33 PRs were written by contributors outside the core team. That's the engine that carries Flarum across the line to a major release.
Contributors this cycle:
- @davetodave178 — HTTP 503 on the upgrade page #4720,
MariaDB in the frontend driver enum #4701, the Extensions page database display name #4747, and serializing an unslugged tag's slug #4746
- @datitisev — base image for small avatar uploads #4740
- @CyberGene — avoid re-fetching embedded initial posts #4703
- @ClaudiusH — accessible label for the new discussion button #4749 and the follow-up aria-label key fix #4758
- @zxbmmmmmmmmm — post mention notification jumping #4686
- @IanM — Audit migration, ambient typing (discussion list & tags), asset-update notifications, the realtime security fix, and the bulk of the rest
If you've ever thought about contributing, this is the moment. 2.0 is being built in the open, in real time, by people like you.
🚀 Help us bring 2.0 home
We're at the doorstep of stable. The single best thing you can do to get us across it is run rc.4 and tell us how it goes — good or bad. Every forum that upgrades is one more real-world proving ground, and every report (especially the "this broke" ones) is a direct line to a more solid 2.0.0.
- See it live — nightly.flarum.site, rebuilt daily, and right here on discuss.flarum.org
- Upgrade your install —
composer update -W, then php flarum migrate, php flarum cache:clear, and php flarum assets:publish (back up first, and test on staging if you can)
- Then come back and tell us — what worked, what didn't, what you'd want in 2.0 final
Where to report:
- Bugs in core — flarum/frameworkissues
- General feedback, questions, extension behaviour —Support
- Security issues —
security@flarum.org (please don't post publicly)
2.0 stable is almost here — and the feedback you send on rc.4 is what gets us the final mile. Let's bring it home together. 💪
📋 Changelog
Added
- (audit) migrate the Audit extension to 2.x, split first-party integrations, and add GDPR/core coverage by @IanM #4711
- (core, realtime) notify browsing users when the forum's assets are updated by @IanM #4752
- (realtime) ambient typing indicators on the discussion list by @IanM #4731
- (realtime) ambient typing dots on the index sidebar tag list by @IanM #4756
- (realtime) make the typing indicator reusable and movable by @IanM #4721
- (gdpr) audit-log the erasure request lifecycle by @IanM #4732
- (approval) log discussion approval in the audit log by @IanM #4748
- (core) add
MariaDB to the frontend DatabaseDriver enum by @davetodave178 #4701
Changed
- (tags) convert
TagLinkButton to TSX with an extensible linkItems list by @IanM #4755
Fixed
- (flags) type
Flag::$reason and $reason_detail as nullable by @IanM #4760
- (a11y) fix the aria-label text key for the new discussion button by @ClaudiusH #4758
- (core) debounce the loading indicator to avoid flicker by @IanM #4750
- (avatar) ensure the base image exists for small avatar uploads by @datitisev #4740
- (tags) delegate tag slug resolution to the active slug driver by @IanM #4739
- (tags) serialize an unslugged tag's slug to the API by @davetodave178 #4746
- (realtime) only relay client events to channels the sender is subscribed to by @IanM #4738
- (nicknames)
TypeError when creating a user with a nickname by @IanM #4734
- (pusher) boot crash and restore discussion-list updates by @IanM #4730
- (core) don't apply JSON:API query validation to internal API requests by @IanM #4728
- (core) return HTTP 503 when the upgrade page is shown by @davetodave178 #4720
- (core) add the missing database display name on the
ExtensionPage by @davetodave178 #4747
- (core) add accessible labels to composer control buttons by @IanM #4723
- (a11y) add an accessible label to the new discussion button by @ClaudiusH #4749
- (realtime) show the correct notification toast, and stop dropping it by @IanM #4713
- (audit) remove circular optional-dependencies on first-party extensions by @IanM #4712
- (core) fix abandoned-extensions sync never being scheduled by @IanM #4707
- (core) avoid re-fetching the page of posts embedded in the initial page load by @CyberGene #4703
- (mentions) fix post mention notification jumping by @zxbmmmmmmmmm #4686
Performance
- (core) avoid loading the target user's groups in the
editCredentials check by @IanM #4729
- (admin) lazy-load sortablejs to shrink the admin bundle by @IanM #4754
Documentation
- (realtime) provide a realtime docs link by @IanM #4722