Elyra
Elyra The coding agent e The native code editor Elyra Grove Native local development environment Askr The real server for Laravel & PHP Elyra Framework Rust + Svelte 5 framework for desktop apps Elyra Conductor Local project conductor Elyra SQL Server MySQL-compatible SQL server in Rust Elyra Félagi Agents as teammates on one board Elyra SQL Client Native desktop SQL workbench Elyra SQL Anywhere Replication-ready SQL engine Elyra Sjá SEO & GEO workspace for macOS Elyra DataGrid Server-driven data grid for Laravel
Start here
Concepts
Release notes
What's new
Elyra
Backup and restore

Backup and restore

Félagi's state is in three places, and a backup that takes one or two of them is the problem this page exists to prevent.

Where What Lost if you skip it
MySQL Everything relational — issues, meetings and their minutes, time, whiteboards, skills Everything
storage/knowledgebase Every knowledge base article and every revision, one HTML file each Every document's prose, and all of its history
storage/app/private Attachments Every uploaded file

Skill files are columns in the database, not files, so they travel with the dump.

Why this page exists

A restore that copies the database and forgets storage/knowledgebase looks completely successful. Every row is present, the article tree draws, the titles are right, search still finds things — and every document is blank.

That is a consequence of a deliberate decision: a missing article file reads as an empty document rather than throwing, because an article has a row before it has a file. The decision is still right. It needed a counterweight, and this is it.

Taking one

php artisan felagi:backup                 # manifest + both file stores + mysqldump
php artisan felagi:backup --manifest      # tell me what to copy, copy nothing
php artisan felagi:backup --to=/mnt/backups

This is not a backup product, on purpose. A tool that appears to take a complete backup and quietly takes most of one is worse than no tool, because the operator stops thinking about it and finds out during the restore.

So it does the two things it can do honestly:

  • A manifest — row counts, file counts and byte totals, written as JSON. That is the useful half: "the restore finished" and "the restore is complete" are different sentences, and a number is what separates them.
  • The file stores, archived. They are safe to copy while the application runs. Archiving is relative (tar -C), because an archive of absolute paths restores onto the machine that took it and nowhere else. A failed archive is deleted rather than left — half an archive is worse than none, because it looks like one.

The database it dumps only if mysqldump is on the path, and says plainly when it is not rather than producing an archive with a hole in it. The password goes through the environment, never the argument list, where every other process on the machine can read it.

If anything referenced by the database is already missing, the command warns before it writes anything. Backing up a broken state without saying so preserves the breakage and hides it in the same move.

Restoring

  1. Restore the database.
  2. Unpack both archives to the roots the manifest records.
  3. Run the check:
php artisan felagi:check

It exits non-zero when anything is missing, so a restore script can stop instead of reporting success. Compare its output with the manifest's counts.

When something is missing

felagi:check separates the two cases, because they are not the same:

Articles The words survive. search_text is a plain-text copy written at the same moment as the file, so the text can be recovered — see below
Revisions and attachments No second copy exists anywhere. If they are not in a backup, they are gone

An article whose file is missing says so on its own page, with what the search index still holds, and offers Recover the text.

That recovery is lossy and the confirmation says so: headings, lists, links and tables are gone, because the search index never held them. It is the difference between an empty page and a readable one, and not a substitute for restoring the file. It is behind a confirmation for that reason — somebody who does it without reading will believe the article was restored.

Nothing recovers automatically. Silently replacing a document with a flattened version of itself would hide the fact that something was lost, which is the failure this whole path exists to stop.

The nightly check

felagi:check --quiet-when-clean runs at 01:00 and says nothing unless something is wrong, because a nightly command that prints on success trains everybody to ignore its output. It writes a warning to the log as well as the terminal: nothing watches a scheduled run, and a problem that only appears on a terminal somebody closed is a problem nobody has.

Not there yet

  • No scheduled backups. felagi:backup is a command, not a policy. Retention, rotation and offsite copies belong to whatever already does that on your machines.
  • No restore command. Unpacking two archives and loading a dump is three lines an operator can read; a tool that did it would be a tool to trust with the one operation where trust is least affordable.
  • MySQL only for the dump. Any other connection is reported and left to you.
  • No consistency snapshot across all three. The dump uses --single-transaction, so the database is self-consistent, but a file written between the dump and the archive lands in one and not the other. For a knowledge base that means an article saved during the backup, which the check will report.