You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 95c4507
Browse filesBrowse the repository at this point in the historyBrowse files
Copy file name to clipboardExpand all lines: CONTRIBUTING.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -7,7 +7,7 @@ Whether you are a human or an AI agent, read these in order before touching the
7
7
1. the [Pimalaya README](https://github.com/pimalaya) for what the project is and how its repositories stack;
8
8
2. the [Pimalaya CONTRIBUTING](https://github.com/pimalaya/.github/blob/master/CONTRIBUTING.md) guide, which chains to the shared architecture and guidelines;
9
9
3. the inline header documentation, starting with src/main.rs: it is the architecture document of this crate;
10
-
4. the cairn/ folder, which follows [Cairn](https://github.com/pimalaya/cairn): `spec/` is current truth, `changes/` holds in-flight proposals, and `log/` is the dated history.
10
+
4. the cairn/ folder, which follows [Cairn](https://github.com/pimalaya/cairn): spec/ is current truth, changes/ holds in-flight proposals, and log/ is the dated history.
11
11
12
12
Everything below documents only what differs from the Pimalaya standards.
Copy file name to clipboardExpand all lines: README.md
+48-29Lines changed: 48 additions & 29 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -20,44 +20,55 @@ CLI to watch PIM collection changes, written in Rust
20
20
21
21
## Features
22
22
23
-
-**Five backends**, mail and not only mail: IMAP idles, JMAP is pushed to, Maildir re-lists, and CalDAV and CardDAV each ask a collection what moved.
24
-
-**One account, one collection, one method**: all three are its config, so nothing is passed on the command line. Each backend names the collection it watches in its own word (`imap.mailbox`, `caldav.calendar`, `carddav.addressbook`), and a hook templates against that same word. Any backend can poll instead, for a server whose idle or push cannot be trusted.
25
-
-**Events named after what they carry**: mail fires `on-message-*`, an addressbook `on-card-*`, a calendar `on-event-*` and `on-task-*`, and anything with flags `on-flag-*`, once per flag. Flag names are the same on every backend, so a filter written once fires everywhere.
26
-
-**Hooks under their backend**: a desktop notification, a shell command, or both, with the event's fields as placeholders. Each backend takes only the events it can report, and each hook only the variables its event carries, so a hook that could never fire is refused when the file is read.
27
-
-**Every account at once**, one thread each, reopening a dropped watch with a capped backoff and reading the credential again each time.
28
-
-**Familiar configuration**: the account block keeps the shape `himalaya` and `himalaya-tui` read, and secrets come from your own password manager.
29
-
-**One prompt to a working account**: `carillon configure` turns an email address into a discovered service, a tested connection and the best watch method its server supports, then writes the account where the loader reads it.
23
+
-**Watch mail, calendars and contacts**: **IMAP**, **JMAP**, **Maildir**, **CalDAV**, **CardDAV**
24
+
-**Desktop notifications**, **shell commands**, or both, on every change
25
+
-**Instant where the protocol allows it**: IMAP idle, JMAP push, a poll everywhere else
26
+
-**Every account at once**, each reconnecting on its own when its server drops it
27
+
-**One prompt setup**: `carillon configure` turns your email address into a tested account
28
+
-**Secrets from your own password manager**, never written into the configuration
29
+
30
+
> [!TIP]
31
+
> Carillon is written in [Rust](https://www.rust-lang.org/) and uses [cargo features](https://doc.rust-lang.org/cargo/reference/features.html) to gate backend support. The default feature set is declared in [Cargo.toml](./Cargo.toml).
32
+
30
33
## Coverage
31
34
32
-
| Spec | What is covered |
33
-
|-----------|-----------------|
34
-
|[2177]| IMAP idle: the held connection a watch waits on, woken by the server on every change |
35
-
|[7162]| Quick mailbox resynchronization: server-named deltas where the server offers them, a local re-read and diff where it does not |
36
-
|[4959]| SASL initial response, forced on or off for providers that advertise it and then refuse it |
37
-
|[2971]| IMAP identification, required by some providers right after authentication |
38
-
|[4616]| SASL plain, the password mechanism |
39
-
|[4505]| SASL anonymous, for servers accepting unauthenticated sessions |
40
-
|[7677]| SASL scram-sha-256, the challenge-response mechanism |
41
-
|[7628]| SASL oauthbearer, an OAuth 2.0 token issued by an external broker |
42
-
|[8620]| The JMAP core: session discovery, the changes and get method shapes, request batching, and the event-source stream a push watch holds |
43
-
|[8621]| JMAP for mail: mailboxes, emails, and the change stream a watch polls |
44
-
|[maildir]| The original Maildir layout, plus the Maildir++ subfolder convention |
45
-
|[4918]| WebDAV itself: the report a collection answers, and the etag that says an item moved |
46
-
|[6578]| Collection synchronization: the token a poll carries, what changed since it, and what to do when the server refuses it |
35
+
| Spec | What is covered |
36
+
|--------------|-----------------|
37
+
|[2177]| IMAP idle: the held connection a watch waits on, woken by the server on every change |
38
+
|[2971]| IMAP identification, required by some providers right after authentication |
39
+
|[4505]| SASL anonymous, for servers accepting unauthenticated sessions |
40
+
|[4616]| SASL plain, the password mechanism |
41
+
|[4918]| WebDAV itself: the report a collection answers, and the etag that says an item moved |
42
+
|[4959]| SASL initial response, forced on or off for providers that advertise it and then refuse it |
43
+
|[6186]| Mail service SRV records, one of the ways the wizard finds a server from an address |
44
+
|[6578]| Collection synchronization: the token a poll carries, what changed since it, and what to do when the server refuses it |
45
+
|[6764]| CalDAV and CardDAV bootstrapping, how the wizard finds a calendar or an addressbook |
46
+
|[7162]| Quick mailbox resynchronization: server-named deltas where the server offers them, a local re-read and diff where it does not |
47
+
|[7628]| SASL oauthbearer, an OAuth 2.0 token issued by an external broker |
48
+
|[7677]| SASL scram-sha-256, the challenge-response mechanism |
49
+
|[8620]| The JMAP core: session discovery, the changes and get method shapes, request batching, and the event-source stream a push watch holds |
50
+
|[8621]| JMAP for mail: mailboxes, emails, and the change stream a watch polls |
51
+
|[autoconfig]| Thunderbird autoconfiguration, the widest-published source of provider settings |
52
+
|[maildir]| The original Maildir layout, plus the Maildir++ subfolder convention |
53
+
|[pacc]| Provider account configuration, the successor the wizard prefers when a domain publishes one |
@@ -107,7 +118,15 @@ Run `carillon` with no command: it offers to generate an account discovered from
107
118
108
119
Everything discovery does not cover is written by hand: copy the annotated [config.sample.toml](./config.sample.toml), keep one backend and the hooks you want, delete the rest. It documents every key.
109
120
110
-
A configuration is read from `$XDG_CONFIG_HOME/carillon/config.toml`, `$HOME/.config/carillon/config.toml` or `$HOME/.carillonrc`, overridden by `-c <PATH>` or `CARILLON_CONFIG=<PATH>`. Those are the paths [himalaya](https://github.com/pimalaya/himalaya) and [himalaya-tui](https://github.com/pimalaya/himalaya-tui) read too, and an account block is written the same way, but one file does not load in all three: every backend block is strict on each side and carries keys the others do not know.
121
+
A configuration is loaded from the first valid path among:
122
+
123
+
- $XDG_CONFIG_HOME/carillon/config.toml
124
+
- $HOME/.config/carillon/config.toml
125
+
- $HOME/.carillonrc
126
+
127
+
Override the path with `-c <PATH>` or `CARILLON_CONFIG=<PATH>`. Multiple paths can be passed at once, separated by `:`. The first one is the base and the rest are deep-merged on top. The full field reference lives in [config.sample.toml](./config.sample.toml).
128
+
129
+
Those are the paths [himalaya](https://github.com/pimalaya/himalaya) and [himalaya-tui](https://github.com/pimalaya/himalaya-tui) read too, and an account block is written the same way, but one file does not load in all three: every backend block is strict on each side and carries keys the others do not know.
111
130
112
131
An account declares one backend block (`imap`, `jmap`, `maildir`, `caldav`, `carddav`) carrying everything that backend needs: the collection it watches under its own name, how it watches (`watch`), and what it fires (`hook`). Declaring several backends is allowed; `-b/--backend` then picks one.
### Requirement: Every configured account, or a chosen one
9
+
Bare `carillon watch` SHALL watch every configured account at once, one thread each under a single shared shutdown. `-a/--account` SHALL narrow the watch to that account. A name no account carries SHALL be an error listing the accounts the configuration does hold, and a command needing one account with no default to pick SHALL name both ways of choosing one, so a failure to resolve an account always says what to do next. One account's watch failure SHALL be logged and retried on its own without stalling the others.
10
+
11
+
#### Scenario: Watch everything
12
+
-**GIVEN** a config with two accounts and no account flag
13
+
-**WHEN**`carillon watch` runs
14
+
-**THEN** both accounts are watched at once, each on its own collection, and Ctrl+C stops them together
15
+
16
+
#### Scenario: A name no account carries
17
+
-**GIVEN** a configuration holding the accounts `perso` and `work`
18
+
-**WHEN**`carillon -a wrok watch` runs
19
+
-**THEN** it fails naming `wrok` and listing `perso, work`
20
+
21
+
#### Scenario: One account's server is unreachable
22
+
-**GIVEN** two watched accounts, one of whose servers refuses connections
23
+
-**WHEN** that watch fails
24
+
-**THEN** the failure is logged and retried for that account alone, and the other account keeps watching
# Align the repository with the Pimalaya guidelines
9
+
10
+
## Why
11
+
12
+
The org-wide guidelines (.github/GUIDELINES.md) are the settled conventions every repository is checked against, and carillon had drifted from several of them. The drift is not cosmetic in every case: a module named after a banned word teaches the wrong vocabulary to the next reader, a changelog entry describing a removal that has since been undone is simply false, and an account error that does not say what the configuration holds leaves the user guessing.
13
+
14
+
## What
15
+
16
+
- naming: the driver module goes away, its code folded into the watch command it served. The word is banned across the codebase, and the module had one caller, so nothing justified a module of its own; the two remaining uses in the wizard docs go with it.
17
+
- inline-005: the dashed section banners in the configuration module are removed. The types carry doc comments already, which is what the guideline says navigation should rest on.
18
+
- inline-004: every remaining bare `//` comment is tagged NOTE, the only tag any of them warranted.
19
+
- naming-012: user-facing error messages start with a capital, log messages stay lowercase.
20
+
- cli-002: a missing named account lists the accounts the configuration holds, and a missing default account names both ways of picking one, which `watch` and `check` did not do.
21
+
- cargo-008 and cargo-009: dependencies are alphabetical, and the docs.rs metadata block a binary has no use for is gone.
22
+
- changelog-001 and changelog-002: every entry opens with a one-line summary, with the detail in indented paragraphs, and the section reports the net state rather than its own history. Two entries described work that has since been undone.
23
+
- markdown-003 and readme-008 and readme-010: paths leave their backticks, the features read as user features rather than as an implementation tour, and the configuration paths follow the documented shape.
24
+
- every feature combination compiles warning-free, including the backendless one, through a `backend` cfg the build script sets.
25
+
26
+
## Not done
27
+
28
+
AGENTS.md keeps its backticked file names: it is vendored from the Cairn convention repository, and diverging from upstream to satisfy markdown-003 would cost more than it buys.
# Align the repository with the Pimalaya guidelines
8
+
9
+
A pass over the whole repository against .github/GUIDELINES.md, rule by rule.
10
+
11
+
## What landed
12
+
13
+
The driver module is now supervisor, which is what its own header called it all along. The word is banned across Pimalaya, and it had also reached two lines of wizard documentation ("email-driven discovery", "it drives the prompt") and one changelog entry; all of them now say what they mean instead. The cairn log entries keep their wording, being immutable history.
14
+
15
+
Three code rules were failing. The configuration module carried eight dashed section banners, which inline-005 bans outright: the types they separated already have doc comments, which is what navigation is supposed to rest on. Twenty-seven bare `//` comments carried no tag, and every one of them turned out to be a NOTE. Twenty-five user-facing error messages started lowercase, against naming-012, while the log messages that must stay lowercase already did.
16
+
17
+
One rule was failing in a way a user would feel. cli-002 asks that a failure to resolve an account name what is missing and what to do about it, and carillon answered "Account `x` not found in config" without saying which accounts exist, or "Cannot find account" without saying that `-a` and `default = true` are the two ways to pick one. Both now say it, which is the only behaviour this pass changed.
18
+
19
+
The manifest lost the docs.rs metadata block a binary publishes nothing to, sorted its dependencies, and took the org author address. The README features section was rewritten as user features rather than an implementation tour, and shortened; the coverage table is sorted by spec name and gained the four discovery mechanisms the wizard actually uses. The configuration section follows the documented path shape, and paths lost their backticks there and in CONTRIBUTING.md.
20
+
21
+
The changelog was reshaped to changelog-001, every entry now a one-line summary with its detail in indented paragraphs. Reading it that way surfaced two entries that had become false: one announced the removal of the wizard and its discovery dependency, both of which came back the same day, and one said Cargo.toml patches io-imap to a local path, which it no longer does. changelog-002 is why they had to go: a section reports the net state, and history belongs here.
22
+
23
+
Every feature combination now compiles warning-free. A build enabling no backend at all still compiles the change vocabulary and the hook runner nothing then feeds, so the build script sets a `backend` cfg and main.rs allows the dead code in that one configuration, rather than gating every item of two modules on four features.
24
+
25
+
## Verification
26
+
27
+
Build, clippy and fmt green on the empty, imap, jmap, maildir, dav and full feature sets; 41 tests pass. The sample configuration was loaded by `carillon check` to prove it still parses after the edits.
28
+
29
+
## Not done
30
+
31
+
AGENTS.md keeps its backticked file names. It is vendored from the Cairn convention repository, and the cost of diverging from upstream is higher than the rule it breaks.
32
+
33
+
## Follow-up: the module went away rather than being renamed
34
+
35
+
The rename landed as supervisor, and supervisor is the same kind of name as driver: it says what the code plays rather than what it is about, which no other module here does (config, event, hook, backend, watch are all subjects).
36
+
37
+
So the code moved instead. `watch_account` and `watch_session` now live in watch.rs, next to the command whose threads run them, which was their one and only caller. Nothing is named after a role any more, and there is one module fewer: the file reads as the command, the accounts it selects, what each thread does, and the helpers under it.
0 commit comments