Configuration (wiki): stale on unreadable rates and on implicit TLS; POSTULO_RECOVERY_RATE is missing #404

Closed
opened 2026-10-01 10:46:27 +00:00 by tiagoagueda · 1 comment
Owner

Found on 2026-09-29 in a read-only review of postulo at 95348908d and the wiki at de1f52467, reading Configuration.md against the configuration checks and the mail settings. Related to #233 (closed), which added the start-up configuration checks, and #158 (closed), which implemented implicit TLS: both changed the behaviour and documented it on this page, and each left an older paragraph standing that now says the opposite. This issue removes the two leftovers and adds the one rate the page never named.

Checked again on 2026-10-01 against main at 0a5cad587: still present. The code is now at core/server_views.py:340-342 (where the recovery rate is spent), and MailSecurity moved to core/mail_choices.py:17-27, with 465 as the default port for MailSecurity.SSL at core/mail.py:31-36 (DEFAULT_MAIL_PORTS).

The wiki's Configuration.md contradicts itself in two places, and in both the stale statement is the one an operator meets first or reads last. The rate-limit section says a mistyped rate quietly means no limit, though start-up now refuses it. The Email section says implicit TLS on port 465 is not offered, though it is, and the same page explains how fifty lines further down. The rate table also omits POSTULO_RECOVERY_RATE.

1. Rate limits: an unreadable rate is said to mean no limit

  • Where: the wiki's Configuration.md:661-663 (the note under the rate table), against Configuration.md:135-140 on the same page and core/checks.py:77-95 (rates, which reports postulo.E002 when throttle.is_well_formed at core/throttle.py:58-66 fails).
  • What happens: the note reads "Anything unreadable also means no limit, deliberately: a mistyped rate should leave you with a working instance rather than a locked one." That was true of throttle.parse alone. Since #233, docker/entrypoint.sh:34 runs manage.py check --deploy --fail-level ERROR, and a rate that is not <times>/<s|m|h|d>, empty or 0 stops the container. An operator who writes POSTULO_API_RATE=600/hour on the strength of the note gets an instance that does not start: the pattern in core/throttle.py:39 accepts only s, m, h or d as the unit.
  • Why it matters: the page says two opposite things about whether a typo can stop the instance, and the wrong one sits in the section an operator reads when setting a rate.
  • Fix: replace the second and third sentences of the note with: "A rate that cannot be read stops start-up with postulo.E002, naming the variable; empty or 0 means no limit."

2. Rate limits: POSTULO_RECOVERY_RATE is not on the page

  • Where: the table at Configuration.md:652-659; config/settings/base.py:433-436, which defines POSTULO_RECOVERY_RATE (default 10/h), and core/server_views.py:339-341, which spends it when an administrator issues a recovery link.
  • What happens: of the eleven POSTULO_*_RATE settings in config/settings/base.py, ten are named in the wiki (six in this table, POSTULO_TEXT_RATE, POSTULO_TEXT_PER_NUMBER_RATE and POSTULO_CONFIRMATION_RATE further down the page, POSTULO_NUMBER_RATE in Your-career-record.md). POSTULO_RECOVERY_RATE appears nowhere in the wiki; only .env.example:202-205 describes it. Getting somebody back in without email (Accounts-and-invitations.md:270) explains recovery links without saying they are bounded.
  • Why it matters: this is the limit on how many recovery links one administrator session can mint, each of them "a whole account in a URL" in the words of the setting's own comment. An administrator who meets "That is a lot of recovery links. Try again shortly." has nowhere to look it up.
  • Fix: add a row for POSTULO_RECOVERY_RATE (10/h, recovery links one administrator may issue) to the table, and a sentence naming it in Getting somebody back in without email that links back to the table.

