# Pond archive format (issue #305) **Format version 1.** The archive a pond admin downloads before deleting a pond, and the one a Site Admin downloads before purging one. It is a **preservation format, not a backup**: Dorfteich has no importer for it, deliberately. What this document buys is that one can be written later without guesswork. Do not confuse it with two neighbours: | | contains | purpose | | ------------------------------------------------------- | ---------------------------------------------------------------------------- | --------------------------- | | Markdown export (`GET /ponds/:id/export/markdown`, #65) | readable pages + the images they embed | everyday "give me my text" | | **Pond archive** (this document) | readable pages + **all** attachments + settings, labels, comments, hierarchy | last resort before deletion | | Restore set (ADR 0015) | the whole instance, database and data directories | operational recovery | ## Layout ``` README.txt plain-text version of this warning, for whoever unpacks it manifest.json everything Markdown cannot carry (see below) pages/.md one file per page, Markdown, wikilinks rewritten to relative links, media references rewritten to media/… media/. EVERY attachment of the pond — including ones no page embeds, which is the whole point of this archive media/..classification.txt companion marking for a classified attachment (#212): the binary cannot carry it, and this file survives copying ``` ## `manifest.json` ```jsonc { "kind": "dorfteich-pond-archive", "formatVersion": 1, "exportedAt": "2026-08-01T18:00:00.000Z", // False when the exporter could not read every page. Stated in the archive // itself so a later reader is never misled about what they hold. "complete": true, "omittedPages": 0, // Highest classification contained (ADR 0022), stated once. "classification": "unclassified", "pond": { "name": "…", "slug": "…", "type": "SHARED", "createdAt": "…", "settings": { … } }, "labels": [{ "id": "…", "name": "…", "color": "…", "parentId": null }], "pages": [ { "id": "…", "slug": "…", "title": "…", "parentId": null, // the hierarchy Markdown cannot express "sortKey": "…", // sibling order (ADR 0012's fractional key) "classification": "unclassified", "labelIds": ["…"], "createdAt": "…", "updatedAt": "…", "file": "pages/.md" } ], "comments": [ { "id": "…", "pageId": "…", "parentId": null, "body": "…", "author": "Display Name", // NOT the account id — see below "createdAt": "…", "editedAt": null, "resolvedAt": null } ], "attachments": [ { "id": "…", "pageId": "…" | null, "fileName": "…", "mimeType": "…", "sizeBytes": 1234, "sha256": "…", // #199, so a reader can verify the bytes "createdAt": "…", "file": "media/." } ], "files": [{ "path": "…", "classification": "unclassified" }] } ``` ## Decisions a reader should know about - **`formatVersion` is a contract.** A reader that does not recognise the version should refuse rather than guess. Additive fields do not bump it; a change in meaning does. - **Comments name a display name, not an account.** The archive is a document that outlives the instance; an account id would be an unresolvable reference the moment the account is gone. - **`files` is the #210 property, kept.** Every file with its level, so the bulk-egress channel stays machine-checkable after the ZIP is unpacked and copied onward. - **An attachment with no page inherits the pond's highest classification.** Nothing narrower governs it, and fail-closed is the rule (ADR 0022). - **Incomplete archives are labelled, not refused.** A pond admin who cannot read every page still gets what they may read — with `complete: false` and the count of what is missing, in the manifest and in the UI before the download. ## Read trail The archive is a bulk-egress channel. One `export` read event is written per classified page **before any classified byte enters the stream** (ADR 0023), so a failed write aborts the download with the evidence intact. Attachments never travel without their page, so they are covered by the same events. The download itself is audited as `pond.archived` (catalogue v1.7) with the page and attachment counts, the number of omitted pages, and whether the archive was complete. ## What is NOT in it - Page history and Yjs update logs. The Markdown is the current state. - Permissions and memberships: they name accounts of _this_ instance, which an archive read elsewhere cannot resolve. - Anything from the trash: trashed pages are not exported. An importer will therefore recreate a pond's content, structure and discussion — not its history or its access rules. That is a deliberate scope, not an oversight.