Backup creates nothing and restores nothing #114

Closed
opened 2026-08-13 22:59:44 +02:00 by Claude · 0 comments
Collaborator

/Backup, /Backup/Manifest, /Backup/Create and /Backup/Restore are all answered. The two reads list what is on disk; both writes refuse — which is what ENDPOINTS.md records and is the honest state, not a defect on its own.

It becomes one the moment this is somebody's actual server: the dashboard offers a backup button that says no, and there is no supported way to take a consistent copy of a database that is being written to.

Upstream's backup writes an archive of the database, the configuration and the metadata directory, and its restore replaces them and restarts. Ours would need the same, plus one thing upstream does not have to think about in the same way — the file is opened with WAL and mapped, so a copy has to be taken through SQLite's backup API rather than by copying bytes.

Scope note: restore is the risky half and can lag. A create that produces an archive upstream's own restore can read would already be worth having.


Where to start

crates/jellyfin-http/src/routes/admin/backup.rs — the two reads answer and both
writes refuse, which ENDPOINTS.md records honestly and is not a defect on its
own. crates/jellyfin-harness/scenarios/backup.json already asks for backups
neither server has; read the section about it in the harness README before
changing what those routes do, because the scenario's meaning changes with them.

Upstream is Jellyfin.Server.Implementations/FullSystemBackup/BackupService.cs,
with tests at tests/Jellyfin.Server.Implementations.Tests/FullSystemBackup/.
It archives the database, the configuration and the metadata directory, and its
restore replaces them and restarts.

One thing that is ours alone: the database is opened WAL and mapped, so a copy
has to go through SQLite's own backup API rather than by copying bytes off a
live file.

Scope note: restore is the risky half and may lag. A create that produces an
archive upstream's own restore can read
is already worth having, and is the
better first commit.

How to know it is done

An archive written here is listed and described identically by both servers
(/Backup and /Backup/Manifest diff clean), and upstream restores it. If
restore lands too, a round trip through ours leaves a database diff-db says is
the same one.

House rules

CLAUDE.md is the short version and these are the parts that bite: tests run
under cargo nextest; upstream's source is the checkout in upstream/ at the
pin
(v12.0-rc5, 6d501ba4) and not the clone beside the repository;
generated files are regenerated rather than edited; a comment says what the code
does, not how it got there; and Dn references stay out of code and comments.
ARCHITECTURE.md decides which crate a thing goes in and which dependency edges
are illegal, DECISIONS.md says why a rule exists.

The reference pair is cargo xtask reference up (and reference provision,
which is idempotent); a run is
cargo run -p jellyfin-harness -- replay --upstream URL --ours URL --scenario FILE --out DIR,
with diff-db for the two databases. crates/jellyfin-harness/README.md says
what a diff means and what an ignore entry has to carry — every entry needs a
reason and an expiry milestone, and an adds-only history is the smell the risk
table names.

`/Backup`, `/Backup/Manifest`, `/Backup/Create` and `/Backup/Restore` are all answered. The two reads list what is on disk; **both writes refuse** — which is what `ENDPOINTS.md` records and is the honest state, not a defect on its own. It becomes one the moment this is somebody's actual server: the dashboard offers a backup button that says no, and there is no supported way to take a consistent copy of a database that is being written to. Upstream's backup writes an archive of the database, the configuration and the metadata directory, and its restore replaces them and restarts. Ours would need the same, plus one thing upstream does not have to think about in the same way — the file is opened with WAL and mapped, so a copy has to be taken through SQLite's backup API rather than by copying bytes. Scope note: restore is the risky half and can lag. A create that produces an archive upstream's own restore can read would already be worth having. --- ## Where to start `crates/jellyfin-http/src/routes/admin/backup.rs` — the two reads answer and both writes refuse, which `ENDPOINTS.md` records honestly and is not a defect on its own. `crates/jellyfin-harness/scenarios/backup.json` already asks for backups neither server has; read the section about it in the harness README before changing what those routes do, because the scenario's meaning changes with them. Upstream is `Jellyfin.Server.Implementations/FullSystemBackup/BackupService.cs`, with tests at `tests/Jellyfin.Server.Implementations.Tests/FullSystemBackup/`. It archives the database, the configuration and the metadata directory, and its restore replaces them and restarts. One thing that is ours alone: the database is opened WAL and mapped, so a copy has to go through SQLite's own backup API rather than by copying bytes off a live file. Scope note: restore is the risky half and may lag. **A create that produces an archive upstream's own restore can read** is already worth having, and is the better first commit. ## How to know it is done An archive written here is listed and described identically by both servers (`/Backup` and `/Backup/Manifest` diff clean), and upstream restores it. If restore lands too, a round trip through ours leaves a database `diff-db` says is the same one. ### House rules `CLAUDE.md` is the short version and these are the parts that bite: tests run under `cargo nextest`; upstream's source is the checkout in `upstream/` **at the pin** (`v12.0-rc5`, `6d501ba4`) and not the clone beside the repository; generated files are regenerated rather than edited; a comment says what the code does, not how it got there; and `Dn` references stay out of code and comments. `ARCHITECTURE.md` decides which crate a thing goes in and which dependency edges are illegal, `DECISIONS.md` says why a rule exists. The reference pair is `cargo xtask reference up` (and `reference provision`, which is idempotent); a run is `cargo run -p jellyfin-harness -- replay --upstream URL --ours URL --scenario FILE --out DIR`, with `diff-db` for the two databases. `crates/jellyfin-harness/README.md` says what a diff means and what an ignore entry has to carry — every entry needs a reason and an expiry milestone, and an adds-only history is the smell the risk table names.
alex closed this issue 2026-08-16 06:51:58 +02:00
Sign in to join this conversation.
No description provided.