#Why this exists
It exists for every system ATDT will never parse natively: Commodore 64 boards, Apple II boards, Atari boards, homebrew software, one-off databases. If you can still read your old system's disks, you can get its messages onto a modern board.
This document is the contract. Version it against the version field; an ATDT
release that does not know a version refuses it rather than guessing.
#The container
One .zip file holding a manifest and one file per message area:
Area files must live under areas/ and must not escape it. The zip is uploaded on
the console's Import page, or unpacked into data/import/ on the server for large
archives.
#manifest.json
| Key | Required · meaning |
|---|---|
| format | Yes. The literal string atdt-import. Anything else is
rejected with a clear message. |
| version | Yes. Integer. 1 for this document. Unknown versions
are rejected, never guessed at. |
| source | No. Free text shown in the preview header and stored on the import batch. |
| encoding | No. One of cp437, utf-8, latin1,
ascii, petscii. Defaults to cp437. The SysOp can
override it on the preview screen. |
| timezone | No. An IANA zone name such as America/New_York, or
UTC. Defaults to the board timezone. Overridable on the preview
screen. |
| areas[].file | Yes. Path inside the zip. Must be under
areas/. |
| areas[].name | Yes. The source area's display name. |
| areas[].description | No. The source area's description. |
| areas[].tag | No. The source tag. Used only to prefill the ATDT tag suggestion on the mapping screen. |
| areas[].echo_tag | No. A FidoNet echo tag, stored verbatim on the created area for later use. |
#Message records
One JSON object per line, newline delimited. Not a JSON array: a forty year archive will not fit in memory on shared hosting, and a malformed record should cost one message rather than the whole import.
| Key | Required · meaning |
|---|---|
| id | Yes. Unique within this file. Its only job is being the target of
reply_to. Any string. |
| date | Yes. ISO 8601: YYYY-MM-DDTHH:MM:SS, or
YYYY-MM-DD HH:MM:SS, or YYYY-MM-DD (time becomes 00:00:00).
Interpreted in the manifest timezone unless it carries an explicit offset. |
| from | Yes. The author's handle, as text. Imported authors are never matched to ATDT accounts. |
| subject | Yes. May be an empty string; an empty subject becomes
(no subject). |
| body | Yes. Line breaks as \n. May be an empty
string. |
| to | No. The recipient handle. Omit it, or use ALL, for a public
post. |
| reply_to | No. The id of the parent message, within the same file.
Null or absent means a thread root. |
#Rules
reply_tomay only reference anidwithin the same file. Cross-area threading is not supported; a cross-file reference is treated as absent and the message becomes a thread root.- A
reply_topointing at anidthat does not exist in the file is treated as absent and the message becomes a root. Counted and reported on the preview screen, never a failure. - Records need not be in date order. ATDT sorts before writing, so message numbers follow chronology.
- An unparseable line is skipped, counted, and reported on the preview screen. At or above 5 percent unparseable lines the parse fails outright, because that means the file is not what it claims to be.
- Reply cycles (A parents B parents A) are detected and broken by making the offending message a root.
#Encodings other than utf-8
JSON strings are UTF-8 by definition, so when the manifest declares cp437,
latin1, ascii or petscii, each string carries the
original bytes as codepoints U+0000 through U+00FF: an exporter writes source byte 0xA4
as the single character U+00A4. ATDT decodes those codepoints with the declared encoding.
Strings under utf-8 pass through as ordinary text.
Either way, characters with no CP437 equivalent become question marks, counted and reported, since ATDT boards render on an 80 by 25 CP437 screen.
#What ATDT does with it
The SysOp maps each area file onto a new or an existing ATDT area on the mapping screen; nothing is imported until they confirm behind a backup checkbox. Messages arrive with the author and recipient stored as text, unattached to any caller account, stamped with an import batch id so the whole run can be undone from Import History. Existing callers' last-read pointers move past the archive so nobody logs into two hundred thousand unread messages.
The full walkthrough from the SysOp's side is chapter 22 of the manual.