70.1.41. pepsi-quota

Manage and reconcile per-account mailbox quotas

Manual section:

1

70.1.41.1.1. Name

pepsi-quota - set per-account mailbox quotas and re-measure mailboxes.

70.1.41.1.2. Synopsis

pepsi-quota [GLOBAL-OPTIONS] set LOGIN SIZE [–messages N]
pepsi-quota [GLOBAL-OPTIONS] unset LOGIN
pepsi-quota [GLOBAL-OPTIONS] show LOGIN
pepsi-quota [GLOBAL-OPTIONS] list [–full]
pepsi-quota [GLOBAL-OPTIONS] measure LOGIN
pepsi-quota [GLOBAL-OPTIONS] reconcile [–all]
pepsi-quota [GLOBAL-OPTIONS] remove LOGIN

70.1.41.1.3. Description

pepsi-quota is the operator interface to the pepsi.mailbox_quota table: the per-account mailbox limits that pepsi-stage-relay-to-maildir(1) enforces, and the usage accounting both it and pepsi-ingress(1) work from. It is not a stage.

Quota is a property of a local account, so every row is keyed on the passwd login — not on an e-mail address, and not on a uid. A login is what pepsi-ingress(1) can compare an envelope recipient against at RCPT time without resolving aliases (which it must not do, and cannot), and it stays one row when several addresses reach one mailbox.

70.1.41.1.3.1. Policy, measurement, estimate

Three layers, and knowing which is which explains everything this tool does.

Policy is [pepsi] MAILBOX_QUOTA (the site default; absent means unlimited), overridden per account by pepsi-quota set, and tightened further by the kernel’s own limit where the filesystem enforces one.

Measurement can only be taken by pepsi-helper-maildir-writer(1), which is the single process that becomes the user and can read a 0700 Maildir. measure and reconcile run it; nothing else here touches a mailbox.

Estimate is the used_* + since_* counters. Pepsi’s own deliveries add to them and nothing ever subtracts, because nothing tells Pepsi when a user deletes mail over IMAP. The estimate is therefore an upper bound on real usage — which is exactly what makes it safe as a trigger (“could this account be near its limit? then measure”) and unsafe as a verdict.

Hence the rule the whole design rests on: nothing refuses a message on an estimate, only on a measurement.

70.1.41.1.3.2. Why reconcile exists

An account at its limit is refused at RCPT by pepsi-ingress(1). Suppose its owner then empties the mailbox over IMAP. Nothing tells Pepsi — and since every message is being refused, no delivery ever runs to take a fresh measurement. The mailbox would stay shut for ever.

Two things prevent that. pepsi-ingress(1) refuses only on a measurement younger than [pepsi] MAILBOX_QUOTA_MAX_AGE (15 minutes by default), so a stale figure lets the message through and the delivery path looks again. And pepsi-quota reconcile, run from cron, re-measures the accounts that are at their limit — a cheap sweep precisely because it is only those.

A site that enforces quotas should run it periodically:

*/10 * * * *  pepsi  pepsi-quota reconcile

70.1.41.1.4. Sub-commands

set LOGIN SIZE [–messages N]

Give one account its own limit, overriding the site default. SIZE accepts a plain byte count or a K/M/G/T suffix in powers of 1024 (2G, 500M, 1048576), or the literal none.

none is not the same as unset: it means explicitly unlimited, and is how a single account is exempted from a site-wide quota, whereas unset restores inheritance of it.

Omitting --messages leaves the message-count limit inheriting [pepsi] MAILBOX_QUOTA_COUNT; setting a byte quota does not silently lift it. --messages 0 lifts it explicitly.

A login with no passwd entry is warned about but accepted — the account may be created afterwards.

unset LOGIN

Drop the account’s own limits, so it inherits the site default again. The usage accounting is kept.

show LOGIN

Show one account’s limits and usage. An account with no row is not an error: it prints the inherited default and “never measured”.

list [–full]

List every account Pepsi has accounted for, oldest measurement first. With --full, only those at or over their limit.

measure LOGIN

Re-measure this mailbox now and store the result.

reconcile [–all]

Re-measure the accounts that are at or over their limit. With --all, also those whose measurement has merely gone stale. One unmeasurable mailbox (an account deleted since its last delivery, say) is logged and the sweep continues; the exit status is non-zero if any failed.

remove LOGIN

Forget the account entirely — its limits and its accounting. The next delivery re-creates the row and measures afresh, so this is also the “these numbers are wrong, start over” button.

70.1.41.1.5. Output

list, show, set and measure print one line per account:

alice             1200M/2G (58%)  4213 messages  measured 91s ago (maildirsize)
bob                 14M/-         112 messages   measured never (-) [site default]

The usage shown is the measurement plus everything delivered since, which is the figure the delivery path acts on. measured is an age rather than a timestamp because the question it answers is whether the figure is still worth believing: a refusal at RCPT rests on a measurement being fresh.

70.1.41.1.6. Privileges

pepsi-quota is installed set-group-id to the pepsi-maildir group (mode 2755, owner root:pepsi-maildir), for the same reason pepsi-stage-relay-to-maildir(1) is: measure and reconcile run the 4750 root:pepsi-maildir measuring helper, and the pepsi service account is deliberately not a member of that group — the setgid bit on the few programs allowed through is the gate. That is what lets an unattended reconcile from cron measure mailboxes without being root.

Run as root it drops to the pepsi service account before connecting to the database, which is the role that owns these rows — keeping the pepsi-maildir group across the drop. That is a consistency fix, not a widening: run from its timer as pepsi the setgid bit supplies that group and no drop happens at all, so without this measure would work from cron and fail for an operator typing it as root. It gains root’s run nothing the timer’s run does not already have.

make install sets the bit up (its install-quota-tool step) provided it is run as root and the group exists; otherwise it prints the exact commands.

70.1.41.1.7. Files

pepsi.mailbox_quota

The table this tool manages: one row per account, carrying its limits, the last measurement and what has been delivered since. Created by pepsi-setup(1) with the rest of the schema; rows appear by themselves on an account’s first local delivery, so nobody has to be pre-registered.

~/Maildir/maildirsize

The Maildir++ size file the helper maintains, in the format Dovecot, Courier and Exim also read and write.

70.1.41.1.8. See Also

pepsi-stage-relay-to-maildir(1), pepsi-helper-maildir-writer(1), pepsi-ingress(1), pepsi-status(1), pepsi.conf(5), maildir(5), quotactl(2)

70.1.41.1.9. Bugs

Report bugs to the Pepsi issue tracker.