54. pepsi-stage-secretary¶
Hold mail from unknown senders until they confirm by replying.
54.1. Role¶
pepsi-stage-secretary is confirm-to-send, after qmail’s qsecretary. Mail from a
sender no whitelist knows is held, and the sender is asked — in their language —
to reply once. The reply whitelists them and releases everything held for them;
later mail from them passes pepsi-stage-check-whitelist and is never held
again. Without a reply by HOLD_TIME the held mail takes the timeout path
(BOUNCE_STAGE if one is wired, else it is deleted). Reference:
pepsi-stage-secretary(1).
It is weaker than pay-to-send (pepsi-stage-anti-spam): a spammer with a
working mailbox can reply, and the declaration the sender agrees to (PENALTY) is
a legal deterrent rather than an economic one. It is still worth having, because
most bulk mail comes from addresses that cannot receive, and a real correspondent
pays one reply, once. Supported Features compares the two.
54.2. What is challenged¶
A null-sender message, locally submitted mail and a whitelisted sender
(state.spam = false) pass untouched; state.spam = true takes the timeout
path. Everything else is challenged — unless it must not be, in which case it goes
to UNCHALLENGEABLE_STAGE (unset: the timeout path):
RFC 3834 says do not answer it (lists,
Precedence: bulk,Auto-Submitted:, service senders, delivery reports);its
From:is not exactly the envelope sender — the challenge goes to MAIL FROM and the whitelist matchesFrom:, so they must be one address;REQUIRE_AUTHENTICATED(default on) and neither SPF nor DMARC passed — the backscatter gate;the sender has had
MAX_CHALLENGES_PER_SENDER(default 5) challenges in the last 24 hours, across every whitelist.
One challenge covers every message from one sender to one whitelist: a second
message joins the open challenge and inherits its deadline. A message whose
recipients expand WHITELIST_NAME to different lists is split one row per list.
54.3. The challenge and the reply¶
The challenge is a null-sender text/plain message with Auto-Submitted:
auto-replied, injected at RESPONSE_STAGE. Its From: and Reply-To: are
the reply address <CONTROL_LOCAL_PART>-<cookie>@<domain> (default
secretary-…), whose cookie is a random 128-bit identifier. It never quotes the
held message — a challenge to a forged sender would otherwise deliver the spam for
the spammer — beyond an 8-character hint of its subject (SUBJECT_HINT_LENGTH).
The text comes from MESSAGE_<LANG> in the stage’s own section (a user’s own,
through the settings layer) or the secretary-challenge.<lang>.body template,
per language, with a built-in English fallback; the fields are SENDER_NAME,
RECIPIENT, SUBJECT_HINT, ORIGINAL_DATE, DEADLINE,
REPLY_ADDRESS and PENALTY. Language choice and the two-space rule are
pepsi-stage-vacation’s.
A reply to the address confirms when it comes from the challenged address (its
envelope sender or From:). A null-sender reply means the challenge bounced, and
releases the held mail to the timeout path at once; a reply marked as automatic
(an out-of-office) is ignored. A reply after the deadline has no effect: the
challenge is gone. The reply itself is always consumed.
54.4. Placement¶
After pepsi-stage-detect-language and pepsi-stage-check-whitelist,
before pepsi-stage-aliases, the list router and local delivery. Run
pepsi-stage-auto-whitelist into the same WHITELIST_NAME on the outbound
path so the people your users write to are never challenged. pepsi-setup’s
wizard wires all of it when asked “Ask unknown senders to confirm by reply”.
54.5. Configuration¶
[stage-<name>]: PROGRAM = pepsi-stage-secretary, NEXT_STAGE and
RESPONSE_STAGE (mandatory), WHITELIST_NAME (mandatory, one name or a
{login}/{localpart} template; the operator may name any whitelist, a
shared one included, while a value an account owner sets by mail must stay in
their <login>/... namespace or be the operator’s – enforced by
pepsi-stage-edit-settings when it stores the value),
BOUNCE_STAGE, UNCHALLENGEABLE_STAGE,
CONTROL_LOCAL_PART, HOLD_TIME, REQUIRE_AUTHENTICATED,
MAX_CHALLENGES_PER_SENDER, DKIM_REQUIRED, CONFIRM_NOTICE, PENALTY,
SUBJECT_HINT_LENGTH, TEMPLATE, SUBJECT, DEFAULT_LANGUAGE, the
MESSAGE_<LANG>/SUBJECT_<LANG>/CONFIRM_MESSAGE_<LANG> overrides and the
locality options. pepsi-setup rejects unresolvable targets and a
DEFAULT_LANGUAGE with no text, and warns when no check-whitelist consults the
whitelist, no auto-whitelist writes it, or CONTROL_LOCAL_PART is a login or an
alias. See pepsi-stage-secretary(1) and Configuration.
54.6. State¶
Inputs:
state.spam,state.local_origin,state.auth(SPF, DMARC, DKIM, ARC),state.language.Outputs:
state.secretaryon a held row (challenge,deadline,whitelist; thenconfirmedorabandoned),state.secretary.unchallengeableon mail routed toUNCHALLENGEABLE_STAGE, andstate.spam = falseon released mail. The timeout path removesstate.secretary.state.dsnis preserved.Transitions: advance to
NEXT_STAGE; pause while held; reroute toUNCHALLENGEABLE_STAGEorBOUNCE_STAGE; delete a reply, or timed-out mail when there is noBOUNCE_STAGE.
54.7. Database¶
pepsi.secretary_challenge (one open challenge per whitelist and sender, deleted
when it is answered, bounces or expires) and pepsi.secretary_sent (when each
challenge was sent, without its cookie, for the daily cap). A confirmation writes an
ordinary pepsi.whitelist row, escaped and anchored exactly as
pepsi-stage-auto-whitelist writes one.
54.8. See also¶
pepsi-stage-check-whitelist, pepsi-stage-auto-whitelist, pepsi-stage-anti-spam, pepsi-whitelist, pepsi-settings, Supported Features, pepsi-stage-secretary(1).