85.1.33. pepsi-stage-edit-settings

change per-address settings from a control e-mail

Manual section:

1

85.1.33.1.1. Name

pepsi-stage-edit-settings - the settings-by-e-mail stage of the Pepsi pipeline.

85.1.33.1.2. Synopsis

pepsi-stage-edit-settings [GLOBAL-OPTIONS] worker

85.1.33.1.3. Description

pepsi-stage-edit-settings is a stage program run by pepsi-dispatch(1) as a persistent worker reading message ids on standard input. It loads that pepsi.workqueue row (refusing to act unless its status is running), reads its [stage-<stage>] section, and decides whether the message is a settings-control message an account owner sent to change their own per-address settings (the pepsi.settings overrides described in pepsi-settings(1)).

A message is a control message only when all of these hold:

  • it is locally originated (state.local_origin is true) and has a non-empty envelope sender (a null-sender message is never a control message);

  • its Subject: is exactly SUBJECT (default Pepsi); and

  • one of its envelope recipients is <CONTROL_LOCAL_PART>@<domain> (default local part pepsi) for a domain the instance accepts ([pepsi-ingress] ACCEPTED_DOMAINS).

Any other message is advanced unchanged to NEXT_STAGE, so the stage is safe to place anywhere on the local-submission path.

Deciding whether a message is a control message — locally originated, the right Subject, addressed to the control local part at an accepted domain — needs only the message metadata, never the body. This stage therefore takes part in stage fusion (see pepsi-dispatch(1)) with a data-dependent gate: it defaults to FUSION = yes and a predecessor fuses it in-process for every non-control message (which it just advances), but declines fusion for a genuine control message, which is then committed and dispatched as its own worker so the full body is loaded for parsing. Set FUSION = no to opt out.

The address whose settings are edited is the envelope sender — the owner edits their own settings, and the reply is mailed back to them. (The operator’s submission authentication must therefore bind the envelope sender; otherwise an authenticated user could edit another address’s settings.)

85.1.33.1.4. The body

