Operations and recovery
The server stores scan and job progress in SQLite, while R2 holds optimized sources, original backups, and restore manifests. Keep the SQLite database on a local persistent volume and retain an off-host snapshot. Backups are retained by default. A job with opt-in backup cleanup deletes each verified backup and its manifest after the optimized source is verified.
Take a consistent SQLite snapshot
Run this from the repository with the app's environment available. Choose a new filename each time:
node --env-file=.env scripts/snapshot-db.mjs --output .data/snapshots/optimizer-YYYY-MM-DD.sqliteOn a Docker or Coolify host, run the same script inside the app container so it uses the mounted /app/data database:
docker compose exec bucketlean node scripts/snapshot-db.mjs --output /app/data/snapshots/optimizer-YYYY-MM-DD.sqliteThe command uses SQLite's online backup API, refuses to overwrite an existing snapshot, verifies integrity, and prints all registered bucket profiles and job/item counts. Copy the snapshot outside the app host. Do not rely on copying only the live .sqlite file while WAL is active.
Backfill manifests for older backups
Select the bucket profile whose backups you want to check. The first command reads only that profile's completed job backups that were retained and verifies sizes and SHA-256 hashes; the second writes and verifies missing manifests in that bucket.
node --env-file=.env scripts/backfill-manifests.mjs --bucket-id photos
node --env-file=.env scripts/backfill-manifests.mjs --bucket-id photos --apply--bucket-id is optional when exactly one bucket is configured. The script compares the selected profile with SQLite and does not modify job rows. The legacy single-bucket database cannot be used with this schema; start with a fresh database as described in Getting started.
Prove that a backup restores
The restore drill reads a manifest and its backup from the selected bucket without using SQLite. It requires a retained backup; jobs using opt-in cleanup remove their backup and manifest after successful verification. By default it chooses the first manifest; --manifest-key KEY selects a specific one.
node --env-file=.env scripts/restore-drill.mjs --bucket-id photos
node --env-file=.env scripts/restore-drill.mjs --bucket-id photos --applyThe dry run verifies the backup hash. --apply writes the original bytes and headers to a new key under __optimizer/restore-drills/, reads the copy back, and verifies that the source key was untouched. It leaves the drill copy for inspection. A whole-bucket scan can discover that copy as a JPEG, so remove it when no longer needed or use a narrow optimization prefix.
For backups retained by default, do not enable a lifecycle expiration rule for __optimizer/originals/ until you have a verified SQLite snapshot, readable manifests, and a successful restore drill for the bucket. Manifests live under a separate __optimizer/manifests/ prefix.
Paused and uncertain jobs
A credential or bucket-wide failure pauses the job. Fix access or configuration, then use POST /api/jobs/<id>/resume. If a backup or replacement result cannot be established, the item becomes needs_attention; inspect its hashes and object keys in GET /api/jobs/<id>, then use POST /api/jobs/<id>/reconcile to recheck the source and backup. See Job endpoints for request details.
Build and publish this documentation
The docs are a separate VitePress site and require no R2 credentials:
npm ci
npm run docs:dev
npm run docs:build
npm run docs:preview
npm run docs:checkdocs:dev starts at http://localhost:5173/ by default. docs:preview serves the built site at http://localhost:4173/. docs:check verifies the static pages and assets, then starts a temporary dev server to check Mermaid's browser dependencies.
The docs have their own docs/tsconfig.json, so these commands work in a fresh checkout after npm ci. They do not require Nuxt's generated .nuxt/tsconfig.json or an R2 environment file.
docs:build writes static files to docs/.vitepress/dist. Publish the contents of that directory to a static host. For a root domain, build with the default / base. For a repository or other subpath, build with a matching base, for example:
DOCS_BASE=/BucketLean/ npm run docs:buildFor other static hosts, publish docs/.vitepress/dist and serve directory indexes such as api/index.html. The docs site is public content; it contains examples and no runtime access to your bucket or app password. See the VitePress deployment guide for host-specific settings.
GitHub Pages for this repository
The Pages workflow builds with DOCS_BASE=/BucketLean/, verifies the generated pages, and deploys docs/.vitepress/dist on pushes to main or by manual dispatch. In the repository's Settings → Pages → Build and deployment, set Source to GitHub Actions. Then push the workflow to main or run Deploy documentation from the Actions tab. The site will be served at https://ahmetomerv.github.io/BucketLean/ after the deployment succeeds.
Do not select Deploy from a branch → main → /docs for this VitePress site. That setting publishes the Markdown source in /docs and does not run npm run docs:build.