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

Change history

A running log of documentation-affecting changes, newest first. Each entry links to the section that changed (or that describes the change) so you can jump straight to it instead of re-reading the whole page.

Versioning. Sections are tagged with a date and a protocol version (MAJOR.MINOR.PATCH):

  • MAJOR — a breaking change to the SithBit public ABI (removing or reordering an instruction variant, changing an account layout that clients read, or repurposing an error code). Instruction enums and error codes stay append-only and the protocol is pre-launch, so MAJOR remains 0 for now.
  • MINOR — an additive public-ABI change (a new instruction, error, or field), a significant change to the economic model that changes how end users use the system (fees, pricing, prepayment rules), or — widened at v0.10.0 — a significant additive capability or default-behavior change that affects deployments (a new enforcement default, a new storage/config backend kind). Earlier entries tagged such changes PATCH.
  • PATCH — documentation-only or otherwise non-behavioral changes.

A run of documentation-only edits between behavioral changes keeps the same version across several dated sections: the version tags the protocol state, the date tags when the docs moved.

2026-07-27 — v0.41.0 (money-path hardening: sender stamp reclaim, purchase slippage guard, admin-close value guard)

  • Senders can now withdraw unspent prepaid postage. A new ReclaimFromboxStamps instruction (discriminant 52) returns the balance above rent to a sender who prepaid against their own wallet address, zeroing the stamp count and leaving the frombox alive on its rent so the recipient keeps the price it set. The frombox derives from the hash of the signer’s address bytes, so reproducing that derivation is the authorization — no stranger can reach someone else’s frombox, and a frombox keyed on an email string stays recipient-managed by design. Documented at Reclaiming unspent stamps, with the concept-level story under Prepaying with stamps. This corrects the threat model, which previously stated that prepaid stamps had no refund path at all. New instruction: MINOR.

  • Stamp purchases carry a slippage ceiling. CreateFrombox and AddStamps gained an additive max_price_lamports field; the recipient controls the per-stamp price and can raise it between the moment a buyer is quoted and the moment their transaction lands, so a purchase above the ceiling now reverts with custom error 107 rather than silently overpaying. frombox stamp pins the ceiling to the price it just quoted by default, with --max-price to pre-authorize a rise and --no-max-price to opt out. Additive ABI field affecting how end users buy postage: MINOR.

  • The admin reclaim tool can no longer reach accounts holding value. AdminCloseAccount, in all three programs, now refuses any target whose balance sits above its rent-exempt minimum (custom error 106), so postmaster reclaim reaps only rent-empty leftover state — never a sender’s escrowed postage, a reply bounty, or a live auction bid. New error code: MINOR.

  • A permissionless crank can no longer capture the requester’s deposit. PendingReclaim records the wallet that funded the request, and domain reclaim --finalize pins the pending account’s rent refund to it. Finalize stays permissionless to crank; only the refund target changed. The new field is noted in the privacy reference. Additive account field: MINOR.

  • Error codes 105 PinLeaseAccountInfo, 106 AdminCloseEscrowPresent and 107 PriceExceedsMax are now listed in the program reference; 105 predates this entry and had simply never been written down.

2026-07-27 — v0.40.7 (timelock docs gate widened to all four constants)

  • The “7 days” figure is now fenced for every timelock, not just mailbox close. check_timelock.py guarded exactly one of mail_model’s four same-valued timelock constants, so the prose describing domain deactivation, reclaim-by-proof and reply bounties could drift from its constant unnoticed — the three constants are deliberately independent literals, so retuning any one of them would have silently falsified those pages. The checker is now table-driven with one row per constant (19 fenced mentions in total, up from 9) and each row is drift-proved independently. Rows own file-exclusive allowlists, enforced at startup, because the context filters really do overlap inside a shared page. Scope stays .md-only: the three diagrams that also say “7 days” are excluded, since the screenshot gate already hashes them. The checker also gained a --self-test proving its exit-code contract against fixture trees, joining the link and anchor checkers. Known gap: a handful of close-family figures remain unfenced — closing-accounts.md, economics.md, threat-model.md and proving-behavior.md each mix figures from two or more constants in one file, which the file-exclusive model cannot express; fencing them needs per-section scoping. Tooling and gate only, no documented behavior changed: PATCH.

2026-07-23 — v0.40.6 (first-run screenshot re-baseline)

  • Webmail first-run screenshot re-baselined. The committed webmail-first-run.png was the last frame still shot on the old capture rig: its standalone capture path had no CDP session, so the frame rendered in the capture host’s own color scheme and could not be reproduced on a box whose desktop theme differed. The shot now rides the same capture driver — and the same dark-theme/timezone pin — as every other committed screenshot, so the full set is byte-reproducible on any box (run-to-run AE=0 across all four driver-captured frames). Same subject, same dark theme; only capture tooling and pixels moved. Documentation only: PATCH.

2026-07-23 — v0.40.5 (API-mode on-chain reply reveal + mailbox-credentials reference)

  • Webmail: “Reply on-chain” now works in API mode. The on-chain compose card was CSS-hidden for the whole page lifetime whenever an account API was configured, so a trustless-viewer Reply click seeded the draft into an invisible card — a silent no-op. The card now reveals itself when the pane opens (it stays hidden until then, so nothing changes in the always-rendered UI), floating in the corner like the server compose pane. This narrows the v0.40.4 entry’s “it was never silent” note: that held for trustless mode only. The webmail screenshot set was proven pixel-neutral (same-rig A/B, AE=0 on all three shots) and re-pinned hash-only. Client-shell UX only, no protocol change: PATCH.
  • CLI reference: new Mailbox credentials page documents sithbit mailbox credentials — the offline, no-RPC derivation of the deterministic mail login (username = the wallet public key, password = the base58 wallet signature over the fixed auth challenge) — and wires it into the Mailboxes command tree beside the client-certificate alternative; the client walk-through pages already showed the invocation and are now cross-linked from the reference. Documentation only: PATCH.

2026-07-23 — v0.40.4 (lease-open acknowledgement + reference and screenshot upkeep)

  • Lease-open acknowledgement — the trustless viewer’s “Lease this message” button now switches the two view-routed shells to the settings view with the lease form prefilled: webmail routes via the #/settings hash (so the browser’s Back button returns to the mail view), while the Outlook taskpane switches its plain view state (no history entry). No reply listener was added — the on-chain Reply compose card already opens in place in the mail view, so it was never silent. The webmail screenshot set was proven pixel-neutral (same-rig A/B, AE=0 on all three shots) and re-pinned hash-only. Client-shell UX only, no protocol change: PATCH.
  • Docs screenshots: webmail-inbox.png and marketplace-listings.png re-baselined on the current capture rig. The webmail capture driver now pins the topbar wallet line to a fixed display base58 (the public key of the checked-in mail-key1 test keypair — the same wallet the marketplace capture signs in with) before the inbox shot; previously a fresh keypair minted per run made that line the frame’s one nondeterministic element. Both shots are re-shot on the chrome-headless-shell 151 rig and proven run-to-run byte-identical (AE=0 across consecutive full captures), so committed-vs-fresh comparisons are directly meaningful again. Capture tooling and images only: PATCH.
  • CLI reference: new Create a client certificate page documents sithbit mailbox create-cert — the offline SASL EXTERNAL certificate mint (<prefix>.crt/.key PEMs plus the deterministic password-less .p12, including --out’s extension-replacement behavior) — and wires it into the Mailboxes command tree; installation stays on the client walk-through pages, now cross-linked. Documentation only: PATCH.

2026-07-23 — v0.40.3 (the gRPC RPC rosters now gate-fenced against the proto)

  • Docs-tooling: the two hand-maintained SolanaMail RPC rosters are now gate-fenced. A new docs-gate leg, mail_docs/check_rpc_rosters.py, diffs mail_api/README.md‘s flat RPC list and the gateway topology appendix’s three role buckets against the service definition in mail_api/protos/sithbit.proto: name-set equality both ways, the buckets’ union covering the proto set with no RPC claimed by two roles, and — where a bucket is introduced by an English number word (“Five RPCs”, “Thirteen RPCs”) — that word matching the bucket’s own list length. Both rosters had silently gone stale twice before (most recently the chain-read bucket omitting v0.40.0’s GetPinLease, caught only by hand at v0.40.1) — that drift class now fails the gate instead of waiting for a manual sweep. Docs tooling only, no protocol or server change: PATCH.

  • Docs-tooling: the committed capture tooling now reproduces the webmail settings screenshot on its own. capture-populated.mjs’s webmail pass now scrolls the settings page to the Pinning-leases pane — the shot’s subject, which sits below the fold — before shooting webmail-settings.png; previously the committed image was reproducible only with an uncommitted modification to the capture driver. The upstreamed step’s output was verified byte-identical to the committed PNG, so no screenshot changed. Docs tooling only, no protocol or server change: PATCH.

  • The trustless viewer now hands the open message to the Pinning-leases pane in all four GUI shells (webmail, Thunderbird, Outlook, Chrome): a Lease this message button beside Reply — shown only once a body has rendered, since a local-only message has no CID to lease — dispatches a sithbit-lease-open {cid, messageId} event that prefills the pane’s create fields with the message’s on-chain CID and id. In webmail and Outlook the prefill waits in the settings view where the pane lives; in Chrome and Thunderbird the pane sits above the viewer on the same page. Prefill only — the user still reviews the deposit and submits — and manual CID entry is unchanged. No committed screenshot changes appearance (the new button is unreachable in every committed capture). Client-side UI only, no protocol or on-chain ABI change: PATCH.

2026-07-23 — v0.40.2 (pinning leases reach the web clients)

  • A Pinning leases pane in all four GUI shells (webmail, Thunderbird, Outlook, Chrome): the v0.40.0 pinning-lease surface — until now CLI-only — is now a shared dashboard pane. Create a lease by a message’s CID and id (the recipient defaults to your own mailbox, the deposit prefills to the protocol minimum straight from the on-chain constant, and the one-time creation fee splits to the recipient’s operator exactly as the CLI resolves it), check whether your wallet holds a lease on a CID, and close a lease anytime to reclaim the deposit — including after the message itself has settled, since the lease is addressed by the CID. The transactions are built and signed in the shared wasm module, byte-parity-fenced against the CLI’s own builders, and the pane is documented on each client page. Client-side UI only, no protocol or server change: PATCH.

2026-07-22 — v0.40.1 (the sender-reputation figures reach the gRPC gateway)

  • New GetSenderReputation RPC on the SolanaMail service: wallet in, the recorded cumulative postage spend and the effective first-contact rate in bps out — the same two figures as the CLI’s sithbit postoffice reputation, computed through the same fenced on-chain rule (tuned floor included), so MX operators and other servers can weigh a sender’s on-chain track record without shelling out to the CLI. Absent reputation account = the ordinary zero-spend/full-price answer; a failed chain read surfaces as UNAVAILABLE rather than masquerading as zero spend. The gateway topology appendix’s chain-read role now counts all thirteen read RPCs (it had also omitted v0.40.0’s GetPinLease). Additive gRPC surface, no on-chain ABI change: PATCH.
  • The settings pane’s wallet-derived mail password is now real markup in all four shells (webmail, Thunderbird, Outlook, Chrome — the pane logic existed but no shell rendered it): a Derive mail password button, gated on the wallet being unlocked, with the same one-time reveal pattern as the encryption-key pane — username and derived password computed entirely client-side (see The mail password). A reactivity fix rides along: unlocking the wallet now re-renders the derive gate immediately (it previously stayed on the “unlock your wallet” hint until a reload). The webmail settings screenshot was re-shot to show the new section. Client-side UI only, no protocol or server change: PATCH.
  • sithbit mailbox create-cert and the extensions’ Certificate sign-in now emit a combined .p12: the CLI writes <name>.p12 — a password-less PKCS#12 bundle (certificate + unencrypted key, deterministic per wallet) — beside the PEM pair whenever --out is given, the wasm module derives the byte-identical bundle client-side, and both the Thunderbird and Outlook extensions’ Download client certificate button now saves <pubkey>.p12 first, ahead of the PEM pair. The client pages’ certificate-login walkthroughs drop the manual openssl pkcs12 -export conversion step — the bundle imports in one step (leave the password prompt blank) — and note that the .p12, like the .key, embeds the wallet secret. Client surface only (CLI output + extension download), no protocol or server change: PATCH.

2026-07-22 — v0.40.0 (pinning leases: paid extended retention for mail bodies)

  • New: pinning leases — a per-(CID, holder) mail-program account (sithbit mail lease create/show/close) escrowing a reclaimable deposit (minimum 0.01 SOL, returned in full at close) that asks operators to keep a message body pinned past the default retention. Deliberately no expiry and no renewal fee; the only spend is a one-time creation fee (default 0.001 SOL, cap 10×, tunable via SetPinLeaseFee / read-only postoffice fee pin-lease) split with the recipient’s domain authority at the operator share. New instructions CreatePinLease (49) / ClosePinLease (50) / SetPinLeaseFee (51), errors 103–105, Postoffice 192→200; see the economics rationale and the program reference.
  • The auto-settle sweeper enforces leases with no new configuration: before releasing a pin it asks the gateway’s new GetPinLease RPC whether the CID is leased — a leased copy still settles (the stamp reclaim is unaffected) but keeps its pin; an unanswerable lookup fails closed and the copy retries next sweep.
  • The privacy reference on-chain account table gains the PinLease row (a lease publicly binds its holder wallet to a message CID), and the compute-units table the three new fences.

2026-07-22 — v0.39.2 (small-item cleanup: cheaper first-contact purchases, fee visibility, reference completeness)

  • CreateFrombox on the default reputation tail costs ~27% less compute: the pricing pass’s postoffice and reputation reads now thread through to the fee-collection leg instead of being re-derived (the second postoffice PDA grind was the bulk of the cost). Measured CU dropped 48,625 → 35,256 and the fenced ceiling 72,000 → 58,000 — see Compute-unit budgets. No account-list, fee, or pricing change: PATCH.
  • sithbit postoffice fee attestation joins the public read-only fee getters: it prints the effective one-time verified-sender attestation fee and its cap without a signature, in every CLI build. The read-surface list also now names the fee settlement getter it had omitted.
  • Reference de-staling: the mail-grpc topology appendix’s chain-read role now counts all eleven read RPCs (it omitted ListParticipants and GetSenderAttestation), and the privacy field reference’s on-chain account table grew from nine rows to the full eighteen account types — adding the marketplace escrow/bid accounts, the participant beacon, the sender attestation/reputation records, and the three pending-timelock markers.

2026-07-22 — v0.39.1 (the verified-sender trust mark reaches the web clients)

  • The readers now show a “✓ Verified” trust mark beside the From line when the sender holds an on-chain verified-sender attestation from its domain — the deferred client half of the v0.38.0 trust-mark decision, across all four shells (webmail, Thunderbird, Outlook, Chrome). The api-backed reader resolves the From address to a wallet on-chain and checks that wallet’s attestation; the trustless viewer binds the program-verified envelope signer instead. Absence renders nothing — no negative indicator. See Using it (the inbox screenshot now shows the mark).
  • The web compose/prepay paths now pass the payer’s attestation to the first-contact frombox purchase when it exists on-chain (one existence check, the CLI’s default since v0.39.0) — so an attested org’s webmail first contact prices at the floor without any CLI step. Top-ups are unchanged (attestation affects first-contact pricing only). No ABI or fee-rule change: PATCH.

2026-07-22 — v0.39.0 (reputation-scaled sender friction: proven senders pay less at first contact)

  • The default price a stranger pays at first contact now scales with the sender wallet’s on-chain track record. New Reputation-scaled first-contact pricing section: a per-wallet SenderReputation account (new mail-side sender_reputation PDA seed — see the PDA seeds table) records the wallet’s cumulative distinct-recipient postage spend at CreateFrombox time, and that spend steps the default first-contact rate: 10,000 bps (full default postage) below 0.1 SOL of spend, 7,500 from 0.1 SOL, 5,000 from 1 SOL, 2,500 from 10 SOL. A verified-sender attestation prices first contact at the floor immediately. Recipient-set prices are never touched — only the default a stranger inherits — and a nonzero price never rounds to zero: first contact is never free. Owner (self) purchases stay on the legacy path, unaffected.
  • The discount floor is delegate-tunable: new mail-side SetReputationFloor instruction (discriminant 48, sithbit postmaster fee reputation-floor <BPS>) tunes reputation_floor_bps (default DEFAULT_REPUTATION_FLOOR_BPS = 1,000 bps = 10% of the recipient’s default postage, capped at MAX_REPUTATION_FLOOR_BPS = 10,000; over-cap refuses with new custom error 102 ReputationFloorAboveCap, and a zero rate stores the “unset” sentinel and resolves to the default). The postoffice account grew 184→192 bytes (versioned reads default older accounts; the setter upgrades in place). See the tunable-constants table and the error-codes tail.
  • Third-party stamp purchases now carry a reputation tail by default: CreateFrombox accepts 6/8/9/10-account forms — the CLI and wasm builders emit the 9-account form (operator pair + the payer’s sender-reputation PDA, lazily created rent-exempt) on every third-party create, and append the payer’s attestation as a tenth account when one exists on-chain. A present-but-invalid attestation fails the purchase (error 19 / error 17) instead of silently repricing. See Reputation-scaled first contact on the stamps page, which also documents the new read-only sithbit postoffice reputation <WALLET> lookup (cumulative spend + effective first-contact rate in bps, floor included) and the floor setter.
  • The wasm frombox builders (create_frombox_tx/create_frombox_unsigned) gained a trailing optional attestation parameter and emit the 9-account reputation tail on third-party creates; existing JS callers are unaffected.
  • The compute-unit table’s CreateFrombox row now measures the 9-account default-tail path — 48,625 CU measured / 72,000 fenced (the old 12,251 / 35,000 row measured the owner-legacy list) — and SetReputationFloor lands at 6,756 / 30,000.

