The Outlook add-in
An Office.js taskpane add-in for self-service on a SithBit account:
wallet login, mail password, timezone, do-not-disturb schedules,
aliases, balances, and delegated encryption-key management — the same
panes as the Thunderbird extension, because both hosts run the same
shared core (webclients/shared/). Everything that must be signed is
signed inside the pane by a WebAssembly module compiled from this
workspace’s own crates; the server only relays already-signed
transactions and never holds your key.
Runs in new Outlook on Windows, Outlook on the web, and classic Windows Outlook (Microsoft 365, version 2307+). Not Outlook for Mac — the unified JSON manifest doesn’t run there; a legacy XML manifest is the recorded fallback if Mac coverage becomes a need.
What the operator must run
Office add-ins are https-hosted web pages, so the operator serves
the built bundle from account-api’s
[static] section — the same origin as the API, which is why the
add-in needs no CORS setup. Office requires https: terminate TLS with
a reverse proxy, or use account-api’s [tls] section (see the
configuration reference,
including the dev-certificate recipe). As with Thunderbird, the
aliases/balances/keys panes need [chain] configured; login and the
settings panes work without it.
Installing from the store
Once the add-in is published, install it from
Microsoft Marketplace (formerly
Microsoft AppSource): in
Outlook open Apps → Get Add-ins and search for
_todo_store_listing_name_, or open _todo_store_listing_url_ in a
browser and choose Get it now. Store installs update automatically.
Note: the add-in is not yet published to Microsoft Marketplace. The
_todo_store_listing_name_and_todo_store_listing_url_placeholders resolve when the listing goes live; until then, install via Building and sideloading below.
Microsoft Marketplace listings are public-only and Microsoft-validated
— there is no unlisted tier. The supported private paths are sideloading (the
atk flow below) and Microsoft 365 admin center → Integrated Apps →
Upload custom app, which deploys the add-in privately to a whole
tenant — the de-facto org-wide pre-release deployment.
Building and sideloading
cd webclients/outlook
./build.sh # wasm-pack build + stages shared/ into staging/ + packages sithbit-outlook.zip
Point account-api at the bundle:
[static]
root = "webclients/outlook/staging" # served under /addin
Sideload sithbit-outlook.zip with the Agents Toolkit CLI
(atk auth login m365, then atk install --file-path sithbit-outlook.zip) or via Teams → Apps → Upload a custom app. The
Outlook-web “Add-Ins” upload dialog accepts only XML manifests — use
one of the two paths above. The full dev runbook, including the
localhost-https trust step, lives in webclients/outlook/README.md.
First run and onboarding
The first time you open the pane in a browser profile with no wallet
stored yet, it opens on the shared five-step
onboarding wizard
instead of the wallet manager. It creates or imports a wallet (the create
branch shows your secret key once, with the save it — it is the only
copy gate), claims an optional handle, sets your
default postage, and mints your mailbox
on-chain — mailbox plus, if claimed, its alias in one transaction, signed
in the pane’s wasm module and relayed through /v1/chain. Until it
finishes, the pane shows only the wizard.
The pane routes to one of four views from the session it probes on open:
- Onboarding wizard — no wallet stored yet, or an unlocked wallet with no mailbox (a returning user finishing setup).
- Unlock — a stored wallet that is locked this run; the shared wallet-list pane below owns the passphrase prompt.
- Dashboard — unlocked and mailbox-registered: the normal panes.
- (a brief loading view while it probes.)
The add-in needs no endpoint configuration for onboarding: because the bundle is served same-origin from account-api, it infers the API base URL from the page’s own origin, and the onboarding create relays through that same API. Only the optional trustless fallback and its on-chain compose read endpoints of their own (RPC, IPFS gateway, and pin service, from localStorage).
Using it
Select any message and open Apps → SithBit Account (the pane is
pinnable, so it stays open as you move around). The pane opens on a
wallet manager, not a single-account gate: paste a Solana keypair file
and choose a passphrase to import it — the key is encrypted with that
passphrase before it is stored in the browser’s storage — and repeat
for as many SithBit addresses as you want available in this browser
profile. Each is unlocked with its own passphrase (needed again each
time the taskpane opens), several can stay unlocked at once, and
switching which one is active needs no passphrase. The settings,
DND, aliases, balances, mail view, and delegated-key panes all act on
the active wallet, each on-chain action signed client-side in wasm and
relayed through /v1/chain. As with Thunderbird, each address still
needs its own native Outlook mail account — Office.js add-ins run
inside an already-configured mailbox and have no API to provision one.
Every flow above is exercised headlessly by the env-gated live suite
webclients/outlook/test/e2e-outlook.test.js — the bundle served from
a real account-api, login and settings through the real storage
adapters, and the key lifecycle confirmed on-chain.
Panes
The taskpane mounts the same shared dashboard the other clients do, so the pane set is identical to Thunderbird’s:
- Balances — wallet SOL, the on-chain mailbox message count, and a per-sender stamp lookup (stamps are held per sender, so there is no single total).
- Aliases — the aliases pointing at the active wallet. Registering and transferring them stays in the CLI.
- Domains — list a domain you hold for sale on the open marketplace, or buy a listed one by name and current authority. Reassigning a domain’s authority is an operator action, not offered here.
- Reply bounties — settle reply bounties on-chain: claim one on a message you received and replied to (bountied message id, original sender, your reply’s id), or refund one you placed that went unclaimed past its deadline.
- Pinning leases — escrow a refundable deposit asking operators to keep a message’s pinned body past the default retention (pinning leases): create one by the message’s CID and id (deposit prefills to the protocol minimum; a one-time fee applies), check a CID for your wallet’s lease, or close it anytime to reclaim the deposit — even after the message has settled. The trustless fallback reader’s Lease this message button (beside Reply, shown once a body has rendered) prefills this pane’s create fields with the open message’s CID and id and switches the taskpane to the settings view (a plain view switch — the taskpane keeps no history); you still review the deposit and submit, and manual CID entry works as before.
- Encryption key — publish, rotate, or close a
delegated X25519 key,
signed in wasm and relayed through
/v1/chain. Export the secret while it is on screen — it is shown once, and mail sealed to an old key still needs that key. - Settings — the derived mail password for the active wallet (see The mail password below) and its IANA timezone.
- Do not disturb — weekly or date-range windows in which senders are asked to retry later, evaluated in your timezone.
- Mailbox — claim your on-chain mailbox and set its registration config: an optional handle (claimed as an alias in the same transaction), the sending domain (blank uses sithbit.com), the default postage unknown senders pay per stamp, and an on-chain-only (no IPFS) option. Once registered the pane shows the message count and points per-sender price changes to the Balances pane.
- Close mailbox — request, cancel, or finish closing the mailbox. This one is not instant: the request starts a fixed 7-day wait and refunds nothing, the mailbox stays open and keeps receiving mail meanwhile, and the pane offers Cancel for the whole period. Only after the clock runs out does finalizing become available, closing the mailbox and returning both its rent and the transient request record’s. The wait exists because an instant refund made discarding a burned sending identity free; it costs an honest owner time on a rare action, not money. The Encryption key close above is unaffected and stays instant.
Zero-config setup (autodiscover)
If the operator runs the domain-sithbit
service, Outlook’s native Add Account flow can fill in the
IMAP/ POP/SMTP connection settings for you — no add-in, no hand-typed
servers. Type your address as <wallet-base58>@<domain>; Outlook POSTs it
to the domain’s autodiscover endpoint and auto-fills the servers, ports,
and TLS modes from the operator’s advertised coordinates.
Two limits, by design:
- It does not create the account. Autodiscover fills in connection
settings only; native account auto-provisioning is deliberately not
offered. Your mailbox must already exist on-chain (
sithbit mailbox create) before you connect. - It does not set your password. The login name is auto-filled as your
wallet address (the base58 public key — the
@domainis not leaked), but you still supply a mail password. Get it from the operator’s self-serve enrollment page (/addin/enroll.htmlon the account API) or offline from the CLI — see The mail password just below.
Where the operator hasn’t enabled autodiscover, enter the same settings by hand; the username and password rules are identical.
The mail password
Like the Thunderbird extension, your IMAP/POP/SMTP client authenticates as your wallet with no separate stored password. The “mail password” is a wallet signature over a fixed challenge that the servers verify against your wallet address; nothing is stored server-side, so a password on the account is now optional (see the account API).
Get the credential for the active wallet from the Settings pane’s Copy mail password button, or offline from the CLI:
sithbit mailbox credentials -k ~/.config/solana/id.json
Enter it in your mail client as username = your wallet address (base58
public key), password = the derived base58 signature, mechanism = SASL
PLAIN over TLS (the signature is bearer-equivalent, so always use TLS).
This is the tested path for reading mail (POP3/IMAP retrieval);
wallet-authenticated submission authenticates identically, and its
envelope sender is fixed to exactly your own wallet address at a domain
the submission server serves — and, when that server is connected to the
chain, one your wallet also owns on-chain (the domain’s recorded
authority), not merely any domain the server happens to serve. Set the
Outlook account’s email address to <wallet-base58>@<the operator's domain>,
the same string the add-in POSTs to autodiscover. Another wallet’s address,
your address at a domain that server does not serve (or, on a chain-connected
server, that your wallet does not own on-chain), and a case-variant of your
own base58 (base58 is case-sensitive, so the match is exact) are each refused
553 5.7.1; the
Thunderbird page and the configuration
reference
carry the full rule and the chain-less-dev-stack gotcha. Regenerating the
credential is free and offline.
Client-certificate login (SASL EXTERNAL)
The passwordless alternative to the mail password above, exactly as on
Thunderbird:
instead of pasting a signature, you hand your mail client a TLS client
certificate minted from your wallet, and the server authenticates you
over the TLS handshake — no password entered, nothing stored server-side.
It requires client_cert_auth on the listener (see the
configuration reference).
-
Mint the certificate — offline, no RPC:
sithbit mailbox create-cert -k ~/.config/solana/id.json --out sithbitThis writes
sithbit.p12— the combined, password-less PKCS#12 bundle your certificate store imports in one step — alongsidesithbit.crtand its PKCS#8 keysithbit.keyas a PEM pair for other apps (omit--outto print just the PEM blocks to stdout). The certificate is a self-signed Ed25519 leaf whose public key is your wallet address — no CA, no expiry to manage. The.p12and.keyembed your wallet secret; guard them like the wallet itself. -
Install it. The Office.js taskpane above manages only the account settings, not the mailbox’s IMAP/POP/SMTP transport, so a client certificate is configured in the host mail app, not the add-in. In classic Outlook that is File → Options → Trust Center → Email Security → import your certificate — pick
sithbit.p12and leave the password prompt blank (the bundle is password-less) — then attach it on the account’s outgoing/incoming server security settings; on new/web Outlook the certificate is selected by the OS/browser certificate store when the server requests one. Set each of the account’s SMTP, IMAP, and POP entries to SSL/TLS and select this certificate as the client certificate. -
Leave the password blank — with EXTERNAL the certificate is the whole credential; keep your wallet address as the username.
The add-in mints the same files without the CLI: the taskpane’s
settings view carries a Certificate sign-in section below the
settings panes, whose Download client certificate button derives
everything in the wasm module and saves three files named after your
wallet address: the combined <pubkey>.p12 first, then the
<pubkey>.crt / <pubkey>.key PEM pair for other apps —
byte-for-byte what sithbit mailbox create-cert writes. Importing
stays manual as in step 2 — an Office add-in cannot touch the OS
certificate store — so bring the .p12 into certmgr on Windows or
Keychain Access on macOS (or your mail app’s own certificate settings)
in one step. The .p12 and .key files embed your wallet secret;
guard them like the wallet itself. The button needs a
wallet unlocked in the add-in’s wasm module: while every wallet is
locked it is disabled, and an external signer (a Phantom or Ledger
wallet) reads as locked too — it holds no local seed to derive from,
so external-wallet users stay on the CLI path above.
Regenerate and reinstall it freely; it carries no secret beyond your wallet key, which never leaves your machine — the derivation is fully deterministic per wallet, so re-downloading on any device yields the identical certificate.
Trustless (server-down) viewing
The taskpane wires two read paths. The primary one is the server
mail view over account-api’s /v1/mail surface (the same three-pane
reader the webmail app shows). Behind it sits a trustless fallback
that reads a message straight from a
Solana RPC endpoint and an IPFS
gateway with the mail server down: it resolves the message’s
on-chain account for its sealed-body CID, fetches the sealed bytes from
the gateway, unseals them in wasm with your unlocked wallet, and renders
the MIME — HTML in a fully sandboxed frame that blocks scripts and
remote loads. The fallback is a no-op until the wallet is unlocked.
Its two endpoints come from localStorage and default to the dev
stack: the Solana RPC (config.rpcUrl, default the local surfpool at
http://127.0.0.1:8899) and the IPFS gateway (config.gatewayUrl,
default http://127.0.0.1:8183 — e.g. a
sithbit-gateway). Paste a bare URL into
either key to point the fallback at a remote endpoint.
The same two caveats as the Thunderbird extension apply:
- A wallet-only read only works if the body was sealed to your wallet; a recipient who published a delegated encryption key needs that delegated secret instead.
- Under the default
[spooler.settle]auto-settle worker a settled message is unpinned past its window (keep_pin = false), so trustlessly reading an old, settled message depends on the operator having kept the pin (keep_pin = true).
Trustless reply and compose
Viewing is no longer the fallback’s whole surface: the trustless
reader’s header carries a Reply on-chain button, and the mail view
mounts the same floating on-chain compose card as
trustless webmail —
a Compose on-chain button opens it blank; Reply seeds it with the
decrypted sender and the parent message’s account address. The card is
always available (the taskpane has no trustless mode to switch into)
and follows the webmail card’s lifecycle exactly: resolve the recipient
and their published key on-chain, seal in the pane’s wasm module, pin
the sealed bytes to your configured IPFS pin service, then sign and
submit the SendMail transaction. The same seam ships in the
Thunderbird extension.
A reply sent this way is an on-chain send, not a host SMTP compose:
it never touches the Outlook account’s own outgoing server, and the
operator’s SMTP/IMAP/spooler pipeline plays no part in delivery — the
reply lands as an on-chain message account even while those servers are
down. Chain reads, sealing, and pinning go straight to your configured
endpoints; the signed transaction is relayed through account-api’s
/v1/chain like every other on-chain action in the pane, and as
always the relay cannot alter what you sign.
The card carries webmail’s three affordances, documented in full there rather than restated here:
- The reply chip — the draft threads to the parent message with the
same privacy-preserving linkage
--reply-tomakes (only its blake3 hash reaches the chain); Clear drops the link and keeps the draft. See Replying, and attaching a bounty. - Attach a reply bounty — an amount in SOL (blank = plain send) and
a claim window in days escrow SOL on the message exactly like
--bounty/--bounty-window. - Inline prepay — no frombox toward the recipient yet? The card holds the draft, quotes the postage (live per-stamp protocol fee included), and Prepay & send buys the stamps then re-sends the held draft once the purchase confirms — the same prepayment rule and behavior as webmail’s card. Settings → Balances remains the standing place to buy stamps outside a compose.
Sending needs one endpoint viewing does not: the connection-settings
pane gains an IPFS pin service URL (config.ipfsPinUrl, default
http://127.0.0.1:8182 — a
sithbit-ipfsd) plus its optional bearer
token (config.ipfsPinToken, default empty), where outbound sealed
bodies are pinned. Operators offering that pin surface should read
the pin lifecycle caveat:
a client-made pin sits outside any mail server’s pin lifecycle.