The single source of truth for auto-update architecture, data safety, and config migration strategy.
Read this before implementing or modifying anything related to installation paths, auto-updates, config file handling, or the folder structure.
Auto-update must NEVER destroy user data.
This rule governs every architectural decision in this document.
The most important structural requirement for reliable auto-updates is separating replaceable app files from irreplaceable user data.
C:\Program Files\DevStackBox\
DevStackBox.exe
apache/ <- Apache HTTP Server binaries
php/ <- PHP binaries (all versions)
phpmyadmin/ <- phpMyAdmin PHP files
resources/ <- Bundled assets
updater files
Everything here can be overwritten by the updater without any data loss.
C:\dsb-data\
www/ <- User's PHP projects / web root
mysql-data/ <- MySQL database files (InnoDB data, indexes)
logs/ <- Apache, MySQL, PHP error logs
config/ <- Runtime configs (php.ini, httpd.conf, my.cnf)
config-backups/ <- Automatic config backups
certs/ <- SSL certificates
backups/ <- Full database / project backups
The updater must never write to this directory. The user chooses this path during first-run setup.
If MySQL's data/ folder lives inside the app installation directory:
- An update that replaces the app folder destroys all databases
- The user loses all their data permanently
- There is no recovery without a backup
This is not theoretical. It has happened to real users of tools like XAMPP and WAMP.
Phase 1.8 (App/Data Directory Separation) is complete. User data lives in
%LOCALAPPDATA%\DevStackBox\ and binaries remain inside the install directory.
C:\Program Files\DevStackBox\ (install dir - safe to replace during updates)
DevStackBox.exe
apache/
php/8.1/, 8.2/, 8.3/, 8.4/
phpmyadmin/
resources/
%LOCALAPPDATA%\DevStackBox\ (user data - NEVER touched by updater)
config/ <- runtime configs (httpd.conf, my.cnf, php.ini, etc.)
config-backups/ <- automatic config backups
logs/ <- Apache, MySQL, PHP error and access logs
mysql-data/ <- MySQL InnoDB data directory
www/ <- user's PHP project files / web root
backups/ <- full backup zip files
sessions/ <- PHP session files
ssl/ <- generated SSL certificates
Config versioning is active: all generated configs include a # configVersion: N
header. The start_apache command checks this on every launch and regenerates the
config when a newer template version is detected (currently v7).
Auto-update backend code is in place (tauri-plugin-updater, auto-updater.tsx).
The remaining blocker is GitHub Secrets (TAURI_PRIVATE_KEY, TAURI_KEY_PASSWORD)
being added to the repository before the first signed release is published.
See ROADMAP.md Phase 4.3 for the full checklist.
DevStackBox v1 supports installed mode only. Portable mode is explicitly deferred.
Why:
- Auto-updates require a stable, known install path
- Portable mode puts app files and user data in the same folder by design
- Combining both modes doubles the complexity of every path-related decision
- MSI installer already implies an installed model
Portable mode (future):
Portable mode will be supported in a future version with a deliberate design:
- No auto-updates in portable mode (manual download only)
- All data stored relative to the portable folder
- Clearly documented as "no auto-update" in the UI
This decision is recorded in docs/adr/006-app-data-separation.md.
When the auto-updater is active (Phase 4.3+), updates must follow this exact flow:
1. Check GitHub Releases for new version
|
2. Prompt user: "v0.1.7 available - Update Now / Later"
|
3. User confirms update
|
4. STOP all running services (Apache, MySQL)
- Flush MySQL logs
- Wait for clean shutdown (or timeout + kill after 10s)
|
5. Backup current user configs to config-backups/
|
6. Download new installer/package via Tauri updater
|
7. Replace App Root files ONLY
(never touch User Data Root)
|
8. Run config migration if configVersion changed
|
9. Restart app
|
10. Restart previously running services
Updating while Apache or MySQL is running risks:
- Locked DLL files blocking the update (Windows)
- Corrupted MySQL InnoDB data if mysqld is killed mid-write
- Partial Apache module replacement
The update flow MUST stop services before replacing any binaries.
Every config file written by DevStackBox must include a version field.
{
"configVersion": 1,
"phpVersion": "8.3",
"mysqlPort": 3306
}- When the app starts, read
configVersionfrom the config - If
configVersion < current, run the migration chain - Each migration function transforms one version to the next
- Always back up the config before migrating
- Log migration actions to the app log
fn migrate_config(config: Value, from_version: u32) -> Result<Value, String> {
let mut config = config;
let mut v = from_version;
if v == 1 {
// v1 -> v2: add apachePort field with default 80
config["apachePort"] = json!(80);
v = 2;
}
if v == 2 {
// v2 -> v3: rename mysqlPort to dbPort
config["dbPort"] = config["mysqlPort"].clone();
config.as_object_mut().unwrap().remove("mysqlPort");
v = 3;
}
Ok(config)
}| Path | On Update | Notes |
|---|---|---|
DevStackBox.exe |
REPLACED | Main app binary |
apache/bin/ |
REPLACED | Apache binaries |
php/{version}/ |
REPLACED | PHP binaries per version |
phpmyadmin/ |
REPLACED | phpMyAdmin files |
resources/ |
REPLACED | Bundled UI assets |
dsb-data/www/ |
NEVER TOUCHED | User's web projects |
dsb-data/mysql-data/ |
NEVER TOUCHED | Database files |
dsb-data/config/ |
NEVER TOUCHED | Runtime configs (migrated, not replaced) |
dsb-data/logs/ |
NEVER TOUCHED | Log files |
dsb-data/config-backups/ |
NEVER TOUCHED | Backup files |
If an update breaks the app, a minimal recovery UI should be available.
This is planned for a future phase. When implemented, recovery mode should offer:
- Reset configs to defaults
- Restore config from backup
- Rollback to previous app version (if supported)
- Open log files in Notepad
- Start/stop services manually
Recovery mode must be reachable even if the main UI fails to load.
Do not enable the auto-updater until ALL of these are done:
- App/data directory separation is implemented (Phase 1.8)
-
get_installation_path()returns deterministic, stable paths - MySQL
data/directory is in the User Data Root, not the App Root - Pre-update service shutdown is implemented
- Config versioning is in place
- Backup-before-update is implemented
- The update flow is manually tested: old version -> new version
- Tauri updater signing key is generated and stored in GitHub Secrets
- GitHub Release JSON endpoint is configured in
tauri.conf.json
See ROADMAP.md Phase 4.3 for the implementation tasks.
Version numbers must be clean semver: X.Y.Z only.
- Never use:
0.1.7-beta,0.2.0-rc1,1.0.0-alpha.3 - Always use:
0.1.7,0.2.0,1.0.0
Reason: MSI bundler rejects versions with hyphens or letters. This has caused failed builds before.
Version must be identical in all three files simultaneously:
package.jsonsrc-tauri/Cargo.tomlsrc-tauri/tauri.conf.json
Keep the update UI minimal:
┌─────────────────────────────────────────┐
│ Update Available │
│ │
│ v0.1.7 → v0.1.8 │
│ │
│ • Improved Apache startup │
│ • Fixed tray minimize │
│ • PHP 8.4 support │
│ │
│ [ Update Now ] [ Later ] │
└─────────────────────────────────────────┘
Do NOT build: a release center, progress analytics, or a large updater dashboard.
One dialog, two buttons. That is all.