Skip to content

Updates

Mikhail Deynekin edited this page Nov 13, 2026 · 1 revision

Updates

The updater installs the release archive — the .zip asset attached to a GitHub release, which ships vendor/. It never uses a branch tarball: a branch tarball cannot contain vendor/, so it can never satisfy the manifest inside it, and the update would refuse itself.

Check

GET /admin.php?action=update.check (or the Updates page) reads the GitHub releases API with a 15-minute cache and reports the version delta, release notes, and whether the release carries an installable archive (archive, installable). The panel disables Apply when it does not.

Dry run

POST updater.php?api_key=<key>&action=install&format=json&dry_run=1

The dry run downloads the release archive, refuses entries with unsafe paths, verifies every file against the manifest embedded in the archive, and reports the files that would be replaced (to, asset, verified, unchanged, steps). It writes nothing to the project — the archive lives in a temporary directory under storage/state/ and is removed before the answer. It fails loudly when the release has no archive instead of reporting success.

Because it judges the release and not the local manifest, "nothing to replace" means the release matches this installation, not merely the files match themselves.

Apply

ALLOW_UPDATE = true is required for the panel button; the machine API treats the key as the credential.

  1. Resolve the release asset and download it once.
  2. Refuse any archive entry with an absolute path, .. or control characters.
  3. Verify each file against the archive's own sha256 manifest — the first mismatch refuses the whole update.
  4. Back up every file about to be replaced to storage/backups/app-<timestamp>/ with a backup.json describing the run.
  5. Copy only the files that actually changed.

content/, storage/ and .md.ini are never touched. The updater does not run migrations, does not change the database (there is none), does not touch services, cron, the firewall, DNS or the PHP configuration.

Restore

ALLOW_RESTORE = true enables restoring application files from an app-* backup (?restore=latest or ?restore=app-YYYYmmdd-HHMMSS), and restoring content from a content backup in the panel. A restore always writes a …-pre-restore snapshot first.

After an update

With opcache.validate_timestamps = 0 (the recommended production posture) a code change is invisible until PHP-FPM is reloaded:

systemctl reload php-fpm8.5   # or the pool your site uses

If it refuses

Message Cause
"The latest release has no .zip archive attached" the release was published without the asset — attach MD.Scribe-<version>.zip
"Integrity check failed for …" the archive does not match its embedded manifest; refused by design, retry or install manually
"ALLOW_UPDATE is false in .md.ini" the flag is off
"The PHP zip extension is required" install php8.5-zip and reload the pool

Clone this wiki locally