Skip to content

v0.144.0 — .cat archives M2 and M3: a backup that loses nothing, and import

Two milestones of the .cat archives program, approved at their gates on 2026-10-30 (M2, continuing) and 2026-11-02 (M3) and released together. M2 makes a campaign archive a backup that loses nothing; M3 brings it back. The tasks, decisions and test scripts are in M2's and M3's milestone files.

Everything a campaign owns (M2)

  • [x] A backup that loses nothing. Drawings with their fill textures, walls (VBL) and fog, map layers, Objects with their parts and the campaign's object types, a generated map's generation record, running timed effects; and every definition a campaign owns: conditions, prop types, attribute groups, property definitions, bars, light source and vision types, damage types, currencies, functions, actions, roll tables, factions, parties, role definitions, art pools, the Treasury's settings, and the wiki with its images.
  • [x] Rows refer to each other. Every row keeps its id in the archive, so a token names its character and a part its parent Object; the importer mints new ids.
  • [x] Every table accounted for. A guard test lists every table a campaign or map owns and requires each to be exported or excluded on purpose (people, chat and history stay behind), so the next table can't be forgotten.

Import (M3)

  • [x] A .cat goes where it belongs. Drop one on the new Import page, open it from Files or share it to the installed app: a character archive goes to character import, a map archive into a campaign you can edit, a campaign archive becomes a new campaign of your own. The archive's kind decides.
  • [x] Campaign and map import. Everything a .cat holds comes back: maps, tokens, Objects, drawings, walls, fog, layers, generated maps, characters (yours, linked), the Treasury, the campaign's definitions, roll tables, factions, parties, roles, art pools and the wiki. Every file is re-uploaded to your storage, every id minted fresh, slugs de-duplicated; a missing or older system module warns and imports as-is. The importer never fetches anything from the web.
  • [x] The black-hole round trip proven. Export, destroy the source's storage, import, play: a route test does it on every build.
  • [x] The old map ZIP retired. Maps export as .cat; the old ZIP import stays one more release with a warning.
  • [x] The help page tells the truth about importing.

For self-hosters and developers

  • A campaign archive keeps every row's id; the importer always mints new ones and remaps every reference (parentId, characterId, objectTypeId, layers, Treasury holders), so the same archive imported twice makes two of everything.
  • Import is guarded: zip entry count and inflated size limits, path checks on every entry, images re-decoded and typed by their bytes, wiki HTML through the wiki's own sanitizer, token actions stored as data and run only in the Action API sandbox. No URL in an archive is ever fetched.
  • js-yaml is now a runtime dependency of packages/server-core (it already was one of the web app).
  • POST /api/maps/import (the old map ZIP) still answers this release, with a deprecation warning in the UI; it is removed in the next .cat release.