Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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 @domain is not leaked), but you still supply a mail password. Get it from the operator’s self-serve enrollment page (/addin/enroll.html on 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).

  1. Mint the certificate — offline, no RPC:

    sithbit mailbox create-cert -k ~/.config/solana/id.json --out sithbit
    

    This writes sithbit.p12 — the combined, password-less PKCS#12 bundle your certificate store imports in one step — alongside sithbit.crt and its PKCS#8 key sithbit.key as a PEM pair for other apps (omit --out to 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 .p12 and .key embed your wallet secret; guard them like the wallet itself.

  2. 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.p12 and 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.

  3. 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-to makes (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.