Docs · License & updates

License & updates

FortPass activates against Medel Platforms with a per-install license key. Once activated, the same client is used to check for new releases and apply them in one click from the admin panel.

Architecture

              install/steps/2.php       /admin/license
                   |                          |
                   |       verify(key)        |
                   v                          v
                    \                        /
                     \                      /
                      \                    /
                       v                  v
                  app/MedelLicenseClient.php
                       |              |
                       v              v
              POST /verify   GET /updates?current=X.Y.Z
                       \              /
                        v            v
                   https://medel.es/api/v1/license
                              |
                              v
                       settings.license_cache
                       settings.app_version

The client

app/MedelLicenseClient.php is a thin curl wrapper. The same file lives in install/lib/ so the wizard can use it before the runtime is bootstrapped.

MethodEndpointReturns
verify()POST /verify{ ok, error?, expires_at? }
checkUpdates($current)GET /updates?current=X.Y.Z{ ok, has_update, release: { version, download_url, file_hash, notes? } }
downloadLatest($current, $dst)Downloads download_urlPath of the downloaded ZIP, or null. SHA-256 verified.

Runtime license guard

app/license.php exposes license_enforce_or_die($con), called at the bottom of config.php. If the cache says the license is invalid and the grace window has passed, every non-recovery route is replaced by a "License required" page that links to /admin/license.

Cache windows

WindowDurationBehavior
fresh after success12 hoursThe cached ok=true is trusted without re-verifying. The first request after the window expires triggers a live verify.
fresh after hard fail5 minutesBad verify (explicit invalid response) is cached briefly so a fixed key recovers quickly without spamming the API.
grace on network error24 hours (from last cached_at)If the live verify cannot reach the API, the previous cached verdict (good or bad) is honoured. Prevents a Medel Platforms outage from breaking every buyer at once.
Effective behaviour: a healthy install hits the API at most twice per day. If the API is unreachable, the buyer's site keeps working for up to 24 hours on the last good cache. After that, the lock-out page appears with a link to /admin/license for recovery.

Whitelisted routes

The guard never blocks the routes the buyer needs to recover from a broken state:

Update flow

The buyer's flow is exactly two clicks: Check nowApply update. The page at /admin/license automatically runs the check on load so the action button is enabled if there is an update available.

Apply pipeline

  1. Maintenance mode ONsettings.maintenance_mode = 1, public visitors see the maintenance page (HTTP 503).
  2. Backup databasebackups/pre-update-<ver>-<ts>.sql using mysqldump if available, otherwise a PHP fallback that walks every table.
  3. Backup file treebackups/pre-update-<ver>-<ts>.zip, honoring the same exclusion list used by the copy step.
  4. Download — the release ZIP from the URL returned by the API, with SHA-256 verification.
  5. Extract — into backups/extract-<ts>/, descending into a single wrapper folder if the ZIP has one.
  6. Copy over — every file in the extracted tree is written into the live tree, respecting updater_excluded_paths().
  7. Run schema changesupdater_run_update_sql() executes update/update.sql in its entirety. Each release ships its own self-contained script.
  8. Bump versionsettings.app_version and the VERSION file are updated to the new value.
  9. Cleanup — remove the downloaded ZIP and the extraction folder.
  10. Maintenance mode OFF.

Rollback

Any failure inside the pipeline throws a RuntimeException. The catch handler:

  1. Logs the error and the message.
  2. Extracts the pre-update file backup ZIP over the live tree, undoing any partial file copy.
  3. Disables maintenance mode.
  4. Returns the captured log to the admin UI for display.

The pre-update SQL backup is not auto-restored — the admin can use it to roll back the database manually if a migration corrupted data.

What is preserved across updates

updater_excluded_paths() in app/updater.php lists every path that is never overwritten:

PathWhy it survives
config.phpBuyer's APP_NAME, APP_URL, demo flags.
app/db.phpDatabase credentials.
app/.installedInstall marker.
app/mail_log/Mail log (runtime state).
assets/img/uploads/Every buyer-uploaded image (blog covers, OG images, PWA logos, branding, avatars).
uploads/vault/Encrypted Documents-module attachments (uploads/vault/<uid>/*.enc).
backups/All previous backups stay intact.
install/The buyer's wizard is never overwritten after first install.

Shipping a release (dev workflow)

  1. Make your code changes.
  2. If there are schema changes, edit update/update.sql with the SQL needed to bring the previous release up to this one. If nothing changed, leave the file with just the header.
  3. (Optional) Regenerate install/sql/schema.sql with mysqldump --no-data so fresh installs land directly on the latest schema.
  4. Bump VERSION (e.g. echo 1.1.0 > VERSION).
  5. Double-click build-dist.bat to package _dist/fortpass-v1.1.0.zip.
  6. Upload the ZIP to Medel Platforms as the new release with its SHA-256.

Admin recovery panel

/admin/license has two halves:

This page is whitelisted by license_enforce_or_die() so it stays reachable even when the license is broken. That's the whole point: the buyer must always be able to fix their own state.
FortPass · © 2026 Medel Platforms · medel.es