docs/Features/ImportExport/Format-Coverage.md
Status: Implementation in progress · Last specification audit: 2026-09-05
This document is the contract for every format shown in WeKan's Import or Export menus. "Import all possible data" means every source field with an equivalent in WeKan is mapped, relationships are resolved after their objects exist, binary content is streamed, and unsupported source data is reported rather than silently dropped. It cannot mean inventing a destination feature that does not exist.
Long operations follow the durable operations contract: persisted checkpoints, idempotent units, expiring leases, restart reclaim, bounded external requests and rate-limit-aware retries.
wekan-board-1.0.0 JSON is the lossless canonical board format. Its ZIP form
contains the same JSON plus attachment bytes and a manifest. The schema version
is explicit; readers accept older additive documents, reject unknown incompatible
major formats, preserve IDs only as source references, and validate every object,
array, date, URL, filename and size before writing.
The canonical field inventory is board metadata and settings; swimlanes and lists with order, archive and color state; cards with text, order, archive, dates, people, labels, custom fields, votes, poker, locations and dependencies; checklists/items; subtasks/linked cards; comments and activities; rules; and attachments with metadata and bytes. Positive round-trip tests compare this inventory, while negative tests prove unknown executable input and unsafe paths, URLs, formulas and markup are refused or neutralized.
The audit uses provider documentation and, for open-source formats, their current code/API schema rather than old sample files:
| Format | Current authoritative shape | Required import coverage |
|---|---|---|
| Trello | Boards API, Cards API, and automated exports | Board preferences, lists, cards, archive/order, members, labels, dates, checklists/items, comments/actions, custom fields, stickers, coordinates, covers/backgrounds and attachment metadata/bytes |
| Jira Cloud | REST API v3 issue search and per-project/type schemas | ADF or string descriptions, status, type, priority, reporter/assignee, labels, components, versions, sprint/epic/parent links, subtasks, dates, estimates, comments, attachments and schema-described custom fields |
| Kanboard | JSON-RPC API and current project export | Project, columns, swimlanes, tasks, order/color/category, assignee/creator, dates/time estimates, subtasks, links, comments, tags, metadata and files |
| Nextcloud Deck | Current Deck server API/code objects | Board, ACL, labels, stacks, cards, order/type, assignees, dates, comments and attachments |
| OpenProject | API v3 HAL+JSON, work-package collection and embedded per-project/type schemas | HAL links, status/type/priority, assignee/responsible, dates/duration, hierarchy/relations, watchers, comments, attachments and schema-described custom fields |
| GitHub | Versioned Issues REST API, comments, events and attachments referenced from Markdown | Issues excluding pull requests, open/closed state, state reason, labels with colors, assignees, reporter, milestone, dates, comments, relationships and URLs; pagination must be completed by API clients |
| GitLab | Current Issues API v4 | State, labels with details, assignees, author, milestone, iteration, weight, due date, time stats, task completion, links, discussions and attachments |
| Gitea / Forgejo | Current issue API and release schema of the selected server | GitHub-like fields plus milestone, deadline, assignees, comments and server-specific labels/state metadata |
| Asana | Current Tasks API with opt-in fields | Sections/memberships, completion, assignee/followers, start/due dates, dependencies, subtasks, tags, stories, attachments and all supported custom-field value kinds |
| Zenkit | Current documented export accepted by the selected Zenkit product | Collections/lists, stages, items, hierarchy, members, dates, labels and exported custom fields; retain unknown fields in the loss report because products differ |
| iCalendar | RFC 5545 plus RFC 7986 additive properties | Unfolded/escaped UTF-8 content lines, UID identity, recurrence, exclusions, timezone, start/end/duration, status, summary, description, URL, attendees and categories |
| CSV / TSV | RFC 4180 CSV and tab-delimited UTF-8 with a header row | Quoted separators/newlines/quotes, BOM, CRLF/LF, locale-independent ISO dates and every documented WeKan column/custom field |
| XLSX | ECMA-376 workbook data consumed through the maintained ExcelJS fork | Multiple worksheets when documented, typed cells/dates, formulas as displayed values, custom-field columns and size/row/column bounds |
| PDF / HTML / SVG | Export-only rendered views | Every selected visible section, Unicode, safe links/images, pagination and deterministic filenames; these are presentations, not lossless re-import formats |
Every parser returns { normalized, warnings, unsupported }. unsupported
contains bounded JSON-pointer-like paths and reasons, never secret values. The
import result and Problems → Recovery show counts and paths. A success with
unsupported fields is completed-with-warnings, not silently completed.
External export follows that provider's creation/request schema, not a copied
response object full of read-only fields. When a provider has no equivalent for
a WeKan field, the formatter emits a documented x-wekan extension block where
JSON permits it and reports the field in _wekan.losses. Consumers may ignore
extensions; a later WeKan import uses them to recover a lossless round trip.
Parsers accept documented additive fields and both current and known legacy spellings. They never infer that the first API page is the complete export: live connectors follow provider pagination links/tokens under the shared rate limiter. Uploaded JSON must already contain the desired pages, and the UI says so. Input depth, objects, rows, strings, decoded bytes and compression ratios are bounded before expensive work.
Dates retain timezone/offset semantics. Rich text retains a safe plain/Markdown form and, where needed for round-trip, its bounded structured source. Usernames are source identities until explicitly mapped; imports never grant board access merely because a source file names a user.
All upload, DDP and REST imports pass through one shared boundary before a
format parser or creator sees them. It walks own enumerable data only, rejects
prototypes, accessors, cycles and dangerous keys such as __proto__, bounds
depth, nodes, arrays, strings and binary declarations, validates finite numbers
and dates, and returns a new null-prototype-safe value. Format adapters may
tighten this schema but may not bypass it.
Text is classified by destination. Titles, names, identifiers and plain text remain text. Permitted rich HTML is sanitized with WeKan's existing DOMPurify configuration and an explicit tag/attribute allowlist; scripts, event handlers, active SVG/MathML, CSS, forms, embeds and unsafe protocols are removed. Markdown is stored as Markdown and rendered through the existing sanitized renderer, not pre-rendered as trusted HTML. URLs use the shared scheme and SSRF validators; file paths and names use the existing traversal and filename guards.
Formula-capable exports neutralize cells beginning with =, +, -, @, tab,
carriage return or line feed when user text is written to CSV, TSV or XLSX. JSON
serialization drops prototype keys and never serializes credentials, filesystem
paths, login tokens or server-only metadata. HTML/PDF/SVG output goes through
the same safe rich-text and URL decisions before rendering.
Every exporter passes its finished value through the shared outbound validator. This catches non-finite numbers, invalid dates, unsafe URLs, accidental secrets, cycles and adapter mistakes even when the stored database row predates current import validation. Sanitization returns warnings with bounded field paths for Problems → Security/Recovery; raw rejected values and secrets are never logged.
Each format has fixtures from its newest documented shape plus legacy fixtures. Tests cover every mapped field, missing optional fields, unknown additive fields, malformed types, duplicate source IDs, reordered arrays, pagination, restart at every checkpoint and export→import round trips. A field-inventory test fails when a new canonical WeKan field is exported but not imported, or when documentation claims a mapping absent from code.