Skim v1.0.9 — "Nice Try, Phish" 🎣
The one where Skim learns to smell a phishing email from across the room and does it all locally, offline, without phoning a single reputation service. Your inbox, now with a healthy sense of suspicion.
New
-
Phishing detection, built right in. Skim now quietly vets every message for the classic tells and warns you before you click something you'll regret.
Sender checks reads SPF / DKIM / DMARC results, catches "reply goes to a totally different domain" tricks, spots when the friendly display name hides a stranger's address, and flags lookalike domains (paypa1.com, we see you).
Link checks a link that says one thing but opens another, user@evil.com sleight of hand, and raw-IP links get stopped cold. Shorteners, plain http, punycode, weird ports and endless subdomains get side-eye too but only when the sender already looks shady, so your newsletters full of tracking redirects don't set off alarms.
A link gate click a suspicious link and Skim tells you where it actually goes and lets you bail or proceed. No more mystery destinations.
"Check for phishing" with AI one tap to have Skim AI read the sender, the auth results, and the links, then explain the whole thing in plain language.
All of it deterministic, all of it offline. No thresholds to tune, no data leaving your machine.
Skim v1.0.5 — "One Inbox to Rule Them All" 🪄
The one where all your mailboxes finally stop pretending they don't know each other. Connect five accounts, get one tidy stream of mail sorted by date. Revolutionary concept: everything in one place.
New
"All inboxes" view pick All inboxes in the account switcher and every connected mailbox pours into a single, date-sorted list of messages and threads. No more clicking between accounts like it's 2009.
From picker in compose replying or writing fresh from the unified view? Skim now lets you choose which account it goes out from, so your work self doesn't accidentally email your mom's book club.
Skim v1.0.24 — "You were knocking with the wrong key" 🔑
The one where OpenAI's smartest models finally answer. Turns out we'd been calling one JSON field by a name they retired back when o1 shipped, and they were not shy about it.
Fixed
-
Reasoning models on a custom OpenAI endpoint work again. Point the OpenAI-compatible provider at OpenAI itself, pick anything from the o-series or the GPT-5 family, and every single request died before the first token: Unsupported parameter: 'max_tokens' is not supported with this model. Use 'max_completion_tokens' instead. Which is a wonderfully clear error message to receive zero times, because you're in a chat window and all you see is that nothing happened.
The lazy fix would be to rename the field everywhere and move on. Except only about half of the OpenAI-compatible world speaks the new name: Mistral answers 422, and Ollama and llama-cpp-python ignore it in complete silence which doesn't fail your request, it just removes the ceiling on the answer. That's strictly worse than an error, because nobody ever finds out.
So Skim now opens with the name every server understands, and switches only for an endpoint that asks for the other one by name, in its own error and only when that error names both keys, which is what separates "use the other name" from "that number is too big". Cost: one rejected request the first time you talk to such an endpoint. No new settings, no model list to maintain, nothing for you to choose. Exactly the amount of thinking about this you should have to do: none.
Huge thanks to @ondrahracek for this one not just the fix, but a writeup that walked through what LangChain and goose each broke trying the obvious thing first, and two tests verified by mutation. Reviewing that was a genuine pleasure.
Housekeeping
Skim v1.0.26 — "Think Less, Answer More" 🧠
The one where OpenAI told us our tools and its own thinking couldn't be in the same room, and we said fine, we'll ask nicely twice.
Fixed
Mailbox chat works again on gpt-5.6 models. Point the OpenAI-compatible provider at OpenAI with a gpt-5.6-* model and every single round that carried tools came back as a 400 and a wall of English error prose in the chat bubble. Retrying showed you the same wall. Very helpful. (#40)
The twist: Skim never sent reasoning_effort at all. The model reasons at medium on its own, then refuses its own default alongside our tools so nothing in our request even named the thing that broke it.We still don't guess. No model-name regex, no "is this OpenAI?" sniffing the same base URL serves OpenRouter, Ollama, LM Studio, vLLM and whatever you typed in that box, and half of them would 422 on the "fix". So Skim sends nothing up front, waits for the endpoint to name the remedy in its own error, and retries exactly once with the value it asked for tools still attached, because answering without the search results would rather defeat the point.
Housekeeping
Skim v1.0.23 — "Press the button, something happens" 🔘
The one where the Restart to update button admits it heard you. Turns out shipping an updater whose only feedback is "trust me bro" was a bold choice.
Fixed
"Restart to update" now actually restarts. Before: you clicked, nothing happened, you clicked four more times out of spite, and a minute later the app quietly closed on its own. The click was stuck waiting on a database write that the mail sync had no intention of letting go of. Now the banner flips to "Installing update " the instant you click it and stops being a button, so those four extra clicks can't each launch their own installer racing the other three for your install folder.
A clean exit before the installer takes over. The updater ends this process with process::exit(0) no goodbyes. So on the way out Skim now parks the sync engines and folds the write-ahead log back into the database, instead of leaving a few megabytes of unfinished business for the next build to untangle on its very first query.
The message list stopped lying to you. "Nothing to skim" used to mean either "this folder is empty" or "the read blew up and nobody told you". Guess which one you got after updating. It now says "Couldn't load retry" and gives you the retry, and only claims a folder is empty when it actually read the folder.
Sync events that arrive before the app is ready no longer vanish. The mail engine starts syncing in Rust before the window has finished asking who owns which mailbox; the first "there's new mail" used to land in the void, and the next scheduled one was five minutes away. They're collected now.
Reopening from the tray does something again. Hiding to the tray only hides the webview no frontend code re-runs on the way back, so a list left empty by a failed read stayed empty no matter how many times you reopened it. The window now says "I'm back", and a broken view puts itself right.
Housekeeping
Skim v1.0.22 — "Ask the MX, not the vibes" 🛂
The one where Skim stops pretending that Microsoft owns exactly four domains on the entire internet.
New
Work & school accounts are finally recognized. If your company lives on Exchange Online, Skim now asks your domain's MX records who really hosts the mail. Everything Microsoft runs answers on *.protection.outlook.com, so the sign-in button that actually works shows up on its own no more falling into the manual IMAP/SMTP form to guess hostnames that were never going to connect. (Yes, hotmail.de too. Sorry it took a whole minor version to notice Germany exists. )
Behind a mail gateway? Proofpoint and Mimecast tenants don't advertise Microsoft in MX at all, so unknown domains now get one quiet "Work or school account?" line instead of a shrug. One line. We counted.
The DNS probe goes through the machine's own resolver (DnsQuery_W) your domain never visits a third party, results are memoized, the whole thing is capped at 3 seconds, and if it fails the screen looks exactly like it did before.
Fixed
Skim v1.0.7 — "Ollama Mia!" 🎶
The one where setting up a local AI brain stops being a spelling test pick a chip, done.
And it's a special one: this is Skim's first release built on a pull request from an outside contributor.
Skim v1.0.18 — "Mind the closing boundary" 🧱
The one where we stop blaming you for emails that render as a beautiful, pristine, completely empty rectangle. Turns out it was us. Well it was the sender's mailer. But we fixed it anyway.
Fixed
Blank emails are no longer blank. Some mailers finish a message on the last base64 chunk and just walk away, never writing the closing --boundary--. The MIME parser reads that as "this part is broken", quietly demotes your email to an attachment, and hands us nothing to show. Skim now closes those dangling multiparts itself (innermost first, like a polite houseguest turning off the lights) and parses again. Your booking confirmations are back.
No more phantom attachment. That mystery .bin full of raw base64 sitting under the empty email? It was your email. It has been returned to its rightful place.
Bad parses no longer latch forever. The old code cached "I fetched this" and "there's genuinely nothing here" as the same thing, so a message that failed to parse once stayed broken until the heat death of the universe. Now Skim can tell the difference and gives previously-mangled messages one more honest attempt.
Truly empty emails say so. If a message really has nothing to render, you get a plain line saying that instead of headers floating above a void. Silence is not a UI.
Stale attachment files get cleaned up on refetch, instead of quietly squatting on your disk forever.
Everywhere
🚀 Skim v1.0.4 is out — "Two inboxes walk into a titlebar" 📬📬
Small update, but a big quality-of-life one: Skim now handles multiple mailboxes at once without turning into a giant multi-account control panel.
What's new:
Multiple accounts, one calm app. Connect as many mailboxes as you like. A slim switcher shows up in the titlebar only when you have more than one, with a per-inbox unread hint.
Ctrl + 1 9 to jump straight to the Nth mailbox.
Everything lands in the right place toast clicks, search hits, AI citations, cold-start opens all switch to the account that owns the message before opening it.
Search & compose stay scoped to the mailbox you're looking at no more "sent from the wrong address" moments.
No duplicate accounts try to add one that's already connected and Skim politely declines before touching your existing setup.
Skim is still what it's always been: a free, open-source AI email client for Windows, ~5 MB installer, bring-your-own-key AI. No account, no telemetry, no bloat.
Skim v1.0.17 — "Кто-то написал вам по-немецки" 🌍
The one where a foreign-language email stops being a research project. Press T, read it. That's the whole feature.
New
Translate an email in place Skim now notices when a message isn't in your language and offers to translate it right there: subject and body, in the reading pane, where the email already was. No copy-pasting into the Ask panel, no reading your mail as a chat bubble. Hit T, or click the offer.
"Show original" is one click away because sometimes the AI's Portuguese is worse than your Portuguese, and you'd like to check.
Translations are cached the first look costs a request, every look after that is free and instant. Re-opening a thread doesn't re-bill you.
Language detection runs locally trigram tables compiled into the binary. No model files, no download, no phoning home just to figure out that "Sehr geehrte Damen und Herren" isn't English.
The AI chat learned to grow Ctrl+E expands it to fill the window, Ctrl+Shift+E throws it into a window of its own, Esc puts everything back. The little arrows finally point where they mean.
