Second of three for background push across accounts (multi-account
spec, MA-8 part 2).
Turning on "Notify me even when inbuxa is closed" now registers this
browser in every signed-in account, each through its own session (the
route from the previous change), and renewal keeps them all current.
GET /api/auth/accounts says which mail account each session is, so the
subscription can name it. The remembered endpoint is kept per account,
and survives the clean-up that follows switching accounts.
The service worker is told who the other accounts are. A push for one
of them is titled with that account's address, shown even while a tab
is focused (the tab only shows the front account's mail), and its
Mark read and Archive act through that account's session. Clicking it
brings that account to the front and opens the message. While push is
on, the tab's own polling of other accounts stops notifying, so nothing
arrives twice.
Signing out of one account removes this device's subscription there
only; signing out of all, or turning push off, removes every one.
Also fixes where every background notification opened: the worker
linked to /mail/inbox/<thread>, and the route takes a mailbox id there,
so a click landed on the inbox list with "That folder no longer
exists". The worker now gets each account's inbox id and links to the
message.
Checked end to end in Chrome against a local server with two accounts:
both registered and verified, a message to the account not in front
showed a notification under its address, and clicking it switched
accounts and opened the message. No new strings. typecheck, tests
(web 1547, server 279) and build pass.
First of three for notifications with the app closed, for every
signed-in account (multi-account spec, MA-8 part 2). No behavior
changes yet.
A JMAP push subscription belongs to whoever signs the request, so an
account that isn't in front can only have one registered, verified and
renewed through its own session. POST /api/auth/accounts/<sessionId>/jmap
forwards to the mail server as that session, when it is one of this
browser's other accounts, and only for PushSubscription/get and /set,
Mailbox/get, and Email/set limited to keywords and mailboxIds updates
(what a notification's Archive and Mark read need). Anything else is
403; the account in front, or a session this browser doesn't hold, is
404. Its OAuth token is renewed first if due. The browser already holds
the session, so nothing new becomes reachable.
On the web app side the push helpers (list, create, extend, destroy,
verify) take a JMAP caller, defaulting to the account in front exactly
as before, and lib/notify/otherAccount gives the caller for another
account through the route.
Tests: the allowlist, the route end to end with two accounts (allowed,
refused, front and unknown sessions), and the caller. The accounts test
file now raises LOGIN_RATE_LIMIT, since it signs in more often from one
address than the default allows. typecheck, tests (web 1543, server
278) and build pass.
With more than one account signed in (#49), mail arriving in one that
isn't in front went unseen until someone switched to it (multi-account
spec, MA-8).
- GET /api/auth/accounts/unread answers the Inbox unread count of each
account not in front, asked through that account's own session (its
OAuth token renewed first if due), kept a minute per account.
- The web app asks every two minutes while another account is signed
in. The account menu shows each one's count beside its name, and the
avatar carries a dot when any of them has unread mail.
- When a count rises while the app is open and desktop notifications
are on, a notification names the account ("New mail for
[email protected]"); clicking it switches to that account. An
account seen for the first time doesn't notify: its mail was already
there.
Not in this change: notifications with the app closed, which need each
added account's own Web Push subscription.
New strings (3, English only in the other ten catalogs): "New mail for
{name}", "Unread in the Inbox: {count}", "Account: new mail in another
account". Tests: the server answers the other account's count and
nothing when alone; the client keeps the counts, notifies only on a
rise and only with notifications on. Checked in Chrome against the
mock. typecheck, tests (web 1541, server 277) and build pass.
Turkish shipped (ihasmail #32/#37), so ten languages ship alongside English, not nine. CONTRIBUTING.md, the PR template and the emlName.ts comment said nine; ROADMAP.md and the i18n-literals.mjs comment describe what shipped on 2026-08-31 and stay as they are. Translations: no user-visible strings added or changed, so no catalog work.
Someone who looks after several mailboxes of their own can now keep
them all signed in in one browser and move between them from the
account menu, without signing out (multi-account spec, MA-B; forum
topic 75).
How it holds them. The session cookie is unchanged: it is the account
in front, and every request is answered with it, so nothing else in the
server changes. The others ride in a second cookie, <name>_more, as a
list of their own session cookies. Each session stays its own -- sealed
credential, expiry and "this is my device" -- and nothing about one is
read through another. At most 5 in all, all on this mail server.
- Add account (account menu): with sign-in on the mail server's page
it goes there with prompt=login, so the server asks again rather than
reuse the first sign-in; with the password form, a small dialog asks.
Refused, and the front stays, when either account's organization has
addAccounts off (inbuxa:SharingPolicy), the account is on another
server, or 5 are open. The same account again just comes to the
front.
- Switching (POST /api/auth/accounts/<id>/front) swaps it into front;
the web app clears what it cached for the previous account and
reloads. A message being written blocks the switch.
- Sign out ends only the account in front, and the next one comes
forward; Sign out of all accounts ends every one.
- GET /api/auth/accounts lists them, the front first, and says whether
one more may be added.
Not in this change: unread counts and notifications for the accounts
not in front (MA-8), which the spec puts last.
The mock can sign in a second user (MOCK_SECOND_USER/PASS) and answer
addAccounts false (MOCK_NO_ADD_ACCOUNTS), for the new server tests:
two accounts joining, switching, the same account twice, a switch to a
session it doesn't hold, signing out of one and of all, and an
organization that forbids it; and the OAuth start asking prompt=login.
The client tests cover listing, switching (cache cleared, reload) and
both sign-outs. Checked in Chrome against the mock: add, switch, sign
out of one.
New strings (9, English only in the other ten catalogs): "Add
account", "Sign out of all accounts", "Add an account", "Both accounts
stay signed in here; switch between them from this menu.", "Working…",
"That account couldn't be added.", "You can't add more accounts here.",
and the other-server and organization refusals. typecheck, tests (web
1538, server 276) and build pass.
The server now marks a shared mailbox (support@, legal@) as a
delegation of kind "sharedMailbox" (multi-account spec, MA-S). Without
this, the webmail would show one exactly as it shows a locked account:
a red bar, a padlock in the tab and on the brand, and its calendars and
files in place of the reader's own.
Now such a mailbox is listed with the other shared mailboxes under
"Mail to show", opens with the shared bar, which also gives the
person's access level and whether they can send as it, and changes
mail only. Its access level still applies exactly as a lock's does:
read changes nothing, organize never deletes, no sending without
send-as. Found without asking Mailbox/get, since the server's mark says
it is mail.
A server that sends no kind is treated as before: every delegation is a
lock. No new strings. typecheck and vitest (174 files, 1534 tests)
pass.
A group's members, and anyone a folder was shared with, could reach that
mail over IMAP but not here: Mail read only the reader's own account.
Calendar, Contacts and Files already list other people's shares; Mail
now does too (multi-account spec, MA-A).
Such an account is listed under "Mail to show" in the account menu,
after any locked account handed to the reader, and opens in place of
the reader's own mail through the same switch AL-7 uses. Only accounts
whose Mailbox/get answers with a mailbox are offered: the server
advertises every capability on any shared account, so a colleague who
shared one calendar would otherwise appear with mail.
While one is in view:
- a bar in the palette's accent (not the locked account's red) names
it, with "Back to my mail"; the tab title names it without a padlock;
- calendars, contacts and files stay the reader's own (viewAccountFor
follows only a delegation now);
- writing a message uses the viewed account's identities, so a reply in
support@ goes out as support@ and is saved in its Drafts and Sent.
Losing the account (removed from the group, share withdrawn) takes the
reader back to their own mail with the existing "You no longer have
access" notice.
New string: "Shared mailbox:" (1, English only in the other ten
catalogs). Checked in Chrome against a scratch server with a support@
group and two members. typecheck and vitest (174 files, 1533 tests)
pass.
On a wide screen the switcher moves from the foot of the folder pane to a
rail down the left edge: Mail (with the inbox's unread badge), Calendar,
Contacts and Files, then Settings and the folder-list toggle at the
bottom. The top bar loses the menu button and the Settings gear there,
both now on the rail. Phones are unchanged: no rail, the tab bar, the
menu button for the drawer and the gear.
- Folder counts are pills: filled with the accent when there is unread
mail, neutral for totals (Drafts, Scheduled).
- Role folders get their own icon tint (Inbox, Drafts, Sent, Archive,
Junk, Trash, Scheduled); a color the reader picked still wins.
- Buttons, Compose and the current selection have a top-lit, raised look
with a pressed state; Compose and the selected rail item are filled
with the accent.
- Collapsing the sidebar keeps Mail's icon strip; Calendar, Contacts,
Files, Settings and Admin have no icon-only form, so their sidebar
hides instead of being crushed.
- The collapsed sidebar no longer draws a cut-off "FOLDERS": the rule
hiding section headings lost to a later one of the same weight (this
is on main today too).
Contrast: the accent behind small text is darkened in light themes and
lightened a touch in dark ones. Measured in a browser across all 12
palettes x 6 accents x both modes: text on pills, the rail badge and
Compose at least 4.83:1, tinted icons at least 3.14:1 on the sidebar.
New strings: 2 ("Show folder list", "Hide folder list"), in all ten
catalogs (1699 -> 1701).
The addresses written to were remembered only in the browser that sent
the message, as a list of recent recipients, so a new device or a cleared
browser suggested nobody. After each confirmed send, the recipients who
are not contacts yet are now saved on the server, in an address book of
their own called Collected, so they are suggested everywhere.
- Only addresses on no card in any address book, own or shared, are
added, de-duplicated, and never the sender's own identities.
- The book is created on first use and remembered by id in the synced
settings, so its name can be anything; a deleted one is replaced.
- Names are split as the contact editor does ("Smith, Jane").
- Settings > Calendar & contacts > Contacts has a switch, on by default,
as mail clients do; the book can be emptied or deleted like any other.
New strings: 3, in all ten catalogs (1699 -> 1702).
Recipient suggestions:
- the words typed match in any order, each one the start of a word in the
name, a nickname, the organization or the address: "jane smi" finds
"Smith, Jane", and "globex" finds the people at Globex;
- someone written to lately ranks a little above an equal match;
- an address already in To, Cc or Bcc is no longer offered in the other
two fields.
Pasting:
- a single web or mailto address pasted over selected words makes those
words the link, instead of replacing them with the address;
- a pasted or dropped image over 10 MB goes in as an attachment rather
than inline, where it would swell every reply.
No new strings.
Turkish came from public ihasmail (#41), which has no strings for what
only this webmail has: delegated and locked accounts, sign-in through
the mail server, the suite's About page, legacy protocol switches, the
data loss prevention notices, and held-for-review sends. Those 88 showed
in English.
They are translated now in the terms the Turkish catalog already uses
(Hesap, Yönetim, Kiracı, posta sunucusu, posta uygulaması), and the 22
Turkish entries no key looks up any more (ihasmail's Stalwart wording,
renamed here) are removed. Turkish: 1611/1699 -> 1699/1699, no stale
keys. These 88 are not a native speaker's; the rest of the catalog is.
When a send's request went out and no answer came back, the composer said
"Send failed" and offered the draft back. If the server had in fact
accepted it and only the reply was lost, sending the draft again
delivered the message twice. Nothing identified the first attempt, so
nothing could check.
Each send now carries its own Message-ID (on the sending identity's
domain, RFC 5322 §3.6.4), kept on the draft if it comes back. A failure
that may have happened after the server acted (no answer, a timeout, a
5xx from the proxy) is followed by asking the server what it did:
- a message with that Message-ID was submitted (EmailSubmission/query by
emailIds, RFC 8621 §7.3): it was sent, and the composer says so;
- one exists with no submission: it was created and never sent; it is
removed from Sent and the failure stands, so resending is safe;
- none exists: the failure stands;
- the server can't be asked either: the composer says it couldn't confirm
and to check Sent, instead of a plain "Send failed".
A refusal (4xx) means the server did not run the request, so it is not
checked. Sending a draft again that came back from a failure asks first,
and sends nothing if a message with its Message-ID already exists;
submissions are expunged after the server's hold period, so later on any
surviving copy counts as sent (every failure path removes the copy it
made).
New strings: 3, in all ten catalogs (1699 -> 1702, no new fallbacks).
On a phone or a narrow list, rows are two lines, and labels went on a third
line of their own. A row is a fixed height at each density, and that third
line fit it only at Comfortable: at Cozy, the default, the labels were cut
off at the bottom, and at Compact they sat outside the row.
They now sit on the sender's line, after the name, which has room to spare,
so the row's height is unchanged at every density. When the line runs short
the name gives way first, then each label truncates; the date stays.
Reported in coffey-labs/ihasmail#35.
(cherry picked from commit 22490d8fe0190d85d8039a977435245b0d7da3cc)
The repository moved from inbuxa/ihasmail-inbuxa to inbuxa/inbuxa-webmail,
matching inbuxa-server and inbuxa-admin. Point the source links, the image
name and the package link at the new name. The OAuth client id stays
ihasmail-inbuxa, since that is what the server registers.
The webmail half of inbuxa's DLP (dlp-and-mail-flow-rules spec, §2.5,
§4):
- A send the server's DLP rules refuse (inbuxa:dlpWarning or
inbuxa:dlpBlocked) comes back to the composer with the rules' notices
instead of a generic "Send failed". A warning offers "Send anyway…",
which asks for a reason and sends again with inbuxa:dlpOverride; the
server records the reason. A block can only be answered by changing
the message.
- A message DLP held for review says so on sending ("Held for review:
it's sent once a reviewer releases it"), from the submission's
inbuxa:held.
- Tests: the override travels with the submission only when there's a
reason; refusals are told apart from other errors.
Nine new English strings (the notice labels, the prompt, the toasts);
the other catalogs fall back to English until translated.
inbuxa can now switch IMAP, POP3 and ManageSieve off one at a time,
server-wide and per organization, and the session lists what is still
allowed for the account (legacyAllowed). Where the webmail said
"legacy protocols are off", it now also covers the case where only
some are:
- Security & sessions, above app passwords: "Your organization has
turned off POP3 for mail apps. Mail apps that use it can't connect
to this account; others still can."
- The Administration dashboard: "Some legacy mail protocols are off for
your organization: POP3."
- An organization's sheet in Administration: its switch stays the
all-or-nothing one; when only some are off it names them and points
to the console, where they're switched one at a time, and "Turn
legacy protocols back on" turns them all back on.
With every protocol off, the existing wording shows, as before. From a
server that doesn't send legacyAllowed nothing new appears.
3 new strings in all nine catalogs, unreviewed.
Tested: unit tests for reading the session and a tenant's switches;
the existing tests updated for the new field; typecheck; the whole
suite (1475 tests); and in headless Chrome against a local server with
POP3 off, where Security & sessions showed the new note.
Deleting a person's account is the console's now, beside locking it and
legal holds: the console asks why, for the audit log, and says when a
hold keeps the data. Where Delete was, the account's page says so and
links to the account in the console when the server names one. Groups,
lists, domains and tenants keep their delete here.
2 new strings in all nine catalogs, unreviewed.
Switching to a locked account handed to the reader moved only the mail.
Now calendar, contacts and files follow it as well, through a new
viewAccountFor that the three stores use for what they show. Settings,
signatures and push keep ownAccountFor, so nothing of the reader's is
ever written into the locked account (inbuxa AL-7).
The picker sorted folders A-Z by path, with Inbox first, so a folder
dragged into place in the sidebar turned up somewhere else when moving
mail. It now walks the tree in compareFolders order, the sidebar's
order with every folder expanded: Inbox, then the saved order, then
the special folders, then A-Z, with subfolders under their parent.
treeOrder lives beside compareFolders. A folder the walk from the top
cannot reach is appended rather than dropped, so it stays pickable as
it was before.
Closes#1
(cherry picked from commit ea03406646062359f74e16ad8a8aed074b4dc409)
With conversation view off, marking a message unread from the list --
the hover button, the right-click menu -- marked that message. Opening
it and pressing Mark as unread in the toolbar above it marked every
message in its thread, and so did Move to, Report spam and Delete.
The setting already reaches all the way into the reading pane: the list
draws one row per message, and `visibleMessages` narrows the pane to the
one opened. The toolbar was half converted. Its labels were right --
Mark as unread against Mark as read, the star, the labels shown -- all
of those read `messages`, which is the narrowed set. Only `rowIds`, the
one thing actually handed to the action, still read `thread.emailIds`.
So the button said one message and did the whole conversation.
`rowIds` is now the same question `visibleMessages` answers for the
pane, asked of the same ids, with the same fallback: an id that names
nothing in the thread -- a link from somebody with conversation view on,
a stale `m` in the URL -- shows the conversation, so the toolbar takes
the conversation. Conversation view on is unchanged: nothing is singled
out, so the whole thread comes back as before.
No new strings.
(cherry picked from commit f627bfc1237d6e8bbc728147022f624c3834d648)
A website contact form mails the site's own address: From and To are
both info@thesite, and the person who filled the form in is in Reply-To.
Replying addressed the draft to info@thesite -- the site's own desk --
instead of to them.
The reply already knows two shapes. A message somebody sent me is
answered to its Reply-To, which is what that header is for. A message
*I* sent is answered to the people I wrote to, and deliberately not to
my own Reply-To, which is where answers to me belong and would send my
reply to myself. A contact form passes the test for the second: every
address in From is mine.
So it fell down the chain the second shape keeps for a message with
nobody obvious to answer -- To without me, then Cc, then, having run
out, every address on the message, which here was mine alone.
The Reply-To now goes in that chain, one step before the last: when no
recipient but me is left and the message names a Reply-To that is not
mine either, that address is who it is really from. Keeping it after the
Cc is what leaves a message I did send alone -- somebody I actually
wrote to still beats my own Reply-To, which is the case the existing
guard was built for and its test still holds.
No new strings.
(cherry picked from commit 01dc322aebcff0e8075c54f81eb7d171a32fb9e7)
Reading a message fetches its remote images through this server, so the
sender learns nothing about the reader. Quoting the same message into a
reply fetched them directly: same pixel, same reader, but the request
carried their IP and user agent -- exactly what the proxy withholds.
A quote now proxies them the way the message view does. That alone would
be wrong, because a proxied URL belongs to this deployment: sent
unchanged it would reach the recipient as images only this server can
serve, broken for them and a beacon back here. So buildEmailObject turns
them back into the addresses they came from, beside the pass that
restores images blocked under pr411 and the one that turns editor blob
URLs into cid: references.
Deployments with the proxy off are unaffected: the quote fetches
directly, as reading does there.
Three tests from pr411 asserted the address sat in src when images were
allowed, which was the old behaviour; they now ask whether the draft
fetches it at all, proxied or not.
No new strings.
(cherry picked from commit 23557a72a2a72088081792f8ea0cabedea4e7bcb)
Replying sanitized the quoted body with allowRemote: true, so quoting
fetched every remote image in the message whatever the reader had
decided about it. A tracking pixel in the quote then reported the
message read, and the address live, to whoever was counting -- the thing
leaving the images blocked was meant to prevent. Edit as new and opening
a draft that quotes a message did the same.
The decision now lives in one place, remoteImagesAllowed(), asked with
the same inputs the reader's answer used: the image policy, the trusted
senders, whether the sender is a contact, and whether Show images was
pressed on that message. The last of those was component state, so it
moves to the mail store, where the composer can see it.
Blocked images already keep their address in data-ihm-remote, so nothing
is lost by not fetching: it goes back on the way out, and the sent quote
is what its sender wrote. The recipient's client decides for itself, as
it would with any other client's reply.
Before pr408 this needed a rich-text default to reach; the format offer
made it reachable from plain text, which is how it was found.
No new strings.
(cherry picked from commit d329b33912921a851c548bffef5085e5fbf72bed)
Switching a reply between plain text and rich text converted whatever
body the draft was showing. Going from plain text to rich, that meant
the quoted message came back as the "> " text quote run through a
converter -- the sender's formatting, images and links gone, even though
the original markup was sitting on the draft untouched.
Both forms of the quote are prepared when the reply opens, so keep them
on the draft and re-attach the right one when the format changes. Only
what the author typed above the quote is converted. Where the quote
can't be found any more -- edited by hand, or a draft that quotes
nothing -- the whole body is converted as before, which is what every
non-reply draft does.
No new strings.
(cherry picked from commit 88f9e6c50a04f8ffc4702d1d1e3cffa6a93e7690)
A reply opened in the format the settings ask for, whatever the message
being answered was written in, and the per-draft switch was buried in
the composer's ⋮ menu. Replying in plain text to a rich text message
throws away the formatting; replying in rich text to a plain-text one
overrides what the sender chose to write in.
When the two disagree the composer now says so above the editor -- "This
message is rich text", with a Switch button and a dismiss -- and the
draft still opens in the format the settings ask for. Switching converts
that draft only and leaves the setting alone; switching from the ⋮ menu
answers the offer too. Forwards get it as well, where the formatting
being passed on is somebody else's.
What counts as rich text is hasHtmlAlternative(), which reads the body
part's own type: `htmlBody` is derived (RFC 8621 4.1.4), so a plain-text
message has one too and its presence proves nothing.
The mock said otherwise -- it returned an empty `htmlBody` for a
plain-text message, where Stalwart 0.16.21 returns the text/plain part
in both lists. Both builders now answer as the server does, so the path
this feature depends on is exercised in development rather than only
against a real mailbox.
Two new strings, translated in all nine catalogs; the buttons reuse the
menu's existing "Switch to plain text" / "Switch to rich text". The
count falling back to English stays at 16 in every language.
Fixes#407
(cherry picked from commit d992442b8194be5e9c48204332c7243d9587b4ca)
Name the reviewer in both Dutch catalogs, FEATURES and ROADMAP, under both of his handles, and record that his wording stands.
(cherry picked from commit aaa86e96d66e2431a8c98467712a977d4035bf76)
Michael (mbjboon-netizen) sent the final corrections for nl.ts and the Dutch
permission labels and signed the language off, so Nederlands no longer
carries the Beta flag in the picker.
The main catalog changes 52 values, mostly "regels" -> "filterregels" and
"post" -> "e-mail(s)". The permission headings move from compound nouns
("Accountbeheer") to verb phrases ("Accounts beheren"), and the reviewer's
note on that is kept in the file. No keys were added or removed, and every
placeholder is intact.
One entry is kept as it was: "It {damage}, ..." stays "Het {damage}, ...".
{damage} is filled with a verb phrase ("stops in the middle of a line"), so
the added "is" would have doubled the verb.
README, FEATURES, ROADMAP and KNOWN-ISSUES now say Dutch has been reviewed
and the other eight have not.
(cherry picked from commit e30fd73d7dbb1463859efbc4048f680d21d64c56)
Folder subscriptions are the reader's own and they have none in an
account handed to them, so only Inbox showed. Every folder shows while a
locked account is in view, and Hide from list is gone there.
When the server hands a locked account to the reader (urn:inbuxa:jmap
delegation), the account popover offers it. Only mail follows the switch;
the reader's own settings, push and notifications stay theirs. A red bar,
a red wordmark with a padlock and the tab title say which account is in
view. Read delegates can't change anything, organize delegates can't
delete, and writing needs send-as. A delegation taken away drops back to
the reader's own mail.
14 new strings in all nine catalogs, unreviewed (inbuxa AL-7, AL-8).
The logo, favicons and app icons are served from public/img under fixed
names with a browser cache of hours, and the service worker fetches them
through that cache. After the mark changed on 2026-09-27, returning
visitors kept the old cat until their copies expired, and the favicon
and an installed app's icon hold on longer still.
Every URL that names one now carries ?v=BRAND_V (src/lib/brand.ts,
brandImage()): the header, sign-in, About, the mail empty state, the
notification icons, index.html's favicon links, the manifest's icons
and the service worker's shell and notification icons. Date-stamped,
never a counter, for the sites' ASSET_V reason; the three static files
carry the value written out, and the comment says to keep them in step.
The webmail showed ihasmail's cat-and-envelope as inbuxa's mark. The new
mark keeps the family's face, paws and colors, over a server with a bay
for each piece of the suite: the letter (webmail), a prompt (console),
status lights (server).
- img/inbuxa-mark.png (header, sign-in, About) and img/logo.png (the
mail empty state and the custom-name fallback).
- favicon.ico, favicon-64, apple-touch-icon (opaque white, as before),
icon-192/512, and icon-maskable, now on an opaque ground with the
mark inside the safe circle.
- Login.tsx: 120x126, the new mark's proportions; 120x143 would have
stretched it. Every other use sizes by one dimension.
- The service worker fetches images network-first, so installed copies
pick the new ones up without a cache version bump.
The lead now says what inbuxa is: a mail server, its administration
console and this webmail, installed together under the AGPL, with the
name set apart in the brand teal. A new "The suite" table lists the
mail server, the console (linked, for sessions that may administer),
this webmail's version and inbuxa.org.
4 new strings in all 9 catalogues; the old webmail-only lead is dropped
from them, since nothing looks it up any more.
Settings > About shows which webmail node answered (NODE_NAME, else the
container hostname) and which inbuxa node it talks to: the address the
server's name resolves to from the webmail, named by its PTR record. The
server only tells administrators its node name, so the webmail works it
out itself. Fetched from /api/about/nodes on every visit, cached for a
minute server-side, for troubleshooting a cluster.
Fork-only: upstream ihasmail runs one webmail against one server.
4 new strings, translated in all 9 catalogues.
When inbuxa-server's AI spam classification is on, it records the model's
answer in an X-Spam-LLM header: a tag (LLM_<category>[_<confidence>]) and,
in parentheses, the model's explanation. The full message now asks for it,
and where it's there:
- the message details show "Language model's opinion" beside the spam
filter's own working, with category, confidence and explanation;
- a message in Junk carries a banner saying the same.
Both say it's one of several signals the spam filter weighed, never the
reason on its own, as the server's spec requires. The explanation is model
output and is only ever rendered as text. Nothing shows without the header,
so a server without the feature, or with it off, looks as before.
Translations: two new strings, "Language model's opinion" and "One of
several signals the spam filter weighed", in all eight catalogues (16
entries). Category and confidence come from the server and aren't
translated.
inbuxa-server renames the identifiers that carried the upstream name (its
SPEC.md §2.4). Upstream's capability for the registry (x:) objects is now
urn:inbuxa:jmap:registry, beside the fork's own urn:inbuxa:jmap, which is
unchanged. There's no alias, so this lands with the server change and
deploys with it. The mock advertises the new name too. No user-visible
strings change.
The browser tab, and anything that takes its name from the document title,
read INBUXA. The manifest, the server's app name and the sign-in card all
have it lowercase; the title was the one place left in caps.
Prod's APP_NAME override was set to inbuxa at the same time; the code
default already was.