OAKENMAIL
Pre-alpha

Sync schedule

Core loads each account's provider and syncs it on a schedule stored in the database. You can see it, edit it live, pause it and run it now. Nothing outside core is involved: no system cron and no second process.

1

The schedule is data

Each account has one row in sync_schedules (raw.db). An IMAP account can also have a row per folder. A row holds:

Column Meaning
spec every 15m (an interval) or a five-field cron expression
timezone an IANA name such as America/New_York, from your system's tz database
quiet_start, quiet_end optional quiet hours, HH:MM, in the schedule's timezone
paused, paused_reason whether it runs, and why not
next_run, last_run, last_result, last_error what happens next and what happened last (the error is scrubbed)
failures consecutive failures, which drive the backoff

config.toml only supplies the defaults for a schedule created for a new account. After that the database is the only place a schedule is edited, so a change made in the UI or CLI is live and survives a restart.

Setting Default Meaning
sync.interval_seconds 900 Default gap between syncs (15 minutes)
sync.timezone UTC Default timezone of a new schedule
sync.min_interval_seconds 60 The floor. A faster schedule is refused when you set it
sync.max_concurrent 2 How many accounts sync at once; the rest wait their turn
sync.backoff_max_seconds 3600 The longest a failing schedule waits
sync.backoff_jitter 0.2 Random spread added to a backoff so accounts do not retry in step
2

When it runs

3

When it fails

A failure backs off: the base gap doubled for each consecutive failure, capped at sync.backoff_max_seconds, with jitter. An authentication failure is different: that schedule is paused with the reason authentication failed, an event is published and a row is written to the audit log. It is not retried until you act. Other accounts carry on. A successful manual sync, or resume, clears the pause.

An account whose provider cannot start is reported with its reason and does not stop the others.

4

Quiet hours mute alerts, not syncs

Quiet hours never stop a sync. They mark the events a sync publishes (SyncFinished, SyncPaused) as quiet, so a client can hold back notifications overnight while mail keeps arriving.

5

Several accounts of one provider

A provider plugin is a class, and core starts one instance per account, named <plugin>@<account id>. Its configuration is the plugin's [plugins.<name>] table with the account's own non-secret settings on top, and its keyring entry defaults to the account id. Three Google accounts and a Fastmail account are four accounts, each with its own credentials.

6

Folders

pull(cursor, folder=None) is one function for both jobs. With no folder it syncs the whole account; with a folder it syncs only that one, which lets an IMAP account check INBOX often and an archive rarely. A provider that has no folders (supports_folders = False, as Gmail) refuses a folder schedule when you set it. A schedule for a folder is stored under the key account/folder.

7

Changes are audited

Every set, pause, resume and remove is written to the audit log with the actor, the reason and the value before and after. Schedule changes hold only the spec, timezone, quiet window and pause fields, never mail content. undo applies to read-state changes only and refuses these.

8

Seeing it

The UI shows a table of the schedules with pause, resume and sync-now per row. The same rows can be exported read-only in crontab form, with a CRON_TZ line per timezone, paused entries commented out and intervals that cron cannot express commented out too. The database stays the only place to edit.

9

Starting up

SyncService.start() runs in this order: start the account providers, resume any read-state write-backs that were queued when the process stopped, make sure every account that can pull has a schedule, then start the loop. Accounts with no usable provider are returned with their reasons.

10

Still to come

IMAP IDLE and Gmail push can call SyncService.request() for "sync now" without any change to the scheduler. The daemon now starts all this, and oaken auth (prompt 17) is what connects it to a real mailbox.