Repository navigation
Releases: libxa-framework/libxa-desktop-releases
Release list
Libxa Desktop 0.12.0
Libxa Desktop 0.12.0 - security hardening and service fixes. Windows 10/11 x64.
Security
- MySQL and nginx listened on every network interface. A fresh MariaDB has
a passwordlessroot, so anyone on the same Wi-Fi could log in, and every
local project was readable from the network. Both now bind127.0.0.1.
A new Settings option, Allow other devices on the network, restores the old
behaviour for testing from a phone. - "Open path" could launch executables. The renderer-supplied path went
straight toshell.openPath; executable types are now refused. Clearing a
site log is limited to log files a site actually owns, instead of truncating
any path it is handed. - A settings patch from the renderer was written to disk unchecked. Ports,
the domain suffix and binary paths are validated, unknown keys are dropped,
and duplicate ports are rejected with a message naming both.
Fixed
- Restarting MySQL left it stopped.
stopreturned before the process had
exited, so the restart'sstartsaw it still running and did nothing, and
the delayed exit then marked it stopped.stopnow waits for the process. - MySQL was force-killed on every stop and on quit, which is how an InnoDB
data directory gets damaged. It is asked to shut down (mariadb-admin shutdown) first and only killed if it refuses. Quitting waits for services
to stop, with a cap, instead of exiting under them. - Double-clicking Start spawned a service twice, and the second copy failed
to bind and was reported as a crash of the first. Concurrent starts now join. - php-cgi exited after 500 requests on Windows, leaving nginx answering 502
until PHP was restarted by hand.PHP_FCGI_MAX_REQUESTS=0is now set. - One php-cgi meant one request at a time, so a page that called its own
site (WordPress loopback, a local API call) waited on itself forever. Each
PHP version now runs a pool of four behind an nginxupstream, and a worker
that dies is replaced. Pool changes are serialised: concurrent refills used
to spawn duplicate workers that outlived the app. - PHP ports collided with Xdebug. 8.0 on 9000 and 8.3 on 9003 sat on
Xdebug's default ports. The range now starts at 9500. - A site pinned to an uninstalled PHP version got a 502. It is routed to the
default version, the site's PHP picker says "(not installed)", and an
uninstalled default is replaced automatically. - Parking a folder, removing a site or changing PHP did nothing until nginx
was restarted. nginx is reloaded in place (afternginx -t, so a bad config
leaves the working one serving). Changed ports get a real restart that checks
the new port first, and Settings no longer tells you to restart by hand. - Orphaned nginx was never reaped. The check looked for
logs/nginx.pid
while the config writesnginx.pid, so after a crash every launch moved the
whole stack to another port. PATH_INFOwas dropped (/index.php/routereturned 404), and large
Ignition/Whoops pages failed with "upstream sent too big header". Both fixed;
HTTP_PROXYis no longer passed through (httpoxy).- Python installs never refreshed the Python list, because every install
rescanned PHP. Each kind rescans its own list. - Installing over or removing a running service failed on locked files. The
service is stopped first and restarted afterwards. - Large downloads failed on a dropped connection ("terminated"). They now
retry with a Range resume, and the checksum covers the finished file. - A runtime that cannot start (missing Visual C++ runtime) installed
"successfully" and then never appeared. Installs are smoke-tested and say
what is missing. - PATH edits flattened
%SystemRoot%-style entries into literal paths and
changed the registry value type, because .NET returns the expanded string.
The registry is now read and written directly, asREG_EXPAND_SZ. - The hosts file could be wiped. If it could not be read (antivirus lock),
the rewrite proceeded from an empty string and dropped every other entry.
An unreadable file now aborts the write. - Closing the app killed the tray with it; an optional Keep running in the
tray setting hides the window instead. Tray actions no longer crash the main
process when a service is not installed, and the Sites and PHP menus refresh
when they change. - The site preview was blocked by the renderer's CSP, and the Mailpit inbox
badge could never load for the same reason. The preview is allowed, and the
count is fetched from the main process. - The site log viewer read the whole file to show its tail, once a second.
- A failed scaffold left a half-created project that blocked every retry.
- The window's log file grew without bound. It rotates at 5 MB.
- Smaller: the Settings page saved a stale copy of settings changed elsewhere
(switching PHP from the tray, then saving, switched it back); the HTTPS port
was not editable; Laravel and WordPress projects ignored a non-default MySQL
port; a Laragon/WAMP PHP layout was never discovered; MariaDB under Program
Files was only found at hard-coded versions; the Composer shim failed when
PHP was not on PATH; MariaDB[Warning]lines showed as errors; the Mailpit
log filter was missing; Services offered "Start" while a stop was in flight.
Libxa Desktop 0.11.0
Your application's log, in the site pane. Plus Symfony and WordPress starter kits.
Application logs
A new Logs tab beside Share shows the framework's own log — the stack trace you actually want when something breaks, not the nginx access log.
Where that lives depends on the framework, so it's derived from the detected type rather than guessed:
| Framework | Location |
|---|---|
| Laravel | storage/logs/ |
| LibxaFrame | src/storage/logs/ |
| Symfony | var/log/ |
| WordPress | wp-content/debug.log |
| Plain PHP | logs/, log/ |
- Follow keeps the newest line in view as the file grows
- Filter narrows to matching lines
- Clear empties the file without deleting it, so the framework keeps writing
- Lines are coloured by level, parsed from the entry header
Daily-rotated files are listed too. A project using Laravel's daily driver has no laravel.log at all, and offering only that name would show an empty panel on a site that's logging perfectly well.
Two limits worth knowing: only the last 256 KB is read — an unrotated laravel.log reaches hundreds of megabytes and loading one would take the window down, so the panel says tail only when it has trimmed. And the file is polled once a second rather than watched through the OS notification API, because fs.watch on Windows misses writes from processes that hold the handle open — exactly what a logging framework does.
Two more starter kits
| Kit | Source |
|---|---|
| Symfony | composer create-project symfony/skeleton |
| WordPress | The official archive from wordpress.org |
WordPress is the odd one out. It isn't a Composer package in any form its own users would recognise, so the release zip is downloaded, checked against the SHA-1 wordpress.org publishes beside it, and unwrapped — the archive contains a wordpress/ folder and the project is its contents, not a folder inside a folder.
Create a database for it first; the Environment tab does that in one click.
Install
Download libxa-desktop-0.11.0-x64.exe below. Windows x64. Existing installs update automatically.
Libxa Desktop 0.10.1
Everything a project needs before it runs — its .env and its database — now lives beside the site. Plus a fix for installing Mailpit.
Fixed: Mailpit would not install
Installing Mailpit always failed:
Checksum mismatch for mailpit-windows-amd64.zip.
Expected , got e4815d2c961a4ad024f4d942d0d286fd7b7da49c49fc7f01c565ff296bd3fdce.
The download was never at fault — those bytes hash to exactly that value. The comparison was.
Mailpit publishes no checksum file, so its entry returned an empty string. The verify step guarded against null, and an empty string is not null, so it compared the real hash against "" and could only ever fail.
The check is now explicit about all three cases rather than inferring them from one nullable field: no published checksum skips the comparison and logs what it saw, a genuine mismatch still discards the download, and an entry that claims to be verifiable while resolving no checksum fails loudly — instead of silently installing something unverified while the interface says otherwise.
Environment tab
Each site gains an Environment tab.
.envtable with credential-looking values masked until you ask- Raw editor, keeping the previous version as
.env.libxa-backup - Create from example — the state every freshly cloned repository is in
- Use local mail points this project's
MAIL_*at the local catcher
The mail rewrite now works on any project with a .env, not only ones created here. Existing keys are replaced in place rather than appended; a commented # MAIL_HOST= is left as documentation, and nothing outside the managed keys is touched — including values containing # or =.
Database
- Name suggested from the site —
my-app.testbecomesmy_app - Creating one also writes
DB_DATABASEinto the site's.env - Listed with table count and size; the server's own are hidden and undroppable
Creating a database and telling the application its name are one task split across two tools. Doing them separately is how a project ends up pointed at a database that was never created.
Databases are utf8mb4 explicitly, because the server default is still latin1 on some builds. Names are validated rather than escaped — SQL has no placeholder syntax for an identifier.
About
A new About page covering the product, VYLOXI — Empowering Your Digital Future — and the head of development, with live counts of the sites and runtimes on your machine. The photograph is bundled with the app, so the page renders identically with no network.
Install
Download libxa-desktop-0.10.1-x64.exe below. Windows x64. Existing installs update automatically.
Libxa Desktop 0.9.0 — Mail
Your sites can now send mail, and you can read it.
Mailpit joins PHP, Nginx and MySQL as a managed service. It accepts everything your sites send on 127.0.0.1:1025 and shows it in an inbox at http://127.0.0.1:8025.
- Live message count in the Services tab, and an Open inbox button
- New sites are pointed at it automatically — a project created here can send mail with no setup
- Copyable
.envblock for projects that were not created here - SMTP and inbox ports configurable in Settings
Why this matters
A framework's stock .env names a mail host that does not exist on a developer machine. The first mail a new project sends fails with a connection error, or vanishes — so "register a user" appears broken for a reason that has nothing to do with your project.
Details worth knowing
Bound to localhost only. Captured mail is real password resets and customer test data; it should not be readable from whatever network your laptop is on.
The .env rewrite is careful. Existing MAIL_* keys are replaced in place rather than appended — a duplicate key wins in some parsers and loses in others. A commented # MAIL_HOST= is treated as documentation and left alone, and nothing outside the managed keys is touched, including passwords containing # or =.
Mailpit publishes no checksum file, so it is marked verification: none and the installer shows you that before you agree, rather than implying a guarantee that isn't there.
Documentation
New FEATURES.md documents the whole app — sites, sharing, services, mail, PHP, Composer, Node, Python, PATH handling, certificates and storage locations — including the reasoning behind the decisions that look surprising:
- why sharing forwards to nginx rather than the project directory
- why enabling a PHP extension also enables
extension_dir - why Python comes from python-build-standalone instead of python.org
- why runtimes are reached through junctions instead of PATH entries
Install
Download libxa-desktop-0.9.0-x64.exe below. Windows x64. Existing installs update automatically.
Libxa Desktop 0.8.3
Two bugs that kept Node and PHP resolving to the old install even after the elevated fix reported success.
Shims left as empty directories
The elevated step created a real, empty directory exactly where the junction had to go. That directory then sat at the front of the machine PATH resolving nothing — so the runtime you replaced kept winning, and the panel had no way to explain why.
Now only the shim's parent is created; the junction is made in one place, a stray plain directory is cleared out of the way, and the result is verified afterwards (mklink can report success while leaving nothing usable behind).
Machines already in this state repair themselves. Opening the PHP, Node or Python tab recreates a shim that exists but isn't a link — safe, because it only restores a link you already asked for.
pip resolved to the wrong Python
python-build-standalone ships pip as an importable module but doesn't generate its console scripts, so Scripts\ contained only a placeholder. The directory was on PATH, and ahead of other installs — yet pip still resolved elsewhere while python -m pip worked fine, which is a genuinely confusing way to fail.
ensurepip doesn't help (it sees pip already installed). Reinstalling the pip package is what regenerates the scripts, and activating a Python now does that first.
Verified end to end
On a machine where all three were shadowed by machine-scope installs:
| before | after | |
|---|---|---|
node |
25.6.0 | 26.7.0 |
npm |
— | 11.19.0 |
php |
8.4.19 | 8.5.9 |
python |
3.14.2 | 3.10.21 |
pip |
25.3 (wrong Python) | 26.2.1 |
All five now resolve through the shims, matching what the app shows.
Install
Download libxa-desktop-0.8.3-x64.exe below. Windows x64. Existing installs update automatically.
Libxa Desktop 0.8.2
UI fix for the PATH status panel added in 0.8.1.
What was wrong
The status text rendered as one run-on paragraph:
python resolves to 3.14.2 — not the version selected hereC:\Python314\python.exepip 25.3 — C:\Python314\Scripts\pip.exeC:\Python314 is in the system PATH…
Every line was there and every line was correct — they just had no line breaks. The health-* classes carry no display rule, and the rest of the app uses <div> for them, but the panels added over the last few releases used <span>, so each line rendered inline.
Now each fact sits on its own line: what python resolves to, the binary path, the pip that came with it, and the explanation of why a system-PATH install wins.
Fixed only in the components that introduced it. The pre-existing inline <span className="health-label"> usages in the runtime rows, phpMyAdmin panel and Sites page are laid out beside other inline content, so a blanket CSS rule would have broken them.
Also
The three switch labels no longer say "(adds it to your user PATH)". Since 0.8.1 the runtime is reached through a fixed shim directory, and where the PATH entry lands depends on whether something is shadowing from machine scope — so the parenthetical was misleading.
Install
Download libxa-desktop-0.8.2-x64.exe below. Windows x64. Existing installs update automatically.
Libxa Desktop 0.8.1
Bug fix: switching PHP, Node or Python version now actually changes what your terminal runs.
What was broken
Switching version updated the UI but node -v and python --version reported the same thing as before. The cause was a wrong assumption in the PATH code — it wrote only to your user PATH.
Windows builds the effective PATH as machine PATH first, then user PATH. On a normal machine:
node -> C:\Program Files\nodejs in SYSTEM path
python -> C:\Python314 in SYSTEM path
Both are machine-scope, so nothing written at user level could ever outrank them. The feature could not have worked.
The fix
Runtimes are no longer put on PATH directly. Each gets a fixed directory holding a junction to the active install:
%LOCALAPPDATA%\Programs\libxa\shims\node -> the selected Node
Switching repoints the junction, so PATH is written once and never again — switches are instant and need no prompt. Junctions require no elevation, and because the target is a real directory, the executable behind one can still be spawned without a shell (a .cmd shim could not).
When an install still wins
If the winning entry is machine-scope, no user-level change can beat it. That case is now detected and named — the panel tells you which directory is winning, that it lives in the system PATH, and why that matters — with a Fix as administrator button that puts the shim directory first. One prompt, once; switches afterwards need none.
It is never attempted silently.
Also fixed
Python registered two PATH entries under a single settings key, so the second registration treated the first as the previous entry and removed it — the Scripts directory holding pip.exe was added and immediately dropped.
Install
Download libxa-desktop-0.8.1-x64.exe below. Windows x64. Existing installs update automatically.
Libxa Desktop 0.8.0
Python joins PHP and Node as a fully managed runtime.
Python
A new Python tab: install any version, switch between them, remove them, and put the active one on your PATH.
> python --version
Python 3.13.15
> pip --version
pip 26.2.1
Both follow the version you select. Same rules as PHP and Node — your user PATH only, no administrator prompt, switching repoints the same entries, and only what this app added is ever removed.
Versions offered: 3.14, 3.13, 3.12, 3.11, 3.10, each resolving to the newest patch automatically.
Where the builds come from
Not python.org, and that was a deliberate call after testing both alternatives:
- python.org's Windows download is an interactive .exe that can't be driven unattended into a per-user directory.
- Its "embeddable" zip deliberately omits pip and venv. Getting pip working needs
import siteuncommented in a._pthfile plus aget-pipbootstrap — and venv is absent entirely. A Python that can't create a virtual environment is half a Python.
So Python comes from python-build-standalone, the same relocatable CPython builds uv uses. pip and venv both work out of the box, and the project publishes a SHA256SUMS file — so downloads are verified, not merely fetched over TLS.
Details worth knowing
- Two PATH entries, not one. The interpreter sits at the install root but
pip.exelives inScripts\. Registering only the root gives you a workingpythonbeside a missingpip, so both are handled. - The Microsoft Store stub is skipped. The
python.exeinWindowsAppsisn't an interpreter — it opens the Store — and probing it hangs. If one is winning on your PATH, the panel says so and tells you how to fix it. - Tarball downloads are unpacked with the
bsdtarWindows has shipped since 10 1803, rather than adding a tar dependency for one runtime.
Install
Download libxa-desktop-0.8.0-x64.exe below. Windows x64. Existing installs update automatically.
Libxa Desktop 0.7.0
Node joins PHP as a global runtime, and Composer installs from inside the app.
Global Node
Use globally on the Node tab now puts the selected runtime on your PATH:
> node -v
v25.6.0
> npm -v
11.8.0
Both follow the version you pick — one PATH entry covers node, npm and npx, since they share a directory. npm's version is shown separately because it moves independently of Node's.
Same rules as PHP: your user PATH only, so no administrator prompt; switching version repoints the same entry; and only the entry this app added is ever removed.
Composer
The PHP tab can now install Composer if you don't have it.
- Downloads the official PHAR and verifies the SHA-256 the project publishes beside it
- Proves it runs before it becomes the installed copy
- Adds
composerto your user PATH — no administrator rights - Update and Remove for the copy this app manages
It lives with the PHP tooling rather than in a downloads list because it isn't independent of PHP: Composer is a PHAR that runs on whichever version you've activated. Enable an extension in php.ini and Composer sees it immediately; switch PHP version and Composer follows, without reinstalling.
An existing Composer from the official installer, Scoop or XAMPP is detected and left alone — the app reports it rather than overwriting it, and offers a managed copy only if you want one.
Install
Download libxa-desktop-0.7.0-x64.exe below. Windows x64. Existing installs update automatically.
Libxa Desktop 0.6.0
Your chosen PHP is now the machine's PHP, and php.ini is editable in-app.
Global PHP
Use globally on the PHP Versions tab now means exactly that. Switch it on and the selected runtime goes onto your PATH, so php -v in any terminal reports the version you picked — not whatever XAMPP left behind.
- Writes to your user PATH only, so no administrator prompt
- Switching version repoints the same entry rather than piling up new ones
- Only ever removes the entry this app added; the rest of your PATH is untouched
- A status line tells you what
phpcurrently resolves to, and warns when another PHP earlier in PATH is winning
php.ini, without the text editor
The usual routine — find php.ini, delete a semicolon, remember extension_dir, restart everything — is now a toggle.
- Extension toggles for openssl, curl, mbstring, intl, gd, zip, pdo_mysql and the rest
- loaded / not loaded badges from
php -m, so you see what PHP actually loaded, not just what the file asks for - Raw editor behind a switch when you need the full file
- A missing
php.iniis created fromphp.ini-development, which is how a fresh Windows PHP ships - Saves are validated by PHP before replacing the live file, and the previous version is kept as a backup
- PHP reloads immediately, so sites pick the change up without a manual restart
One thing worth knowing
Enabling an extension also enables extension_dir in the same step. On Windows the first half without the second silently does nothing, which is why "I uncommented the line and it still doesn't work" is so common.
The stock php.ini actually ships two commented candidates:
;extension_dir = "./"
;extension_dir = "ext"Uncommenting the first points PHP at the wrong directory. This release picks the "ext" line explicitly.
Install
Download libxa-desktop-0.6.0-x64.exe below. Windows x64. Existing installs will be offered this automatically.