Files
fpc-msgbase/docs/attributes-registry.md
Ken Johnson 6b225fedfc 0.4.0: PKT moves to fpc-ftn-transport (breaking change)
Removes all PKT code from fpc-msgbase. The wire format and its
container concerns now live in the sibling fpc-ftn-transport
library (units tt.pkt.format, tt.pkt.reader, tt.pkt.writer,
tt.pkt.batch).  Pair this commit with fpc-ftn-transport's
0.2.0 (commit 6bb71a6).

Why: the previous "reader here, writer there" split (briefly
landed in 0.3.5) baked in a coupling that didn't survive a
fresh look. The writer reached into fpc-msgbase for types,
the wire format lived in the wrong house, and consumers reading
fpc-msgbase saw "PKT support" that was actually only half-
support. Cleanest split: PKT is a wire format, both directions
belong with the wire-format-aware library; fpc-msgbase becomes
purely real message bases (Hudson / JAM / Squish / MSG /
PCBoard / EzyCom / GoldBase / Wildcat).

Also a cleaner separation-of-concerns story: a BBS that just
reads JAM/Squish never needs fpc-ftn-transport. A pure store-
and-forward node doing only ArcMail unbundle never depends on
storage formats. Each library = one concern.

Removed:
  src/formats/ma.fmt.pkt.pas       -> tt.pkt.format
  src/formats/ma.fmt.pkt.uni.pas   -> tt.pkt.reader
                                      (TPktMessageBase -> TPktReader)
  src/ma.batch.pas                 -> tt.pkt.batch
                                      (TPacketBatch class name unchanged)
  tests/test_batch.pas             -> tests/test_pkt_writer.pas
                                      (consolidated PKT tests)
  examples/example_tosser.pas      -> moves with the batch helper

Reduced in src/ma.types.pas:
  - PacketRecord
  - FlavourType / FlavourTypeSet / DateTimeArray
  - FlagsToFido / FidoToFlags
  - VersionNum (PKT-product-code stamping)
  All moved to tt.pkt.format.

Kept in src/ma.types.pas:
  - mbfPkt enum value (so tt.pkt.reader can register the backend
    with the unified-API factory; consumers still use the
    standard MessageBaseOpen(mbfPkt, ...) shape)

Migration for vendoring consumers:

  before:                      after:
    uses ma.fmt.pkt;             uses tt.pkt.format;
    uses ma.fmt.pkt.uni;         uses tt.pkt.reader;
    uses ma.batch;               uses tt.pkt.batch;
    (no writer surface)          uses tt.pkt.writer;

    TPktMessageBase              TPktReader
    TPktFile, TPktMessage,       (unchanged class names)
      TPktHeaderInfo, etc.
    TPacketBatch                 (unchanged)

Docs sweep:
  - README: PKT row called out as "moved to fpc-ftn-transport";
    TPacketBatch removed from features.
  - docs/architecture.md: layer diagram drops PKT + ma.batch;
    new sibling-library box added for fpc-ftn-transport.
  - docs/attributes-registry.md: PKT column dropped from per-
    format support matrix; pointer to fpc-ftn-transport.
  - docs/API.md: PKT cheat-sheet entry redirects to
    fpc-ftn-transport; TPacketBatch section reduced to a
    "moved" pointer with the new uses-clause shape.
  - docs/ftsc-compliance.md: Type-2 / 2+ / 2.2 / AuxNet rows
    annotated as living in tt.pkt.format.

Suite: 47/47 across 9 programs (was 9 with test_batch; now 9
with the PKT bits dropped from test_consumer_round1 and
test_hwm).  All other tests untouched.
2026-04-18 11:32:42 -07:00

10 KiB

Attribute registry

This document is the source of truth for attribute key names used across all fpc-msgbase backends.

TUniMessage has only two areas:

TUniMessage = record
  Body:       AnsiString;       { only the message text }
  Attributes: TMsgAttributes;   { everything else, key/value }
end;

Every header field, kludge, control line, and per-format flag a backend understands lives in Attributes under one of the keys below. Backends drop keys they don't recognise on Write (RFC 822 X-header semantics). Callers can query base.SupportsAttribute(K) before setting a key to know up-front which backends carry it.