3. Email: implicit TLS on port 465 is said not to be offered

  • Where: the wiki's Configuration.md:481-482, against Configuration.md:529-557 (STARTTLS or implicit TLS, and why the port decides), config/settings/base.py:713-723 (POSTULO_EMAIL_SECURITY: none, starttls or ssl), core/mail.py:26-46 (MailSecurity.SSL, with 465 as its default port) and plugins/smtp/__init__.py:77, which passes use_ssl.
  • What happens: the paragraph reads "Only STARTTLS is supported, which is a server on port 587 that upgrades the connection after opening it. Implicit TLS on port 465 is not offered yet, in the environment or on the page." Both are offered since #158: POSTULO_EMAIL_SECURITY=ssl in the environment (listed in the table just above, at Configuration.md:468) and the three-state Connection security control on Server settings → Email. An operator whose provider documents SMTPS on 465 first, which the page itself says of Gmail, Microsoft 365, Fastmail, OVH and most shared hosting (Configuration.md:537-538), reads the earlier paragraph, concludes Postulo cannot reach it, and routes mail through a relay or a transport plugin they did not need.
  • Why it matters: the page contradicts itself on one of the most common mail configurations, and the wrong statement comes first.
  • Fix: delete Configuration.md:481-482, or replace it with one sentence pointing to STARTTLS or implicit TLS, and why the port decides below.

Done when

  • Configuration.md no longer says an unreadable rate means no limit, no longer says implicit TLS is unavailable, and names POSTULO_RECOVERY_RATE with its default.
  • A test in tests/test_wiki_surface.py asserts that every name core.checks.rate_settings() returns appears somewhere in the wiki; it fails today on POSTULO_RECOVERY_RATE. (Like the rest of that file it runs only where the wiki is checked out beside the repository.)
  • The same file asserts that Configuration.md contains neither "unreadable also means no limit" nor "not offered yet", so neither paragraph comes back; both assertions fail today. The behaviour itself is already covered by tests/test_checks.py and tests/test_mail_security.py.
