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
- Restore the database.
- Unpack both archives to the roots the manifest records.
- 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:backupis 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.