The webmail app
A plain-browser webmail client: the folder rail, a newest-first message
list with per-folder search, a sandboxed reader, and a compose pane —
plus the same wallet login and settings panes the Thunderbird and
Outlook clients carry, because all three hosts run the same shared core
(webclients/shared/). Everything that must be signed is signed
in the page by a WebAssembly module compiled from this workspace’s
own crates; the server only relays already-signed transactions and
never holds your key.
Navigation is hash-only — #/mail (the three-pane mail view) and
#/settings (the shared dashboard panes). There is no build framework
and no CDN: the bundle is static files served by
account-api itself.
What the operator must run
The app is served same-origin from account-api’s [static]
section, so the API base URL is simply the page’s own origin and no
CORS setup exists at all. Reading mail rides the /v1/mail surface,
which needs only the shared [store]. Compose (POST /v1/mail/send)
routes recipients the way sithbitd’s submission port does, so its reach
follows the API’s config:
- No extra config — bare base58 wallet addresses deliver locally; anything else answers 503 (alias resolution needs the chain).
[chain]— aliases anduser@your-domainaddresses resolve through the gateway, with the frombox postage precheck applied before the message is accepted.[mail](local_domains,[mail.dkim]) — mirrors sithbitd’s submission settings: which domains are yours (everything else is relayed) and the DKIM keys relayed mail is signed with.
As with the other clients, the aliases/balances/keys settings panes
also need [chain]; login, mail reading, and the account settings work
without it.
Building and serving
cd webclients/webmail
./build.sh # wasm-pack build + stages shared/ into staging/
staging/ is the deployable bundle. Point account-api at it:
[static]
route = "/mail"
root = "webclients/webmail/staging"
then open http://127.0.0.1:8180/mail/index.html (or your deployment’s
origin). First run: paste your Solana keypair file and choose a
passphrase — the key is encrypted with that passphrase before it lands
in the browser’s localStorage, and the passphrase is asked for again
each time you open the app.
Using it
The #/mail view is three panes. The folder rail lists your folders
with unseen counts; the middle column stacks per-folder search over the
message list (keyset-paged, newest first); the reader marks messages
read on open, toggles text/HTML/raw views (HTML render in a fully
sandboxed iframe that blocks scripts and remote loads), downloads
attachments, flags, moves, and deletes. Compose floats bottom-right:
To/Cc take comma-separated addresses — aliases, user@domain, or bare
wallet addresses — and the reader’s Reply action opens it prefilled
(sender as To, Re: subject, threading header). A successful send
files an already-read copy in Sent and refreshes the folder counts;
recipient problems (unknown alias, no stamps on your frombox) surface
on the pane with your draft intact.
The #/settings view is the shared dashboard: mail password, timezone, do-not-disturb, aliases, balances, and delegated encryption-key management, exactly as documented for the Thunderbird extension.
The compose loop is exercised headlessly by the env-gated live suite
webclients/webmail/test/e2e-webmail.test.js — the bundle served from
a real account-api, a real send to a throwaway wallet, and the message
read back from the recipient’s INBOX (the runbook lives in
webclients/README.md).