Found on 2026-09-29 in a read-only review of postulo at 95348908d and the wiki at de1f52467, reading `Configuration.md` against the configuration checks and the mail settings. Related to #233 (closed), which added the start-up configuration checks, and #158 (closed), which implemented implicit TLS: both changed the behaviour and documented it on this page, and each left an older paragraph standing that now says the opposite. This issue removes the two leftovers and adds the one rate the page never named. Checked again on 2026-10-01 against `main` at `0a5cad587`: still present. The code is now at `core/server_views.py:340-342` (where the recovery rate is spent), and `MailSecurity` moved to `core/mail_choices.py:17-27`, with 465 as the default port for `MailSecurity.SSL` at `core/mail.py:31-36` (`DEFAULT_MAIL_PORTS`). The wiki's `Configuration.md` contradicts itself in two places, and in both the stale statement is the one an operator meets first or reads last. The rate-limit section says a mistyped rate quietly means no limit, though start-up now refuses it. The Email section says implicit TLS on port 465 is not offered, though it is, and the same page explains how fifty lines further down. The rate table also omits `POSTULO_RECOVERY_RATE`. ## 1. Rate limits: an unreadable rate is said to mean no limit - **Where:** the wiki's `Configuration.md:661-663` (the note under the rate table), against `Configuration.md:135-140` on the same page and `core/checks.py:77-95` (`rates`, which reports `postulo.E002` when `throttle.is_well_formed` at `core/throttle.py:58-66` fails). - **What happens:** the note reads "Anything unreadable also means no limit, deliberately: a mistyped rate should leave you with a working instance rather than a locked one." That was true of `throttle.parse` alone. Since #233, `docker/entrypoint.sh:34` runs `manage.py check --deploy --fail-level ERROR`, and a rate that is not `<times>/<s|m|h|d>`, empty or `0` stops the container. An operator who writes `POSTULO_API_RATE=600/hour` on the strength of the note gets an instance that does not start: the pattern in `core/throttle.py:39` accepts only `s`, `m`, `h` or `d` as the unit. - **Why it matters:** the page says two opposite things about whether a typo can stop the instance, and the wrong one sits in the section an operator reads when setting a rate. - **Fix:** replace the second and third sentences of the note with: "A rate that cannot be read stops start-up with `postulo.E002`, naming the variable; empty or `0` means no limit." ## 2. Rate limits: `POSTULO_RECOVERY_RATE` is not on the page - **Where:** the table at `Configuration.md:652-659`; `config/settings/base.py:433-436`, which defines `POSTULO_RECOVERY_RATE` (default `10/h`), and `core/server_views.py:339-341`, which spends it when an administrator issues a recovery link. - **What happens:** of the eleven `POSTULO_*_RATE` settings in `config/settings/base.py`, ten are named in the wiki (six in this table, `POSTULO_TEXT_RATE`, `POSTULO_TEXT_PER_NUMBER_RATE` and `POSTULO_CONFIRMATION_RATE` further down the page, `POSTULO_NUMBER_RATE` in `Your-career-record.md`). `POSTULO_RECOVERY_RATE` appears nowhere in the wiki; only `.env.example:202-205` describes it. *Getting somebody back in without email* (`Accounts-and-invitations.md:270`) explains recovery links without saying they are bounded. - **Why it matters:** this is the limit on how many recovery links one administrator session can mint, each of them "a whole account in a URL" in the words of the setting's own comment. An administrator who meets "That is a lot of recovery links. Try again shortly." has nowhere to look it up. - **Fix:** add a row for `POSTULO_RECOVERY_RATE` (`10/h`, recovery links one administrator may issue) to the table, and a sentence naming it in *Getting somebody back in without email* that links back to the table. ## 3. Email: implicit TLS on port 465 is said not to be offered - **Where:** the wiki's `Configuration.md:481-482`, against `Configuration.md:529-557` (*STARTTLS or implicit TLS, and why the port decides*), `config/settings/base.py:713-723` (`POSTULO_EMAIL_SECURITY`: `none`, `starttls` or `ssl`), `core/mail.py:26-46` (`MailSecurity.SSL`, with 465 as its default port) and `plugins/smtp/__init__.py:77`, which passes `use_ssl`. - **What happens:** the paragraph reads "Only STARTTLS is supported, which is a server on port 587 that upgrades the connection after opening it. Implicit TLS on port 465 is not offered yet, in the environment or on the page." Both are offered since #158: `POSTULO_EMAIL_SECURITY=ssl` in the environment (listed in the table just above, at `Configuration.md:468`) and the three-state *Connection security* control on *Server settings → Email*. An operator whose provider documents SMTPS on 465 first, which the page itself says of Gmail, Microsoft 365, Fastmail, OVH and most shared hosting (`Configuration.md:537-538`), reads the earlier paragraph, concludes Postulo cannot reach it, and routes mail through a relay or a transport plugin they did not need. - **Why it matters:** the page contradicts itself on one of the most common mail configurations, and the wrong statement comes first. - **Fix:** delete `Configuration.md:481-482`, or replace it with one sentence pointing to *STARTTLS or implicit TLS, and why the port decides* below. ## Done when - `Configuration.md` no longer says an unreadable rate means no limit, no longer says implicit TLS is unavailable, and names `POSTULO_RECOVERY_RATE` with its default. - A test in `tests/test_wiki_surface.py` asserts that every name `core.checks.rate_settings()` returns appears somewhere in the wiki; it fails today on `POSTULO_RECOVERY_RATE`. (Like the rest of that file it runs only where the wiki is checked out beside the repository.) - The same file asserts that `Configuration.md` contains neither "unreadable also means no limit" nor "not offered yet", so neither paragraph comes back; both assertions fail today. The behaviour itself is already covered by `tests/test_checks.py` and `tests/test_mail_security.py`.
tiagoagueda added this to the 0.6.0 milestone 2026-10-01 10:46:27 +00:00
Author
Owner

Fixed in ebb9290d0 on main.

Fixed in ebb9290d0 on main.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Postulo/postulo#404
No description provided.