OSS Roundup: October 2026
Author: Andrew Porter
Published: 10/28/2026
-
Four pull requests across three projects, the problems behind them, and a few places where you can help us get them over the line.
Roundup
We sent four pull requests this month, fixing two ESP32 NATS bugs and bringing a stale Mailslurper fork back in sync while backporting its fixes to the upstream.
This Month at a Glance
| Project | What we sent | Who it helps | Status |
|---|---|---|---|
| espidf-nats | #26 (mTLS client certs) | ESP32 devices on a NATS server with verify: true | Open |
| espidf-nats | #28 (TLS socket leak) | Anyone using TLS with cert verification on ESP-IDF | Open |
| mailslurper | #193 (UI first-load race) | Anyone opening the web UI on a fresh browser | Open |
| ory/mailslurper | #1 (fork upstream sync) | Ory Quickstart users | Open |
Why Now?
For a while, CHaaS ran on a handful of local patches to libraries we depend on. That’s a perfectly normal place to be while a product is still finding its feet, but it’s not where we want to stay. Now that our user service is live and in production, we’re paying that debt back - and these are our first contributions since launch. We’re excited to contribute back to the community where we can and we have some additional upcoming projects to continue that work.
Project 1: espidf-nats
What it is: a NATS client library for ESP-IDF, by Debashish Sahu (@debsahu) that saved us weeks of time when we needed an embedded NATS.io client. Even in an AI-enabled shop this was an enormous head start on delivering a successful project. At 315Concepts we use this as part of our CHaaS project to ensure that our devices have a stable library for talking to the high-performance messaging fabric that backs the entire product experience, an understandably critical part of the architecture! You never want to find issues in critical parts of your stack but we’re excited to be helping get these issues resolved not just for ourselves but for anyone else trying to do the same stuff we are.
Fix A: mTLS client certificates weren’t being sent
| PR | debsahu/espidf-nats#26 |
| Issue | #27 |
| Touches | nats_client.h, tcp_transport.h |
| Symptom | You configure a client certificate and key, the TLS upgrade reports success, and then the very first read fails on every connection - because the server rejected you. |
| Cause | nats_tls_config_t accepts and validates client_cert and client_key, but upgrade_to_tls() never hands them to mbedTLS. The handshake goes out with no client identity, so a server running verify: true closes the door. |
| Fix | After the CA chain is parsed, we parse the cert and key and attach them with mbedtls_ssl_conf_own_cert(). If anything fails, we log the mbedTLS error and bail out cleanly. Configs without a client cert behave exactly as before. |
Results on our bench (ESP32-C6, ESP-IDF v6.0.1, mbedTLS 4.0):
| Outcome | Fix off | Fix on |
|---|---|---|
| Tests passed | 0 of 17 | 17 of 17 |
Fix B: a socket leak after a failed TLS upgrade
| PR | debsahu/espidf-nats#28 |
| Issue | #29 |
| Touches | nats_client.h (NATS::disconnect()) |
| Symptom | A device that can’t verify its server’s certificate retries forever, and after 16 attempts everything on the device that needs a socket stops working - OTA, MQTT, HTTP, all of it - until a reboot. |
| Cause | disconnect() returns early when connected is false, and connected is only set after INFO and CONNECT succeed. After a failed TLS upgrade it’s still false, so ::close(sockfd) never runs. Each retry orphans one LWIP socket until CONFIG_LWIP_MAX_ACTIVE_TCP (default 16) runs out. |
| Fix | Remove the early return so cleanup always runs, and remember was_connected so the on_disconnect callback still only fires when it did before. |
Results on our bench (ESP32-C6, ESP-IDF v6.0.1, mbedTLS 4.0):
| Socket | Fix off | Fix on |
|---|---|---|
| First 16 attempts | climb from 48 to 63 | stay on 48 |
| Attempts 17-20 | fail with ENOBUFS | succeed in reusing the socket |
A note on severity. We labelled the PR “critical” because the failure is unrecoverable without a power cycle and takes the whole device’s networking with it. Scored with CVSS 3.1 it comes out at 5.9 (Medium), because it needs fairly specific failure types to trigger. Maintainers know their users best, so please rate it however you see fit. The confirmed trigger is a host verification failure, such as a SAN mismatch or a botched certificate rotation. No attacker is needed, so a routine (and trusted) misconfiguration is all that’s needed to take the device offline - which is why we rate it critical operationally.
If you’re affected today:
- Make sure the certificate matches the host or IP you connect to, and rehearse rotations before deploying them
- Watch your free socket count, and restart after repeated
socket()failures - Please don’t set
skip_cert_verification = trueas a workaround. It stops this leak but also turns off server authentication.
Reproduce it yourself
Both PRs link to demo branches under SwissArmyBud/espidf-nats with a Dockerized NATS server (make nats-tls), a flag to toggle each fix, and the raw logs. Flash it once with the fix off and once with it on, and compare.
Project 2: Mailslurper
What it is: a small SMTP server with a web UI that catches outgoing email, which is handy for checking that your auth flows really are sending what you think they are. It’s part of FiMOr’s debug module, which is how we ended up here. When we were putting that project together we noticed the copy of Mailslurper used by the Ory Quickstart had drifted well behind upstream but it carried some thoughtful Ory and non-Ory changes. We wanted to help out and slap a new coat of paint on the fork, but the internal APIs had moved enough that a plain merge wouldn’t produce working software. So we ended up taking two different approaches and tried to make each one useful to someone other than us.
Step 1: bring the Ory fork up to date
| PR | ory/mailslurper#1 |
| Branch | aporter/261027-merge-upstream into master |
We merged mailslurper/master and then re-applied Ory’s own custom behavior on top, aiming for the smallest change that keeps the Kratos Quickstart working. The merge brings along Go modules, a configurable config-file location, new SMTP command executors, storage updates, proxy support (aha!), and Docker packaging work. The Ory-specific changes we preserved are the settings-service behavior, the Dockerfile-smtps build, and the MySQL LONGTEXT change for attachments over 64 KB.
We used Claude Code to read the compact Ory changes and re-implement them against the new APIs. We QA’d the results and a bot reviewer also left a number of comments, which we’ve replied to on the PR.
Step 2: send the non-Ory fix back to upstream
| PR | mailslurper/mailslurper#193 |
| Touches | SettingsService.js, HomeController.js, AdminController.js, www.go |
| Symptom | On a browser with empty localStorage, the first page load can throw on JSON.parse(undefined) or build a bad service URL. |
| Fix | A new ensureServiceSettings() promise that returns immediately if settings are stored, and otherwise fetches /servicesettings and stores that result before resolving. Both controllers now wait on it and nothing changes when settings already exist. |
Once the Ory fork was working again, we looked at which of its changes weren’t really about Ory at all and we spotted a good one - a first-load race condition lives in the shared web UI, so anyone running upstream Mailslurper on a fresh browser will hit it. A single browser refresh fixes it, which is exactly the kind of bug that’s easy to shrug off for a long time… until now! The original fix came from Ory’s fork, so credit to Aeneas Rekkas (@aeneasr) for the approach. We isolated just that change, left every Ory-specific piece behind, and sent it to upstream as a small standalone PR, with Claude Code helping on the port, just as in Step 1.
Where You Can Help
None of this is finished until someone other than us has looked at it and the code is merged. Here are the gaps we know about, and you are welcome to help us plug some of them.
Embedded testing we haven’t done:
- [ - ] Run PR #26 or #28 on an ESP-IDF 5.x build (mbedTLS 3.x)
- [ - ] Try them on a chip other than the ESP32-C6
- [ - ] Check mTLS on TLS 1.2 - all our runs negotiated TLS 1.3
- [ - ] Try the WebSocket transport and the reconnect path under
process()with the socket fix - [ - ] Trigger the socket leak with a TLS error other than 0x2700
Costs you nothing to star the projects. Mailslurper and espidf-nats are small projects that carry real weight for the people who use them.
Thank-Yous
- Debashish Sahu for espidf-nats
- Adam Presley and the Mailslurper contributors, including Wasim Sheikh, whose Docker work is in the upstream we merged
- Aeneas Rekkas and the Ory team for the original settings fix and the Quickstart we’ve been building on
Spotted something we got wrong, or have a project you think we should look at next month? Smack a comment on any of the Issues or PRs above, or find the rest of our work on GitHub.