Naming convention: lowercase, dot-namespaced. Universal / FTSC-defined keys are unqualified (from, msgid). Format- specific keys are prefixed with the format name (jam.msgidcrc, squish.umsgid, pcb.confnum).

Tier 1 — Universal headers

Every Fido format sets these on Read; every backend reads them on Write. Equivalent to the always-present headers in any classic BBS message.

Key Type Meaning
msg.num int Backend-assigned message number / index
from string Author name
to string Recipient name
subject string Message subject
date.written date When author wrote the message
date.received date When local system received
addr.orig ftn Originating FTN address (zone:net/node[.point])
addr.dest ftn Destination FTN address
area string Echo area tag (echomail)
board int Conference / board number for multi-board formats
cost int Cost in cents (FTS-1 backends)

Tier 2 — Canonical attribute bits

Boolean flags derived from the FTS-1 attribute word, plus extensions some backends carry as separate bits. Always written via SetBool(K, true); absent (or false) when not set.

Key FTS-1 Meaning
attr.private 0x0001 Private message
attr.crash 0x0002 Crash priority
attr.received 0x0004 Recipient has read
attr.sent 0x0008 Sent to destination
attr.fileattach 0x0010 File attached
attr.intransit 0x0020 In transit
attr.orphan 0x0040 No matching destination
attr.killsent 0x0080 Kill after sending
attr.local 0x0100 Originated locally
attr.hold 0x0200 Hold for pickup
attr.filereq 0x0400 File request
attr.returnreceipt 0x0800 Return receipt requested
attr.isreceipt 0x1000 This message is a receipt
attr.auditreq 0x2000 Audit trail requested
attr.fileupdreq 0x4000 File update request
attr.deleted Tombstoned (per-base)
attr.read Marked-read (per-user)
attr.echo Echomail (vs netmail)
attr.direct Direct flavour
attr.immediate Immediate flavour
attr.locked Locked (no edit)
attr.netpending Pending netmail toss
attr.echopending Pending echomail toss
attr.nokill Protected from purge

Tier 3 — FTSC kludges

Standard FTSC kludge lines, named after the kludge name without the leading ^A. Multi-line attributes (SEEN-BY, PATH, Via) join their lines with #13.

Key Type Spec Meaning
msgid string FTS-9 Globally unique message ID
replyid string FTS-9 Reply linkage to a previous MSGID
pid string FRL-1004 Producer ID (creating tosser/editor)
tid string FSC-46 Tosser ID
flags string FRL-1005 Routing/handling flags
chrs string FTS-5003 Character set declaration
tzutc string FTS-4001 Time-zone offset from UTC
seen-by multi-string FTS-4 SEEN-BY lines (one node-list per line)
path multi-string FTS-4 PATH lines (one node-list per line)
via multi-string FTS-4009 Via lines (one per relay)
intl string FSC-4008 INTL kludge: <dest-zone:net/node> <orig-zone:net/node> for cross-zone netmail
fmpt string FSC-4008 FMPT (origin point number)
topt string FSC-4008 TOPT (destination point number)

Unknown FTSC kludges

Any ^A<NAME>: <value> line whose <NAME> is not in the table above is preserved as kludge.<lowername> regardless of which backend stored it. Example: ^aXFOO: barkludge.xfoo = 'bar'.

This is the universal forward-compat slot for FTSC-form kludges the library doesn't recognize natively. All four kludge-aware backends (JAM, Squish, MSG, PKT) use the same kludge.* namespace so a consumer can find passthrough kludges without switching on format.

JAM's numeric SubField IDs that have no FTSC analogue stay namespaced as jam.subfield.<id> (those aren't FTSC-form kludges; they're JAM-specific binary subfields).

Multi-line attribute helpers

Attributes that store multiple lines (seen-by, path, via, trace) join their lines with #13. Use the typed accessors on TMsgAttributes to avoid manual splitting:

list := msg.Attributes.GetList('seen-by');     // TStringDynArray
msg.Attributes.SetList('path', list);          // joins with #13
msg.Attributes.AppendListItem('seen-by', '3/777');

Tier 4 — Format-specific keys

