# QRX Machine Sync Protocol (QS/QF/QD) Machine-readable inbox sync for APRS client software (desktop messengers, station apps, etc.). Human commands (I/R/N/D) stay unchanged; these are parallel commands with compact pipe-delimited responses that fit the 67-char APRS message limit and never require screen-scraping prose. All commands require registration (`REG`). Transport is ordinary APRS messaging: the client sends a message to `QRX`, QRX replies with one or more messages. Every QRX response carries a `{msgid` -- clients should ACK each response packet so igates/digis stop retransmitting. ## Commands ### QS -- status (inbox poll) ``` Client: QS QRX: QS|3|2 3 unread msgs from 2 senders QS|0 empty inbox QS|ERR|NOREG not registered ``` ### QF -- fetch message at cursor ``` Client: QF QRX: QF|1/3|KE0SMR-5|0428T1307|Hey are you on the ridge? ^^ ^^^ ^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^ cmd pos sender MMDDThhmm body QF|END nothing at cursor (inbox empty / all fetched) QF|ERR|NOREG not registered ``` - `pos` is `/`, newest first (same ordering as R). - Timestamp is UTC, `MMDDThhmm`. - QF is idempotent: repeated QF re-sends the same message. Only QD advances. Fetch loop: `QS` -> repeat (`QF` -> store -> `QD`) until `QF|END`. - Drain semantics: if the shared read cursor is past the end (e.g. the user stepped through with human R earlier) but messages remain, QF restarts from the newest message rather than reporting `QF|END`. Because QD deletes as the loop progresses, nothing already drained is served twice. - Senders' callsigns never contain `|`; the body may. Clients must split on the first 4 pipes only. **Continuation:** when metadata + body exceed 67 chars, the total field gets a `+` suffix and the remainder follows in a second packet: ``` QRX: QF|1/3+|KE0SMR-5|0428T1307|first 41 or so chars of the bo QRX: QF+|1|dy that did not fit in packet one ^^^ ^ cmd pos ``` Client concatenates (no separator). Stored bodies are <=67 chars, so one continuation packet always suffices. ### QF ALL -- bulk dump ``` Client: QF ALL QRX: QF|1/3|KE0SMR-5|0428T1307|Hey are you on the ridge? QRX: QF|2/3|W1XYZ|0429T0900|Testing QRX service QRX: QF|3/3|KM4ACK-7|0430T1200|Great video thanks QRX: QF|END ``` Read-only preview of the whole inbox (continuation packets included where needed), paced by QRX's inter-message delay, terminated by `QF|END`. Does not move the cursor and does not mark anything delivered -- use the QF/QD loop for consume-and-delete sync. ### QD -- done (delete fetched message, advance) ``` Client: QD QRX: QD|OK|2 deleted, 2 msgs remain QD|OK|0 deleted, inbox now empty QD|ERR|NOMSG nothing fetched yet (send QF first) QD|ERR|NOREG not registered ``` Deletes the message returned by the last QF (same semantics as human `D` after `R`) and makes the next message current. At-least-once delivery: if the client dies between QF and QD, the message is served again on the next QF. ### QW -- machine WHO (per-sender counts) ``` Client: QW QRX: QW|2|W4XYZ:2|KE0SMR-5:1 ^^ ^ ^^^^^^^ ^^^^^^^^^^ cmd total CALL:count entries QW|0 empty inbox QW|ERR|NOREG not registered ``` The first field after `QW|` is the total entry count (it never contains `:`, so it cannot be confused with a `CALL:count` entry). When entries don't fit one packet, overflow packets carry entries only (`QW|W1AAA:1|W2BBB:3`); accumulate entries across packets until you have ``. Lets a client show per-conversation mailbox badges before (or instead of) fetching. ## Compatibility policy This protocol is a public interface. Once a client ships against it, QRX must not break it. Rules for all future changes: - **Existing responses are frozen.** The field order and meaning of every response documented here (QS/QF/QD/QW, END, ERR) will never change. A field is never repurposed or removed. - **Additions are appended or new.** New capabilities arrive either as new fields appended AFTER the existing ones in a response, or as entirely new Q-prefixed commands -- never by altering existing fields. - **Clients should be liberal in what they accept:** ignore trailing fields you don't recognize, and ignore Q-prefixed response types you don't know. That makes today's client compatible with tomorrow's QRX. - **New error codes may be added** under the existing `|ERR|` shape. Treat an unrecognized code as a generic failure for that command. - If a change cannot satisfy these rules, it ships as a new command, not a modification. ## Error codes `|ERR|` -- codes: `NOREG` (not registered), `NOMSG` (QD with no prior QF). Rate limiting still returns the human string ("Rate limit. Try again in a few min.") since it is emitted upstream of command dispatch; clients should treat any non-`Q`-prefixed response as a soft error and back off. ## Client guidance - ACK every QRX response packet (they carry `{msgid`). - Poll with QS on your own beacon cadence or when QRX's beacon notification arrives ("QRX: N msgs..."). Machine-format notifications (`QN|...`) are a possible future addition. - The command rate limit (15 commands / 5 min / base callsign) applies. A full sync of N messages costs 1 + 2N commands (QS + Nx(QF+QD)) -- budget accordingly and prefer event-driven polls over tight loops. - Mixing human R/N reads and QF in the same session is not recommended; both move the same cursor.