2026-07-22 — v0.38.0 (verified-sender attestation: a domain vouches for its sending wallet)

  • A domain can now attest its sending wallets on-chain. New Verified-sender attestation concept page and Attest a verified sender CLI reference: a sending organization proves control of its domain’s DNS — the same staged DNSSEC proof domain authorize rides — and mints a SenderAttestation record binding the domain to a wallet, the protocol’s trust mark for organizational senders. Attesting requires no MailDomain account and confers no serving rights; a domain may attest any number of wallets, one revocable record per (domain, wallet) pair.
  • Two new domain-program instructions: AttestSender (discriminant 17, permissionless — the attested wallet rides the payload) and RevokeSenderAttestation (18, holder-signed close with rent refund; the PDA re-derives from the signer, so no other key reaches the record), plus the new sender_attestation PDA seed — see the program reference and the new blake3 table row.
  • The attestation fee is delegate-tunable: new mail-side SetSenderAttestationFee instruction (discriminant 47, sithbit postmaster fee attestation <LAMPORTS>) tunes the one-time fee AttestSender pays the postoffice (default DEFAULT_SENDER_ATTESTATION_FEE_LAMPORTS = 0.01 SOL, capped at MAX_SENDER_ATTESTATION_FEE_LAMPORTS = 0.1 SOL; over-cap refuses with new custom error 101 SenderAttestationFeeAboveCap, and a zero fee stores the “unset” sentinel and resolves to the default). The postoffice account grew 176→184 bytes (versioned reads default older accounts; the setter upgrades in place). See the tunable-constants table.
  • Both query surfaces ship: the read-only sithbit domain attestation <MAIL_DOMAIN> <WALLET> lookup (every build), and the gRPC gateway’s new GetSenderAttestation call — {domain, wallet}{attested, attested_at}, where a clean absence answers attested = false and a failed chain read is UNAVAILABLE, never a false. A client badge over these reads is planned but not yet shipped. See Looking up an attestation.
  • The compute-unit table’s three attestation rows (landed with the measurement suite) are part of this release: AttestSender 322,474 CU measured / 345,000 fenced, RevokeSenderAttestation 11,224 / 34,000, SetSenderAttestationFee 6,546 / 30,000.

2026-07-22 — v0.37.0 (client-certificate download from the extensions)

  • The Thunderbird and Outlook client pages’ certificate-login sections now document the extension path: each extension’s settings surface gained a “Certificate sign-in” section whose Download client certificate button derives the <pubkey>.crt/<pubkey>.key pair in-extension — byte-identical to sithbit mailbox create-cert’s output, and deterministic per wallet (re-downloading anywhere yields the identical certificate). Import into the mail client or OS store stays manual; locked and external (Phantom/Ledger) wallets cannot derive and the CLI path remains the canonical route.

2026-07-22 — v0.37.0 (privacy concept diagrams)

2026-07-22 — v0.37.0 (do-not-disturb vs. autoresponder diagram)

  • The Do not disturb concept page gained a side-by-side diagram contrasting the classic accept-and-autoreply flow (mail piles up with postage to settle; the “I’m away” reply may never reach the sender) with SithBit’s refuse-at-the-door 450 (the sender’s own mail server queues and retries; nothing piles up and no stamp is burned).

2026-07-21 — v0.37.0 (stamp-fee operator split + honest gRPC fee fields)

  • The per-stamp protocol fee now splits with the recipient’s MX operator. CreateFrombox/AddStamps accept an optional trailing “operator tail” — the recipient’s mailbox, its named domain, and the domain authority. When present (the CLI, wasm builders, and web prepay all build it automatically), the authority receives operator_share_bps (default 10%) of the hybrid fee and the postoffice the remainder; the buyer’s total is unchanged. Lapse and filler rules mirror the settlement share, and legacy account lists keep the whole fee with the postoffice — the tail is optional, so no client breaks and no instruction payload changed. The owner waiver still precedes the split. See The per-stamp protocol fee.
  • FromboxResponse can now say “fee unknown”. New bool stamp_fee_known (field 6) on the gRPC response: false means the gateway’s postoffice read failed and the two fee arms are 0 — unknown, not free — which no value convention could express since a stored flat fee of 0 legitimately charges nothing. The failed read stays non-fatal and uncached.
  • The wasm frombox builders (create_frombox_tx/add_stamps_tx and their unsigned twins) gained trailing optional operator_domain/operator_authority parameters (both-or-neither); existing callers are unaffected.

2026-07-21 — v0.36.0 (Core Concepts go GUI-first, with concept graphics)

2026-07-21 — v0.36.0 (glossary: sans-io)

2026-07-21 — v0.36.0 (settlement basis-point rates: hybrid stamp fee + tunable operator share)

  • The per-stamp protocol fee is now a hybrid: third-party stamp purchases at AddStamps/CreateFrombox pay the greater of the flat per-stamp fee and a bps share of the escrowed postage (stamp_fee_bps, default DEFAULT_STAMP_FEE_BPS = 100 = 1%, capped at MAX_STAMP_FEE_BPS = 1 000). At the defaults the arms cross at 0.01 SOL of postage per stamp — cheap friend-tier stamps still pay the flat fee, while a default-priced 1-SOL stranger stamp now pays 0.01 SOL instead of 0.0001. The recipient-self-funding waiver covers the whole hybrid unchanged (“friends mail you free” is untouched), and the refundable signature surcharge is never in the bps base. See The per-stamp protocol fee.
  • The operator share is now delegate-tunable: the 10% OPERATOR_SHARE_BPS split at DeleteMail settlements, reply-bounty claims, escrowed alias transfers, marketplace sales, and auction settlements now reads the postoffice’s operator_share_bps field (default 1 000 = today’s behavior, capped at MAX_OPERATOR_SHARE_BPS = 2 000). Behavior at the default is byte-identical; an unreadable postoffice charges the protocol defaults (the rate read never blocks a settlement).
  • New SetSettlementBps instruction (discriminant 46, delegate-only) sets both rates in one instruction; over-cap rates refuse with new custom errors 99 OperatorShareBpsAboveCap / 100 StampFeeBpsAboveCap. A zero rate stores the “unset” sentinel and resolves to its protocol default — the bps rates cannot be tuned to literal zero. The postoffice account grew 160→176 bytes (versioned reads default older accounts; writers upgrade in place). See the program reference and the tunable-constants table.
  • Every quote surface knows the hybrid: sithbit postoffice fee stamp prints both arms, the new sithbit postoffice fee settlement / sithbit postmaster fee settlement <OPERATOR_SHARE_BPS> <STAMP_FEE_BPS> read and tune the rates (Postmaster administration), the CLI stamp-purchase preview quotes the hybrid against the actual postage, the gRPC FromboxResponse gained stamp_fee_bps, and the webmail prepay card and onboarding funding page price quotes through a new wasm stamp_purchase_fee export.
  • New section: Modeling the postoffice’s revenue base — the honest segmentation (waived owner purchases, flat-dominant friend tiers, priced-out strangers) that motivates the settlement rates as the scalable, capped levers.

2026-07-21 — v0.35.0 (length-tiered premium pricing for short alias names)

  • Registering a 1–4 character alias now pays a per-length premium claim fee instead of the flat fee; names of 5 or more characters are unchanged. Defaults: 10 SOL (1 char), 1 SOL (2), 0.1 SOL (3), 0.05 SOL (4) — short names are scarce assets (36 one-character combinations) and are priced accordingly, on the registrant’s side per the positioning principle. The schedule lives on the postoffice (ALIAS_TIER_FEES_LAMPORTS, account grown 128→160 bytes, versioned reads default older accounts) and is delegate-tunable via the new SetAliasTierFees instruction (discriminant 45), each slot capped at 10× its default (MAX_ALIAS_TIER_FEES_LAMPORTS; over-cap refuses with new custom error 98 AliasTierFeeAboveCap). See Economics — Alias holders and the tunable-constants table.
  • Delegate reservations stay fee-free at every length — the postmaster reserves premium short names for rent alone and resells them on the marketplace at seller-set prices; see Reserve aliases in bulk.
  • The price always shows before you pay. sithbit alias create prints a fee preview (per premium name + run total, or the delegate waiver notice), sithbit postoffice fee alias prints the effective per-length schedule with its caps, and the new sithbit postmaster fee alias-tiers <1> <2> <3> <4> tunes it — see Create an alias and Postmaster administration. The webmail aliases pane quotes “Registration fee: N SOL” live as you type (from the fetched postoffice account, protocol defaults when unreadable), and the onboarding wizard’s funding check prices a premium handle by its length.

2026-07-21 — v0.34.0 (IMAP SPECIAL-USE mailbox attributes, Tier 1)

  • The IMAP server now advertises SPECIAL-USE (RFC 6154) and marks the well-known top-level mailbox names — Sent, Trash, Drafts, Junk (also Spam), Archive — with their \Sent-style attributes in LIST responses, case-insensitively, so clients file sent/deleted/draft mail into the same folders everywhere. INBOX and nested names carry no role; the LIST (SPECIAL-USE) selection filter and CREATE-SPECIAL-USE are not supported. See the Standards support IMAP table.

2026-07-21 — v0.33.0 (IMAP advertises SASL-IR)

  • The IMAP server now advertises SASL-IR (RFC 4959) in the greeting and CAPABILITY responses, wherever the AUTH= mechanisms are offered. The initial-response form of AUTHENTICATE was already accepted; the advertisement lets clients discover it instead of probing. See the Standards support IMAP table.

2026-07-21 — v0.32.0 (docs: setup and earnings join the SithBit CLI; onboarding leads with the web wizard)

Documentation-only: the version tags the unchanged protocol state.

  • The two CLI walkthroughs move into the SithBit CLI reference tree: First-run setup (sithbit setup, now the tree’s first subtopic) and Revenue snapshot (sithbit earnings, between Campaigns and Closing accounts). Cross-links follow (Fromboxes’ USD-annotation pointer, the CLI Quickstart’s walkthrough link, and Solana clusters’ faucet note).
  • “Setup and earnings” becomes Getting started — the page now opens with the browser wizard the four web clients share (the audience most users belong to), keeps the standalone get-started and refused-sender pages, and points terminal-comfortable readers at the two relocated CLI topics. The page’s URL and section anchors are unchanged.

2026-07-21 — v0.32.0 (docs: tables wrap in place instead of scrolling)

Documentation-only: the version tags the unchanged protocol state.

  • Prose tables no longer cut off their last column behind mdBook’s horizontal scrollbar — felt hardest in the Configuration reference’s key/default/meaning tables. Book-wide CSS (css/brand.css) now spans tables across the text column, slims the cell padding, left-aligns headers, and lets long tokens in every column but the first break at the overflow point (config keys never break mid-token; a width floor keeps “Default” readable beside a long “Meaning”).

Documentation-only: the version tags the unchanged protocol state.

  • Running a mail server’s service table now lists the optionally-embedded IPFS node among sithbitd’s roles, with its “needed when” column noting that role applies only under [ipfs] kind = "embedded" — a fleet delegates to sithbit-ipfsd or a pinning service instead.
  • Choosing a commodity provider — each provider name now links to that provider’s developer sign-up page (or product home page where sign-up URLs are region-specific).

2026-07-21 — v0.32.0 (docs: Icon legend joins the Glossary)

Documentation-only: the version tags the unchanged protocol state.

  • The Icon legend now sits in the Glossary sidebar section, right after Terms and definitions, instead of under Appendix: Reference — the two term-lookup pages now live side by side. The source file (and its deployed URL) is unchanged; only the SUMMARY.md placement moved.

2026-07-21 — v0.32.0 (docs: landing-page hero leads with earned postage)

Documentation-only: the version tags the unchanged protocol state.

  • Welcome landing tweaks — the hero paragraph now says the sender-paid postage is earned by you, not just that it prices out spam, and the “No gatekeeper, no single company” card drops its featured accent border to sit as a regular card; the spam-pricing card is the page’s only featured one.

2026-07-21 — v0.32.0 (sithbitd installs as a systemd or Windows service)

MINOR — an additive deployment capability.

  • Running as an OS service — the new sithbitd service install / sithbitd service uninstall subcommands install the daemon under systemd (a generated unit file with restart-on-failure, network ordering, and commented unprivileged-user/low-port-capability lines; --print renders it without writing) or the Windows service control manager (an auto-start registration whose internal service run verb re-anchors the recorded working directory and config before the daemon boots). Neither install activates anything behind the operator’s back — the systemctl / Start-Service step is printed, not run.

2026-07-21 — v0.31.0 (Closing accounts joins the SithBit CLI reference)

PATCH — documentation-only.

  • The closing-accounts reference now lives in the SithBit CLI tree as its Closing accounts subtopic, right after Campaigns — retitled from “Closing accounts and reclaiming rent”, following the campaign reference out of the appendix. All cross-links follow, and the old deployed URL (appendix/closing-accounts.html) redirects to the new page so external bookmarks keep working.

2026-07-21 — v0.31.0 (Campaign CLI reference moved under the SithBit CLI)

PATCH — documentation-only.

  • The sithbit campaign reference now lives in the SithBit CLI tree as its Campaigns subtopic, beside the other command references, instead of in the appendix. All cross-links follow, and the old deployed URL (appendix/campaign-cli.html) redirects to the new page so external bookmarks keep working.

2026-07-21 — v0.31.0 (Sponsored mailbox creation: a domain authority provisions for its users)

MINOR — additive public-ABI change (a tail field on CreateMailbox and on the Mailbox account, plus three appended error codes).

  • A domain’s on-chain authority can now create a mailbox for a different owner. Sponsored mailboxes explains the concept and its three guards: only the named domain’s authority may pay, the default postage is forced to the 1-SOL spam floor, and no self-alias is bundled. The CLI surface is mailbox create --for <address> (requires --domain).
  • The mailbox account records its funder, and closing refunds the funder. The Mailbox account gains a tail funder field (shown by mailbox get and the wasm account decoder); mailbox close --finalize now routes the mailbox’s rent to the recorded funder — the owner itself on a self-created mailbox (unchanged), the sponsor on a sponsored one — with the CLI passing the funder account automatically. The pending-close account’s rent still refunds to the owner who funded the request.
  • Program & PDA reference gains error codes 95–97 (SponsoredMailboxRequiresDomain, UnauthorizedDomainSponsor, FunderAccountInfo) and updates the CreateMailbox/FinalizeCloseMailbox rows. This retires the last TODO in the workspace’s Rust tree (the payer/owner split in the mail program’s create processor).

2026-07-21 — v0.30.1 (Navigation reorder: standards up front, clients first under Using SithBit)

PATCH — documentation-only.

  • Standards and RFC coverage moved to the front matter, directly after the Introduction — the wire-compatibility story now greets a reader before the concept chapters instead of trailing them.
  • “Using SithBit” reordered around the reader’s journey: GUI clients leads the section, followed by Setup and earnings (moved here from Core Concepts), with Economics after them.
  • “RFC” is now a glossary term. The glossary’s Mail protocols section defines it, so RFC references linked to it get the standard hover tooltip; the standards page links its first prose mention.

2026-07-21 — v0.30.1 (Full sithbit-console tutorial and reference in the appendix)

PATCH — documentation-only.

  • The sithbit-console admin TUI now has a full appendix page. The sithbit-console admin TUI documents the operator console end-to-end: prerequisites (a reachable account API and the admin_wallets allowlist), running and configuring it, the wallet-challenge login, a tutorial through both tabs (Accounts → Mailboxes → Messages with chain states, the on-chain balances pane, queue depths and the confirmed dead-letter requeue/discard workflow with the cloud-store claim window), a complete key reference, the console’s deliberate scope limits (API-only, no store access, no chain writes), and a troubleshooting table. The job-queues section and the configuration reference now link to it; previously the console was documented only in fragments across those two pages.

2026-07-21 — v0.30.1 (Client pages note on-chain domain ownership for wallet submission)

PATCH — documentation-only.

  • The Outlook and Thunderbird client pages now carry the on-chain-ownership half of the wallet-submission envelope rule. Both Outlook and Thunderbird previously phrased the sender rule as “your own wallet address at a domain the server serves / is authoritative for”, which omitted the v0.29.0 enforcement: on a chain-connected submission server the wallet must also own that domain on-chain (its recorded GetMailDomain authority), not merely have the server serve it. The pages now state that nuance at end-user altitude and add the matching 553 5.7.1 refusal case; the full rule (including the chain-less-dev-stack fallback to server-served domains only) still lives in the configuration reference.

2026-07-21 — v0.30.0 (Onboarding wizard warns on an unfunded wallet before the mailbox create)

MINOR — a new client capability and default onboarding behavior.

  • The web onboarding wizard now checks the wallet balance before it claims a mailbox. On the Review and Finish steps, a wallet that can’t cover the create cost (the account rents, plus the flat alias fee when a handle is claimed — about 0.0013 SOL bare, 0.0124 SOL with a handle) gets a plain-language warning naming the wallet, its balance, and how much more to transfer. The warning does not block the flow — you can fund the wallet out of band and continue.
  • A funded-then-failed create no longer shows the raw chain error. If the create is attempted with too little SOL, the node’s “Attempt to debit an account but found no record of a prior credit” preflight rejection is rewritten into the same funding guidance, across the create, import, and connect-wallet paths.
  • Single source of truth for the figure. The required-funding amount the wizard quotes is computed by the same core routine the CLI sithbit setup wizard uses, so the web and CLI figures can never drift.

2026-07-20 — v0.29.0 (Wallet submission envelope now checks on-chain domain ownership)

MINOR — a new enforcement default that affects deployments.

  • A gateway-backed submission listener now requires the authenticated wallet to own the envelope domain on-chain. For a wallet-literal Wallet submission envelope, a listener with a chain gateway ([grpc] configured) no longer accepts <wallet>@<domain> merely because the domain is in local_domains; the domain must also be one the wallet is the recorded on-chain authority for, checked via the gateway’s GetMailDomain lookup (exact base58 match). local_domains still scopes which domains the listener serves; the authority check scopes which of those the authenticated wallet may send as.
  • Chain-disabled listeners are unchanged. A listener with no chain gateway (an empty [grpc] / dev MX) has no per-wallet lookup available and falls back to local_domains alone, so empty-config dev stacks keep sending.

2026-07-20 — v0.28.0 (Chrome extension gains trustless compose/reply parity)