These are namespaced and only meaningful to the format that produces them. Other backends ignore them on Write (silently dropped — fine).

JAM

Key Type Meaning
jam.msgidcrc int Index fast-path CRC of MSGID
jam.replycrc int Index fast-path CRC of ReplyID
jam.dateprocessed unix-int Tosser timestamp
jam.passwordcrc int Per-message password CRC
jam.cost int JAM-level cost (separate from cost)
jam.timesread int Times-read counter
jam.replyto int Parent in reply chain
jam.reply1st int First child in reply chain
jam.replynext int Next sibling in reply chain
jam.attribute2 int Reserved JAM attribute2 word
jam.subfield.<id> multi Passthrough for JAM-numeric subfields with no FTSC kludge analogue

(JAM's JAM_FTSKLUDGE subfields are parsed through the shared kludge dispatcher, so their content lands in the canonical slot — msgid, intl, etc., or kludge.<name> for unknowns — rather than a JAM-specific bag.)

Squish

Key Type Meaning
squish.umsgid int UMsgID (per-area unique number)
squish.utcofs int UTC offset in minutes
squish.replyto int Reply chain parent
squish.kludge.<name> multi Passthrough for unknown CtrlInfo kludges

Hudson / GoldBase

Key Type Meaning
hudson.prevreply int Previous message in reply chain
hudson.nextreply int Next message in reply chain
hudson.timesread int Times-read counter
goldbase.prevreply int (GoldBase variant)
goldbase.nextreply int (GoldBase variant)
goldbase.timesread int (GoldBase variant)

EzyCom

Key Type Meaning
ezy.extattr int EzyCom ExtAttr byte
ezy.prevreply int Reply chain prev
ezy.nextreply int Reply chain next

PCBoard

Key Type Meaning
pcb.refnum int RefNum field
pcb.status int Raw PCB status byte
pcb.active int Active flag
pcb.echo int Echo flag
pcb.export int Export flag
pcb.extra2 int Extra2 byte (incl. Allfix-sent bit 6)
pcb.extra3 int Extra3 byte
pcb.hastags int HasTags flag
pcb.origin int Origin flag
pcb.readnum int ReadNum word
pcb.extendedstatus int Extended status byte
pcb.password string Per-message password (max 12 chars)

Wildcat

Key Type Meaning
wildcat.confnum int Conference number this message lives in
wildcat.mflags int Raw mFlags word from TMsgHeader

PKT

PKT-format attributes (pkt.cost etc.) live in fpc-ftn-transport's docs/ since the PKT backend moved out of fpc-msgbase in 0.4.0. Unknown FTSC kludges still follow the universal kludge.<lowername> convention regardless of which library produced them.

MSG (FTS-1)

Key Type Meaning
msg.replyto int Reply chain parent
msg.nextreply int Reply chain next
msg.timesread int Times-read counter
msg.kludge.<name> multi Passthrough for unknown body kludges

Per-format support matrix

X = the backend's ClassSupportedAttributes lists the key. Blank = backend has no slot for it; setting the key has no effect on Write, and the key won't appear in Attributes after a Read.

Key JAM Squish MSG Hudson GoldBase EzyCom PCB WC
msg.num X X X X X X X X
from / to / subject X X X X X X X X
addr.orig / addr.dest X X X X X X X X
date.written X X X X X X X X
date.received X X X X
area X X X X X X X X
board X X X
cost X X X X X
attr.private X X X X X X X X
attr.crash X X X X X X
attr.received X X X X X X X
attr.sent X X X X X X X
attr.killsent X X X X X X
attr.local X X X X X X X
attr.hold X X X X X
attr.fileattach X X X X X X
attr.returnreceipt X X X X X
attr.deleted X X X X X X X
msgid X X X
replyid X X X
pid X X X
flags X X X
seen-by X X X
path X X X
Format-specific (<fmt>.*) X X X X X X X X

(Always check at runtime via SupportsAttribute(K) rather than relying on this table — it can drift. The capability list each backend ships in ClassSupportedAttributes is authoritative.)

PKT's column lives in fpc-ftn-transport since the PKT backend moved out of fpc-msgbase in 0.4.0.