forked / Architecture

ADR-032: Schema.org Recipe Export and forked Backup

Status: Accepted Date: 2026-10-16

Context

forked promises open data ownership without vendor lock-in and export without a hosted dependency (Section 3.4). Browser storage eviction can wipe local-only data (Section 11).

Other recipe managers do not share a native format, but Mealie, Tandoor, and Nextcloud Cookbook all import a Nextcloud Cookbook zip: one folder per recipe with a Schema.org recipe.json. Schema.org cannot hold forked-only data: shopping lists, ingredient categories (grocery aisles), planned dates, the “usually at home” flag, and which ingredients a step uses.

Decision

  1. Settings > Export & backup offers two downloads, created fully on-device:
    • Recipe export for other apps: a Nextcloud Cookbook style zip of Schema.org Recipe files (<slug>/recipe.json). Fields: name, recipeYield as "<n> servings", ISO 8601 prepTime/cookTime/totalTime, recipeIngredient as "<amount> <ingredient>" lines, recipeInstructions as HowToSteps, and dateCreated/dateModified.
    • forked backup: a versioned JSON file (format: "forked-backup", version: 1) holding every persisted document without revision metadata.
  2. Restoring a backup merges: documents with the same ID are replaced, all others are kept. Settings documents (device preferences) are not restored. With sync enabled, restored documents replicate like any other local write.
  3. The zip is written with fflate, the only dependency added for this.

Consequences

  • Recipes move to Mealie (verified with v3.28 through its Nextcloud Cookbook migration), Tandoor, and Nextcloud Cookbook without a server or account.
  • Local-only users get a lossless recovery path against storage eviction; no data leaves the device and both work offline.
  • The recipe export is lossy: forked-only data is left out.
  • The backup format is a contract. Changes to persisted document types must keep older backups readable or bump the backup version.
  • Merge restore can produce two ingredients with the same name when the backup and the device created them independently.

View source on GitHub