V2.1.20
Every change in the v2.1.20 release, in detail
v2.1.20 is one of the biggest RKHM updates so far. Shady 2 brings a new desktop design, desktop and phone themes and landing pages are now separate, file protection has been rebuilt for audio and video, FFmpeg skips conversions it doesn't need, PayPal moves to its current API, and you get new storage providers, new gateways and new admin controls for cron jobs and AI.
This article also covers the Firewall and the Statistics system. They arrived in v2.1.00 and v2.1.01 with only a one-line changelog entry each, so the full picture is included at the end.
If you are updating an existing site, please read “Before you update” first. A few changes need a minute of your attention once the update is done.
Before you update
- Back up your database and the full installation, including uploaded files, configuration, themes and plugins. See Updating.
- On v2.0.78 or older? Update to v2.0.79 first (the updater offers it), then run the updater again to reach v2.1.20.
- Check your mobile theme after updating: Setting → Themes → Mobile Theme & PWA should be Shadyf0n. Shady no longer contains phone styles (see the warning below).
- Edited Shady's files for phones? Re-apply those changes to Shadyf0n. Shady is now desktop-only.
- Custom theme based on Shady? Widget covers are now sent as compact image data instead of ready-made HTML. Until your theme's parts are updated the same way Shady's were, sliders and lists show text instead of cover images.
- Have a native app? Rebuild it after updating. An installed app keeps the theme files it was built with, and after the update those older files can show text instead of covers in sliders. See Native Apps – Building.
- Running nginx? Replace your hls_keys rule with the new one from File Protection. The older rule stopped HLS items from playing on nginx. Also add the /serviceworker.js block from Nginx.
- Moving servers or uploading by FTP? Make sure files/protected/.htaccess and files/hls_keys/.htaccess arrived. Many FTP clients hide files whose names start with a dot, and on Apache these two files are what keep those folders private.
- Using Coinbase? Coinbase Commerce closed on 31 March 2026. You'll need a Coinbase Business API key (see Payments below).
Themes: separate desktop and mobile
Desktops and phones can now run different themes, and each one can be developed on its own.
- Two theme slots: the Theme Manager (Setting → Themes, also Modules → Themes → Theme Manager) now has Desktop Theme and Mobile Theme & PWA. Phones, the installed PWA and the native app use the mobile slot. See Plugins.
- Shady is now two themes: Shady for desktop and Shadyf0n for phones, the PWA and the app. Both look exactly as before. Shadyf0n understands the same custom classes; see Shady theme custom classes.
- Boxi pairs with Shadyf0n: Boxi stays desktop-only and sends phones to Shadyf0n, just like Shady.
- The theme constant is now a fallback: the desktop theme you activate in the Theme Manager is saved in the database. theme in config_user.php is only used when no desktop theme has been activated. The Demo Importer still checks it. See config_user.php options.
- Lighter page data: widget responses now carry compact image data and the browser builds each image, instead of the server sending ready-made image HTML for every cover. Boxi uses the new format throughout.
- Updated jQuery and jQuery UI.
Phones use Shadyf0n. This demo homepage illustrates the separate mobile layout. Click the image for full size.
Shady 2: a new desktop theme
Shady 2 is a new desktop theme with search in the header, a collapsible sidebar, larger artwork cards and a floating player. Album and artist pages, track lists, plans and sign-in screens have also been redesigned.
Install Shady 2 (listed as Shady v2 in the Theme Manager) and select it for Desktop Theme in the Theme Manager. Keep Shadyf0n selected for Mobile Theme & PWA. Your existing pages and catalogue carry over. See Shady vs Shady 2 for ten side-by-side comparisons.
Different landing pages for desktop and mobile
Admins can now choose a different homepage for each device. Under Setting → Page Builder, use the Desktop Home and Mobile Home columns to choose an active page for each slot. You can use one page for both, or build a more compact landing page for phones with Page Builder.
These choices control the page visitors see at your site's root URL (/) independently of the theme slots. Until you choose a page for a device, RKHM keeps using the existing active page whose URL is /.
Example: Page Builder uses “Shady 2 beta” as Desktop Home and “Big Slider #1” as Mobile Home. The two choices are independent. Click the image for full size.
File protection, rebuilt for audio and video
Until now, protection was a single on/off Protect premium files switch, and only for audio. v2.1.20 replaces it with a complete system. Full guide: File Protection.
- Audio and video: in Setting → Files → Upload Setting, the Audio and Video groups each have a Protect files setting with three choices: Off (the default), Premium ( priced ) files only, or All files. Use All files when access depends on a subscription plan or user role rather than a price.
- Rules per item type: every item type that has media sources gets its own Protect files rule further down the same page. Leave it on Default to follow the Audio or Video setting.
- Private folder, short-lived links: protected files move into files/protected on your server. They can only be reached through short-lived links, which visitors get only after RKHM checks they are allowed to play or download.
- Choose who delivers protected files under Protected files → Serve protected files with (compared in the table below).
- Test button: tests the selected delivery mode before you save. It creates a throwaway protected file, fetches it through a real link, then tries a forged one, and tells you whether the web server or PHP answered.
- Safe fallback: if Apache can't check a link itself (an older Apache, or a disabled module), the same link falls back to protector.php. Nothing breaks; you just don't get the speed.
- HLS keys stay on your server: with HLS Encryption on, the encrypted pieces can live on any storage, even a public one. Each file's key stays in files/hls_keys and is only given to visitors who are allowed to play.
- Expired links are cleaned up by the cronjob every minute (Apache mode). Keep your cronjob running every minute.
- Service worker reworked for offline playback, HLS streams and protected files.
Setting → Files → Upload Setting → Audio: choose which uploads to protect. The Video group has the same three choices. Click the image for full size.
| Delivery mode | Best for | What it needs |
| PHP ( protector.php ) | The default. Small sites and audio. | Nothing extra; works on every host. Each active stream holds one PHP worker, so long videos with many viewers can slow the site down. |
| Apache signed links | Selling content from your own server on Apache | Apache 2.4.26 or newer with mod_rewrite and mod_version, the shipped .htaccess file, and a cronjob running every minute |
| nginx secure_link | Selling content from your own server on nginx | nginx's secure_link module (included in most builds) and the rules shown in the setting's tip, with your secret filled in |
Setting → Files → Upload Setting → Protected files: choose the delivery mode and test it before saving. Click the image for full size.
Recommended setups: for free content, leave Protect files Off. To sell content from your own server, choose Premium ( priced ) files only or All files, switch delivery to Apache or nginx, and press Test. To sell content from third-party storage, turn on HLS Encryption.
Smarter FFmpeg processing
FFmpeg now checks every upload before converting it, and skips work it doesn't need to do. Full guide: FFmpeg.
- FFprobe support: a new FFprobe group under Setting → General → External Tools has its own switch, path and Test button. Enable and test both FFmpeg and FFprobe.
- Copy instead of encode: with FFprobe, each upload is checked first. Files that are already compatible are left alone, files that only need repackaging are copied (fast), and only the rest are fully converted. Without FFprobe, every file is fully re-encoded.
- Hardware encoder: use NVIDIA NVENC or Intel QuickSync if your server has a supported GPU. It is much faster than encoding on the CPU. A GPU job that fails (a session limit, a driver update, a busy device) retries once on the CPU instead of failing the upload.
- Encoding priority: Speed, Balanced or Smaller size trades server time for output file size.
- HLS encryption and output checks: converted files are verified before they are used.
- Every conversion is recorded: what was decided, why, and how long it took. See the new Stats → System → Media processing panel (under Statistics below).
- Better diagnostics: expanded FFmpeg, FFprobe and protected-file checks in the admin panel.
Setting → General → External Tools → FFprobe: test the executable path before enabling it. FFmpeg must be enabled too. Click the image for full size.
Administration and operations
- Parallel cron: turn on Parallel execution under Setting → Cronjob → Setting and set Max jobs at once. Each due job then starts as its own background process, so one long import no longer holds up every job behind it. Locks and heartbeats stop the same job from running twice. Use a main cron command with bof_cronjobs=yes; commands containing ignore=, job= or solojob= keep their existing behavior and do not enable this mode. This needs command-line PHP on a Unix host with exec() allowed and support for background processes. Elsewhere, or with the switch off, jobs run one after another as before.
- AI settings: the AI API setting page only loads model lists for the services you use (text, image, speech). It adds OpenRouter image models, checks which parameters each model supports, and shows a clear message when an API key or model list is unavailable. See Lorem AI.
Setting → Cronjob → Setting → Parallel execution: Max jobs at once limits the number of background jobs. This example shows a limit of four. Click the image for full size.
Payments
- PayPal: checkout now uses PayPal's Orders v2 API through PayPal's official server SDK. It verifies the capture, renews access tokens automatically, and retries safely: a retried request can't create a duplicate payment. Test in Sandbox mode before switching to Live.
- Stripe: updated SDK, checkout and payment verification.
- Coinbase: the Crypto Gateway plugin now uses Coinbase Business's Checkout API, because Coinbase closed Coinbase Commerce on 31 March 2026. Instead of the old Commerce key, create a Coinbase Developer Platform Secret API Key with the ECDSA signature algorithm. It accepts USD, EUR, AUD, CAD and GBP, settles in USDC, and is currently available to businesses in the United States and Singapore.
- Extra Gateways plugin (premium, updated separately): new hosted gateways including PhonePe, PayU India, Instamojo, Midtrans Snap, Telr, MyFatoorah, Airwallex, Mollie, Paddle and Xendit. That makes 28 integrations in total.
- Payments are no longer lost when buyers close the browser: if a buyer pays with one of the new hosted gateways but closes the browser before returning to your site, a cron task rechecks unpaid payments from the last seven days every 15 minutes. It credits each one the provider confirms. Keep your cron running.
- Gateway notes: MyFatoorah needs a Webhook v2 subscription and its secret key; PayU India needs a fallback billing phone. See Payment gateways.
Storage
- New providers: Azure Blob Storage, DigitalOcean Spaces, Google Cloud Storage, Hetzner Object Storage and Oracle Cloud Object Storage.
- Local Directory: store files in another folder on the same server, such as a second disk. Enter an existing directory the web server can write to, and the web address that folder is served from.
- Updates to existing providers. See Storage.
Sign-in, search and fresh installs
- Social login: updated libraries. Fixed Twitch account matching: the email and profile now come from Twitch's OpenID Connect user-info endpoint. Test each provider you use after updating.
- Search history repaired: on some sites the search-history table had been created without a primary key, so saving search history could fail. The update repairs the table automatically, only where needed.
- PHP 8.3: v2.1.20 requires PHP 8.3 or newer. The installer refuses to start on older versions (earlier releases accepted PHP 7.0.33). See Installation – Requirements.
- Fixed: on a fresh install, passwords were stored in a column too short for modern Argon2id hashes, so the accounts it created could not sign in.
- Fresh installs get the full database: the firewall, statistics, search-history, push-notification and payment structures that upgraded sites already had are now included.
- Fresh-install defaults: development mode, firewall off, comments off, no session limits, and the built-in PHP search engine. Review them before you launch. See config_user.php options.
Updated libraries
- PayPal Server SDK 2.4.0
- Stripe PHP 21.3.2
- Meilisearch PHP 1.17.0
- AWS SDK for PHP 3.398.1
- jQuery and jQuery UI
Firewall
The built-in Firewall arrived in v2.1.00, and Alerts were added in v2.1.01. It is free, there is nothing extra to install, and it works alongside a host firewall or a CDN. It is off on a fresh install. Full guide: Firewall.
How it works
- Every request first passes a very fast check: is this address on the block list? Blocked addresses are stopped before your site does any real work. Visitors see a blocked-access page, and app/API clients get a clean error.
- Separately, a scanner reviews your recent request logs on a schedule (every minute) and scores anything suspicious from 0 to 9. An address is blocked when one detection reaches the minimum to block (7 by default), or when its detections add up past the accumulated threat threshold within your time window.
- Automatic blocks last 24 hours by default, or permanently if you prefer, until an admin removes them.
Three detection engines
- Rate limits: requests per address per minute, per 10 minutes and per 60 minutes; “dead-end” requests to pages or actions that don't exist; and how many different site features one address touches in 10 minutes.
- Automated attacks: known scanning tools (such as sqlmap, nikto or a bare curl script), requests with no browser identity, probes for paths like WordPress admin pages, .env, .git or phpMyAdmin, and SQL injection, cross-site scripting, path traversal and command injection payloads.
- Endpoint attacks: well-formed requests aimed at something that isn't the requester's, such as failed logins, editing someone else's item, or a tampered price. The site's own code reports these as it handles the request.
Built-in safety
- Active and Enforce are separate switches. With Active on and Enforce off, the firewall records everything under Hits but blocks no one. Use this log-only mode to tune it.
- Whitelisted addresses are never blocked. An address with an active admin session is never blocked automatically, and your primary admin account's traffic is never scanned.
Admin pages and actions
- Firewall → Statistics: status, scanner health, key counts, recent hits and blocks, and your exact cron line with your real path filled in.
- Setting: the Firewall, Rate limits, Automated attacks, Endpoint attacks and Alerts tabs.
- Hits (the evidence) and Blocks (add a block by hand, edit its reason or expiry, or delete a row to unblock it immediately).
- Actions: Run scan now, Unblock a single IP, Reset hits and Reset blocks. The resets can't be undone.
- Alerts: Attack detected, Severe threat and PRO sync failed, delivered by Email, Telegram or WhatsApp through Admin Notifications. Nothing is sent until you enable those events under Setting → Notifications → Admin Notifications.
- Firewall PRO (needs active support): your site reports the addresses it blocks to the Busyowl network and gets the combined list back every hour, so an attacker caught on another site is already blocked on yours. PRO never overwrites a block you made yourself.
Recommended setup
Give the scanner its own cron entry, running every minute:
* * * * * php /path_to_rkhm/api/app/admin/cronjob.php solojob=fw_scanThen add ignore=fw_ to your general cron entry so the scan doesn't run twice:
php -f /path_to_rkhm/api/admin.php bof_cronjobs=yes ignore=fw_- Turn Active on and leave Enforce off.
- Install the cron entry above, or press Run scan now while you wait for it.
- Watch Hits for a day or two, and adjust the limits if normal traffic shows up.
- Whitelist addresses you never want touched, such as your office or a monitoring service.
- Turn Enforce on.
- Optionally, enable the Firewall events under Admin Notifications so you hear about real attacks.
Statistics
The statistics system was rebuilt in v2.1.01, and v2.1.20 adds a Media processing panel. Everything lives in the Stats section of the admin sidebar: Dashboard, Visits, System, Users and Financial. Each installed content plugin (Music, Radio, Podcast, Audiobook and Beats) adds its own dashboard with that plugin's plays, top items and growth. Full guide: Statistics.
How the numbers are made
- Raw events are logged as they happen: page requests, payment attempts, playback attempts, starts and ends, API outcomes and media conversions.
- The Statistics Rollup cron job turns them into daily totals every 15 minutes. It is always on, and it re-processes yesterday as well as today, so a bad run fixes itself on the next one.
- The Stats pages read only those daily totals, which keeps them fast even with millions of events. Today's figures are always partial until the day is over.
What each page shows
| Page | Highlights |
| Dashboard | A Needs attention checklist: gateways under 20% approval, playback failures above 20%, failed or hung cron jobs, new recurring errors, and subscriptions expiring within 7 days (each one dismissible). A live Right now feed. Active users, plays, new users, listening hours, playback success and USD revenue against the previous period. The biggest movers among gateways and endpoints. |
| Visits | Real visitors (distinct IP addresses), requests, top countries, most requested pages, and the desktop/mobile mix. The background keep-alive poll is excluded; there is no other bot filtering. |
| System | Job health, API health per endpoint, Media processing (new in v2.1.20), success rate and latency for external services, top error signatures, firewall activity, and file counts and sizes. |
| Users | DAU, WAU and MAU, stickiness, the activation funnel, the engagement distribution, and 6-week retention cohorts. |
| Financial | Payment attempts, approvals and approval rate, a scorecard for each gateway, the payment funnel, and revenue per currency (never blended into one total). |
New in v2.1.20: Media processing
Shows what happened to every upload: skip (already compatible, no work needed), copy (repackaged, fast) or encode (fully converted), with the exact compatibility check that sent a file to a full encode. It needs FFprobe: without it, every file shows no_probe and is fully re-encoded. Check it after a batch of uploads to see whether your library is mostly compatible already or mostly needs converting.
Settings and troubleshooting
- Setting → General → Statistics: daily statistics are kept for 400 days by default (enough for year-over-year comparisons), raw event logs for 14 days, and dismissed alerts stay hidden for 7 days.
- Everything reads zero? Check that Statistics Rollup shows recent runs under Setting → Cronjob → Run logs, and that your server's cronjob is actually running.
- Google Analytics? The built-in Statistics are your source of truth for operations and revenue: failing gateways, hung jobs and conversion results. Add Google Analytics if you also need marketing attribution or audience demographics.
Found a problem after updating? Report it on the Bug Reports form, which is free for everyone whether or not your support is active. Feature ideas go to Feature Suggestions, where you can also vote for the ones you want most.