MINOR — a new client capability and a new shipped default that affect deployments.

  • The Chrome extension can now send trustlessly, at parity with webmail, Thunderbird, and Outlook. The Trustless viewer’s header gains a Reply on-chain button, and the popup mounts the same floating on-chain compose card the other GUI clients carry — a Compose on-chain button opens it blank, Reply seeds it with the decrypted sender and the parent message’s account address, and the seal → pin → SendMail lifecycle is signed in the extension’s wasm module with no mail server in the path.
  • The extension ships a default IPFS pin origin. Connection settings gains ipfsPinUrl (default http://127.0.0.1:8182 — an unauthenticated loopback sithbit-ipfsd) and its optional ipfsPinToken (default empty), where outbound sealed bodies are pinned; saving a non-loopback pin origin prompts for that host’s permission.

2026-07-20 — v0.27.1 (Second CID pointer linked on the mailbox page)

PATCH — documentation-only, no protocol change.

  • The “Opting out of IPFS storage” section now links “CID”. The Opting out of IPFS storage prose said the on-chain message carries a “fetchable CID” as bare text; it now points to What is a CID?, matching the same page’s No-IPFS bullet, which already linked the term.

2026-07-20 — v0.27.0 (All nine dashboard panes documented on every GUI client; CID explained for non-technical readers)

PATCH — documentation and docs-tooling only, no protocol change.

  • The Domains, Reply bounties, and Mailbox panes are now documented on all four GUI client pages. The Thunderbird, Outlook, Chrome and webmail pages previously described only six of the nine shared dashboard panes; the domain-marketplace pane (list or buy a domain), the reply-bounty settlement pane (claim a bounty on a message you replied to, or refund an expired one you placed), and the mailbox-config pane (claim the mailbox and set its handle, sending domain, default stamp price, and opt-out-of-IPFS flag) are now described on each, in that page’s own form.
  • “CID” is now explained for non-technical readers. The IPFS storage: benefits page opens with a new “What is a CID?” section that explains a content identifier as a fingerprint computed from a message’s exact bytes — the same content always yields the same CID (so it is the address you fetch by) and any change yields a different one (so it doubles as a tamper check) — with a two-row illustration and a note that SithBit produces CIDv1 byte-for-byte identically to Kubo. The glossary’s terse CID entry now links to it.
  • Docs-tooling: the mailbox-close timelock figure is now fenced. A new mail_docs/check_timelock.py gate leg parses MAILBOX_CLOSE_TIMELOCK_SECS from mail_model/src/constants.rs and asserts, both ways, that the “7 days” quoted in the mailbox-close docs matches it — so retuning the constant or drifting the prose fails the docs gate. It is scoped by an explicit allowlist plus a mailbox-close context filter, so the identical “7 days” literal used for the domain-deactivation, reclaim, and bounty-window constants is not swept in.
  • The dashboard chain panes now load over a direct RPC connection, not only the account API. Balances, the encryption key, mailbox settings and the mailbox-close request now populate for a wallet-unlocked client with no account API configured — previously they re-rendered but stayed empty until an API token existed. The Aliases pane still needs the API, since there is no on-chain alias index to read directly. (The web-client screenshots were re-pinned to the updated source: this change is confined to the API-less load path, which the documentation screenshots — captured in API-backed mode — do not exercise, verified by re-capturing the inbox, settings and marketplace shots.)
  • Docs-tooling: check_anchors.py gains a --self-test leg. A checked-in, build-free fixture tree under mail_docs/tests/anchor_fixtures/ (a clean/ root that must exit 0 and a broken/ root that must exit 1) proves the checker’s exit-code contract, mirroring check_links.py --self-test. Unlike the link fixtures, each anchor-fixture root ships both a src/ and a hand-authored book/ HTML tree, because the checker validates #fragment links against built-book anchors. It is a standalone dev command, not wired into the docs-gate chain; the default check_anchors.py run is unchanged.

See The Chrome extension, The webmail app and IPFS storage: benefits.

2026-07-20 — v0.27.0 (External wallets can buy stamps and claim a mailbox; Glossary promoted)

PATCH — client and docs only, no protocol change.

  • External wallets (Phantom/Ledger) can now buy stamps and claim a mailbox. The dashboard gated those buttons on holding an unlocked in-app wallet key, even though the unsigned-transaction paths behind them were already wired and working for external wallets. Setting a stamp price, publishing an encryption key and settling reply bounties still require the in-app key — those have no unsigned equivalent — and the panes now say so specifically instead of telling every user to “unlock your wallet”.
  • The mailbox-close pane is documented on the Thunderbird, Chrome, Outlook and webmail client pages, including the 7-day wait, that it is cancellable throughout, and that the encryption-key close stays instant. Outlook gained a full pane list, which it previously lacked entirely.
  • The account-closing figure no longer shows CloseMailbox as an instant one-step close; the mailbox and key legs now carry their own timings.
  • local_domains on the submission listener is shown as a real configuration example rather than described in prose. Each SMTP role carries its own list — the MX section’s copy does not carry over — which is the domain half of the wallet-envelope rule.
  • The Glossary is now a top-level section in the navigation, immediately before Appendix: Reference. Its page keeps its existing address, so every existing link to it still works.
  • The web-client screenshots were regenerated, which replaced a marketplace listings image that had been shipping as a blank page.
  • The Core Concepts pages no longer assume you can read the source. References to internal file, function and type names, and to configuration keys and their file sections, have been rewritten as plain statements of what the system does — the concepts pages now explain the protocol without requiring a copy of the code beside them. Every fact those references carried is retained; only the way of stating it changed.

See GUI clients, Close a mailbox, Configuration and Terms and definitions.

2026-07-20 — v0.26.0 (Closing a mailbox is timelocked; the one-step close is disabled)

MINOR — BREAKING for clients that emit CloseMailbox. Closing a mailbox is now a two-step, 7-day flow. RequestCloseMailbox (42) starts the clock and refunds nothing — the mailbox stays open and keeps receiving mail; FinalizeCloseMailbox (44), legal only after MAILBOX_CLOSE_TIMELOCK_SECS (604,800 s), closes it and refunds the mailbox’s rent and the transient pending record’s together; CancelCloseMailbox (43) aborts the request meanwhile. The CLI spells these mailbox close, mailbox close --finalize and mailbox close --cancel, and the flow is reachable in webmail, Outlook, Thunderbird and Chrome, including trustless mode.

  • The one-step CloseMailbox (discriminant 16) is refused with error 94, InstantCloseDisabled. The discriminant still decodes, so indexers replaying history resolve old transactions — the same shape item 30 used for TransferAlias / UnilateralTransferDisabled (85). MAJOR stays 0: the protocol is pre-launch.
  • New errors 91–94: MailboxCloseAlreadyPending, NoPendingMailboxClose, MailboxCloseTimelockNotElapsed, InstantCloseDisabled. New PDA seed PENDING_MAILBOX_CLOSE_SEED (pending_mailbox_close) — the prefix is load-bearing twice over: the Mailbox PDA is bare-seeded on the address, and PendingMailboxClose, PendingDeactivation and PendingReclaim all serialize to the same eight bytes, so only the derivation distinguishes them.
  • Why a delay and not a fee. Instant rent reclamation made a burned sending identity free to discard. The owner of a mailbox is a recipient, and the positioning principle puts the cost burden on senders — so the lever is time, not money: the rent still comes back in full. Spammers get capital stuck for a week per burned identity and operators get a flagging window; honest owners see a delay on an action they take approximately never.
  • CloseKey deliberately stays instant. It revokes a compromised delegated encryption key; a seven-day window there would leave MX servers sealing to a key the attacker holds, protecting the attacker rather than the owner.
  • Compute units: three new fenced rows — RequestCloseMailbox 13,334 / 36,000, CancelCloseMailbox 12,156 / 35,000, FinalizeCloseMailbox 12,858 / 36,000.

See Close a mailbox, Closing accounts, the threat model and Economics.

2026-07-20 — v0.25.0 (Wallet submission envelopes are pinned to the wallet’s own address)

MINOR — a new enforcement default on the submission path; no protocol change. A wallet-authenticated submission session may now present exactly one envelope sender: its own wallet base58, at a domain the listener is authoritative for (local_domains). Another wallet’s address, its own address at a domain the server does not serve, and the null sender MAIL FROM:<> are all refused 553 5.7.1.

  • The case-sensitivity fix is the security-relevant part. The previous rule compared the envelope local part case-insensitively, which for base58 is wrong: Alice and alice decode to different keys, so a wallet session could send as a neighbouring valid wallet. The comparison is now exact.
  • Scope, stated honestly. The domain leg checks the domains this server serves, not the domains this wallet’s mailbox holds on-chain — the per-wallet reverse lookup is not reachable from the SMTP driver without new gateway surface. On a multi-domain instance a wallet may still send as itself at any domain that instance serves.
  • Alias and password submission are byte-for-byte unchanged; the new rule is consulted only for wallet-literal identities.
  • Dev-stack trap. With local_domains empty the check falls back to hostname, which the empty-config dev stack leaves as localhost, and the chain-disabled dev stack never discovers domains — so sending as <wallet>@sithbit.net there now returns 553 where it previously worked. The refusal text names the sender, not the domain, so both the symptom and the one-line fix are written down. Note each SMTP role reads its own local_domains: setting it under [smtp] does not affect the [submission] listener.

See Wallet submission envelopes, Thunderbird and Outlook.

2026-07-19 — v0.24.0 (Onboarding: passphrase confirmation, reachable connection settings)

PATCH — client/docs only, no protocol change. Four fixes found smoke-testing the Chrome extension loaded unpacked, all in shared client code, so every client gets them.

  • Passphrase reveal and confirmation. The wallet passphrase set during onboarding could not be seen and was typed only once — and a typo there is unrecoverable: the wallet seals fine and only fails later, at unlock, with no way back. Every passphrase field now has a reveal (“eyeball”) toggle, and every field that sets a passphrase (the onboarding wizard’s step 1, the wallet manager’s import, the marketplace sign-in) now requires a matching confirmation before it will seal anything. See Web onboarding: the browser wizard.
  • Connection settings are reachable during onboarding. In the Chrome extension they had been gated behind the signed-in view, which requires a registered on-chain mailbox — which in turn requires a reachable account API, the very thing those settings configure. With no API running, a new user was pinned in the onboarding wizard with no way to correct the URL or switch to trustless mode. They now sit in a collapsed disclosure at the foot of every view. See The Chrome extension.
  • A meaningful message when the account API is unreachable. A refused connection surfaced the browser’s bare Failed to fetch. Clients now name the endpoint and the remedy, in language matched to the reader: a loopback URL means the reader runs the stack themselves, any other host means they are somebody’s mail customer.
  • A degraded popup no longer reads as broken. An unreachable account API is reported as a warning with the fix, and onboarding continues, instead of dumping a raw error and blocking.

2026-07-19 — v0.24.0 (Chrome extension: in-popup mail reader + side panel)

PATCH — client/docs only, no protocol change. The Chrome extension gains a real in-popup mail reader, at parity with webmail: the shared three-pane reader (folder rail, message list, message view, compose, and search) over the account API’s /v1/mail surface when an account API is reachable, and a trustless on-chain inbox (the mailbox’s messages listed straight from Solana, bodies unsealed in wasm, no server) as the server-down fallback and the only reader when the API url is left blank. The same surface now also opens in Chrome’s persistent side panel via an Open in side panel button (the toolbar-icon click still opens the transient popup). See The Chrome extension.

2026-07-19 — v0.24.0 (Core Concepts section rename)

PATCH — docs only. The first documentation part, previously titled Basic Concepts, is now Core Concepts in the navigation. Only the displayed part title changed; the page URLs under basic-concepts/ are unchanged, so existing links and bookmarks still resolve. (Earlier change-history entries that name the old title are left as-is — they record what the section was called at the time.)

2026-07-19 — v0.24.0 (Cloudflare backend: true multi-daemon writes)

MINOR — deployment-affecting default-behavior change. The cloudflare store’s single-writer delivery caveat is removed: IMAP-uid allocation is now a server-side atomic UPDATE … RETURNING on D1, and keyed leases moved from Workers KV (best-effort, no CAS) to the same strict single-statement CAS the SQLite/Turso stores run, on D1’s leases table — so any role may run N-wide on Cloudflare, exactly as on postgres/aws/azure (see Scaling out’s checklist and Which stores support which split). The DMARC drain also became one atomic DELETE … RETURNING, so concurrent daemons partition report rows instead of double-reporting. The [store.cloudflare] kv_namespace_id key is retired: still accepted so existing TOMLs keep parsing, but ignored, and no longer a required id — the KV namespace itself is no longer a provisioning prerequisite. The Durable-Object route the glossary recorded for this work was superseded by these plain atomic D1 statements (no Worker-side code).

2026-07-18 — v0.23.0 (Addresses/Fromboxes/Email/Marketplace move into Basic Concepts + Technical Reference)

PATCH — docs only. Wave 1 of the docs audience reorg (item 42): Addresses, Fromboxes, Email, and Marketplace now split cleanly between a new Basic Concepts section (pure conceptual/explanatory prose, no CLI examples) and the CLI reference under Technical Reference → SithBit CLI. Every command-reference page now opens with a short referral link back to its concept page. Mailboxes, Aliases, Domains, and the GUI-clients pages are untouched this wave — the deferred remainder of the reorg. See Basic Concepts → Fromboxes and Basic Concepts → Email for the split’s shape.

2026-07-18 — v0.23.0 (CLI Quickstart relocated ahead of the docs audience reorg)

PATCH — docs only. The developer quickstart page moved from getting-started.md to CLI Quickstart under a new Technical Reference section, retitled to avoid colliding with a future end-user “Getting Started” tutorial section (item 42’s audience reorg, in progress). Operating a SithBit Server re-nested under Technical Reference alongside it; no operator pages moved, only the SUMMARY.md heading structure changed. Every inbound link across the book was repointed to the new path.

2026-07-18 — v0.23.0 (DNS rows for the autoconfig/autodiscover hostnames)

PATCH — docs only. The DNS guide’s client-access section now spells out the two hostname records the native-wizard fallback path needs — autoconfig.<domain> and autodiscover.<domain> pointed at the domain-sithbit host — with the TLS-certificate SAN caveat. The routes themselves were already documented on the domain-sithbit page; the zone-side half was missing.

2026-07-18 — v0.23.0 (compute-unit table now gate-fenced against the suite)

PATCH — docs tooling. A new docs-gate leg, mail_docs/check_cu_rows.py, diffs the compute-unit table’s measured/ceiling values against the integration suite’s fenced constants (mail_client/tests/api/cu.rs), both ways, and checks the methodology prose still quotes the suite’s grind allowance. The rounded-auction-rows drift the previous entry corrects had sat silent since the auction wave — this class of drift now fails the gate instead of waiting for a manual sweep.

2026-07-18 — v0.23.0 (auction rows now quote exact measured CU)

PATCH — docs only. In the compute-unit table, the three auction rows had rounded “Measured CU” values while every other row quotes the integration suite’s exact fenced measurement. Aligned to the suite’s constants: SellAlias (open auction) 26,800 → 26,798, BidAlias 19,700 → 19,715, SettleAuction 26,700 → 26,674. Ceilings unchanged; all 25 rows now match the suite exactly.

2026-07-18 — v0.23.0 (cancel-instruction CU ceilings documented)

PATCH — docs only. The compute-unit table now covers the three marketplace cancel flows fenced by the integration suite: CancelTransferAlias (alias transfer cancel, 24,828 measured / 48,000 ceiling), CancelAliasListing (alias sell --cancel, 19,183 / 42,000), and CancelDomainListing (domain sell --cancel, 16,001 / 39,000). The methodology’s bump-grind variance note gains alias transfer cancel as the widest swing recorded (9,816–24,828 CU).

2026-07-18 — v0.23.0 (threat-model lockbox metadata retitle)

PATCH — docs only. In the threat model, the lockbox scope bullet formerly led “Body only, in v1.” — stale now that the v2 sealed-envelope engine has landed. Retitled “Metadata stays visible.”: the SMTP envelope and headers travel unsealed regardless of lockbox version; the bullet’s substance is unchanged.

2026-07-18 — v0.23.0 (opt-in wallet-literal recipients on a chain-less MX)

MINOR — new configuration setting (default preserves existing behavior everywhere):

  • [smtp] accept_wallet_literals (default false). A chain-less MX (no [grpc] configured) refuses every recipient today; with this switch on, it accepts syntactically valid 32-byte base58 wallet-literal local parts — mirroring the account API’s chain-less compose route, where a literal wallet resolves to itself. No postage check applies on that path (no chain to consult), which is why the default stays off: leaving it unset keeps the postage gate intact and behavior byte-identical. Inert when [grpc] is configured. Applies to sithbitd and the standalone smtp-server alike. See Configuration.

2026-07-18 — v0.22.0 (marketplace guards: no deactivation while listed, no sale of an inactive domain)

MINOR — additive on-chain behavior change (new refusals using existing error codes; one instruction gains a required account):

  • RequestDeactivateDomain refuses while a listing is open. The instruction now takes the domain-listing PDA as a required read-only account and refuses with DomainHasPendingListing (code 64) when a marketplace listing stands — a deactivation can no longer be staged under a live listing. The sithbit domain deactivate builder passes the new account. See Program reference → Marketplace.
  • BuyDomain refuses an inactive domain. Settlement now re-checks is_active at purchase time and refuses with InactiveDomain (code 25) — a deactivation finalized after listing can no longer sell a dead name. Inactive domains stay listable by design; the sale completes once the domain is reactivated. (An in-flight pending deactivation was already refused at buy time, code 29.)
  • Reference corrections riding the change: DeactivationAlreadyPending is code 29 (the page said 28), and BuyDomain’s account list is 9 slots (the row predated the pending-reclaim slot).

2026-07-18 — v0.21.1 (devnet keypair locations reconciled)

PATCH — repository layout and documentation only (no code behavior, on-chain ABI, instruction, error-code, or configuration change):

  • Keypair homes reconciled. keypair/ again holds the mainnet-track vanity keypairs for all three programs; the live devnet mail/alias program keypairs moved beside the domain one at mail_client/tests/*-dev-keypair.json (the retired first-generation devnet pair is now git-history-only). The devnet vanity-ID appendix’s keypair-location prose and both cp staging workflows now reflect the layout, and the per-program README deploy snippets are correct as written again.

2026-07-18 — v0.21.0 (lockbox envelope: rich HTML + attachments in the engine)

MINOR — additive capability in the shared lockbox engine (no on-chain ABI, instruction, error-code, or configuration change; the shipped plugins’ user-facing behavior is unchanged today):

  • The sealed payload is now a structured envelope. The shared compose/read engine seals a JSON envelope carrying the text body plus, when the sending client supplies them, rich HTML and attachments — as one sealed unit, with no wasm or on-chain change. Messages sealed by earlier versions remain readable (bare-body fallback). A 12 MiB pre-seal ceiling refuses oversized payloads with a clear error (sized so the double-base64 result clears the SMTP server’s default 25 MiB message-size limit). The Thunderbird and Outlook plugins still hand the engine only the plaintext body — host-side compose glue for HTML/attachments is a planned addition. See How it works and What v1 does — and does not — do.

2026-07-18 — v0.20.7 (smoke script rebuilds images)

PATCH — tooling and documentation only (no code behavior, on-chain ABI, instruction, error-code, or economic change; no configuration key, value, or default moves):

  • docker/smoke.sh rebuilds before probing — the script now brings the compose stack up with docker compose up -d --build, so a standalone smoke run rebuilds the images instead of silently probing stale local ones (the already-exported chain profile covers the mail-grpc build too). Documented in The compose dev stack.

2026-07-18 — v0.20.6 (web-client terminology sweep)

PATCH — code comments only (no rendered UI, logic, or configuration change):

  • “knob” retired from the web clients — the eight remaining occurrences in webclients/ source comments (the shared panes and onboarding-wizard modules, and the fund/DND standalone pages’ operator-endpoint headers) now read “setting”, completing the v0.20.1/v0.20.4/v0.20.5 terminology sweeps. The screenshot manifest was re-pinned hash-only — no pixel changed, so the recorded captures remain valid.

2026-07-18 — v0.20.5 (build-features heading & terminology polish)

PATCH — documentation and comments only (no code behavior, on-chain ABI, instruction, error-code, or economic change; no configuration key, value, or default moves):

  • Deploy’s build-features section renamed — the heading is now Slim-build features (formerly “Storage-backend build features”), reflecting everything the section grew to cover: the storage backends and the key-source (akv/asm/gsm) and app-config (awsconf/azconf) cloud features. Inbound links in Deploy, Scaling out, and this page’s earlier entries follow the new anchor (URL only — the old entries keep their wording).
  • Configuration-reference cross-links — the key-sources and cloud-app-config passages in the configuration reference each point at Slim-build features for the per-binary slim build commands.
  • Terminology residuals — the retired “knob” leaves its last holdouts (the ipfs_daemon and ipfs_gateway crate READMEs and a check_config_keys.py comment; now “setting”), completing the v0.20.1/v0.20.4 sweeps.

2026-07-17 — v0.20.4 (terminology sweep completed in source)

PATCH — source comments and example-config prose only (no code behavior, on-chain ABI, instruction, error-code, or economic change; no configuration key, value, or default moves):

  • Terminology sweep, source side — the informal “knob” is now retired from the Rust source comments and test names (mail_spooler, smtp_server, account_api, mail_store, mail_client, mail_grpc, ipfs_swarm, pop3_proto, app_config, key_source) and from the prose comments of the two canonical example configs (sithbitd.example.toml, sithbit_ipfsd.example.toml), completing the v0.20.1 book sweep. Same standing rule, same replacements: “switch” for boolean enable/disable entries, “setting”/“option” for tunable values, and case-appropriate rewrites (e.g. “deliberately not configurable”) elsewhere. Meaning is unchanged everywhere; earlier entries on this page keep their wording as the record of the retired term.

2026-07-17 — v0.20.3 (slim per-binary builds)

PATCH — build features and documentation only (no on-chain ABI, instruction, error-code, or economic change; a default build compiles the exact same feature set as before):

  • Slim per-binary builds are now real — every binary crate forwards its dependencies’ cloud features under the same names, so --no-default-features --features <what you need> works at the binary you actually build. Forwarded alongside the storage backends: akv/asm/gsm (the credential-sealing key source’s cloud secret managers) and awsconf/azconf (the cloud app-config sources). See the build-features section for concrete slim build commands. Defaults still compile every cloud; a config naming a compiled-out cloud parses in every build and fails at load with a purposeful NotCompiled error naming the cargo feature to rebuild with.
  • mail_store’s key-source edge is feature-forwarded — its formerly unconditional dependency on all of key-source’s clouds now follows the same per-cloud features, so store-consuming binaries can strip clouds too.

2026-07-17 — v0.20.2 (compose smoke runs mail-grpc live)

PATCH — dev/CI tooling and documentation only (no on-chain ABI, instruction, error-code, or economic change; nothing a deployment configures moves):

  • docker/smoke.sh now boots the chain profile live — it exports COMPOSE_PROFILES=chain for every compose call it makes (teardown included), so mail-grpc starts with the rest of the compose dev stack and must pass the healthy-wait. No validator is required: the gateway’s readiness gates on its own gRPC listener, never on chain connectivity. The previous smoke only statically parsed the profile (docker compose --profile chain config -q), which let a stale broken image sit undetected on a dev host — the live boot caught exactly such an image on landing.
  • CI’s smoke job lifts the profile too (.github/workflows/docker-publish.yml): the job-level COMPOSE_PROFILES builds the mail-grpc image alongside the other five before the smoke runs, so the gRPC gateway is exercised live on every push and pull request, not merely compiled.

2026-07-17 — v0.20.1 (terminology sweep: “knob”)

PATCH — documentation only (no code change):

  • Terminology sweep across the book, per style direction: the informal “knob” gives way to industry-standard terms — “switch” for boolean enable/disable entries, “setting”/“option” for tunable values, and “settings” for collections. Meaning is unchanged everywhere, including in earlier entries on this page, which keep their versions, dates, and facts.

2026-07-17 — v0.20.0 (smarthost implicit-TLS dial)

MINOR — an additive capability affecting deployments (no on-chain ABI, instruction, error-code, or economic change):

  • New [spooler.smarthost] implicit_tls switch — default false; when set, the relay dials the smarthost with TLS from the first byte (the port-465 “SMTPS” style, named after the inbound listeners’ switch) instead of the default in-band STARTTLS, so STARTTLS never happens on the wire. The port is not auto-switched to 465 — it stays whatever the operator set — the handshake keeps the smarthost path’s strict webpki verification, and the direct-to-MX path is unchanged. This is the first consumer of the SMTP client machine’s implicit-TLS dial mode (SendMachine::new_tls) — see the [spooler] reference.

2026-07-17 — v0.19.0 (truthful TLS-RPT rows; report retention setting)

MINOR — a default-behavior change and a new deployment setting (no on-chain ABI, instruction, error-code, or economic change):

  • TLS-RPT success rows are now flag-truthful — a §4.1 success row is recorded only when the completed conversation actually ended on TLS (the send outcome carries the negotiated flag), so a success row can no longer describe a plaintext session; a completed plaintext opportunistic delivery — including a declined STARTTLS offer that continued in the clear — records no row at all (neither a TLS session nor a failed attempt). This closes v0.18.0’s recorded gap; the honest limit that remains (“never offered” vs “offered but declined” — both unrecorded) is in the rewritten RFC 8460 conformance section.
  • Pre-dial policy exclusions now record TLS-RPT rows — hosts a policy excludes before dialing land never-dialed failure rows instead of vanishing (the other v0.18.0 gap): a DANE-unusable host records dnssec-invalid with a bare tlsa policy block, an MX target outside an enforce-mode MTA-STS policy records sts-policy-invalid rendering the enforce policy body, with the planner’s diagnostic in failure-reason-code. Unreachable/timed-out hosts and STS testing-mode mismatches still record nothing — see the configuration reference.
  • New [spooler] report_retention_days setting — default 0 = keep forever; when set, an hourly worker prunes ingested DMARC aggregate reports (dmarc_rua/) and pending TLS-RPT rows (tlsrpt/pending/) older than the window. Off by default deliberately: dmarc_rua/ is the data GET /v1/admin/dmarc-reports serves, and retention removes reports from that surface — see the [spooler] table.

2026-07-17 — v0.18.2 (Outlook + Thunderbird gain the trustless reply/compose card)

PATCH — additive client UI over existing chain capability (no on-chain ABI, instruction, error-code, or economic change; the compose threads SendMail’s existing bounty/expiry/reply parameters through the already parity-fenced builders — the same call as item 39’s webmail card, v0.8.8):

  • Outlook: Trustless reply and compose — the taskpane’s trustless reader gains webmail’s Reply on-chain action, and the mail view mounts the floating on-chain compose card (reply-chip threading, bounty SOL + claim-window-days fields, inline stamp prepay). Replies are on-chain sends — never a host SMTP compose. The connection-settings pane gains the IPFS pin service URL/token (config.ipfsPinUrl / config.ipfsPinToken, default the loopback sithbit-ipfsd, unauthenticated).
  • Thunderbird: Trustless reply and compose — the same seam on the extension’s dashboard; the options page surfaces the pin URL/token, and a non-loopback pin origin joins the Save-click host-permission grant automatically.

PATCH — documentation only (no code change):

2026-07-17 — v0.18.0 (TLS-RPT reporting; DANE for MX-less domains)

MINOR — an additive capability and a default-behavior change affecting deployments (no on-chain ABI change):

  • DANE now covers MX-less domains — the outbound relay’s default-on dane enforcement previously skipped domains with no MX record (the documented RFC 7672 §2.2.1 subset). A DNSSEC-proven MX denial (Secure proof on the negative answer’s SOA) now marks the implicit-A fallback secure, so TLSA at _25._tcp.<domain> is consulted and enforced for signed MX-less recipients — see the rewritten DANE conformance caveat and the dns.md publishing note. The narrower remaining subset: denials without a validatable SOA stay insecure. Unsigned zones behave exactly as before.
  • TLS-RPT (RFC 8460) sending — new default-off [spooler.tlsrpt] switch: dialed relay attempts record per-host TLS results, and a drain worker folds them into per-domain aggregate reports delivered to recipients publishing _smtp._tls.<domain> rua targets — over both mailto: (DKIM-signed via the outbound relay) and https: (application/tlsrpt+gzip POST). The new RFC 8460 conformance section carries the honest gaps (certificate-* taxonomy collapses into validation-failure with detail preserved; pre-dial exclusions record nothing; duplicate-on-crash tolerated via deterministic report ids). This closes the “no TLS-RPT” limitation recorded by the MTA-STS and DANE landings.

2026-07-17 — v0.17.0 (DMARC aggregate-report ingestion; postmaster alias claim)

MINOR — additive capabilities affecting deployments (all off by default or init-time only; no on-chain ABI change):

  • DMARC RUA ingestion — SithBit deployments can now receive the aggregate reports other operators send about their domains. Three pieces, documented across dns.md, the configuration reference, and a new RFC 7489 §7.2 conformance section: [smtp] postmaster_wallet (RFC 5321 §4.5.1 — bare/domained postmaster bypasses alias resolution and the frombox/postage gate so external reporters can deliver at all; unset keeps refusals byte-identical), [spooler.dmarc_rua_ingest] (matched delivered recipients get their reports parsed with the vendored RFC 7489 parser and stored as JSON under the fixed dmarc_rua/ blob prefix — additive to delivery, never a diversion), and the account API’s GET /v1/admin/dmarc-reports[/{id}] admin reader. Scope is deliberately ingest + store + surface only: auto-disabling accounts from RUA data is rejected on record (aggregate rows carry no join key to a local wallet; failing rows are almost always third-party spoofers). No ruf ingestion, no blob pruning yet.
  • postmaster init claims the postmaster alias — the init transaction now atomically registers the global postmaster alias to the delegate, fee-waived by construction (the alias program’s delegate waiver reads the postoffice state written one instruction earlier; rent-only). A squatted name refuses loudly with nothing submitted — the postoffice is deliberately never created without its name; the alias program still has no reserved words, so the squat window is narrowed to deploy→init, not closed.

2026-07-17 — v0.16.1 (config-key docs gate; example-config drift fixes)

PATCH — documentation/tooling only (no behavioral change):

  • New docs-gate legmail_docs/check_config_keys.py diffs the six canonical example configs’ keys (commented-out entries included) against the configuration reference in both directions, killing the drift class where a key ships in an example TOML but never reaches the docs (or vice versa).
  • Drift repaired by the new gatesithbitd.example.toml gained the documented-but-missing [store.cloudflare] backend block, client_cert_auth on the three authenticated listeners, [smtp.server] limits.*, and the [swarm] service-record freshness settings (the last also added to sithbit_ipfsd.example.toml); both IPFS binaries’ examples gained observability.otlp.metrics_interval_seconds; the reference gained store.aws.sqs_wait_time_seconds and the gateway’s public_host, and the [store.blobs] s3/azure key lists are now machine-checkable code spans.
  • Example-value fixdomain_sithbit.example.toml’s [mail_hosts.smtp] showed the retired 587/STARTTLS pair while claiming to show defaults; the in-code default (and the documented RFC 8314 posture) is 465/SSL.
  • RefundBounty CU fence — the expiry-gated sender reclaim was the one bounty-family instruction never CU-measured; the compute-units table gains its row (7,579 max measured, 31,000 ceiling — the cheapest fenced instruction: no reply-linkage check and no operator share on the refund path).

2026-07-17 — v0.16.0 (chain-account proxy read; accepting_at on the DND check)

MINOR — additive public-API changes (a new route and a new response field; no on-chain ABI or economic-model change):

  • New authenticated chain readGET /v1/chain/account/{address} on the account API returns any raw account verbatim (owner base58, data base64; 404 when absent), the generic escape hatch API-mode web shells use to decode accounts client-side — e.g. the bounty-claim resolver’s domain-account read, so a domained claim pays the domain authority instead of falling back to the filler pair.
  • accepting_at on the anonymous DND checkGET /v1/dnd/{wallet} now carries the RFC 3339 UTC instant the wallet accepts mail again, gated three ways: currently excluded, owner opted in via expose_dnd_schedule, and the schedule ever reopens (a full-week recurring schedule omits it). The schedule page localizes it to the sender’s own clock (“accepting mail again at …”); the not-opted-in default stays yes/no only.

2026-07-17 — v0.15.2 (docs: introduction reordered around the no-token pitch)

Documentation-only: the introduction’s “Priced in SOL — No New Token to Trust” subtopic moved up to lead the protocol sections (ahead of “Decentralized Email”), and the former “Only on Solana” subtopic became a note box directly beneath it.

Documentation-only: What you control and the privacy reference now name the expose_dnd_schedule opt-in — anonymous schedule checks return only the yes/no “away right now” answer unless the owner opts in — linking to What the refused sender sees for the semantics. The deploy guide’s GCP mail-tier bullet now points at the PROXY-protocol container recipe as the exception to “VMs, not serverless”.

2026-07-17 — v0.15.2 (ChainSender: pin service lazily required by send)

PATCH — web-client behavior change, no public-ABI change: ChainSender no longer demands an IPFS pin URL at construction — only send() pins, so the pin service is lazily required at pin time (a send without one refuses at the pin stage before anything is pinned or submitted; prepay() never pins). The onboarding fund page drops its dummy-pinUrl workaround.

2026-07-17 — v0.15.1 (mail-migrate replays the DND-exposure opt-in)

PATCH — behavioral defect fix, no public-ABI change: sithbit-migrate now replays each account’s expose_dnd_schedule opt-in onto the target store; previously a migration silently reset the flag to hidden (fail-safe, but lossy for owners who had opted in). The migration guide’s field list updated to match.

2026-07-17 — v0.15.0 (docs: diagrams catch up to the three-leg bounty split)

Documentation-only (the settlement change itself shipped at v0.14.0): the reply-bounty flow and campaign lifecycle diagrams — titles, box labels, and the campaigns page’s alt text and “paid twice” bullet — still described the retired 90/10 recipient-to-postoffice split. All now state the three legs: 90% to the claimant, 10% to the claimant’s active domain authority, with the postoffice collecting the share only when no active domain resolves.

MINOR — new default-off capability plus a public-API default-behavior change (no on-chain ABI or economic-model change; the widened-at-v0.10.0 rule): a single new [smtp]/[submission] setting, self_service_base_url (default unset = every refusal stays byte-identical to the legacy text), makes the RCPT-time refusals link self-service pages — the postage refusals (450 4.7.0 out of stamps, 550 5.7.0 no frombox) append {base}/fund.html?to=…&from=…, and sithbitd’s do-not-disturb refusal (450 4.2.1) appends {base}/dnd.html?to=… (the standalone smtp-server carries only the funding link — it has no DND gate). The two pages ship in the onboarding web bundle: fund.html quotes the stamp price trustlessly off the chain (postage + settlement surcharge + live protocol fee) and takes a Phantom/Ledger prepay; dnd.html shows the recipient’s away state. Alongside them, the anonymous GET /v1/dnd/{wallet} route’s default behavior changed — privacy-tightening: it used to return the full exclusion list to any caller, and now always answers excluded_now but includes the exclusions array only when the owner opted in via the new expose_dnd_schedule account flag (PATCH /v1/account, default false; a wallet with no account answers excluded_now: false).

  • New Do not disturb page: the case for reject-at-RCPT DND over accept-and-autoreply (the sender’s MTA queues and retries; no unread pile-up; the sender learns at send time; a refusal burns no stamp), what a refused sender finds on each linked page, and the schedule-sharing opt-in.
  • Configuration reference: the self_service_base_url row in the [smtp]/[submission] table and a new one-setting-two-pages subsection, including the standalone-smtp-server scope note.
  • account-api: the DND schedule surface — the authenticated GET/PUT /v1/account/dnd routes, the expose_dnd_schedule flag on GET/PATCH /v1/account, and the changed public GET /v1/dnd/{wallet} contract, called out as a behavior change.
  • Self-service pages for refused senders: the two pages in the onboarding bundle and their <meta> endpoint settings (sithbit-rpc-url / sithbit-api-url, localStorage fallbacks, same-origin default under the account API’s [static] root).

2026-07-16 — v0.14.0 (ClaimBounty operator share to the domain authority)

MINOR — economic-model change (no wire-ABI change: the instruction’s discriminant and payload are untouched): ClaimBounty’s 10% operator share (OPERATOR_SHARE_BPS) now pays the claimant’s domain authority when their mailbox names an active domain — the DeleteMail settlement’s domain-resolution rules, filler-account guards included — and falls to the postoffice when the chain legitimately doesn’t resolve (no mailbox, no domain named, domain closed or inactive; the prior behavior, preserved for domainless claimants). The claimant keeps the remainder including the rounding dust, exactly as before, and RefundBounty is unchanged. The instruction grew from 4 to 7 accounts (mailbox, domain, and authority appended; the mailbox PDA stands in as a filler when no domain is named). Re-measured: 24 847 CU (was 21 624), fenced at 48 000 (was 45 000).

  • Reply bounties: the claim-split bullet is rewritten for the three destinations — claimant, domain authority when active, postoffice fallback — and ties the operator leg to the DeleteMail settlement’s resolution rules.
  • Claiming the bounty: the user-facing payout callout now names the domain authority as the operator leg’s destination, with the postoffice fallback.
  • Compute-unit consumption: the ClaimBounty row carries the new measurement and ceiling, noting it is measured on the domained 7-account path.

2026-07-16 — v0.13.0 (domain-sithbit mail_hosts table correction)

Documentation defect fix (no version bump — docs-only): the domain-sithbit [mail_hosts] table still claimed the advertised SMTP default was 587/STARTTLS; the code default has been 465/SSL since the RFC 8314 cutover (v0.9.0, fenced in domain_sithbit/src/config.rs tests), and configuration.md already said so. Same drift class as the v0.11.6 sithbitd port corrections.

2026-07-16 — v0.13.0 (MTA-STS policy publication)

MINOR — new default-off capability (no on-chain ABI or economic-model change; the widened-at-v0.10.0 significant-additive-capability rule): domain-sithbit now publishes a domain’s MTA-STS policy (RFC 8461) at GET /.well-known/mta-sts.txt, rendered from a new optional [mta_sts] config section (mode default "testing", mx patterns, max_age default one week). The section is validated fail-fast at boot — an unknown mode, an enforce/testing policy without an mx pattern, or a max_age above the RFC’s one-year ceiling (31557600, fenced equal to the value the sending relay clamps fetched policies to) refuses to start — and with no section the endpoint replies 404, so existing deployments are untouched. TLS stays the fronting proxy’s job (a certificate for mta-sts.<domain>), and the _mta-sts.<domain> discovery TXT record stays operator-managed DNS.

  • New domain-sithbit: publishing the MTA-STS policy subsection: the route, the opt-in 404 contract, the startup failure modes, and the mta-sts.<domain> A/CNAME + TLS-proxy fronting.
  • New [mta_sts] — MTA-STS policy publication reference section: the key/default table and the boot-time failure modes.
  • RFC 8461 conformance section: the “sending side only” scope bullet is replaced by a publish-side bullet — what is implemented (§3.2 serializer, well-known route, startup validation, the shared one-year ceiling) and what is not (no per-domain policy map, no TLSRPT, TXT record stays operator DNS).
  • Sending mail: SPF, DKIM, DMARC, PTR: the MTA-STS inbound guidance now points at the built-in endpoint instead of “host the policy file yourself”, and ties the TXT id bump to editing the [mta_sts] section.

2026-07-16 — v0.12.0 (DANE outbound enforcement)

MINOR — additive behavioral change (no on-chain ABI or economic-model change; the widened-at-v0.10.0 new-enforcement-default rule): the outbound relay now looks up and enforces recipient MX hosts’ DNSSEC-validated DANE TLSA records (RFC 7672) by default on direct-to-MX delivery — a validated usable TLSA set pins the STARTTLS handshake to the published certificate data (preferred over MTA-STS wherever both apply), and any failure defers rather than downgrading — behind a new [spooler] dane switch (default on). Send-side only: publishing TLSA records stays operator DNS work. Only tightens delivery to domains that sign their zones and publish TLSA; everything else keeps the MTA-STS/opportunistic posture.

  • New RFC 7672 conformance section: TLSA discovery over a DNSSEC-validating resolver and the per-host outcome matrix, DANE-EE/DANE-TA verifier semantics (EE skips name/expiry/chain; TA path-validates anchored at the matched chain cert), the DANE-over-MTA-STS composition rules, the documented CNAME and implicit-A subsets, and the send-side-only scope.
  • [spooler] — outbound workers: the dane key and a paragraph on the pinned handshake, the stricter-of-both rule under an MTA-STS enforce policy, never-dialed bogus hosts, and the debugging escape hatch.
  • Sending mail: SPF, DKIM, DMARC, PTR: a DANE note — outbound needs no configuration; publishing _25._tcp.<mx-host> TLSA records in a DNSSEC-signed zone protects your own inbound mail, with the recommended 3 1 1 form, the openssl digest recipe, and key-rollover guidance.
  • Threat-model subsection updated: DANE is now implemented and closes both MTA-STS residuals (trust-on-first-use and cache lifetime) for recipient domains that deploy DNSSEC + TLSA.

2026-07-16 — v0.11.6 (sithbitd default-port corrections)

PATCH — documentation/example corrections only (no code change; the binds themselves never moved): the sithbitd docs and example config claimed default ports the code never had. The in-code defaults are SMTP 2525, IMAP 1430, POP 1100 (imap_server/src/config.rs, pop_server/src/config.rs), and the submission listener has no distinct default — disabled by default, it would inherit SMTP’s 2525, so its bind_addr must be set explicitly when enabled. The 2143/2110/2587 numbers are the docker-compose files’ explicit rebind convention, not defaults. Corrected in the sithbitd page, the listener-section table, the deploy quick-start, and mail_spooler/sithbitd.example.toml (whose [store] prose also now lists the cloudflare kind alongside the other backends).

2026-07-16 — v0.11.5 (production import documents for the cloud config stores)

PATCH — deployment content and repo-side tooling only (nothing a deployed binary does changes: the cloud config tier itself shipped at v0.11.0, and importing these documents is an operator opt-in): the repository now ships ready-to-import production configuration for a complete six-service sithbit.com deployment — sithbitd, account-api, mail-grpc, domain-sithbit, sithbit-ipfsd, sithbit-gateway — for both cloud config stores, under iac/appconfig/.

  • AWS AppConfig: six commented per-service TOML documents (iac/appconfig/aws/), imported verbatim as freeform hosted configuration profiles — AWS stores the document opaquely, so the TOML comments are the in-store field documentation. These documents are the single source of truth for both clouds.
  • Azure App Configuration: a generated kvset import file (iac/appconfig/azure/sithbit.kvset.json) — per-service-prefixed :-separated keys on the NULL label, each TOML comment carried as the key’s description tag (the kvset profile is the only import path that preserves per-key metadata). Sparse per-service overrides (iac/appconfig/azure/overrides/) swap the store kind, blob shape, and key sources to their Azure forms; the appconfig-gen workspace tool merges and emits, and the test suite fails on a stale kvset, a document that no longer parses into its service’s real config struct, or a broken cross-service invariant.
  • Secrets stay out of the store by construction: the documents carry key-source coordinates (Secrets Manager / Key Vault) and obvious CHANGE dummies, machine-enforced by pinned placeholder tests.
  • The cloud app-config section points at the import documents (and now lists all six bootstrap prefixes — the two IPFS binaries were missing); the Production deployment bullet gains the same pointer; the store-creation and import runbooks live in iac/README.md.

2026-07-16 — v0.11.4 (store-name refresh on the extension pages)

PATCH — documentation only (no code change): the store-install sections now link the stores themselves, and Microsoft’s rebrand is reflected.

  • Chrome: Installing from the store — “Chrome Web Store” now links to the store.
  • Outlook: Installing from the store — Microsoft AppSource has been renamed Microsoft Marketplace; the section says so (linking the store) and uses the new name throughout. The v0.11.2 entry below keeps its historical “AppSource” wording. The in-client Apps → Get Add-ins flow is Outlook UI and is unchanged.
  • External links open in a new window via the site-wide external-links.js hook, as usual — no per-link markup.

2026-07-16 — v0.11.3 (PROXY protocol trusted-proxies allowlist)

PATCH — additive hardening setting, default off-path (the defaults preserve prior behavior exactly; no on-chain ABI or economic-model change): listeners running with proxy_protocol = true can now restrict which socket peers are permitted to speak the preamble, instead of trusting whoever reaches the port.

  • New proxy_trusted key in the shared [*.server] section: a CIDR allowlist (e.g. ["10.0.0.0/8", "2001:db8::/32"]; bare addresses count as /32 or /128, and v4 entries match v4-mapped peers on dual-stack listeners) of the peers allowed to send a PROXY preamble. Untrusted peers are refused before a single header byte is read, closing the address-spoofing hole a directly reachable client would otherwise have. Empty (the default) trusts any peer — the pre-allowlist behavior, suitable when only the balancer can reach the port. Ignored unless proxy_protocol is on; a malformed entry fails serve startup with an error naming it.
  • The four annotated example configs (sithbitd.example.toml, smtp_server.toml, imap_server.toml, pop_server.toml) show the default (proxy_trusted = []) commented out beside proxy_protocol, house style.

2026-07-16 — v0.11.2 (extension store-install instructions)

PATCH — documentation only (no code change; the extensions are not yet published to any store): the three extension-client pages each gain an Installing from the store section ahead of the build-and-sideload path, with obviously-placeholder listing tokens (_todo_store_listing_name_ / _todo_store_listing_url_) that resolve when the real store listings go live, plus an honest note on what each store offers for pre-release distribution.

  • Thunderbird: Installing from the store — addons.thunderbird.net search/listing install; MailExtensions need no signing, so the self-distributed .xpi stays a fully supported permanent channel (ATN listings are public-only).
  • Chrome: Installing from the store — Chrome Web Store “Add to Chrome”; the Web Store’s Unlisted/Private visibilities and trusted-tester draft sharing allow a non-public pre-GA listing.
  • Outlook: Installing from the store — AppSource / in-client Get Add-ins search; AppSource is public-only, so the private paths are sideloading and the Microsoft 365 admin center’s Upload custom app tenant-wide deployment.
  • The GUI clients overview points at the three new sections with the placeholder caveat.

2026-07-16 — v0.11.1 (commodity hosting: generic VM + Postgres + S3)

PATCH — documentation only (no code change; the recipe rides existing backends): the vendor-independence claim gets its commodity chapter — any provider with a VM, Postgres, and S3-compatible object storage runs the full stack — plus a ranked six-provider comparison.

  • New Hosting on a generic VM: Postgres + any S3-compatible storage section: the two-edit recipe ([store] kind = "postgres" + [store.blobs] kind = "s3", with [ipfs.blobs] riding the same trait), the four operational caveats a big cloud would otherwise absorb (build features, certbot with a restart --deploy-hook for the load-once TLS acceptor, file-based key sources, outbound port 25), and a six-provider ordered list — Hetzner, OVHcloud, Scaleway, Linode, Vultr, DigitalOcean — ranked on port-25 posture and PTR/rDNS control first, managed-Postgres availability second.
  • Containers behind a PROXY-protocol balancer are documented as viable for the mail tier (decision 2026-07-16), superseding the older VM-only guidance: the listeners’ existing proxy_protocol = true support recovers the real client IP for DNSBL/limits/SPF, so LB-fronted containers qualify when the balancer injects the preamble; proxies without it stay ruled out. (iac/README.md’s client-IP constraint note was revised to match — outside this book.)

2026-07-16 — v0.11.0 (cloud app-config sources)

MINOR — additive capability (no on-chain ABI or economic-model change): every TOML-config binary can now pull its settings from AWS AppConfig or Azure App Configuration — a new resolution tier directly above the config file, opted into per binary by a single bootstrap env var ({PREFIX}_AWSAPPCONFIG / {PREFIX}_AZAPPCONFIG) and skipped entirely when neither is set, so the zero-config contract is untouched. Settings only, never secrets — key material keeps going through key sources.

  • New Cloud app-config sources: AWS AppConfig or Azure App Configuration section: the bootstrap variables, the per-provider payload idiom (AWS: one whole TOML document, deep-merged; Azure: per-key values nested on :, case-sensitive), the neither/both/compiled-out rules (awsconf/azconf features), ambient authentication, and the env-gated live probes.
  • How a setting resolves: the chain gains the cloud tier between the TOML file and ./.env{PREFIX}_{PATH} env overrides still win over cloud values.
  • Container images and Production: config can arrive from a cloud app-config source instead of a mounted TOML file; the mail-grpc keypair callout notes only key-source coordinates travel through it.
  • The three annotated example files (sithbitd.example.toml, mail_grpc.example.toml, domain_sithbit.example.toml) spell the cloud tier into their layer-chain headers; the loader’s crate-level reference is app_config/README.md (outside this book — the configuration section is the operator-facing description).

2026-07-16 — v0.10.0 (MTA-STS outbound enforcement)

MINOR — additive behavioral change (no on-chain ABI or economic-model change): the outbound relay now discovers and enforces recipient domains’ published MTA-STS policies (RFC 8461) by default on direct-to-MX delivery — an enforce-mode policy means verified TLS to a policy-matching MX or a deferral, never a plaintext fallback — behind a new [spooler] mta_sts switch (default on). Send-side only: SithBit publishes no policy endpoint of its own.

  • New RFC 8461 conformance section: policy discovery/parsing, the enforce branch and its defer semantics, the §5.1 policy cache, testing-mode logging (no TLS-RPT), and the send-side-only scope.
  • [spooler] — outbound workers: the mta_sts key and a paragraph on the three policy modes, the defer semantics, and the debugging escape hatch.
  • Outbound mail and port 25: direct-to-MX delivery honors recipient policies by default; smarthost deployments are unaffected.
  • Sending mail: SPF, DKIM, DMARC, PTR: an MTA-STS note — outbound needs no configuration; publishing the _mta-sts TXT record and policy file protects your own inbound mail.
  • New threat-model subsection: the STARTTLS-downgrade threat on MX-to-MX delivery, what opportunistic TLS does not protect against, and the trust-on-first-use / cache-lifetime residuals.

2026-07-16 — v0.9.3 (hosting on Google Cloud)

PATCH — no ABI or economic-model change (docs + infrastructure templates only; zero application code). SithBit’s third hosting cloud, proving the vendor-independence seams end to end:

  • New Hosting on Google Cloud section in the deployment chapter: blobs = a GCS bucket over its S3-interop endpoint (HMAC credentials, region = "auto" — the existing s3 blob kind, no new backend); tables/leases/queue = kind = "postgres" against Cloud SQL; secrets = the v0.9.3 gsm key source; outbound port 25 is unconditionally blocked on GCE (unlike AWS/Azure, no lift on request) so the smarthost is the supported outbound shape (inbound MX unaffected); the mail-port tier belongs on a GCE managed instance group behind an external passthrough Network LB (source-IP preservation — the GCP analog of the VMSS-not-ACI rule), while the private mail-grpc gateway fits Cloud Run.
  • New iac/gcp Terraform module: the GCS bucket + dedicated service account + HMAC key always; an optional customer-managed Cloud KMS key; opt-in Cloud SQL Postgres; an opt-in internal-only Cloud Run v2 mail-grpc unit with the gsm keypair selector. iac/aws gains the matching mail_grpc_keypair_asm selector (task-role GetSecretValue) as the volume-free alternative to the EFS mount.
  • Scaling out, the glossary (new GCS entry), migration, monitoring, and the production compose example now name the GCS/Cloud-SQL shapes where they list backends.

2026-07-16 — v0.9.3 (multi-cloud secret managers)

PATCH — no ABI or economic-model change (a config/deployment capability; every existing config parses byte-identically). Key sources now fetch from AWS Secrets Manager (kind = "asm") and Google Secret Manager (kind = "gsm") alongside the existing Azure Key Vault (kind = "akv") and local files, everywhere a key source is accepted — JWT/DKIM/credential-sealing keys, every server’s TLS pair, the domain-sithbit delegate key, and mail-grpc’s signing keypair:

  • Configuration reference rewritten: the key-sources section (heading renamed — old deep links to #key-sources-files-or-azure-key-vault now target the cloud-secret-managers anchor) documents all three clouds’ TOML shapes, auth chains (managed identity / AWS credential chain / ADC), and the live-probe env vars; per-field tables and the account-api, domain-sithbit, and mail-grpc pages generalize their file-or-Key-Vault phrasing.
  • Per-cloud cargo features (akv/asm/gsm, all default-on): the build-features caveat in Deploy now describes the real mechanism — compile out the clouds you don’t use; a compiled-out kind still parses and fails at load naming the feature.
  • Cloudflare deliberately absent: its secrets products are write-only over the API (no fetch path), noted in Deploy and the configuration reference.
  • Every example TOML’s commented akv line gains an (or kind = "asm" / "gsm") pointer.

2026-07-16 — v0.9.2 (the Marketplace topic & campaigns)

PATCH — documentation only, no ABI or economic-model change (the participant beacon, campaign CLI, and their money flows all shipped in v0.9.0; this surfaces them in the user-facing guide). A new top-level Marketplace topic under “Using SithBit” introduces the three things that trade on SithBit — alias names, domains, and attention — with two subtopics: Trading names (the alias/domain resale market, linking the existing per-name how-tos) and Campaigns, an audience-facing introduction to opt-in inbox monetization written for both advertisers and non-technical participants, with a hand-drawn campaign-lifecycle diagram. Supporting changes:

  • Campaigns highlighted as a headline feature. A new campaign feature card on the Welcome page and a new campaign term icon (megaphone) across the icon system (legend).
  • Economics gains a Campaigns section tracing the campaign-wallet-funded per-recipient flow (rent + postage + bounty + fees) as a batch of existing flows, and Economics moved ahead of “GUI clients” in the reading order.
  • The marketplace pane doc now documents its Participants tab, reconciling a gap with the shipped web surface.
  • Running a mail server opens with a vendor-independence pitch (the storage, blob, IPFS, and secret seams are trait-abstracted with multiple backends), and its subtopics are reorganized into a motivated arc — Go-live essentials, The services, Day-2 operations.
  • Welcome page card grid rebalanced. The feature cards no longer strand “Works with the inbox you already use” alone on its own row: the spam-pricing hero card keeps its full-width row, and the remaining six cards now flow three across in two even rows (“No gatekeeper” keeps its accent border but joins the grid).

2026-07-16 — v0.9.1 (mail-grpc honors JSON_RPC_URL)

PATCH — config surface, no ABI or economic change. The mail-grpc gateway now honors a bare JSON_RPC_URL environment variable (precedence: env > the configured json_rpc_url > the Solana CLI config), the one legacy env name kept from the clean break, for parity with the sithbit CLI and the standard Solana convention. See the mail-grpc chapter migration note and the json_rpc_url row in the configuration reference. This also fixed an integration-suite singleton race (the in-process gateway’s config::get() no longer force-initializes the process-wide config, so a read-only gateway boot can’t pre-empt the binary’s one config::install).

2026-07-16 — v0.9.0 (the sithbit campaign CLI)

MINOR — additive CLI surface over the existing beacon ABI (item 44, the group-offer authoring flow of the participant-pool marketplace). No new on-chain instruction: the command tree drives the create/update/close beacon instructions that shipped with item 43 and the existing SendMail, so nothing about the ABI moves. What is new is the chain-direct authoring surface a participant and a campaign wallet use:

  • A new sithbit campaign CLI reference documents the whole tree — a participant’s create/update/close (opt in, rewrite, opt out; the advertised price is the mailbox default_postage, so a beacon needs a mailbox first) and a campaign wallet’s search/quote/send. search runs the trustless getProgramAccounts scan and recovers each match’s sendable wallet from the beacon’s on-chain owner field (the D-P1 append this session); quote prices the matched set term-for-term against the on-chain SendMail funding math; send executes N direct-signed bountied sends, continuing past a per-recipient failure so one bad address can’t strand a paid campaign.
  • The participant-marketplace note and its item-44 line are the design record; each campaign bounty rides the ordinary reply-bounty escrow, refundable to the campaign wallet if a recipient never replies.

2026-07-16 — v0.9.0 (participant-pool web surface)

MINOR — additive web/API surface (item 45, the browser read path of the participant-pool marketplace design). The browser read path adds no on-chain ABI itself; the beacon’s on-chain layout shipped with item 43 under this same v0.9.0 and was extended this session with an appended 32-byte owner field (PARTICIPANT_BEACON_LEN 113→145; tags stay first at offset 0) so the trustless scan recovers each participant’s sendable wallet from the PDA. The opted-in participant pool is now browsable end to end: a new ListParticipants gRPC RPC on mail-grpc runs the trustless on-chain beacon scan (getProgramAccounts + a memcmp filter over the fixed-offset tag bitmap), account-api proxies it as the authenticated GET /v1/chain/participants?tags=… index route (account-api), and the name marketplace grows a Participants tab beside its For sale / Expired / Sold tabs — a lazy-loaded, JWT-authenticated browse-and-filter surface over the pool. A beacon must carry every filtered tag bit to match. Known limitation: the browser clients have no shared tag-name vocabulary yet, so both the filter input and each row render tags as raw on-chain bit positions (the mail_model TAG_* constants), not human labels; group-offer authoring (quote/send) stays CLI-first (item 44).

2026-07-16 — v0.9.0 (RFC 8314: production implicit-TLS mail posture)

PATCH — docs and config defaults only; no on-chain ABI, instruction, error-code, or economic change (item 47). The reference stack’s transport posture is hardened to RFC 8314 (“Cleartext Considered Obsolete”) — TLS on connect for submission and access, credentials refused before the connection is protected — and documented end to end. Nothing about what the servers can be configured to do changed at the wire level (STARTTLS listeners and the plaintext loopback dev stack still work); what moved is the advertised production default and the prose describing it.

  • Protocol conformance gains an RFC 8314 subsection and checklist item citing the three on-by-default require_tls enforcement guards — SMTP submission (530 with AUTH hidden from EHLO), IMAP (LOGINDISABLED + NO [PRIVACYREQUIRED]), and POP3 (-ERR Must issue STLS command first).
  • mail_spooler/sithbitd.example.toml grows the commented production implicit-TLS stack — [submission.server] / [imap.server] / [pop.server] on 465/993/995 with implicit_tls = true and matching [*.tls] — with the STARTTLS 587/143/110 listeners demoted to opt-in secondaries, and docker-compose.prod.example.yml publishes those same 465/993/995 (plus 25 MX) as the primary host ports.
  • Running a mail server: Production and a new configuration production-posture subsection document the implicit-TLS primaries, the MX-on-25 exception, and the require_tls-on-by-default rule.
  • domain-sithbit’s advertised submission default flips 587/STARTTLS → 465/implicit-TLS (SSL) across MailHostsConfig::default, the Mozilla autoconfig (socketType=SSL) and Outlook autodiscover (Encryption=SSL) documents it serves, the [mail_hosts] reference default, and the DNS setup submission caveat. STARTTLS on 587 remains a supported opt-in; the ports now match the implicit-TLS SRV records that same page recommends.

2026-07-16 — v0.9.0 (threat-model prune)

PATCH — docs only. Removed the threat model subsection “History before the cutover shows the old postmaster key” — it advised rotating away from a pre-cutover single-postmaster key at adoption, but there has never been a production deployment, so no such historical key or pre-adoption chain history exists to rotate away from. The custody model itself (ceremony seeds + rotate-on-schedule delegate) is unchanged.

2026-07-16 — v0.9.0 (client-access SRV records)

PATCH — docs only. DNS setup gains a section on RFC 6186 / RFC 8314 SRV records for client autoconfiguration: the implicit-TLS labels SithBit’s production posture prefers (_imaps 993, _pop3s 995, _submissions 465) and the STARTTLS secondaries (_imap 143, _pop3 110, _submission 587), the RFC 2782 priority/weight/port/target fields, the .-target convention for disabling a protocol, and how these relate to the autoconfig/autodiscover documents and to the DHT-based service discovery that avoids DNS altogether. Ports match the [mail_hosts] defaults.

2026-07-16 — v0.9.0 (on-chain participant beacon)

MINOR — additive public ABI (item 43, the first build-out of the participant-pool marketplace design): three MailInstruction variants appended — CreateParticipantBeacon (39), UpdateParticipantBeacon (40), CloseParticipantBeacon (41) — plus the ParticipantBeacon account (fixed 113-byte layout; the coarse tag bitmap sits at account offset 0 so getProgramAccounts memcmp search works without deserializing), the append-only 24-tag starter vocabulary in mail_model constants (32-byte bitmap = 256 slots; bits are deliberately NOT validated on-chain, so vocabulary appends never need a redeploy), the participant_beacon PDA seed, and error codes 86–90. Creating a beacon requires the wallet’s mailbox to exist, because the advertised participation price is the mailbox’s default_postage (decision 5); closing it refunds the rent — opting out is free and complete. See the program reference for the instruction/seed/error tables. CLI authoring (sithbit campaign, item 44) and the web surface (item 45) build on this next.

2026-07-16 — v0.8.11 (trustless webmail: external-wallet flavor on the Balances pane)

Client-side only: no on-chain ABI, instruction, error-code, or economic change — balancesPane.buyStamps() now supports the external Phantom/Ledger signing flavor (the unsigned wasm builders plus sendUnsigned), mirroring mailboxConfigPane.commit()’s existing split. Item 40 (v0.8.9) deliberately scoped this to the compose card only; this fills in the Balances pane, so PATCH per the v0.8.7 precedent.

2026-07-16 — v0.8.10 (screenshot manifest: curated shared-file lists replace wholesale hash)

Tooling-only: no on-chain ABI, instruction, error-code, or economic change — a fix to check_screenshots.py’s own drift detection, so this is a PATCH bump.

  • mail_docs/screenshots.manifest.json no longer hashes webclients/shared wholesale for every client. Each of webmail, onboarding, and marketplace previously carried a blanket webclients/shared source entry, so editing a shared file only one client actually reaches (e.g. connection-settings.js, webmail-only) falsely flagged the other two as needing a re-shoot — the same false-positive class the item-40 docs commit (ee30da5) had to re-pin around after an unrelated webclients/shared edit landed. Each client’s sources list now names only the shared files its app.js/index.html actually reach (traced via the import/fetch/ mount closure), while the client’s own directory stays wholesale (rglob’d) as before.
  • check_screenshots.py’s iter_source_files hashes an individual file directly when a sources entry names one rather than a directory, alongside the unchanged wholesale rglob. A new --selftest mode fixture-proves both directions: a one-client-only file edit leaves the other two clients untouched, and a shared-by-all file edit (e.g. panes.js) still flags all three.

2026-07-16 — v0.8.9 (docs: participant-pool marketplace design note)

Documentation-only: the version tags the unchanged protocol state.

  • New design note: The participant-pool marketplace records the settled design for advertiser/survey campaigns over reply bounties — the on-chain participant beacon (coarse tag bitmap + sealed detail CID, coarse-by-construction privacy), mail-native key handout for the detail blob, the two search paths over one fixed-offset layout, the CLI-first sithbit campaign authoring surface, the advertised-price-is-default_postage identity, the rejected alternatives, and the three implementation items it spawns. Nothing in it is implemented yet.

2026-07-16 — v0.8.9 (trustless webmail: inline prepay on the compose card)

Client-side only: the trustless compose’s no-frombox refusal becomes an inline Prepay & send — quote, purchase, and automatic re-send of the held draft, in both signing flavors. The new wasm exports (create_frombox_unsigned/add_stamps_unsigned, postoffice_account_address/decode_postoffice_account) are client surface over unchanged instructions — no on-chain ABI, instruction, error-code, or economic change (the prepayment economics shipped long ago; this is an affordance over them) — so this is a PATCH bump per the v0.8.7/v0.8.8 precedent.

  • No frombox yet? Prepay inline documents the card — the stamp-count input (the ≥1-stamp non-owner floor), the exact per-stamp funding quote (postage + settlement surcharge + the postoffice’s live protocol fee, owner-waived), the fresh create-vs-top-up read at click time, and the deliberate no-auto-retry on a still-pending purchase.
  • Creating the frombox on first purchase cross-references the webmail surface riding the same create-or-top-up decision as frombox stamp.

2026-07-16 — v0.8.8 (trustless webmail: reply + bounty authoring)

Client-side only: the trustless webmail now authors what it could already display — the reader gains a Reply on-chain action and the compose card gains reply-bounty fields. No on-chain ABI, instruction, error-code, or economic change (the compose threads SendMail’s existing bounty/expiry/reply parameters through the already parity-fenced builders, both signing flavors), so this is a PATCH bump per the v0.8.7 precedent. This closes the v0.8.7 entry’s “future work” note for this surface.

  • Replying, and attaching a bounty documents the new compose surface — Reply pre-fills the decrypted sender and carries the parent message’s account address (the blake3-hashed --reply-to linkage, no bounty required); the bounty fields mirror --bounty/--bounty-window with the same 7-day default window, and a born-expired window is refused in the page before anything is pinned — the same rule the chain enforces.
  • Attaching a bounty names the trustless compose as an authoring surface — and restates the standing decision that bounty authoring is direct-signed only: sends composed through a mail server stay bounty-less.

2026-07-15 — v0.8.8 (trustless webmail: send-lifecycle and pin-caveat diagrams)

Documentation-only: no ABI, instruction, error-code, or economic change — two hand-authored diagrams illustrating already-documented behavior.

  • Sending without a server gains a lifecycle diagram covering the four client-side steps (resolve, check mailbox, check frombox, seal) through the pin step and the in-page-wallet/external-wallet signing branch to submit-and-poll.
  • The pin lifecycle caveat gains a comparison diagram showing the operator’s server-delivery pin lifecycle and a trustless client’s pin lifecycle as independent paths converging on the same CID — why GC never reclaims a client-made pin on the operator’s behalf.

2026-07-15 — v0.8.7 (trustless webmail: external-wallet send)

Client-side only: the trustless webmail compose can now send through a connected external wallet (Phantom/Ledger) — previously it required an in-page wallet. No on-chain ABI, instruction, error-code, or economic change (the external flavor emits the byte-identical SendMail wire transaction through the already-parity-fenced unsigned builder), so this is a PATCH bump per the kit-migration precedent.

  • Sending without a server documents the two signing flavors — an unlocked in-page wallet signs in the page; a connected external wallet approves the same transaction built unsigned with it as fee payer, with sealing always happening in the page before anything leaves it. Bounty and reply fields remain future work on this compose surface (they exist in the CLI and the builders today).

2026-07-15 — v0.8.6 (domain-sithbit compose parity: mounted TOML, dead env var retired)

Deployment-surface only: the compose files move the last env-configured service onto the mounted-TOML pattern — no on-chain ABI, instruction, error-code, or economic change, and no server behavior change (no binary is touched) — so per this file’s own rules this is a PATCH bump, following the v0.8.4 compose-migration precedent. It is also a correctness fix: the production example still set DOMAIN_SITHBIT_POSTMASTER_KEY_FILE, a config field removed by v0.8.2’s hard break (the field is now delegate_key_file), and because unknown DOMAIN_SITHBIT_* variables fail startup loudly, copying the example verbatim crash-looped the domain-sithbit container.

  • The production example gains sithbitd/account-api/mail-grpc paritydomain-sithbit was the last service configured through a wall of env vars: docker-compose.prod.example.yml now mounts a real domain_sithbit.toml (via DOMAIN_SITHBIT_CONFIG; start from domain_sithbit/domain_sithbit.example.toml) plus a separate read-only delegate-keypair mount the TOML’s delegate_key_file names, with a minimal-TOML sketch and the Azure Key Vault table-form alternative inline — mirroring the mail-grpc block v0.8.4 shipped.
  • The dead DOMAIN_SITHBIT_POSTMASTER_KEY_FILE var is retired from everything runnable: the production example’s crash-looping setting is gone, and docker-compose.yml’s stale comment telling operators to set it now points at the current contract (delegate_key_file in the mounted TOML). The surrounding prose also stops calling it the “postmaster key” — the signer is the postoffice’s standing delegate key.

Deployment-surface only: the iac/ templates gain opt-in units that run the unchanged published image — no on-chain ABI, instruction, error-code, or economic change, and no server behavior change (no binary is touched) — so per this file’s own rules this is a PATCH bump, following the v0.8.4 deployment-surface precedent. It lands item 34, the last of the three hardening items queued with the gateway topology decision.

  • Both IaC templates gain an opt-in mail-grpc unit (deploy_mail_grpc in Terraform, deployMailGrpc in Bicep; default off — a plain apply/deploy keeps producing the store footprint with zero diff): ECS Fargate on AWS (security group + cluster/task/service), a VNet-integrated ACI container group on Azure. Both are BYO network (an existing VPC + private subnets, or an existing delegated subnet — the templates never create one) and private-only by construction: no public IP or load balancer, ingress on the gRPC/health ports only from caller CIDRs or the VPC’s own CIDR — the private-network posture as infrastructure rather than convention. See Provisioning with IaC and iac/README.md for the full parameter ↔ config mapping.
  • Keypair delivery splits per cloud, because the gateway’s keypair is a key source (a file path or a Key Vault secret — never key content in an env var): AWS mounts an optional EFS volume read-only and points MAIL_GRPC_KEYPAIR at the file; Azure wires the AKV source through the nested env overlay (MAIL_GRPC_KEYPAIR__KIND=akv + __VAULT_URI/__SECRET_NAME) authenticated by the container group’s system-assigned managed identity — the operator grants that identity secret read on the vault (the mailGrpcPrincipalId output exists for exactly that role assignment).
  • Durable constraint recorded on the Azure unit: ACI cannot pass through the client source IP — acceptable for mail-grpc (private gRPC; callers are our own servers), but a future SMTP-server unit must be a VM scale set, because SPF/DNSBL need the real peer IP.

2026-07-15 — v0.8.4 (compose files onto the mail-grpc TOML/env config)

Deployment-surface only: the compose files, smoke script, and dev scripts move onto the configuration surface v0.8.3 shipped — no on-chain ABI, instruction, error-code, or economic change, and no server behavior change (the binaries are untouched) — so per this file’s own rules this is a PATCH bump. It lands item 35, queued by the v0.8.3 hard break, and closes that break’s last loose end: nothing runnable in the repo drives mail-grpc with the retired GRPC_SERVER_ADDRESS/DEFAULT_KEYPAIR names any more.

  • The dev chain profile is operative again: docker-compose.yml wires mail-grpc through MAIL_GRPC_* overrides (bind, RPC URL, alias-index path), and the signing keypair is a file mounted read-onlySITHBIT_CHAIN_KEYPAIR names the host path (default: the checked-in devnet test key the retired .env flow held as JSON content) — keypair content in an env var is gone for good. SITHBIT_CHAIN_RPC still retargets the cluster; the OTLP overlay now sets MAIL_GRPC_OBSERVABILITY__OTLP__ENDPOINT.
  • The production example gains sithbitd/account-api parity: docker-compose.prod.example.yml mounts a real mail_grpc.toml (via MAIL_GRPC_CONFIG) plus a separate read-only keypair mount, with a minimal-TOML sketch and the Azure Key Vault alternative inline.
  • docker/smoke.sh now statically parses the chain profile (docker compose --profile chain config -q), so a compose regression there fails the smoke run; mail_docs/screenshot-tools/serve-stack.sh and webclients/README.md — the last live dead-name consumers — swept onto the MAIL_GRPC_* overrides.
  • Trap for compose authors: compose (v2.29.7) interpolates ${VAR:?} even in inactive profiles, so the profile’s variables take defaults (${VAR:-…}) rather than being required.

2026-07-15 — v0.8.3 (docs: sithbit CLI glossary entry)

Documentation-only: the version tags the unchanged protocol state.

  • New glossary entry: sithbit CLI (Operations & infrastructure section), disambiguated from the external Solana CLI it’s a substitute for in the sithbit config/sithbit wallet create workflow. CLI Quickstart’s first mention of “CLI” now links into it, picking up the sitewide hover/focus definition preview js/glossary-tooltip.js already gives every glossary link — no new JS or CSS needed.

2026-07-15 — v0.8.3 (docs: marketing landing page)

Documentation-only: the version tags the unchanged protocol state.

  • New landing page: Welcome, now the book’s index.html — mdBook always renders a source tree’s README.md to index.html regardless of SUMMARY.md order, so the new landing content took over the README.md filename and the Prelude moved to its own prelude.md file (rendering as prelude.html) to make room; SUMMARY.md still lists Welcome ahead of the Prelude for the sidebar reading order. The landing page itself is a hero with the SithBit lockup and tagline, a feature-card grid ordered by end-user value proposition (spam priced out at the source, getting paid for your own inbox, an address tied to your wallet, sealed end-to-end mail, drop-in SMTP/IMAP/POP compatibility, no gatekeeper operator model), and closing links onward into the Prelude and Introduction. Styled by the new css/landing.css, scoped under .sb-hero/.sb-grid/.sb-card so it never affects the rest of the book; reuses the existing term-icon set and mdBook theme variables rather than introducing new artwork or colors.

2026-07-15 — v0.8.3 (mail-grpc onto TOML config; [grpc]-only verification for sithbitd)

Server-behavior changes, config surface only: no on-chain ABI, instruction, error-code, or economic change, so per this file’s own rules this is a PATCH bump, following the v0.8.2 precedent (config-surface breaks and server behavior outside the protocol ABI move PATCH). It is not docs-only — previously-required environment variables are now ignored, a boot that previously refused without them now comes up on in-code defaults, and a [grpc]-only sithbitd now verifies recipients it previously could not.

  • HARD BREAK — mail-grpc moved onto the layered TOML/env configuration every other SithBit binary uses (item 32(a); a clean break, user decision 2026-07-15). The legacy env-only surface is gone: GRPC_SERVER_ADDRESS, JSON_RPC_URL, DEFAULT_KEYPAIR (and its _VAULT_URI/_SECRET_NAME companions), ALIAS_INDEX_DB, ALIAS_INDEX_POLL_SECONDS, ALIAS_CACHE_SECONDS, and HEALTH_BIND are no longer read. Config now comes from mail_grpc.toml (or MAIL_GRPC_CONFIG) with MAIL_GRPC_* env overrides; every key has a dev-friendly default, so an empty file runs a loopback gateway on 127.0.0.1:50051 — the private-network posture is now the default, not a convention — with the chain endpoint and signing keypair falling back to the operator’s Solana CLI config, exactly like the sithbit CLI. The keypair-content-in-an-env-var shape is gone with it: the TOML keypair is the sixth key source (a keypair-file path, or an Azure Key Vault secret). See the rewritten mail-grpc chapter and its configuration table.
  • sithbitd’s [grpc] section now stands alone (item 32(b)): [grpc] without [ipfs] enables RCPT-time recipient verification (alias resolution + postage checks), at-rest sealing key reads, and alias logins, with the chain pipeline off (delivered copies stay received; boot logs the verification-only posture at info) — the MX posture that previously demanded an [ipfs] provider the edge never used. [ipfs] without [grpc] warns and boots with chain access disabled. Both sections together remain the full pipeline, unchanged. See the chain-pipeline section and the updated role-split presets.

2026-07-15 — v0.8.2 (domain-sithbit delegate key source; postmaster_key_file removed)

Server-behavior change, config surface only: no on-chain ABI, instruction, error-code, or economic change, so per this file’s own rules this is a PATCH bump, following the distinguishable-suspend-replies precedent (server behavior outside the protocol ABI moves PATCH). It is not docs-only — a previously-accepted config key is now refused at boot (second bullet) — and pre-launch, a config-surface break does not rise to the on-chain-ABI bar MAJOR is reserved for.

  • domain-sithbit’s delegate_key_file is now a key source — the fifth of the file-loaded secrets to take one. A bare string stays a local file path, byte-for-byte compatible with existing configs; { kind = "akv", vault_uri = …, secret_name = … } opts into fetching the keypair JSON from an Azure Key Vault secret instead (a kind-less { path = … } table is also a file). The hot-rotation custody contract is preserved: the key is loaded fresh from the configured source on every POST /domain (for akv, a fresh vault fetch per request), so rotating the delegate still needs no restart. Boot validation covers whichever source is configured — a bad vault secret fails startup exactly like a bad file did; unset still means POST /domain replies 503.
  • HARD BREAK — the deprecated postmaster_key_file alias is removed (user decision 2026-07-15). The config struct rejects unknown keys, so a config still naming postmaster_key_file now fails startup loudly instead of being accepted with a warning. Operators must act: rename the key to delegate_key_file before upgrading. See the domain-sithbit configuration table.

2026-07-15 — v0.8.1 (docs: populated web-client screenshots)

Documentation-only. Adds the populated-state screenshots the web-client pages were missing: a mail-in-it inbox on the webmail app, the signed-in settings dashboard (balances + postage), and live marketplace listings. Captured by driving the real client bundles against a local mock account-API that serves the same response shapes the clients are unit-tested against (mail_docs/screenshot-tools/, Option C) — no chain, store, or devnet dependency, so the frames are deterministic. Registered in screenshots.manifest.json; protocol version unchanged.

2026-07-15 — v0.8.1 (docs: onboarding wizard steps 2–4 screenshots)

Documentation-only. Completes the browser-onboarding walkthrough in Web onboarding: the browser wizard with screenshots of the remaining wizard steps — claim-your-handle, set-your-price, and review. These required a running backend (the wizard calls the account API to establish the wallet and check mailbox ownership), so they were captured against a local account-api + mail-grpc→devnet stack with a fresh throwaway wallet. Registered in screenshots.manifest.json; protocol version unchanged.

2026-07-15 — v0.8.1 (docs: mail-grpc gateway topology design note)

Documentation-only: the version tags the unchanged protocol state.

  • New design note: The mail-grpc gateway topology — records the 2026-07-15 decision that mail-grpc stays a separate service (three roles in one process: write gateway, read gateway, alias/sales indexer; options considered; what would reopen it), the two-key custody clarification (the gateway’s fee-payer keypair is not domain-sithbit’s standing delegate), and the now-explicit private-network-only posture — the gRPC surface has no TLS or authentication, so it must never be publicly reachable.
  • mail-grpc chapter gains the network-posture callout (prefer loopback/private binds over 0.0.0.0); Scaling out notes the gateway is not a fleet member — many workers share one gateway safely because store leases serialize each wallet’s writes.

2026-07-15 — v0.8.1 (docs: onboarding wallet-generation screenshot)

Documentation-only. Adds a screenshot of the onboarding wizard’s “Create a new wallet” step — the one-time secret-key reveal, passphrase, and “I have saved it” gate — to Web onboarding: the browser wizard. Captured from the standalone (no-backend) client with the generated secret key redacted. Deeper wizard steps (handle/price/review) are not included: advancing past the wallet step requires the account-API backend, so those await a stacked capture. Registered in screenshots.manifest.json; protocol version unchanged.

2026-07-15 — v0.8.1 (distinguishable suspend replies & operational polish)

Server-behavior changes, all backward-compatible: no on-chain ABI, instruction, error-code, or economic change, and nothing previously accepted is refused — so this is a PATCH bump, not the MINOR that at-rest sealing took (which changed what the system does with mail). The version moves because reply texts on the wire are behavior, not documentation.

  • Suspended accounts now hear why (user decision 2026-07-15: distinguishable everywhere). SMTP AUTH answers 535 5.7.13 Account disabled (RFC 3463 “user account disabled”, was the deliberately indistinguishable 5.7.8), IMAP answers NO [CONTACTADMIN] account disabled; contact your administrator (RFC 5530), and POP answers -ERR [SYS/PERM] account disabled; contact your administrator (RFC 3206). Safe disclosure: every disabled reply is issued only after the credentials verified, so only the account holder ever sees it. See the per-surface refusal table.
  • sithbit-migrate carries the abuse controls. The account pass now copies the suspend flag and replays the rolling hour/day outbound-usage totals as of the migration instant (bucket timing is not recoverable through the store surface — caveat recorded); the run summary prints accounts: N (M suspended). See the migration page.
  • Undatable dead jobs are pruned, not spared. Every storage backend now stamps the bury time into the dead message itself, and the hourly retention prune treats an entry with no readable date as older than any cutoff — previously such entries escaped pruning forever. The prune ([spooler] dead_retention_days, default 30) is now documented under The job queues.
  • Mail listings surface reply and bounty facts. sithbit mail get’s per-message listing appends Reply-to-hash:, Bounty:, and Bounty-expires: lines when set (absent otherwise — a plain send’s listing is byte-identical to before). See Get mail.
  • The trustless viewer marks replies. The web/plugin viewer panes render a “Reply to <hash>…” line for reply messages, mirroring the CLI listing’s reply-to-hash treatment.
  • Internal: the sealed-envelope decompression cap now reports a distinct, operator-diagnosable error when a payload claims to inflate past the 64 MiB cap (a corrupt stream stays opaque, anti-probing). The cap itself is unchanged.

2026-07-15 — v0.8.0 (Solana clusters & RPC-endpoint appendix)

Documentation only; no code, ABI, API, or CLI change — the protocol version is unchanged per the versioning note above.

  • A new appendix: Solana clusters and RPC endpoints. One page answering the questions every “configure an RPC endpoint” step assumes away: what Solana’s public clusters (devnet, testnet, mainnet-beta) are, which ones SithBit uses today — devnet hosts the live test deployment, local work runs on surfpool, mainnet-beta awaits launch — and what the URL you configure actually points at. CLI Quickstart introduces it at the point you first configure an endpoint.
  • Book-wide cross-links. The natural “RPC endpoint” mentions across onboarding, deployment, the configuration reference, the GUI-client pages (Thunderbird, Outlook, Chrome, Lockbox), and the glossary now link to the appendix, so the term resolves to its explanation from anywhere in the book.

2026-07-15 — v0.8.0 (docs: first web-client screenshots)

Documentation-only. First screenshots of the browser clients, captured from the standalone (no-backend) first-run states and embedded in their pages: the webmail first-run wizard, the web onboarding wizard, and the marketplace sign-in screen. These are now guarded by check_screenshots.py (registered in screenshots.manifest.json), so a webclients/** UI change that isn’t re-shot fails the docs gate. Protocol version unchanged.

2026-07-15 — v0.8.0 (docs: Apple Mail extensibility note)

Documentation-only. New reference appendix Apple Mail extensibility (MailKit) records Apple Mail’s supported extension surface (the four MailKit extension points, the Sonoma removal of legacy mail bundles) and why SithBit ships no Apple Mail client today — MEMessageSecurityHandler could unseal mail on macOS, but there is no MailKit on iOS/iPadOS and no room for the shared account pane. Protocol version unchanged.

2026-07-15 — v0.8.0 (at-rest sealing goes live in production)

The item-27 at-rest sealing machinery — shipped 2026-07-14 as a fixture-proven capability — is now wired on in production. No on-chain change; the bump is MINOR because the running system’s behavior changes for end users and one previously-accepted SMTP credential shape is now refused.

  • Sealing is automatic on chain-connected deployments. Any sithbitd or account API with a chain gateway seals password-less accounts’ delivered mail at spool time; there is no config setting (decided: the per-account rule — stored password ⇒ plaintext — is the only gate). The chain-less dev stack stays plaintext. See What your operator holds and sithbitd: At-rest sealing.
  • The IPFS copy is sealed at spool time too (decided: pin format “seal-for-IPFS at spool”). crypto_box_seal is non-deterministic, so the spool-time bytes are the canonical artifact the worker pins — a crash-window rerun re-pins identical bytes and the recorded CID never drifts. Reply-linkage ids are parsed in the same pass and persisted (a sealed body can never be re-parsed). Spool-time facts are canonical: a reading key rotated between delivery and pin takes effect from the next message.
  • SMTP refuses the reading-secret suffix (decided: reject). A wallet-signature AUTH whose password carries the .base58(secret) login suffix is refused with a normal 535 on the submission path — the secret belongs only where reading happens (IMAP/POP/webmail login).
  • Wrap hygiene: deleting a message’s last copy now drops its DEK wrap rows and any not-yet-pinned sealed IPFS artifact alongside the blob bytes, on every storage backend.

A breaking protocol change: every alias transfer now requires the recipient’s consent — the escrowed offer/accept flow is the only transfer path, and the unilateral TransferAlias refuses. This reverses the recorded 2026-07-05 decision (user decision, 2026-07-15): the “never planted on a wallet that didn’t ask for it” guarantee is now protocol-wide instead of contradicting the old immediate path. Per the versioning preamble MAJOR stays 0 pre-launch (the enum stayed append-only in place), so this lands as a MINOR bump with the break stated plainly: transactions submitting TransferAlias no longer execute.

  • TransferAlias is disabled. Discriminant 1 still decodes (history replays cleanly, e.g. in the gRPC indexer) but the dispatch arm refuses with the appended error 85 (UnilateralTransferDisabled, “Unilateral TransferAlias is disabled; stage an OfferTransferAlias (fee may be 0) for the recipient to accept”). See Transfer an alias.
  • Zero-fee offers are legal. OfferTransferAlias no longer requires a positive fee: alias transfer init stages a free hand-off by default (--fee defaults to 0, and --expires-in no longer requires it). The recipient still accepts — consent is structural on every path (accept/buy/bid signatures). OfferFeeZero (51) is retired in place, kept only for the frozen error-code ABI.
  • The flat transfer fee moved to the zero-fee accept. A free hand-off’s AcceptTransferAlias charges the delegate-tunable ALIAS_TRANSFER_FEE_LAMPORTS (payer → postoffice), waived when the offer’s holder is the standing delegate — the waiver identity moved from the old path’s payer to the holder, keeping bulk-reservation hand-offs fee-free end to end. Priced offers keep the pure 90/10 split; no flat fee rides on top.
  • Clients follow. The CLI’s transfer init is one offer-staging code path (the accept surfaces the flat fee or its waiver before signing); mail_wasm retires transfer_alias_tx / transfer_alias_unsigned, and the web clients’ transfer pane stages a zero-fee offer with “recipient must accept” copy. The CU table swaps the TransferAlias row for OfferTransferAlias (18,894) and the zero-fee AcceptTransferAlias (13,838).

2026-07-14 — v0.6.0 (the domain-program split — BREAKING)

A breaking protocol change: the domain registry moved out of the mail program into a new, third on-chain program. Per the versioning preamble MAJOR stays 0 pre-launch (the instruction enums themselves stayed append-only in place), so this lands as a MINOR bump with the break stated plainly: transactions that submit domain instructions to the mail program no longer execute.

  • A third on-chain program owns the domain registry. domain_program, ID DmaiNcmXsPw2juV9JoZSC47V5epAysQi3DJVk3fiBuUv (devnet twin DmaiNHGvprK2op7xqZHXp8UVXXmPUtkas96Goh5sCJQn), carries the domain lifecycle, the DNSSEC-proof authorize/reclaim flows, the domain marketplace, and its own AdminCloseAccount (discriminant 16) as DomainInstruction discriminants 0–16. The domain, domain-listing, pending-deactivation, pending-reclaim, and proof-witness PDAs now derive under and are owned by the domain program. See the reworked Program & PDA reference.
  • The sixteen mail-side domain discriminants are retired. Sending 9–12, 22–24, 26–28, 31–33, or 35–37 to the mail program is rejected with the appended error 84 (InstructionMoved, “This instruction has moved to the domain program”). SetDomainFee (17) and SetRootKsk (25) stay mail-side: they mutate the postoffice, which remains a mail-program account the alias and domain programs read cross-program (fees, root KSK, delegate gate) — the split moved the registry, not the treasury.
  • The mail program is now unconditionally modexp-free. The dnssec-proof Cargo feature — and the sol_big_mod_exp deployability problem it gates — moved to the domain program (default-on there); the mail and alias programs’ default builds now deploy on any cluster. The modexp-free build page now describes domain_program, the only program that still needs it.
  • No client-facing surface changed. The sithbit domain … commands, the postmaster domain admin flows, the proof staging, the gRPC gateway’s domain/listing scans and sale-history walk, and domain-sithbit’s POST /domain all follow the domain program transparently — account-meta lists are byte-identical pre/post split. sithbit postmaster reclaim now drains all three programs’ accounts, postoffice last.

2026-07-14 — v0.5.9 (cleanup bundle: per-cause POP sealed refusals)

Server/client maintenance only; no on-chain ABI, economic, API-contract, or CLI change.

  • POP3 now says why a sealed message can’t be served. Retrieving a DEK-sealed message a session cannot read now answers with a cause-specific -ERR [SYS/PERM] message is sealed at rest: … line — no reading key in this session, no wrapped key for this reader, or reading key does not match — using the same sealed-refusal vocabulary as the account API. Previously every such fetch collapsed to the generic -ERR [SYS/TEMP] message unavailable, which still covers genuine storage trouble (retryable).
  • Trustless clients decode the whole mailbox account. The web clients’ direct-RPC chain reader now surfaces default_postage, domain, and no_ipfs alongside mail_count, matching the wasm decoder — a strict superset of what the account-API proxy returns.

2026-07-14 — v0.5.8 (web clients: web3.js → @solana/kit codec bundle)

Client build/packaging maintenance only; no on-chain ABI, economic, API, or CLI change.

  • The web clients’ vendored Solana library shrank by ~74%. The shared client library’s vendored @solana/web3.js bundle (web3.esm.js, ~682 KiB) is replaced by a codec-only bundle built from @solana/kit 7.0.0 (kit-codec.esm.js, ~178 KiB). The external-wallet bridge (Phantom/Ledger transaction signing) now rides kit’s wire codecs behind the same interface — no behavior change for any client. All six shells (webmail PWA, marketplace, onboarding, Outlook, Thunderbird, Chrome) rebuild and repackage against the new bundle.

2026-07-14 — v0.5.7 (trustless webmail, at-rest mail sealing)

No on-chain ABI or economic change — client, server/API, storage-schema, and infrastructure work only.

  • Trustless webmail. The webmail PWA can now run with no account API at all: leave the API URL blank in connection settings and the app unlocks with the wallet alone, reads the chain over direct RPC (the same wasm signing/decoding module the plugins use), enumerates the on-chain inbox, opens sealed bodies locally, and sends — sealing to the recipient’s published key, pinning through a configured pin service (sithbit-ipfsd), and submitting the SendMail transaction itself. The recipient’s no_ipfs opt-out is honored client-side. Bodies pinned by the client sit outside the operator’s unpin/settle sweep — the client owns that pin’s lifecycle. See Trustless webmail. Both IPFS HTTP surfaces (sithbit-ipfsd, sithbit-gateway) now answer cross-origin browser requests (permissive CORS).
  • At-rest mail sealing (deployment capability). For accounts without a stored mail password, a server deployment can envelope-encrypt delivered copies at rest: each body sealed once under a fresh per-message AES-256-GCM key, wrapped per reader with the sealed-box construction. The client-derived reading secret rides the wallet-signature login (IMAP/POP password suffix; reading_secret on the account API token exchange), lives only in session memory, and unlocks decrypt-on-read over IMAP FETCH, POP RETR, and /v1/mail. Sessions without the key get a clear refusal, never ciphertext. Storage gains a per-reader wrap table (all six backends). Honest scoping — what this does and does not protect against — in What your operator holds.
  • Cleanups. The DNSLink config no longer prints its API token in debug output; the AWS Terraform module’s DynamoDB GSI moved off the provider-deprecated hash_key/range_key arguments; pin/unpin mentions across the book carry a pin icon (see the icon legend).

2026-07-14 — v0.5.6 (customer-managed KMS, first IaC, secret-log hygiene)

No on-chain ABI or economic change — a new optional store setting, deploy templates, and cleanups.

  • Customer-managed KMS keys. The AWS store gains an optional kms_master_key_id under [store.aws] (key ID, alias, or ARN): set, the DynamoDB table is created with KMS-backed SSE and the SQS queues switch from SSE-SQS to SSE-KMS under the same key; unset (the default) keeps today’s provider-managed encryption. See Cloud-store overlays.
  • First infrastructure-as-code. A new iac/ directory at the workspace root provisions what the servers otherwise create at startup: a Terraform module for the AWS store (DynamoDB + SQS, optional CMK, optional default-off S3 blob bucket) and a Bicep module for the Azure storage stack. Templates are statically validated only — the binaries remain fully zero-config-capable without them.
  • Secrets kept out of logs. The IPFS provider configs (Pinata JWT, Filebase secret key, remote daemon token) now redact their credentials from debug output, matching the store configs.
  • Docs. A committed fragment-link validator (mail_docs/check_anchors.py) now guards the book against silently broken anchors; the blake3 appendix’s “postoffice commitment set” icon matches its term; account API and IPFS benefits link to per-recipient pin providers.

2026-07-14 — v0.5.5 (per-recipient pin providers, docs repairs)

No on-chain ABI or economic change — a new operator-local account setting plus a docs pass. Storage gains three internal account columns for the sealed provider credentials (nullable, backward-compatible).

  • Per-recipient pin providers. A mailbox owner can register their own IPFS pinning provider (Pinata, Filebase, or a self-run sithbit-ipfsd) with their mail server via the account API (GET/PUT/DELETE /v1/account/pin-provider, JWT wallet auth); delivery then pins their inbound sealed bodies to that provider in addition to the operator’s default pin — best-effort, never affecting delivery or chain state, and the operator pin stays authoritative. Credentials are sealed under the server credential key like mail passwords; the no_ipfs opt-out continues to suppress all pinning. See Per-recipient pin providers.
  • Anchor-link repairs. Seven intra-book fragment links to headings with a mid-heading icon were written with a single hyphen where mdBook’s slugifier emits a double hyphen, and silently pointed nowhere; all fragment links across the book now resolve.
  • TOC nesting. The name marketplace and Lockbox pages are now nested under the GUI clients parent in the sidebar — they are features riding the clients, not top-level topics.
  • Postoffice terminology. “Postmaster commitment set” is corrected to “postoffice commitment set” in the blake3 appendix and glossary, matching the code — the Merkle commitment lives on the postoffice account.

2026-07-13 — v0.5.4 (at-rest SSE, DMARC report completeness, GUI-client docs)

No on-chain ABI, economic, or public-API change — server-side hardening, DMARC report content, and a docs pass. Storage gains four internal DMARC columns (nullable, backward-compatible).

  • Encryption at rest on cloud stores. The AWS backend now requests server-side encryption when it auto-creates its resources — SSE on the DynamoDB tables (AWS-owned key) and SSE-SQS on its queues — with no key configuration. Azure Storage/Tables and Cosmos are always encrypted at rest by the platform. Customer-managed KMS keys remain a deferred option. See Scaling out and Deploying.
  • DMARC aggregate reports carry the published policy. RUA reports now emit the evaluated <policy_published> p/sp/adkim/aspf (previously left unset), stored per-record across every storage backend. See [spooler.dmarc_report].
  • DMARC forensic reports carry the message envelope. RUF/ARF failure reports now include the RFC 5965 Original-Mail-From, Original-Rcpt-To, and Original-Envelope-Id identifiers. See [spooler.dmarc_ruf].
  • blake3 documentation & naming. The blake3 appendix intro and table now enumerate all five blake3 uses (adding the postoffice Merkle commitment set); the PDA-derivation helpers’ hash parameters were renamed *_sha256*_blake3 to match what they actually carry (cosmetic — PDA values unchanged).
  • Threat model — per-authority accountability (S1). The threat model now documents the MX spoof-burn trust gap as a known, policy-mitigated assumption, with cryptographic per-authority accountability recorded as deferred work.
  • GUI-client docs regrouped. Thunderbird, Outlook, webmail, and Chrome are now sibling subtopics under a new GUI clients landing page, and the browser onboarding wizard counts Chrome as its fourth shared-wizard web client.

2026-07-13 — v0.5.3 (Chrome extension, installable webmail, docs backfill)

No ABI, API, CLI, or storage change — two new client-side surfaces and a docs pass. Nothing on-chain is renumbered or relaid-out.

  • Chrome extension. A fourth web-client shell (webclients/chrome), an installable Manifest V3 popup for onboarding and wallet management — equivalent to the Thunderbird/Outlook shells, not a Gmail integration and not an in-popup mail reader (mail read/send rides any IMAP/POP/SMTP client). Reuses the shared Alpine+wasm core over a new chrome-store.js (chrome.storage) adapter; build.sh produces a loadable staging/ tree and a packaged sithbit-chrome.zip. See The Chrome extension.
  • Installable webmail (PWA). The webmail app now ships a web app manifest + a static-shell service worker, so a browser can install it (desktop “Install app” / mobile “Add to Home Screen”) and load the shell offline. Mail content stays live — the service worker never caches the account API (/v1/*).
  • Reference/docs backfill. The Program & PDA reference MailInstruction table is now complete (all 39 variants); the glossary blake3 entry is corrected to its five current uses; and hand-written target="_blank" was retired from the remaining pages (the global external-links hook governs them).
  • Docs landing page. The Prelude (README.md at the time; moved to prelude.md 2026-07-15 when the marketing Welcome page took over the README.md/index.html slot) was the book’s root landing page; the Introduction moved to its own page.

2026-07-13 — v0.5.2 (SithBit brand identity)

Presentational only — no ABI, API, CLI, or storage change. A shared visual identity now spans the mdBook docs, the four web shells, and the two browser plugins.

  • One brand, everywhere. A “dark-side” palette (near-black backgrounds, a violet primary, a crimson accent used sparingly), an S-monogram mark + sithbit wordmark, and a matching favicon/plugin-icon set. The docs, webmail, marketplace, onboarding, Thunderbird, and Outlook all carry it. See Brand & identity.
  • Single source of truth for the tokens. The palette is defined once as --sb-* CSS custom properties in webclients/shared/brand.css (consumed by the shells and plugins) and mirrored onto mdBook’s per-theme variables in mail_docs/css/brand.css; the plugin icons are rasterized from one canonical mark SVG.

2026-07-13 — v0.5.1 (reach: discovery keyserver, incoming delivery, key headers)

No on-chain ABI change — an additive gRPC field (AliasRequest.domain), a new public HTTP endpoint, and a client mail-header feature build “reach” on top of v0.5.0’s domain-scoped namespace. Nothing on-chain is renumbered or relaid-out.

  • Incoming mail to user@verified-domain. A SithBit MX now accepts inbound RCPT TO:<user@domain> for a verified domain: the recipient domain threads through the gRPC gateway’s domain-scoped DomainAlias lookup (global-alias fallback preserved; empty domain = the legacy path) to the designated wallet’s mailbox. Completes the delivery leg of the domain-scoped namespace — those addresses can now receive mail, not just be registered and resolved by a native client. See Receiving mail at a domain-scoped address.
  • Public discovery keyserver — GET /v1/chain/cert?email=. An unauthenticated account-API endpoint (HKP/WKD-style) resolves an email / alias / wallet to the recipient’s published X25519 key so any sender can discover it. Empty key ⇒ 200 (seal to the wallet itself); unknown recipient ⇒ 404. The data is already public on-chain; the endpoint is a convenience and a public enumeration surface operators may wish to rate-limit. See the cert keyserver.
  • Autocrypt-style key headers on plugin mail. The lockbox plugins now advertise the sender’s published key in an opportunistic X-SithBit-Key header on outgoing mail and cache it from received mail, so a correspondent’s key is auto-discovered without a chain round-trip on replies. SithBit-specific (X25519, not OpenPGP Autocrypt); the chain stays the source of truth. See Autocrypt-style key discovery.

2026-07-13 — v0.5.0 (domain-scoped aliases)

Additive on-chain ABI: three new AliasInstruction variants (RegisterDomainAlias, RemoveDomainAlias, UpdateDomainAlias), a new DomainAlias account, and three appended error codes. Nothing existing is renumbered — the protocol stays append-only and pre-launch.

  • Domain-scoped alias namespace — user@verified-domain → wallet. A verified domain’s authority can now map [email protected], [email protected], … to wallets in a namespace only that authority may write to — distinct from the shared global-alias namespace, and with no registration fee (the authority already owns the domain). New CLI: sithbit alias register-domain <local@domain> --wallet <k>, alias update-domain (repoint in place), alias remove-domain (refund rent). See Domain-scoped aliases.
  • Resolution precedence. sithbit alias get user@domain — and the lockbox plugins’ recipient resolver — now resolve a domain-scoped mapping first and fall back to the global alias when none exists, so existing global aliases keep resolving unchanged. Lockbox mail to user@verified-domain seals to the domain-designated wallet.
  • Trust model. The domain authority alone controls its user@domain mappings (create/repoint/remove) — the same authority already trusted to relay the domain’s mail. See the threat-model note.

2026-07-13 — v0.4.5 (lockbox mail v1)

New client capability; no on-chain instruction, account layout, or error-code change (it reuses the existing SetMailboxKey instruction and the sealed-box crypto). One additive CLI flag.

  • Lockbox mail — client-side end-to-end encryption over ordinary email. The Thunderbird extension and Outlook add-in can now seal a message body to its recipient before it leaves your machine and unseal it after it arrives, so the mail server, relay, and stored copy all see only ciphertext. v1 seals to SithBit-native recipients (a raw wallet or a global alias); a recipient that can’t be resolved is sent ordinary plaintext, and sealing is all-or-nothing per message. Both ends need the plugin. See Lockbox: end-to-end encrypted mail.
  • Recoverable reading key. sithbit mailbox set-key --derive publishes a reading key derived from the wallet (a deterministic wallet signature run through a KDF), so it regenerates on any device — including a hardware wallet — with nothing to back up. The trade-off (anyone who can make the wallet sign the fixed message learns the key) is documented in the threat model.
  • The user@verified-domain namespace is planned but not yet shipped — v1 aliases are domain-blind, so [email protected] and [email protected] resolve to the same global alice. A domain-scoped namespace owned by each verified domain’s authority is the next step.

New client surface; no on-chain instruction, account layout, error-code, or CLI change (the on-chain CreateMailbox/CreateAlias/SetMailboxKey instructions the flow uses already existed).

  • Onboard with Phantom or Ledger. The web onboarding wizard — in all three mail clients and on the standalone get-started page — gains a third wallet path alongside create/import: connect an external browser wallet. Your funds-holding signing key never enters the browser (you approve the mailbox create in the extension); because a hardware wallet cannot open sealed mail, a low-value delegated reading key is generated in the browser and published in the same one-approval transaction, and mail reading routes through it. The honest trade-off (a browser-held reading key you save once, rotatable, whose loss costs only already-received mail) is documented. See Web onboarding.
  • Add another wallet from inside a client. Each signed-in client dashboard now has an “Add another wallet” button that re-opens the wizard for a fresh wallet, so onboarding is no longer a first-run-only flow. See Onboarding a second wallet.

2026-07-12 — v0.4.3 (standalone onboarding page)

A new client surface; no on-chain instruction, account layout, error-code, API, or CLI change.

  • A standalone “get started” page. The five-step onboarding wizard — create or import a wallet, claim a mailbox and an optional handle, set your default postage — is now also served on its own shareable URL (the webclients/onboarding/ bundle), so a brand-new user can be pointed straight at it with no mail client installed. It runs the identical wizard the webmail, Outlook, and Thunderbird clients show at first run, and is served same-origin by account_api like the standalone marketplace page. See Web onboarding.

2026-07-12 — v0.4.3 (self-contained web clients & dependency maintenance)

Build, packaging, and dependency maintenance; no on-chain instruction, account layout, error-code, API, or CLI change.

  • The web clients no longer load code from a CDN. @solana/web3.js is now vendored into the shared client library and served from the same origin, so the webmail, Outlook, and Thunderbird clients (and the standalone marketplace page) fetch no third-party script at runtime. This removes the Thunderbird extension’s last remote-code reference — the blocker for an add-on–store submission — and lets the clients run fully self-hosted and offline.
  • Dependency housekeeping. OpenTelemetry (operator telemetry) moved to the 0.32 release train; every wildcard (*) workspace dependency was replaced with a proper version floor and mail-auth pinned exactly, hardening reproducible builds. No runtime behavior change.

2026-07-12 — v0.4.2 (marketplace GUI & modexp-free deploy)

Client-surface, tooling, and operator additions; no on-chain instruction, account layout, or error-code change — the new deploy build gates code out without touching the default ABI.

  • The name marketplace, in the web clients and plugins. Browse aliases and domains listed for sale, buy them, and list your own — as a standalone web page and as a pane inside the webmail, Outlook, and Thunderbird clients, with For sale / Expired / Sold filters (default: For sale). Buying and listing sign with a connected Phantom or Ledger wallet (external signing), distinct from the in-wasm keypair used elsewhere; browsing and sale history read the /v1/chain/listings and /v1/chain/sales endpoints. See The name marketplace.
  • A modexp-free deploy build for AdminCloseAccount. mail_program gained a dnssec-proof Cargo feature (default on); a --no-default-features build drops the sol_big_mod_exp syscall so postmaster reclaim — and everything else — can deploy to devnet/mainnet-beta today, where that syscall is still inactive, at the cost of DNSSEC-by-proof coverage in that build. See The modexp-free deploy build.

2026-07-12 — v0.4.1 (marketplace read API, wallet-adapter builders & privacy docs)

Off-chain API, client tooling, and documentation additions; no on-chain ABI change.

  • Browse listings and sale history. New read endpoints back the marketplace: GET /v1/chain/listings (a BrowseListings scan of every alias and domain listed for sale) and GET /v1/chain/sales (ListSales — full alias + domain sale history, parsed from program logs). See the account API.
  • Wallet-adapter (unsigned) transaction builders. mail_wasm gained unsigned builders for the marketplace and onboarding instructions, so a Phantom or Ledger wallet can sign externally — the basis for the marketplace GUI’s external-signing path.
  • What’s public and private. Two new pages map SithBit’s privacy model end to end: a plain-language What’s public and private overview and the exhaustive field reference — covering the harvest-now-decrypt-later trade-off and public marketplace-purchase metadata. Cross-linked from the threat model.

2026-07-12 — v0.4.0 (postmaster reclaim)

Adds an on-chain administrative instruction; it is append-only, so no existing client breaks.

  • Reclaim and reset accounts. A new AdminCloseAccount instruction (in both programs) lets the standing postmaster delegate close program-owned accounts and refund their rent — the basis of a new sithbit postmaster reclaim tool (behind a compile-time reclaim feature, with a mainnet typed-confirm guardrail). See The Postmaster.

2026-07-12 — v0.3.1 (postmaster CLI regrouping & custody docs)

CLI-surface and documentation changes; the underlying on-chain instructions are unchanged.

  • Postmaster commands shortened. postmaster install-commitment becomes postmaster commitment, and postmaster rotate-delegate becomes postmaster delegate (command labels only — the InstallCommitment / RotateDelegate instructions are unchanged). See Postmaster key custody.
  • New reference material. A per-server RFC-coverage table and two service-discovery diagrams.

2026-07-12 — v0.3.0 (alias auctions & web onboarding)

  • Alias auctions. Aliases can now be sold by ascending-bid auction, not only at a fixed price: new SellAlias (auction mode), BidAlias, and SettleAuction instructions, with an anti-snipe extension and a 90/10 fee split. Drive it with sithbit alias sell --auction / bid / settle-auction. See Auction an alias.
  • Guided web onboarding. A 5-step first-run wizard (create wallet → mailbox → alias → keys → earnings) now greets new users across the webmail, Thunderbird, and Outlook clients, mirroring the CLI setup flow.

2026-07-11 — v0.2.1 (CLI tooling & ergonomics)

CLI-surface changes only; no protocol ABI or economic change.

  • Build a proof witness from live DNS. New sithbit domain gather-witness <domain> collects a domain’s signed DNSSEC chain from a recursive resolver and serializes the witness domain authorize / domain reclaim stage, re-walking it locally before it is ever submitted (behind the opt-in gather build feature). See Building the witness with gather-witness, and the zone-setup notes — including Cloudflare-hosted domains — in Publishing the DNS records the proof needs.
  • Root-KSK commands regrouped under ksk. postmaster set-root-ksk becomes postmaster ksk set, postmaster root-ksk-from-iana becomes postmaster ksk iana, and a new read-only postmaster ksk get prints the fingerprint currently anchored on the PostOffice (or unset). See Prerequisite: publish the root KSK.
  • Derive the root-KSK fingerprint from IANA. sithbit postmaster ksk iana turns IANA’s published root trust anchor (root-anchors.xml) into the base58 value ksk set wants — a pure offline conversion. Its new --fetch-anchors <DIR> downloads the anchor, its detached S/MIME signature, and ICANN’s CA bundle, then prints the openssl verification command and the follow-up derive step (it does not derive until you have verified). During a root-KSK rollover (two active anchors, as with the current KSK-2024 introduction) it warns and recommends the newest by validFrom instead of erroring blindly, and a new --key-tag <TAG> pins a chosen anchor. See Rollovers: more than one active anchor, Downloading and verifying the anchor, and Getting ICANN’s CA independently.
  • Fee getters grouped. sithbit postoffice fee stamp / fee domain / fee alias replace the flat fee / domain-fee / alias-fee, and the delegate setters are now sithbit postmaster fee stamp / fee domain / fee alias (was set-stamp-fee / set-domain-fee / set-alias-fee). See The Postmaster → Checking status.
  • Alias transfer grouped. sithbit alias transfer init / transfer accept / transfer cancel replace transfer / accept-transfer / cancel-transfer. See Transfer an alias.
  • Alias listing cancel folded in. sithbit alias sell <alias> --cancel replaces alias cancel-sell. See List an alias for sale → Cancelling a listing.
  • Alias create is variadic. sithbit alias create now takes one or more aliases (reading stdin when none are given), absorbing alias create-bulk, which is removed. See Bulk reservation.
  • mailbox credentials. sithbit mailbox derive-password is renamed sithbit mailbox credentials (it prints the mail username + password pair).

2026-07-11 — v0.2.0 (Set 7)