Expand description
The training corpus: every message of every folder of every account, since forever, kept small enough for any disk.
Sioul’s sync already brings all your mail, but never into the disk’s
reserve, and whole messages since forever rarely fit. Training needs much
less: per message, the header block (at most 32 KB), its MIME structure
from BODYSTRUCTURE (the parts’ types and names, attachments included), and
the text the Porch reads of it: the first 6 000 characters of its text
part, and its HTML (up to 64 KB) when it has no text part or only a
stand-in (“view it in your browser”); which part is which, mail-parser
says, as for the Porch (spamcore::bodies). A part is read whole up to
256 KB and cut here (so it decodes as the Porch decodes it), only its
start beyond. With where it sits and its
flags and keywords ($Junk, $NotJunk). Records are JSON lines, gzipped
a batch at a time and appended to one file per account and folder, under
$XDG_DATA_HOME/sioul/spam/corpus/<account>/.
Downloading uses Sioul’s own IMAP session (the sync’s TLS, login and
Google sign-in) and changes nothing on the server: folders are opened with
EXAMINE, messages read with BODY.PEEK. Trash, Drafts and Sent are left out.
Gmail’s All Mail is read last, for what Gmail keeps only there (archived
mail): your own messages (from one of your addresses, or labelled \Sent)
and those kept already from another folder (by Message-ID) are left out
of it. Another server’s “all mail” folder is left out: what it holds is
its own (the trash, junk?). It is incremental: where each folder stopped is kept
after every batch (state.toml), so it resumes there; a folder the server
renumbered (a new UIDVALIDITY) is read again, its older records kept. A
record is never deleted when the server deletes the message: that is how
junk outlives the provider’s purge (Gmail keeps Junk a month).
It never fills the disk: it stops before the free space falls under 1 GB,
and says so (status), for the window and the command line. A message
that breaks the connection (one the IMAP parser cannot read) is taken
alone on a new one and, failing twice, set aside (skipped), so that it
never holds back its folder.
Each message is checked as Sioul checks the mail it stores (Checked,
sioul_sync::verify: DKIM on the whole message, SPF from the server
that handed it to your provider, DMARC, ARC, the reverse DNS), so that
training reads the same checks as the sorting does: fetched whole (up to
CHECK_WHOLE; a larger one by its header block alone), in memory only,
checked, and dropped; only the checks’ results are kept, in the record.
Records kept before, or while the DNS did not answer, are checked later
(verify, and every download after its new mail): each message fetched
once by its UID, the results appended beside the records
(checks.jsonl.gz), so a run stopped goes on there; a message its server
no longer has stays unchecked for good (Checked::gone). The DNS: one
resolver per run, so each answer is asked once; a few checks a second, a
few at a time, each within CHECK_LIMIT; when no DNS server answers (not
even that a name made up under the mail server’s domain does not exist),
the checks stop, and what is left waits, unknown, for the next run.
The corpus is private: never shared, never shown to an AI agent.
Structs§
- Account
State 🔒 - Account
Status - AllMail 🔒
- In Gmail’s All Mail, what is left out: your own messages (from one of
own, or labelled \Sent whenlabels), and copies of messages kept already from the account’s other folders (seen, by Message-ID). - Check
Counts - What Sioul’s checks say in the corpus, per account, as the training reads
them (
Checked::reading: old signed mail’s failures unknown): totals only, never a message. - Check
Line 🔒 - A record’s checks made after it was kept (
verify), by its place in its account: one line of the account’schecks.jsonl.gz. - Checked
- Sioul’s own checks of a message (SPF, DKIM, DMARC, ARC, the reverse DNS
of the server that handed it to your provider:
sioul_sync::verify), made as the Maildir fetch makes them as mail arrives, on the whole message, when the corpus fetched it (or later:verify). The message itself is never kept: only what the checks found. - Checker 🔒
- Sioul’s checks for a whole run: one resolver, so that each DNS answer is
asked once in the run (its cache); at most
CHECKS_PER_SECONDstarted each second,CHECKS_AT_ONCEat a time, each withinCHECK_LIMIT. Offline (no DNS answer, not even that a name made up does not exist), it stops for the rest of the run. - Folder
State 🔒 - One folder’s download, by its server name.
- Folder
Status - Head 🔒
- What the first FETCH says of a message, before its text is asked for.
- Held
- Leaf
- Place
- A message’s place on its server, which names it for good (RFC 9051 §2.3.1.1).
- Record
- One message as training reads it.
- Run 🔒
- One folder’s checks under way: those kept, and those of the last
messages that got no DNS answer at all, held until it is known whether
the DNS is there (
settle). - Status
- The download’s state, as the window and the command line say it.
- Tally
- One check’s results over many messages, as the training reads them.
- Text
- A text kept, and the part it comes from.
- Update
- What one download did.
- Verdicts
- What the corpus’s headers say of your providers’ own spam filters: totals only, never a message.
Enums§
- Node
- A message’s MIME structure, as BODYSTRUCTURE gives it.
- Stop 🔒
- Why an account’s download ended early.
Constants§
- BATCH 🔒
- Messages per FETCH and per appended batch (a few in tests, to cross batches).
- BATCH_
BYTES 🔒 - What one batch may take on disk, at most, before compression.
- CHECKS_
AT_ 🔒ONCE - CHECKS_
FILE 🔒 - The account’s checks kept apart from its records (
verify). - CHECKS_
PER_ 🔒SECOND - Checks started each second, at most, and at once.
- CHECK_
GROUP 🔒 - Whole messages asked in one FETCH: at most this many bytes in all.
- CHECK_
LIMIT 🔒 - One message’s checks, at most; each DNS question, asked once, at most
DNS_TIMEOUT. - CHECK_
WHOLE - A message up to this size is fetched whole for Sioul’s checks, in memory only; a larger one is checked by its header block alone (SPF, the reverse DNS; DMARC when SPF passes it), what needs the body left unsaid.
- DNS_
TIMEOUT 🔒 - EXCERPT_
CHARS 🔒 - What the Porch reads of the text part (card.rs
EXCERPT_LIMIT), and under how many visible characters it reads the HTML after it (card.rsSTUB). - FETCH 🔒
- HEADER_
BYTES - The header block kept, at most.
- HTML_
BYTES - The HTML kept, decoded, when the Porch reads it: a mail’s first kilobytes are often only its styles, and the Porch reads the text after them.
- KEEP_
FREE - The download stops before the disk’s free space would fall under this.
- MOST_
PARTS 🔒 - Parts listed per message, at most.
- MOST_
RECONNECTS 🔒 - New connections an account’s download may make after a broken batch, in one run.
- OFFLINE_
STREAK 🔒 - So many messages in a row without any DNS answer, and the network is asked whether it is there.
- STUB_
CHARS 🔒 - TEXT_
BYTES - The start of a larger text part read, in bytes as the server stores it: enough for the Porch’s 6 000 characters, in base64 or quoted-printable too.
- WHOLE_
PART 🔒 - A text or HTML part up to this size is read whole, and cut here; a larger one, only its start (a partial FETCH, which some servers write wrongly).
Functions§
- account_
host 🔒 - The mail server’s name, under which the DNS is asked whether it answers at all.
- answered 🔒
- Whether a message’s checks got any answer from the DNS: not when SPF failed for want of one, nor DKIM when no server was there to check SPF on.
- append 🔒
- Appends one batch to a folder’s file (or the checks’ file) as a gzip
member, after cutting off whatever a crash left past
committed; the file’s new committed length. - check_
kept 🔒 - The messages of an account kept without Sioul’s checks (
verify’s work, and every download’s after its new mail), on a session of their own: folder after folder, the newest first, each group’s checks appended tochecks.jsonl.gzas they come, so a run stopped goes on there. Their folder gone or renumbered, they aregone: their UIDs name nothing any more. - check_
records 🔒 - New records checked as they come (
Checked): each message fetched whole (or its header block, aboveCHECK_WHOLE) from the folder open insession, checked, the checks put in its record. Those left unchecked (the DNS silent, a group cut short) wait forcheck_kept; a connection lost is said, the checks made until then kept. - checks
- What Sioul’s checks say in the corpus, per account (
CheckCounts). - copies_
of - Where a message is kept in the corpus, by its Message-ID (bare, as the
label log writes it): each copy’s place and its folder’s role, account
after account. For a label said of a message no longer stored here
(
sioul spam label mid:…): its place on the server is what the label log names it by. - cut 🔒
- A text cut to at most
bytes, on a character’s edge. - decode_
text 🔒 - The start of a text part, decoded as Sioul reads mail: its transfer encoding and charset undone by mail-parser, which forgives a part cut short. HTML stays HTML: what is visible is read from it at training.
- decode_
words 🔒 - RFC 2047 encoded words, read as mail-parser reads a subject.
- fetch_
batch 🔒 - One batch: its records, and the UIDs of All Mail’s messages left out (
AllMail). - fetch_
raw 🔒 - Messages of the folder open in
session, by UID: whole, or their header block (BODY.PEEK: nothing changes on the server), in memory only; a UID answered without them, None; one the server does not have, absent. A FETCH refused whole is asked again one message at a time; one refused alone counts as answered without it. - fetch_
raw_ 🔒once - fetch_
records 🔒 - One batch’s records, and the UIDs not kept: left out (All Mail), or given nothing for alone, so that they are not asked for again and again. A batch the server refuses whole is asked again one message at a time.
- fetch_
texts 🔒 - Each part asked for: whole up to
WHOLE_PART, else its firstlimitbytes; one FETCH per part number (“1”, “1.1”, “2”…) and way. The bytes as stored, with the leaf that says how they are encoded. - file_
name 🔒 - The file of a folder’s records, in its account’s directory: “INBOX.jsonl.gz”, “_Junk.jsonl.gz” (the folder’s Maildir++ name, made safe for any disk).
- flag_
name 🔒 - “\Seen”, “$Junk”: a flag as the server writes it.
- free_
space - The free space on the disk holding a path; None when the system does not say.
- groups 🔒
- Messages to fetch together, from their (UID, size): the whole ones in
groups of at most
CHECK_GROUPbytes andBATCHmessages, then the larger ones by their header blocks. - head_of 🔒
- header_
text 🔒 - The header block as text: UTF-8 as it is; anything else byte for character (Latin-1), which gives the bytes back.
- is_
gmail 🔒 - Whether the server is Gmail’s: it says X-GM-EXT-1 (its labels, its All Mail).
- isolate 🔒
- A broken batch’s messages, one at a time, each on a working connection:
one that breaks it alone, twice, is set aside (
skipped), so that it never holds back the rest of its folder. When none of them comes, the fault is the server’s or the line’s, not a message’s: nothing is set aside, and the error is said. The connections it made. - keep 🔒
- One batch kept: its records appended to the folder’s file, their UIDs marked done; those not kept (the server could not give them, or All Mail leaves them out) marked skipped; where the folder stopped saved.
- keep_
checks 🔒 - Checks made after their records were kept: appended to the account’s
checks.jsonl.gz, then committed in its state. - kept
- Whether a folder’s mail enters the corpus: not the trash, drafts or what
you sent; Gmail’s All Mail (
gmail: the server is Gmail’s, X-GM-EXT-1), for the mail Gmail keeps only there; never another server’s \All folder. - kept_
ids 🔒 - The Message-IDs of what an account’s folders but
excepthold in the corpus. - name_
param 🔒 - A name parameter (
filename,name), decoded: RFC 2231’skey*(a charset and percent-encoding, possibly in numbered pieces), else the plain one (RFC 2047’s encoded words read as mail-parser reads a subject). - node_of 🔒
- A message’s structure from BODYSTRUCTURE: its multiparts and their parts,
each leaf’s type, names, charset and encoding; an attached message is one
leaf. At most
MOST_PARTSleaves (countcounts them). - own_
addresses 🔒 - Your addresses, lowercase: every account’s address, and its login when that is one.
- percent_
decode 🔒 - ranges 🔒
- “1:5,7,9:12” for a set of UIDs; read back by
read_ranges. - read_
all - Every record of the corpus, account after account, folder after folder,
each with Sioul’s checks of it, made as it was fetched or later (
verify). - read_
checks 🔒 - An account’s checks kept apart from its records, by (folder,
UIDVALIDITY, UID), up to
committedbytes; the newest of a place wins. - read_
file - Reads a folder’s records, up to
committedbytes (all when None); a line that cannot be read is skipped, a member cut short ends the reading. - read_
ranges 🔒 - reading_
order 🔒 - The folders read, in the order read: the server’s, All Mail last, so that what the other folders hold is kept from them and left out of it.
- run 🔒
update(download), orverify.- save_
status 🔒 - status
- What the corpus holds and how its last download went.
- status_
path 🔒 - to_come 🔒
- About how many messages of an account’s kept folders are not recorded yet: each one’s MESSAGES (STATUS), less what is recorded under the same UIDVALIDITY.
- update
- Brings the corpus up to date: every account given, every folder kept
(
kept), what is new since the last time.passwordgives an account’s password as the sync uses it (sioul_sync::secret::password);roomthe free space on a disk (free_space; tests say less). - update_
account 🔒 - An account’s download (
download), its new messages checked as they come, then its messages kept without checks checked (check_kept). - update_
folders 🔒 - verdicts
- The providers’ verdicts in the corpus, per account.
- verify
- Sioul’s own checks of the messages kept without them (
Checked), and nothing else: nothing new is downloaded (sioul spam fetch --verify). Each message is fetched once by its UID, whole (or its header block, aboveCHECK_WHOLE), in memory only; its checks are appended beside the records after each group, so a run stopped goes on there. A message its server no longer has (its folder gone or renumbered, its UID gone) isgone: unknown for good. Progress:Verify, account after account. - whole_
base64 🔒 - Base64 up to its last whole group of four characters.
- write_
whole 🔒 - Writes a file beside its place, then moves it there.