The message body is read as a lenient INI snippet: [stage-<name>] section headers and OPTION = value assignments. Blank lines, comments (#/%/;), a leading greeting, and a trailing signature (a -- line) are ignored, so an ordinary e-mail containing the configuration works. Each assignment adds or overrides the stored override for that stage’s option; an assignment whose value is empty (OPTION =) removes the override, reverting the option to its INI default.

Only sections whose stage name appears in EDITABLE_STAGES may be changed; a body addressing any other section is rejected.

For safety, an assignment that sets a stage’s PROGRAM is only accepted when its value is a pepsi-stage-* command (with no path component), so an account owner cannot redirect a stage at an arbitrary executable; and an assignment that sets SIGNING_DOMAIN is refused outright, however permissive EDITABLE_STAGES is. That option overrides the domain a message is DKIM-signed as, so allowing it would let any account owner have their mail signed as any domain this host holds a key for. Setting UNRESTRICTED_UNSAFE_STAGES = YES in the stage’s configuration lifts both restrictions (unsafe; the default is NO).

For the same reason pepsi-setup --wizard never puts a signing stage in EDITABLE_STAGES: it lists only the inbound gates and the vacation responder, and where none of those is enabled it does not configure this stage at all.

The resulting combined settings — the INI defaults, plus the address’s existing overrides, plus the new assignments — are validated by running each affected stage’s own configuration parser, plus the checks that apply only to an account owner’s own choice: the WHITELIST_NAME of a stage that writes a whitelist on the owner’s behalf – pepsi-stage-auto-whitelist(1), which records the recipients of their outgoing mail, and pepsi-stage-secretary(1), which records the senders who answer a challenge – must name a whitelist in the sender’s own <login>/... namespace, or the one the operator’s configuration already names for them; a shared whitelist or another user’s is refused. The login is resolved with the locality options (LOCAL_DOMAINS, RECIPIENT_DELIMITER, TARGETS) of the operator’s stage section, not with any override of the owner’s own. The rule applies only when the message changes the whitelist the sender’s mail feeds, so a value already stored in the row – which the operator may have set with pepsi-settings(1) – does not stop the owner from editing other options. This stage is the only way an account owner can write pepsi.settings, so it is where the rule is enforced; the operator’s own layers (the INI file, pepsi.config_override at any scope, and rows written with pepsi-settings(1)) may name any whitelist. The settings are judged on top of the operator’s configuration for the sender, domain: and address: overrides of pepsi.config_override included, and the reply quotes that same effective configuration. If anything is invalid, no change is made and a human-readable reply explains every problem. If everything is valid, the merged overrides are stored and a reply quotes the full effective option set (defaults plus overrides) of every editable stage, in INI syntax.

A stage listed in EDITABLE_STAGES has its whole effective configuration quoted back to any account owner who sends a control message, not just the options they changed. Option values whose names look credential-bearing (PASSWORD, PASSPHRASE, SECRET, TOKEN, CREDENTIAL, CLIENT_ID, PEPPER) are replaced with ***, the same masking GET /api/v1/config applies — but the rest of the section, including host names and file paths, is disclosed verbatim. Do not list a stage whose section carries anything you would not show a local user.

Either reply carries Subject: SUBJECT, the null envelope sender <> (RFC 3834, so it is never itself gated or bounced), and is injected at RESPONSE_STAGE (where it is DKIM-signed and relayed like any outbound mail). The control message itself is then removed.

The framing prose of both replies is localised: it is rendered from the operator-customizable edit-settings.<lang>.body Mustache template under [pepsi] TEMPLATE_DIR, choosing <lang> from the sender’s detected language(s) (the state.language Accept-Language string set by pepsi-stage-detect-language) and always falling back to English (edit-settings.en.body). The template’s {{#success}} / {{^success}} branches frame, respectively, the effective-settings echo and the problem list.

This stage loads the message body but does not modify it.

85.1.33.1.5. Configuration

Options live in the stage’s own [stage-<name>] section (PROGRAM = pepsi-stage-edit-settings): the required EDITABLE_STAGES allowlist (which must name at least one existing stage), the required RESPONSE_STAGE the reply is injected at, and a NEXT_STAGE for ordinary mail — also required, since pepsi-setup(1) refuses a section without one: every non-control message is advanced there. The optional SUBJECT/CONTROL_LOCAL_PART/RESPONSE_FROM control parameters and the UNRESTRICTED_UNSAFE_STAGES safety switch complete the section. They are documented in pepsi.conf(5).

The stage reads its own section from the base configuration, never through the per-address override layer, so no account owner’s pepsi.settings row can change what this stage considers a control message or what it allows.

85.1.33.1.6. State

Inputs: state.local_origin (gates whether the message is a control message), state.language (picks the language of the reply), and the envelope sender/recipients and Subject: header.

Outputs: the stage rewrites the pepsi.settings row of the envelope sender and injects a reply; it does not modify the state of the original message (which it removes). The state layout is described in pepsi.state(7).

Transitions:

  • an ordinary message (not locally originated, or not matching SUBJECT / CONTROL_LOCAL_PART) → advance to NEXT_STAGE unchanged;

  • a control message → inject the reply (the INI echo on success, or the error on a rejected/invalid edit) at RESPONSE_STAGE and finish (the original row is deleted).

The stage never pauses, fails or reroutes.

The confirmation is rendered before the new settings are stored, so a missing reply template is retried with nothing changed. Once they are stored, a reply that cannot be queued is logged and the request deleted: the change is made, and must not be made again.

85.1.33.1.7. Commands

worker

Run as a persistent pepsi-dispatch(1) worker, reading message ids on standard input.

85.1.33.1.8. Global Options

-c FILE, –config FILE

Read the configuration from FILE instead of searching the default locations.

-L LOGLEVEL, –log LOGLEVEL

Set the logging verbosity (default info).

-v, –verbose

Show log messages from all sources.

-h, –help; -V, –version

Print a usage summary / the version and exit.

85.1.33.1.9. Exit Status

0

The message was processed (passed through, or applied/rejected with a reply).

1

An error occurred (message not found or not running). The reason is written to the log.

A fault of the host (the database, a template or helper that cannot be used) is not reported as a failure: the message is paused and retried, as Stage errors in pepsi-dispatch(1) describes. A section that does not parse makes the worker refuse to start (status 78) instead of failing each message in turn.

85.1.33.1.10. Examples

A pipeline section that lets owners edit their whitelist and language policy, replying via the outbound path:

[stage-edit-settings]
PROGRAM = pepsi-stage-edit-settings
EDITABLE_STAGES = check-whitelist, block-language
RESPONSE_STAGE = dkim-sign
NEXT_STAGE = srs

A control e-mail body (Subject: Pepsi, To: pepsi@example.org) that sets a whitelist and clears a language blacklist:

[stage-check-whitelist]
WHITELIST_NAME = my-contacts

[stage-block-language]
BLACKLIST =

85.1.33.1.11. See Also

pepsi-settings(1), pepsi-config(1), pepsi-dispatch(1), pepsi-stage-check-whitelist(1), pepsi-stage-block-language(1), pepsi.conf(5), pepsi.state(7), pepsi-setup(1)

85.1.33.1.12. Bugs

Report bugs to the Pepsi issue tracker.