A channel the instance can reach a locked-out person on #143
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
security
tier
1
tier
2
tier
3
tier/4
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Blocks
#144 The account's primary number is a way back in
Postulo/postulo
Reference
Postulo/postulo#143
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Observation
Prerequisite. There is no SMS anywhere in Postulo — one comment in
notifications/transport.pynaming it as a route that has not landed, and nothing else.What exists
Two ways out of the machine, and they are not the same kind of thing:
reminders. It is instance plumbing, belongs to nobody, and is deliberately exempt from the
per-person plugin policy;
Connectionthat person made andwhose credentials they supplied.
Connectionis anOwnedModel.recovery_routes()returnsemailwhen a transport is selected, andpasskeywhen noactive account is without one. Its docstring names what is missing:
What this asks for
A way for the instance to reach somebody on a telephone, good enough to carry a verification
code and a way back in.
Worth being careful about
A recovery channel cannot be a per-person notifier, and this is the crux. A notifier's
credentials belong to the person: their Twilio account, their Apprise endpoint. A person
locked out of their account is exactly the person whose gateway may also be unreachable, and
worse, "the account holder configured the channel that proves they are the account holder"
is circular. A recovery SMS has to go through something the instance operates — which
makes it a transport in shape, not a notifier, even though the message is not mail.
That may mean a second transport kind, a transport that carries more than mail, or a new kind
altogether. Deciding it is most of this issue.
Every gateway is somebody else's jurisdiction. Postulo's whole argument is that a
self-hoster's data answers to them; an SMS route means a telephone number, a message and a
timestamp reaching Twilio, Vonage or a national aggregator on every send. That is a real
cost to state in the interface where the channel is configured, not a detail — and it is a
reason some operators will not want it, which means it must stay optional and the lock must
stay honest without it.
#103 lists three candidates and SMS is only one. An administrator issuing a recovery link
needs no third party at all and solves the same problem for a self-hosted instance with one
administrator; Apprise reaches a dozen services through one connection. Whether SMS is the
right first answer is worth asking inside this issue rather than assuming, given the
suggestion arrives with #103 already open.
Rate limits and cost. Mail that fails costs nothing; SMS that loops costs money and
annoys a stranger whose number was mistyped.
POSTULO_CAPTURE_RATEand its siblings are theprecedent — a send limit per account and per number, set low, and a refusal that says so.
A code sent by SMS is a second factor's worth of security at best. SIM swapping is the
standard attack and it is not exotic. Whatever lands should say what it is worth: enough to
get back into a self-hosted job-search application, not enough to be the only thing guarding
an account that also holds passkeys.
It has to be a plugin, and therefore subject to the plugin rules — #126's surface,
#128's data question, and #129's self-containment. A transport that is also a recovery route
is already locked against being switched off (#104) while it is the last way in.
The open question here — a recovery channel cannot be a per-person notifier, because the account holder configuring the channel that proves they are the account holder is circular — has an answer from the maintainer, filed as #149: mail splits into an instance half and a person half, and only the instance half is ever a recovery route.
That settles the shape question this issue raised without settling this issue. A recovery SMS still has to be carried by something the instance operates, and the split says what that means: transport-shaped, ungoverned, locked by #104 while it is the last way in. Whatever lands here follows the same rule as the mail transport rather than inventing a second one.
The decision, which the issue said was most of the work: one kind, two mediums.
Not a notifier — the issue's crux, and it is right. A notifier's credentials belong to the
person, somebody locked out is exactly the one whose own gateway may be unreachable, and "the
account holder configured the channel that proves they are the account holder" is circular.
Not a second kind either. Selection, configuration, the #104 interlock, the exemption from
per-person policy and the plugins page are identical for mail and text; what differs is the
payload. That is what a field describes and what a kind would have duplicated four times over.
So
transportgains amedium—mailortext— and a transport that does not say carriesmail, so nothing already written has to be edited. Each medium keeps its own configuration
columns, because both are selected at once and one blob could hold only one of them.
Postulo ships no gateway. That is the answer to "every gateway is somebody else's
jurisdiction", not an unfinished edge: a number, a message and a timestamp reaching Twilio,
Vonage or an aggregator on every send, from an application whose argument is that a
self-hoster's data answers to them. The kind exists; the package is somebody else's, exactly
as for mail over an HTTP API. Everything works without one.
On whether SMS is the right first answer — it is not, and that is recorded rather than
assumed. It needs a third party, costs money per message, and is defeated by a SIM swap. An
administrator issuing a recovery link needs nobody else and answers the same question for the
self-hosted instance with one administrator, which is most of them (#103). What a text gateway
is for here is confirming a number at all (#142); being a recovery route is something it may
earn afterwards.
And it earns it on the same bar the passkey route is held to: a gateway and a confirmed
number on every active account. This list decides whether mail may be switched off, so a route
covering nine accounts in ten would strand the tenth.
Two limits, not one, because two different mistakes want bounding.
POSTULO_TEXT_RATE(5/h) is somebody driving a resend button.
POSTULO_TEXT_PER_NUMBER_RATE(3/h) is a strangerwhose number was mistyped into a form — the one that matters, since they never asked to be
involved and cannot switch anything off. The per-number limit applies even when nobody is
signed in, which is exactly the state a recovery flow is in.
A gateway failure is logged without the number or the body: that line reaches an operator's
log and the body is a code that gets somebody into an account.
TelephoneChannel.carrier()now answers with the gateway's name, so #146'sconfirmable()becomes true the moment an operator installs one — nothing in the contract changes, only the
answer.
25 tests in
tests/test_text_channel.py, each bringing its own gateway; a wiki section underConfiguration; five strings in all 39 European catalogues.
Shipped in
c9796e0on0.3.0, withmainkept level.