CHANGELOG.md
Rama main · réplica de lectura del repositorio original.
# Changelog
What changed in each MDMesh release, for the people running it, newest first. A version's section is its tag
annotation and GitHub release notes, word for word ([RELEASING.md](RELEASING.md)). From v0.3.2 on, its sections come
in this order, empty ones left out: Upgrading, Heads-up, New, Fixes, Under the hood. Feature releases (0.x.0) ship
every three weeks; patch releases (0.x.y) only for a security fix or something broken in the field. Each PR adds its
line under Unreleased.
## Unreleased
Upgrading
- The database adds three columns (`devices.wipedAt`, `configurations.camera`, `configurations.locationServices`) on
start. Devices wiped before the update aren't marked.
- The database adds three columns to enrollment tokens on start (`maxUses`, `useCount`, `revokedAt`) and stores
the tokens hashed. Existing codes stay single-use and keep working; on PostgreSQL older than 11 they stop
instead, so make a new code on the Enroll page. After a downgrade, codes made on this version don't work.
- Quick start: run `docker compose pull && docker compose up -d` once after this update. Updates never touch the
supervisor, and one from before 0.5.0 keeps applying updates by tag. From source, or with `SUPERVISOR_VERSION` set by
hand: [UPGRADING, v0.5.0](https://github.com/MDMesh-app/MDMesh/blob/main/docs/UPGRADING.md#v050).
New
- Configurations can allow or block the camera. Block covers every app, kiosk apps too. Not managed after Block
gives the camera back on agent 0.5.0; on older agents, choose Allow to lift a block (thanks @letternumber).
- Kiosk: staff can tap Return to kiosk in the MDMesh Agent after leaving with the exit PIN, even offline. It shows
only when the device's configuration enables kiosk, asks first (and warns when that kiosk has no exit on the
device), and tells the server at once when online. Needs agent 0.5.0; no server update needed (thanks @tisign).
- Enroll: one code can set up a batch. Choose Devices (one, or up to 10, 25 or 100) and Valid for (24 hours to 30
days); a code for several devices works for anyone who has it until it expires or its devices are used up. The
default is still one device for 24 hours, the code to send to one person. **Stop all codes** ends every code that
still works, and making a new code says when the one it replaces still works. Codes are stored hashed, making and
stopping them is in the audit log, and the server log names the code each device enrolled with. The API takes
`maxUses` and `validHours` on `POST /private/agent/v1/token`, and `POST /private/agent/v1/token/revoke-all`
(thanks @alessfi).
- Configuration → Network can manage Android Location services independently of MDMesh location reporting on Android
11+ Device Owner devices. Needs agent 0.5.0 (thanks @letternumber).
- Settings → Users & roles: create, edit and delete console accounts, choose the devices and configurations each
can reach, and edit roles and their permissions (thanks @lared3294).
- A wiped device shows as **Wiped** instead of Online and then Offline, and is left out of the counts, Needs attention
and rollouts; the Devices list's Wiped filter shows it (thanks @Przemek-Magic).
- Sync now and console commands say when the device isn't connected and will get it at its next check-in.
- Updates and the quick start run exactly the images the signed release names, pinned by digest; the quick start
also checks each file it downloads, stops instead of falling back to `main`, and needs OpenSSL 3.0+, or an older
OpenSSL and `minisign`.
Fixes
- The console recognizes an on-device Return to kiosk and does not offer the same action again; a later kiosk exit,
custom kiosk, or configuration apply makes it available again.
- Audit log: on Docker installs, entries show the client's address instead of the edge container's (thanks
@Przemek-Magic).
- Kiosk: after someone leaves kiosk on the device with the exit PIN, the agent checks in right away, so the console
shows kiosk off without waiting for the next heartbeat. Needs agent 0.5.0 (thanks @letternumber).
- Agent rollout: when every device is in the canary, the rollout finishes once they've updated instead of staying
open, and Settings stops offering the same agent again. If one can't update, it offers Finish instead of Promote
(thanks @letternumber).
Under the hood
- NOTICE now says which parts of the agent are ported from Headwind MDM, and those files carry its license header.
## v0.4.0 — 2026-11-03
The console is new and replaces the old one. Every page was rebuilt, and each page loads only when it's opened.
- **Overview** (now at `/`): counts that open the Devices list filtered, a Needs attention list with one action per
device (Sync now, Update agent or Open device), and sync per configuration. It refreshes while its tab is open.
- **Devices**: an Attention filter with each device's reason, sorting, pages, and Sync now and a menu on each row.
- **Device page**: details in a column beside the tabs, a new Apps tab, and Retry apply when its configuration isn't
applied.
- **Configuration editor**: its own address (`/configurations/12`), a count of changes per section, Discard and Save
and apply in a bar, and a prompt before you leave with unsaved changes.
- **Apps**: the Library is a table, with a source filter and the configurations each app is used in.
- **Settings** has Change password and a Match system theme; Density is gone for now. **Enroll** has a full-screen QR.
- On phones the Devices list turns into cards, section lists scroll sideways and the save bar sits at the bottom.
- Server updates show in a card in the sidebar. The fonts ship with the console: nothing loads from Google Fonts.
Upgrading
- Update the server: Settings → Updates (quick start), `git pull && ./setup.sh` (from source), or
`git pull && sudo ./setup.sh --native` (native). The database updates itself on start (three new columns and an
index), and there is nothing new to set. No device re-applies its configuration, and the update queues no app
installs.
- There is a new agent, 0.4.0: roll it out from Settings → Agent rollout. Alerts on QR-enrolled devices and the
connectivity mode need it. Rolling back to 0.3.9 is safe:
[UPGRADING, v0.4.0](https://github.com/MDMesh-app/MDMesh/blob/main/docs/UPGRADING.md#v040).
Heads-up
- Apps added to a configuration now install on its devices at their next check-in, without Install config apps.
Apps it already had are not sent again (thanks @letternumber).
- The console now hides the pages and buttons a user's role can't use, which the server already refused. A link to
a hidden page says "You don't have access to this page". What each permission shows:
[UPGRADING, v0.4.0](https://github.com/MDMesh-app/MDMesh/blob/main/docs/UPGRADING.md#v040).
- A role change reaches an open console when its tab is shown again or the page is reloaded, with no new sign-in.
- Old bookmarks still work: `/dashboard` opens the Overview at `/`, and `/configs` opens `/configurations`.
- Forks: the console's page files moved, so a patch to a v0.3 page won't apply, and the console build stops on a
v0.3 file left in the tree. Each file's new place, its slot and what to check after the merge:
[UPGRADING, v0.4.0 for forks](https://github.com/MDMesh-app/MDMesh/blob/main/docs/UPGRADING.md#v040-for-forks).
New
- Connectivity per configuration (Configuration → Network): Always-on keeps the agent's live connection up, so
commands reach a locked phone without waiting for its next check-in. "—" leaves each device's own mode. Needs agent
0.4.0 (thanks @Przemek-Magic).
- Device page, Apps tab: the apps with a launcher icon from the device's latest scan, with search, a system-apps
filter, Export CSV and Scan now (thanks @Przemek-Magic).
- The Location tab has a time range: the last hour, 24 hours or 7 days, or All (the default). A range shows its
newest 500 fixes at most and says when it hits that cap; All shows the newest 500, as before (thanks @Oxym0r3).
- Audit log: device commands, such as wipe, lock, PIN reset, kiosk and alert, and Install config apps, are recorded
with who sent them, from where and to which devices. Their settings are not, since they can hold a password
(thanks @Przemek-Magic).
- **Console extensions for forks**: pages, device tabs, configuration and Settings sections, Overview cards, and list
columns and actions, added in `web/src/extensions/<id>/` without editing core files. The guide is
[EXTENDING](https://github.com/MDMesh-app/MDMesh/blob/main/web/EXTENDING.md).
A breaking change to their API (`@mdmesh/console`) gets one release of deprecation first.
- **Server plugins for forks** go in `plugins/fork/<id>/`. For the customers it is on for, a plugin can check its data
in a configuration's new `extensions` object on save, and add `x.<plugin>.<key>` policy keys to what devices apply.
The device page names such a key in words (`x.netaccess.blockTethering` is "netaccess: block tethering").
- What's new: after an update, the console shows the new version's notes once in each browser, but not on a
browser's first visit. Settings → Server opens them again, beside the version.
- A device's open tab is in its address (`/devices/WH-12?tab=telemetry`), so a link opens that tab and Back returns
to the previous one. The arrow keys change tab in place, so Back then leaves the page.
Fixes
- Alerts show on devices enrolled by QR code, with the "MDMesh active" notification, and an alert is reported failed
when notifications are off on the device. Needs agent 0.4.0 (thanks @Przemek-Magic).
- Device page: Enrolled shows when the device enrolled with the MDMesh agent. Devices enrolled before this update
keep it empty (thanks @Przemek-Magic).
- Storage and Local IP show on the device page. The old console read keys the agent never sends, so both were "—".
- Saving a configuration keeps its apps' Headwind launcher layout (screen order, key code, bottom row, long tap).
- Enroll shows a configuration's EAP Wi-Fi security as EAP, not as WPA / WPA2.
- Sign-in starts a fresh session, and so does changing your own password.
- Audit log: requests from API clients are recorded with their user, and a request is recorded however its path is
spelled.
Under the hood
- The console has unit tests (vitest), and CI runs them on every console change.
- The server no longer packs the removed AngularJS console's plugin files.
- The console no longer keeps the sign-in token (`authToken`) or a password-reset token in browser storage. When it
loads, it removes the copies an older version left there.
- Setting a password with a reset token (`POST /rest/public/passwordReset/reset`) answers OK with no data. It returned
the account, with the new password's stored hash in `newPassword`.
- A server plugin whose `plugins` row is `disabled` (set by hand; MDMesh never sets it) is now off for every customer:
its private REST answers 404 and its checks and policy keys don't run. Restart the server after changing it.
- Dependencies: react-router 8.5.1, vite 8.3.5, docker/login-action 4.7.0.
## v0.3.9 — 2026-10-27
Upgrading
- Update every server: this is a security release. Devices need no new agent, and the updater (supervisor) is
unchanged. Quick-start installs: apply the update from Settings → Updates. From source: `git pull && ./setup.sh`.
Native installs: `git pull && sudo ./setup.sh --native`.
Heads-up
- Sign-in is limited. After 10 failed sign-ins from one address in 15 minutes (or 50 in 6 hours), that address is
refused for a while; after 20 failed sign-ins for one account in 15 minutes, new browsers on new addresses are
refused for it for a while. A browser or API client that signed in to the account before (the MDMESH_SIGNIN
cookie), and an address that signed in to it in the last 30 days, keep signing in. The console says "Too many
failed sign-ins"; the API answers 429 with Retry-After: 900. Checking the current password for a password or
e-mail change counts too. A server restart clears every limit (docs/OPERATIONS.md).
- A token sign-in (/rest/public/jwt/login) also sets the MDMESH_SIGNIN cookie: API clients that keep it are spared
someone else's failed sign-ins.
- Native installs behind a proxy on another host must list it in proxy.addresses, and Docker installs behind a CDN
or another proxy in front of Caddy must add it to TRUSTED_PROXIES in .env: otherwise every client shares one
sign-in limit (docs/UPGRADING.md).
- On an install whose BASE_URL is https, the console session cookie is Secure, and the console can no longer be
signed in to over plain http (for example through Tomcat's own port on a native install). Use the https address.
- The edge, and on native installs the server, send browser security headers: the console can't be put in a frame,
and https installs get Strict-Transport-Security for six months.
- A request path with a `;` or an encoded `/`, `\`, `;` or `.` is refused with 400. No MDMesh client sends one. A
hosted file whose name has a `;` can't be downloaded: upload it again (uploads now replace `;`).
Security
- Requests to the console's and plugins' private routes always need a signed-in user, however the path is spelled.
- Failed sign-ins no longer hold a server thread, and password guessing is limited per address and per account.
- The session cookie is Secure on https installs. The edge and the server send no-framing, nosniff, Referrer-Policy and
(on https) HSTS headers.
- The mail library is Jakarta Mail 1.6.8 (CVE-2025-7962).
- Thanks to Przemek-Magic for the report.
## v0.3.8 — 2026-10-26
Upgrading
- Update every server: this is a security release. The security fixes need no new agent; the location fix below
needs agent 0.3.8.
- Update the updater too, since updates never touch it. Quick-start installs: apply the update from Settings →
Updates, then `docker compose pull && docker compose up -d` (if `.env` pins `SUPERVISOR_VERSION`, set it to 0.3.8
first). From source: `git pull && ./setup.sh`, which also moves the pinned `SUPERVISOR_VERSION`. Native installs:
re-run the installer, which updates both. Either order works.
- Single-customer installs: check for customers you didn't create, and remove them (docs/UPGRADING.md, v0.3.8).
The server lists them at startup.
Heads-up
- Updates are for the admin who runs the install: a super-admin, or the org admin (the built-in Administrator role) of
a single-customer install (every install made with quick start, setup.sh or the native installer is one). Once an
install has a second customer, only a super-admin can apply, roll back, check for or schedule updates. The recovery
token still rolls back.
- Setting a password ends that account's other console sessions and its API tokens. The session that changed its own
password stays signed in. API clients sign in again.
- Password reset links work once, for 24 hours. Links sent before the update no longer work: ask for a new one.
- Impersonating a user or a customer's admin is a POST; a GET answers 405. The console doesn't use these routes.
Security
- The self-signup API is removed. MDMesh never offered self-signup; its routes now answer 404. At startup, a
single-customer install logs any other customer it has, with how to remove it.
- The updater asks the server whether the session may manage the deployment before it applies, rolls back or changes
automatic updates. On an install with more than one customer, only a super-admin may. In front of an older server,
only a super-admin or the org admin of customer 1 may.
- Every customer account route needs a super-admin.
- Setting a password, by reset link, at first sign-in, by the user or by an admin, ends the account's other sessions
and API tokens. Reset links expire after 24 hours and work once. An account gets at most 3 recovery mails an hour.
- The console session cookie is SameSite=Lax: another site's forms and scripts can't POST, PUT or DELETE with it.
Purging message history in the messaging and push plugins, and impersonating, are POSTs.
- A new customer admin's first password, the signature a refused download should have had, and a deleted customer's
file paths are no longer logged.
- Thanks to a security researcher (#120) for the report.
Fixes
- Location: devices enrolled by QR code report their location. The agent grants itself the background location access
it was missing, and "GPS (fresh fix)" configurations get a fresh fix instead of always timing out. Needs agent
0.3.8; no server update needed (thanks @lared3294).
## v0.3.7 — 2026-10-22
Upgrading
- Update the server, then roll out agent 0.3.7 from Settings → Agent rollout, to one device first.
- Until devices have agent 0.3.7, set Safe Mode to Block on configurations whose devices you put into kiosk with Enter
kiosk.
Fixes
- Kiosk: Safe Mode is blocked whenever a device is in kiosk, including kiosks started with Enter kiosk on the device
page, unless the configuration's Safe Mode setting is Allow. The block is lifted once nothing asks for it: when such
a kiosk ends, or when kiosk is turned off on the configuration, unless the setting is Block. Needs agent 0.3.7.
- Kiosk: an exit PIN that can't be checked right now says so ("Exit unavailable right now") instead of closing.
- The console's kiosk help says which behaviour needs agent 0.3.6 or later.
## v0.3.6 — 2026-10-21
Upgrading
- Update the server first, then roll out agent 0.3.6 from Settings → Agent rollout, to one device first. This is a
security release for kiosk devices: update every server.
- Kiosk configurations without an exit PIN have no exit on the device with agent 0.3.6. Set an exit PIN first if you
need one.
- Kiosk devices re-apply their configuration once after the server update, because Safe Mode is now blocked on them
unless allowed.
Heads-up
- Kiosk: a kiosk without an exit PIN has no exit on the device; end it from the console. Set an exit PIN on the
configuration to keep the on-device exit. Needs agent 0.3.6.
- Legacy device API (`LEGACY_DEVICE_API=1`): removing a Headwind content app from a configuration on the Apps page no
longer turns kiosk mode off by itself. Turn kiosk off on the configuration page if you want it off.
Fixes
- Kiosk: a kiosk app that keeps stopping no longer ends kiosk. The device stays in kiosk on an "App closed" screen with
an "Open again" button, until kiosk is ended with the exit PIN or from the console. Needs agent 0.3.6.
- Kiosk: a kiosk change that fails on a device already in kiosk leaves the device in the kiosk it had. Needs agent
0.3.6.
- Kiosk: Recents and Notifications stay off while Home is off, as Android requires. Before, kiosk didn't start with
that combination.
- Kiosk: too many wrong exit PINs lock the exit for a while, and administrators get an email (needs SMTP). Needs
agent 0.3.6.
- Kiosk: Safe Mode is blocked on kiosk devices unless the configuration's Safe Mode setting says Allow.
- Saving app assignments on the Apps page no longer changes other configurations' settings, and devices pick up the
change right away.
- A device whose agent stopped while applying its configuration (Android ended the app, the phone restarted, the
agent updated) gets the configuration again on its next check-in. Before, newer changes could wait up to 6 hours.
## v0.3.5 — 2026-10-21
Upgrading
- Update the server: Settings → Updates (quick-start installs), or re-run the installer on native installs. Then
roll out the new agent from Settings → Agent rollout, to one device first. Devices enrolled by QR code on 0.3.4 or
earlier apply their restrictions once they have the new agent (or after a restart).
Fixes
- Agent: restrictions that need Device Owner (camera, screenshots, factory reset, Bluetooth, USB storage, Safe Mode,
Wi-Fi) apply on devices enrolled by QR code. Before, they showed as "unsupported" until the phone restarted.
- Re-running the native installer to upgrade keeps `BASE_URL`, `GITHUB_TOKEN` and a `GITHUB_REPO` you set from the
existing install, so `sudo ./setup.sh --native -y` is enough. A repo that came from the clone's origin keeps
following the origin. A value you pass still wins, and `GITHUB_TOKEN=` clears the token.
- The native installer says when GitHub refuses the token, and downloads the release without it. Rate limits are
reported as rate limits.
- Saving a configuration keeps its configuration files and app settings.
- Configuration copies keep their apps, and saving one never overwrites the original.
- Device pages always open the device you clicked, even when a device's number matches another device's internal id.
Device action dialogs, Enter kiosk included, name the device.
- Enroll: "New code" keeps the chosen configuration.
- Location popups in the device map render as plain text.
- The "Password updated" notice shows on the sign-in page.
- The enrollment QR code is drawn at its real size, so it isn't blurred and scans reliably (thanks @vanek-Impacts).
- A device's recorded public address comes from the edge, not from headers the device sends. Native installs: set
your proxy's headers and limit Tomcat's port to the proxy, as docs/OPERATIONS.md "Device public address" describes.
Under the hood: dependency updates (React Router 8, Caddy 2.11.7, pgjdbc 42.7.14, Vite, Guava), and CI runs the
agent's protocol tests on every change.
## v0.3.4 — 2026-10-14
A follow-up security release with console and agent fixes. Please update every server.
Upgrading
- Update the server first (Settings → Updates, or re-run the installer on native installs), then roll out
agent 0.3.4 from Rollouts, to one device first. Server first matters: a new agent talking to an older
server still has its location stored on "Do not track".
- After the update, devices on "Do not track" configurations re-apply their configuration once. Kiosks on
agent 0.3.0–0.3.2 relaunch once, and a device someone took out of kiosk by hand goes back into kiosk if
its configuration still has kiosk on.
Heads-up
- "Do not track" now really collects no location. It is the default for the configurations the installer
creates and for new ones, so those devices stop reporting location. To keep it, set Location reporting to
"Last known (passive)" (was "Network") or "GPS". Location already stored is kept.
- Kiosk exit PIN: new installs get a random PIN, shown (and editable) in the configuration editor. A
configuration still on the old default 12345678 gets a warning in the console. Set your own PIN there.
- The Headwind launcher's QR routes now answer 404 unless LEGACY_DEVICE_API=1 (the console draws the
agent's QR itself). The installer-created configurations get new QR keys.
- The read-only Observer role can no longer send push messages or edit device descriptions.
- SECURE_ENROLLMENT=1 also blocks the agent's APK downloads; the server now warns about it at startup.
Leave it at 0 for MDMesh agents.
New (agent 0.3.4)
- Wi-Fi radio on/off per configuration.
- Off is skipped while Wi-Fi is the device's only network, shown as deferred, and tried again at the
next configuration change, reboot or agent update. Phones that drop mobile data while on Wi-Fi can't
apply it.
- If a device can't reach the server for about 30 minutes after Wi-Fi went off (SIM removed, airplane
mode, server only reachable over Wi-Fi, a server outage), it turns Wi-Fi back on and keeps it on until
the configuration changes. The console shows "Reset by device".
- The old Wi-Fi setting, now "Wi-Fi settings", still only locks the settings screen.
- Lock volume buttons in kiosk. Android mutes the device while it's locked, so Ring device is silent.
"Lock hardware buttons" is the power menu only.
Fixes
- The configuration editor shows only settings the agent applies, with clearer Not managed / Allow / Block
choices and kiosk-only labels. Old Headwind launcher settings moved to their own section.
- The dashboard only says "in sync" after a complete check, and flags devices whose configuration isn't
applied.
- "Deferred" results show as a warning instead of a failure.
- The hosted-APK check uses the server's address, and the device page shows just the agent version.
- App SHA-256 must be 64 hex characters.
Under the hood: agent protocol 1.4 (additive), unused Headwind dependencies removed.
## v0.3.3 — 2026-10-13
This is a security release. A full review of the server found a number of permission and input-handling
issues, several of them serious, mostly in code inherited from Headwind. All are fixed here. Please update
every server as soon as you can.
Upgrading
- Update the server first: Settings → Updates (quick-start installs), or re-run the installer on native
installs. Then roll out the new agent from Rollouts, to one device first. Older agents keep working
until they update.
Heads-up
- App installs are now always checked against a SHA-256. Uploaded APKs and F-Droid apps are covered
automatically. For an app added by an external link, add its SHA-256 in the Library (new "SHA-256…"
button) or upload the APK; until then its installs stop with a message saying so.
- The old Headwind launcher endpoints are now off by default (LEGACY_DEVICE_API=0). The MDMesh agent
doesn't use them. Unused legacy routes (self-update, config-file and video uploads, stats, AppList
upload) are removed.
- Organizations with two-factor sign-in turned on no longer get API (JWT) tokens.
- Users limited to some device groups only see and act on those devices. Creating, promoting and
cancelling rollouts needs access to all devices.
- Changing your email address asks for your current password. Org admins can't create, edit or
impersonate superadmin accounts.
- Icon uploads need the Settings permission (max 1 MB, 1024 px).
New
- Disable Safe Mode (Configuration → Restrictions), so Safe Mode can't be used to get out of kiosk.
Needs the new agent.
- The native installer brings its own Node 24 and JDK 17 when the distro's are too old, so Debian 12/13
and Ubuntu 24.04 work out of the box.
- Password-recovery email on native installs, and SMTP_SSL / SMTP_STARTTLS on Docker.
Fixes
- Agent updates that got stuck are sent again, and the agent no longer freezes waiting on an install.
- The agent connects as soon as it's enrolled, so commands don't wait for the first check-in.
- Moving a device to another configuration (e.g. kiosk) applies right away.
- Command results survive an agent restart, and large backlogs are sent in batches.
- Setting statuses refresh after an agent update, without a kiosk flicker.
- The old Headwind demo apps are no longer sent to devices.
Under the hood: agent protocol 1.3 (additive), Node 24 across the build, a stricter release pipeline.
Thanks @TalesRayn and @m87938510-ctrl for the detailed bug reports, and @kmehmonov for the Safe Mode request.
## v0.3.2 — 2026-10-08
Upgrading
- Update the server from Settings → Updates (quick-start installs) or with the command it shows.
Then roll out the new agent from Rollouts. Agents on v0.3.1 keep working until they update.
Heads-up
- Kiosk: apps set to hide their icon are no longer allowed in the kiosk launcher. Tick
"Show in kiosk" on any app you still want there.
- The old Headwind launcher entry is removed from the app library and from configurations
that used it as the main app.
New
- Block factory reset (Configuration → Restrictions). Needs the new agent.
- "Show in kiosk" per app, separate from installing it.
- Remove apps from the Library. Devices keep the installed app.
Fixes
- Agents install APKs hosted on other servers, and pick up queued commands as soon as they reconnect.
- Manual and bulk "Enter kiosk" keep the configuration's launcher colours.
- One-click update and rollback work on Docker installs. Native install and upgrade fixes.
- An expired session takes you to sign-in, then back to the page you were on.
- Dialogs stay on screen on long pages.
- The device page shows the last known location.
- Configurations show app versions, and sync status refreshes after saving.
- Library: only installable apps are listed, no duplicate entries, uploaded versions are kept, and
shared APK files are no longer deleted with another entry.
Under the hood: React 19, dependency updates, quieter logs.
Thanks @letternumber, @Keidevk and @benailjamim for the contributions.
## v0.3.1 — 2026-09-25
v0.3.1 — security fixes, Docker supervisor fix (#27), Cloudflare-mode fix
Security
- Agent admin and rollout APIs now enforce the `edit_devices` permission for every change: queueing commands (incl.
wipe / passcode reset), bulk commands, sync, app sync, minting enrollment tokens, and creating, promoting or cancelling
rollouts. Users without it (the seeded Observer role, or custom roles) now get "permission denied"; read-only views are
unchanged. The built-in Administrator and User roles already have it.
- New auth tokens, password-reset tokens and generated passwords now come from SecureRandom (previously java.util.Random),
and a build-time guard fails on any weak random generator. Reset links and server-generated org-admin passwords issued
BEFORE this release came from the old generator: re-issue reset links and have those users change their password
(changing a password also rotates that user's stored session token).
The default JWT signing key (used only by API clients of /rest/public/jwt/login) is still generated at each server
start, now from SecureRandom.
- Device command history no longer returns command payloads (configuration and kiosk commands embedded the configuration
admin password hash); the API returns the target package as `subject` for app.install/app.uninstall. The queue endpoint
(POST …/devices/{id}/commands) answers with the same slim view instead of echoing the payload. Integrations that read
`payload` or `deviceNumber` from these endpoints must adapt.
Fixes
- Docker (#27): the updater/recovery supervisor crash-looped on every Docker install since v0.1.0
("Cannot find module '/project/server.js'"). Settings → Updates, the recovery page and the Docker /files/agent.apk
download (the APK URL in enrollment QR codes) now work. On from-source Docker installs the recovery page shows status and
the manual update steps (no one-click Roll back). Thanks @detobel36 for the diagnosis.
Quick-start: `docker compose pull && docker compose up -d` (if .env pins SUPERVISOR_VERSION, set it to 0.3.1 first).
From source: `git pull && ./setup.sh` (not just `docker compose up --build`).
The supervisor now runs for the first time on existing Docker installs: take a DB backup before your first one-click update.
- Existing quick-start installs (CURRENT_VERSION=0.0.0) show "Update available: 0.3.1" after pulling; applying it once pins
the versions (it takes a DB backup and restarts the server briefly). New quick-start installs pin the release they install
(the supervisor stays on :latest so pulls deliver its fixes).
- From-source Docker installs no longer offer one-click Update (it could only fail and roll back); update with
`git pull && ./setup.sh`, which now also records the running version from the checkout's tag on every run, so the console
no longer shows a permanent false "Update available". Pull and native installs are unchanged.
- Cloudflare mode: the web container's Caddy config now starts with an empty ACME_EMAIL. Own-domain mode unchanged.
CI / release
- The supervisor image is smoke-tested (booted the way compose runs it) in the fast gate and before any release push, and the
release publishes exactly the image that passed. `:latest` image tags move only after the release is published (same digest
as `:VERSION`). The Caddyfile is validated in every hosting mode and all compose files are checked in CI.
## v0.3.0 — 2026-09-22
v0.3.0: desired-state configuration — saving a configuration now converges its devices
Configurations are now enforced on devices, not just stored.
- Server: on every check-in the server derives a desired-state document from the device's configuration, hashes it, and sends one `config.apply` command when the device has drifted (delivered in the same check-in). Saving a configuration wakes its devices. New admin endpoints: `GET /rest/private/agent/v1/devices/{id}/configStatus` and `GET /rest/private/agent/v1/configurations/syncSummary`.
- Agent: applies Wi-Fi, Bluetooth, USB storage, screenshots, kiosk (same path as `kiosk.enter`) and location capture mode; reports per-key outcomes (applied / unsupported / failed); persists the revision only when nothing failed; re-applies after boot or self-update. Advertises the new `configApply` capability.
- Console: an "Enforced" chip marks the 18 fields the agent applies; the rest are collapsed under "Legacy Headwind fields". Configuration list shows "N/M in sync"; device pages get a "Configuration status" card; kiosk-affecting edits ask for confirmation with the affected device count. The main-app picker now stores the correct application-version id.
- Protocol: additive (1.1). Old agents are never sent `config.apply` and show "Agent too old" until updated.
Behaviour change on upgrade: the first check-in of every updated device applies its configuration's managed policies, and devices whose configuration has kiosk ON enter kiosk. Kiosk is never lifted by the upgrade itself; turning kiosk OFF in a configuration only lifts it on devices that entered through that configuration. Location capture mode follows the configuration (GPS = active, otherwise passive). See DEPLOY.md "Upgrading to v0.3.0".
Database: two nullable columns on device_state (Liquibase, automatic). Docker installs: pull the new images as usual.
## Older releases
v0.1.0 to v0.2.9: see their notes on the [GitHub releases page](https://github.com/MDMesh-app/MDMesh/releases).