Bugs resolved in Moz-Phab 2.15.4:
- bug 2058150
moz-phab patchcrashes withKeyErrorwhen a stack relative is not visible to the user
Discuss these changes in #engineering-workflow on Slack or #Conduit Matrix.
1 post - 1 participant
Firefox Tooling Announcements
Bugs resolved in Moz-Phab 2.15.4:
moz-phab patch crashes with KeyError when a stack relative is not visible to the userDiscuss these changes in #engineering-workflow on Slack or #Conduit Matrix.
1 post - 1 participant
Firefox Nightly
This past May, we shared our vision for the future of Firefox. Starting today, you can try out the next design evolution of Firefox in Nightly.
It’s still Firefox, now with a more cohesive look and feel across tabs, menus, panels, and other browser surfaces. You’ll notice softer tab shapes, a warmer color palette, updated icons, and – after hearing from many of you – the return of Compact Mode and new theme options to make Firefox your own.
Many of you have already spotted pieces of the new design in Nightly over the past few months. Now they’re coming together into one complete experience.
You’ll continue to see updates over the coming weeks as we polish the new Firefox design before it reaches Firefox users more broadly later this year. As you browse, we’re especially interested in any visual or functional issues you encounter.
Keep an eye out for things like:
If something doesn’t look or behave as expected, please file a bug in Bugzilla.
When possible, include:
If you have broader thoughts or questions about your experience, join the discussion on Mozilla Connect.
Thanks for using Nightly and helping us improve Firefox. Every bug report helps us identify issues and continue refining the new Firefox design before it reaches Firefox users more broadly later this year.
Firefox Tooling Announcements
The following changes have been pushed to bugzilla.mozilla.org:
The following changes have been pushed to bugzilla.mozilla.org:
Discuss these changes in the BMO Matrix Room
1 post - 1 participant
Mozilla Addons Blog
We had a bumper release of WebExtensions API updates in Firefox 153. To start, there is a permissions change that affects how your extensions access local files. We then have two contributions from the community members: userScripts.execute() and the new publicSuffix API. We’re covering those contributions in more depth, including the people behind them, in a separate post. And there is more, read on…
Extensions that need to read file:// URLs used to get that access as part of the “Access your data for all websites” host permission. Starting in Firefox 153, file access is a separate, explicit permission, “Access local files on your computer”, shown in the extension’s permissions settings. It’s off by default for every extension, including ones already installed.
This change has a few concrete effects on code:
async function checkFileSchemeAccess() {
const isAllowed = await browser.extension.isAllowedFileSchemeAccess();
if (!isAllowed) {
await browser.notifications.create("file-scheme-access-needed", {
type: "basic",
iconUrl: browser.runtime.getURL("icons/icon-48.png"),
title: "Local file access required",
message:
'This extension needs "Allow access to file URLs" enabled to work ' +
"with local files. Go to about:addons → select this extension → " +
"turn on that setting, then reload the page.",
});
return false;
}
return true;
}
devtools.inspectedWindow.eval() calls targeting file:// URLs are affected the same way; they now require this permission to succeed.
If your extension depends on file:// access, expect existing users to see that access stops after upgrading (until they enable the permission), and consider adding a prompt or fallback path, for example by specifying an embedded options page (options_ui) and calling browser.runtime.openOptionsPage() to open about:addons and including instructions to toggle the setting in the “Permissions and data” tab.
Firefox 153 adds two community-contributed APIs:
Both APIs were built by contributors motivated by real needs in their extensions. We take an in-depth look at these contributions, their developers, impact, and history in a forthcoming post.
Firefox 153 introduces documentId, a stable identifier for a document instance, including a new runtime.getDocumentId() method, several webNavigation events and methods, webRequest events, scripting injection targets, and the extension messaging APIs.
Many WebExtension APIs use tabId and frameId to identify where to perform an operation. However, because frameId identifies the frame rather than its content, the loaded document can change and the extension’s subsequent operation ends up targeting the new (intended) document. documentId addresses this problem by providing a unique ID for the document. Now, if an extension uses the ID and the frame’s document has changed, the operation fails rather than silently targeting the wrong document.
See Work with documentId for the full list of supported events and methods, along with guidance on using it.
Content scripts can now access document.adoptedStyleSheets and ShadowRoot.adoptedStyleSheets directly.
const sheet = new CSSStyleSheet();
sheet.replaceSync("* { background: pink; }");
document.adoptedStyleSheets = [sheet];
This enables extensions to inspect or modify constructed stylesheets from a content script, without using .wrappedJSObject, a workaround that risks interference from the web page.
The theme manifest key’s images.additional_backgrounds property now accepts CSS gradients alongside image URLs. A new properties.additional_backgrounds_size property controls the size of each additional background item.
If your extension supports contextual identities, you now have access to two new methods: contextualIdentities.getSupportedColors() and contextualIdentities.getSupportedIcons(). These methods return the supported colors and icons, so your extension doesn’t need to hardcode either list.
Also, the colors have been updated to align with the new UI theme: “turquoise” is now “cyan”, “toolbar” is now “gray”, and “violet” has been added. The old names still work for backward compatibility, but your extension should switch to using getSupportedColors() rather than hardcoding either the old or new names.
While this isn’t about new APIs, I wanted to mention a change that’s part of our work to make source code review faster and more reliable. When you submit an extension version, AMO now attempts to build your extensions from the submitted source code and compares the result to the package you uploaded. When the two match, reviewers don’t have to verify the build manually. This means submission can move through its review faster.
For now, this applies only if you submit source code that includes a package.json file to build your extension. If your extension has no build step, or you use a different build system, nothing changes. The AMO builder keeps its zero-config approach.
So, if your extension’s source code uses a package.json file, add an npm script named build-for-amo that runs the commands needed to build your extension for Firefox:
{
"scripts": {
"fx-build": "some commands to build your add-on for Firefox",
"build-for-amo": "npm run fx-build"
}
}
If you’ve a Firefox-specific build command, just point build-for-amo at it. When present, the builder invokes this script instead of guessing how to build your extension. And while you are at it, make sure all your dev dependencies are listed in the package.json file.
For more information, including documentation and Bugzilla links, see the Changes for add-on developers section of the Firefox 153 for developers release notes on MDN.
As always, file extension-related issues on Bugzilla under the WebExtensions product, cross-browser API proposals are discussed in the W3C WebExtensions Community Group, and questions are welcome on the Add-ons Discourse.
The post Firefox 153 WebExtensions API updates appeared first on Mozilla Add-ons Community Blog.
Thunderbird Blog
As we head into the summer months, a new Extended Support Release (ESR) is in full bloom. Thunderbird 153 “Meadow” is out now, and from all of us at MZLA, the Thunderbird Council, and our global community of contributors, we can’t wait for you to try it out.
“Meadow” builds on Thunderbird 140 “Eclipse,” along with the steady stream of features and improvements that have landed in the Monthly Release channel over the past year. This release makes first-time setup smoother with a redesigned Account Hub, brings native Microsoft Exchange support out into the open, and lets Thunderbird take on the colors of your desktop. Add privacy-minded networking, friendlier notifications, and a healthy crop of refinements, and Meadow is ready to grow with you.
The all-new Account Hub makes setting up accounts faster and more intuitive than ever, with improved autodiscover, automatic protocol detection, and automatic setup of connected calendars and address books.
The new Account Hub modal lets you set up all types of local and remote address books. Existing email accounts are automatically scanned to detect available address books that haven’t been configured yet.
We’ve added full support for Exchange email servers via Exchange Web Services: set up Microsoft Exchange accounts natively to read, manage, and write emails, no add-ons required. Experimental Microsoft Graph support is already in core but temporarily disabled behind a preference while we finish it, with full support plus Calendar and Address Book integration aimed for later this year.
Meadow now inherits your operating system’s accent color to match your preferred look. You can also customize your colors with the new accent color settings in the Appearance tab.
Mark an email as read, delete it, flag it as spam, and more right from the native notifications on your operating system.
In addition to these headline features, there’s a whole host of other updates you’ll love, including:
OAuth in Browser
Thunderbird now supports OAuth authentication directly in your default browser.
Login With Thundermail
If you have a Thundermail account, you can sign in to Thunderbird with one-click authentication in the Account Hub.
Folder Sorting
An improved UI and better visual indicators make sorting your emails easier than ever.
Bug Fixes and Improvements
Thousands of bug fixes and performance improvements bring you the smooth, reliable Thunderbird experience you expect.
Thunderbird 153 “Meadow” might seem soothing and calm, but we’re excited to get these features into your hands. And if you’d like updates like these more often, there’s no need to wait for the annual release: switching to Thunderbird Release gets you new updates on a monthly basis.
Even with QA and beta testing, any major software release may have issues that only surface after significant public use. That’s why we’re rolling out automatic updates gradually, enabling them more broadly as we confirm everything is stable.
Manual upgrade to 153 is now enabled via Help > About — you can upgrade now or wait to receive automatic updates. Thunderbird 153.0 is also offered as a direct download from thunderbird.net. Be sure to select ‘Thunderbird Extended Support Release’ in the ‘Release Channel’ drop-down menu.
For Linux users running Thunderbird from the Snap or Flatpak, 153 will be available within the next few weeks. Likewise, Thunderbird 153 will arrive on the Microsoft Store by mid-July.
Full release notes can be found here.
If you have any issues, please reach out to support.
Have an idea? We want to hear it! Submit your ideas here.
The post Thunderbird 153 “Meadow” is out now! appeared first on The Thunderbird Blog.
Firefox Tooling Announcements
The latest version of PerfCompare is now live!
Check out the change-log below to see the updates:
[kala-moz]
[moijes]
[sumairq]
Thank you for the contributions!
Bugs or feature requests can be filed on Bugzilla. The team can also be found on the #perfcompare channel on Element. Come and chat!
1 post - 1 participant
This Week In Rust
Hello and welcome to another issue of This Week in Rust! Rust is a programming language empowering everyone to build reliable and efficient software. This is a weekly summary of its progress and community. Want something mentioned? Tag us at @thisweekinrust.bsky.social on Bluesky or @ThisWeekinRust on mastodon.social, or send us a pull request. Want to get involved? We love contributions.
This Week in Rust is openly developed on GitHub and archives can be viewed at this-week-in-rust.org. If you find any errors in this week's issue, please submit a PR.
Want TWIR in your inbox? Subscribe here.
tracing-reload - reload layer without panicsThis week's crate is xan, a TUI toolkit to work with CSV files.
Thanks to Simeon H.K. Fitch for the suggestion!
Please submit your suggestions and votes for next week!
An important step for RFC implementation is for people to experiment with the implementation and give feedback, especially before stabilization.
If you are a feature implementer and would like your RFC to appear in this list, add a
call-for-testing label to your RFC along with a comment providing testing instructions and/or
guidance on which aspect(s) of the feature need testing.
No calls for testing were issued this week by Rust, Cargo, Rustup or Rust language RFCs.
Let us know if you would like your feature to be tracked as a part of this list.
Always wanted to contribute to open-source projects but did not know where to start? Every week we highlight some tasks from the Rust community for you to pick and get started!
Some of these tasks may also have mentors available, visit the task page for more information.
If you are a Rust project owner and are looking for contributors, please submit tasks here or through a PR to TWiR or by reaching out on Bluesky or Mastodon!
Are you a new or experienced speaker looking for a place to share something cool? This section highlights events that are being planned and are accepting submissions to join their event as a speaker.
If you are an event organizer hoping to expand the reach of your event, please submit a link to the website through a PR to TWiR or by reaching out on Bluesky or Mastodon!
576 pull requests were merged in the last week
dep_graph: deduplicate task reads with an epoch-filtered index recorder#[diagnostic::opaque] attribute to hide backtraces of macrosast::Expr64Iterator::count impl for str::EncodeUtf16bool::toggleconst_binary_searchDebug helpers via CellVecDeque::truncate_to_rangepin!() more foolproofstd::io::BufRead to alloc::iostd::io::Read to alloc::iostd::io::read_to_string to alloc::iotimings: only report units the job queue actually ranblock_scrutinee lintref_as_ptr suggestions in const/static initializers== 0 on unsigned types as a manual_clamp lower boundif_not_else linting on macro expanded conditionsneedless_collect suggests a suggestion that cannot be typednon_zero_suggestions: don't lint signed integer div/rem as NonZeromanual_filter: don't eat comments in the and_then suggestionas _ for indirectly used traits in clippy sourcesmin_ident_chars#[must_use] determination from the compiler.await's IntoFuture::Output.unwrap() calls in SyntaxFactoryhir crate leaking bound variables from skipped bindersInferenceContext:identity_args using the wrong DefIdenum variants in next-solver genericspattern_matching_variant lowering due to recoveryWherePredicate::ForLifetimes into WherePredicate::TypeBoundast::make::expr_call()Result for the lsp-server Response payload typeExprScopeThe two most notable changes this week were #159115, which resulted in pretty nice instruction count wins for full incremental builds on several benchmarks, and #159091, which enabled PGO for rustdoc, which makes it ~3-4% faster across the board.
There were two large rollups with tiny performance regressions, which made it difficult to find the offending PRs.
Triage done by @Kobzol. Revision range: 5503df87..d527bc9b
Summary:
| (instructions:u) | mean | range | count |
|---|---|---|---|
| Regressions ❌ (primary) |
0.4% | [0.2%, 1.0%] | 40 |
| Regressions ❌ (secondary) |
0.7% | [0.2%, 4.6%] | 69 |
| Improvements ✅ (primary) |
-2.0% | [-6.2%, -0.2%] | 136 |
| Improvements ✅ (secondary) |
-2.6% | [-8.4%, -0.2%] | 119 |
| All ❌✅ (primary) | -1.4% | [-6.2%, 1.0%] | 176 |
2 Regressions, 3 Improvements, 6 Mixed; 4 of them in rollups 34 artifact comparisons made in total
Changes to Rust follow the Rust RFC (request for comments) process. These are the RFCs that were approved for implementation this week:
Every week, the team announces the 'final comment period' for RFCs and key PRs which are reaching a decision. Express your opinions now.
bool::toggleraw_borrows_via_references lint#[path] attributes on inline modulesnote when calling rustc without specifying an editiontarget_feature_available_at_call_siteNo Items entered Final Comment Period this week for Cargo, Language Reference, Language Team or Rust RFCs.
Let us know if you would like your PRs, Tracking Issues or RFCs to be tracked as a part of this list.
Rusty Events between 2026-07-22 - 2026-08-19 🦀
If you are running a Rust event please add it to the calendar to get it mentioned here. Please remember to add a link to the event too. Email the Rust Community Team for access.
Please see the latest Who's Hiring thread on r/rust
We were planning on publishing a blog post announcing this at the same time as making the repo public, but ran out of private repo CI usage 😭.
– Carl Lerche on r/rust about the launch of topcoat
Despite a lamentable lack of suggestions, llogiq is glad to have found this quote.
Please submit quotes and vote for next week!
This Week in Rust is edited by:
Email list hosting is sponsored by The Rust Foundation
Firefox Developer Experience
WebDriver is a remote control interface that enables introspection and control of user agents. As such, it can help developers to verify that their websites are working and performing well with all major browsers. The protocol is standardized by the W3C and consists of two separate specifications: WebDriver classic (HTTP) and the new WebDriver BiDi (Bi-Directional).
This newsletter gives an overview of the work we’ve done as part of the Firefox 153 release cycle.
Firefox is an open source project, and we are always happy to receive external code contributions to our WebDriver implementation. We want to give special thanks to everyone who filed issues, bugs and submitted patches.
Firefox 153, multiple WebDriver bugs were fixed by contributors:
WebDriver code is written in JavaScript, Python, and Rust so any web developer can contribute! Read how to setup the work environment and check the list of mentored issues for Marionette, or the list of mentored JavaScript bugs for WebDriver BiDi. Join our chatroom if you need any help to get started!
A complete list of developer-facing changes included in this Firefox release is available in the MDN Firefox 153 Release Notes.
The Mozilla Blog
Today, we’re excited to announce the Preview of Containers in Firefox version 153, which lets you keep separate parts of your online life (work, shopping, personal, banking) logged into different accounts in the same browser window, but keeps your cookies and ad tracking isolated inside each container.
This means that stuff you do in one container isn’t seen by other containers. No longer will you search for a new hat to wear to a party, only to be inundated with ads for hats at every twist and turn on the internet for weeks to come.
For almost a decade, many of you have relied on our Multi-Account Containers extension to keep work, personal, and privacy-sensitive browsing separate without needing multiple browsers or profiles.
We’ve heard your feedback and understood the value you find in that separation. Now, we’re bringing the power of the Multi-Account Containers extension directly into the heart of Firefox for all to benefit from.
Whether you’re managing multiple social media accounts, separating work projects from personal shopping, or simply keeping your banking activity distinct, Containers are designed to help you organize your digital space. By making Containers a native, first-party feature in Firefox 153, we are:
In this preview release, you can:
If you’re already using the Multi-Account Containers, there’s nothing special you need to do. Not all of the features of the add-on are available in the first-party version of containers just yet, we’re still building them out. You can continue to use the add-on alongside the built in containers, no need to uninstall the add-on.
This release represents our first step in making Firefox more adaptable to how you actually live and work online. While this is just the beginning, we have plans to refine this experience and build a foundation for future features that make context separation even more seamless.
We’re eager to hear how this native experience fits into your daily routine. As you explore the new Containers Preview in Firefox 153, please let us know what you think by posting your feedback in this Mozilla Connect thread. Your feedback helps us shape the future of these tools and ensures we’re building features that truly matter to you.
The post Experience Better Browsing: Introducing Native Containers in Firefox 153 appeared first on The Mozilla Blog.
The Mozilla Blog
You’re planning a trip. Reading an article. Following a recipe.
Then a question pops into your head. Sometimes it leads down a rabbit hole – with more searches, more tabs and plenty to explore. Other times, you just need a little context so you can get back to what you were doing.
That’s why we’re introducing Quick Answers for Firefox on iOS.
With Quick Answers, you can ask a question using your voice and get a concise answer. Just open a new tab, long-press the voice button and ask.
For example:
If a quick answer is all you need, you’re done.
If you want to dig in deeper, the links to supporting sources are there for exploration.
We’ve built Quick Answers to be transparent about how it works and what data is shared.
Voice is processed on your device using Apple’s speech recognition technology. No raw audio is stored or sent to the server, and Firefox doesn’t share your browsing history or personal context with the AI model. Only the transcribed text of your question is sent to generate an answer. You can turn the feature off at any time in Settings → AI Controls.
Quick Answers is starting to roll out today to Firefox for iOS users in the U.S. using English.
Oh, and if you’re wondering…
The post Quick Answers: For the questions in between appeared first on The Mozilla Blog.
The Mozilla Blog
Tabs pile up fast on mobile. Imagine you’re planning a summer barbecue, and you start by searching for the best rib recipe. Twenty minutes later, you’re 17 tabs deep: comparing marinades, debating side dishes, checking the weather, making a grocery list and adding songs to a playlist.
None of those tabs are organized. They’re mixed in with everything else you’ve been browsing, making it hard to keep track of what you’re saving for later.
Now you can group related tabs in Firefox for Android, keeping them together in labeled, colored groups so you can actually find what you need when you need it.
Drag one tab onto another, or select a few and tap “Add to group.” Name it, pick a color, and you’re done.
Each group appears as a single card in the tab tray rather than a dozen separate tabs. You can open it, rename it, recolor it, or delete it whenever you want. Search still finds tabs inside a group, too.
So when you’re standing in the produce aisle looking for that rib recipe, you won’t have to scroll past dozens of unrelated tabs just to find it. Everything for your barbecue is organized together in one place, ready when you need it.
Tab grouping was the most requested feature from Firefox mobile users in 2025. And we get it: your tabs shouldn’t get harder to manage the more you use your browser. Download the latest version of Firefox for Android now to try Tab Groups, with iOS support on the way.
The post Your Android tabs just got a lot more organized with Firefox appeared first on The Mozilla Blog.
Firefox Tooling Announcements
The latest version of the Firefox Profiler is now live! Check out the full changelog below to see what’s changed:
Highlights:
profiler-cli thread network (#6172)profile meta command to profiler-cli (#6177)startTime and endTime columns to be Float64Array (#6169)Other Changes:
build-profiler-cli script to build-cli (#6191)Big thanks to our amazing localizers for making this release possible:
Find out more about the Firefox Profiler on profiler.firefox.com! If you have any questions, join the discussion on our Matrix channel!
1 post - 1 participant
Martin Thompson
Today, the IETF held the CURRENT BoF, where the goal was to develop a new protocol. That protocol would be substantially like TLS, reusing its record layer and basic structure, but it would drop in MLS for key exchange.
This is somewhere between a pretty bad idea and a horrible idea.
The wholesale replacement of a huge chunk of protocol architectures is a hallmark of a lot of the AI-generated protocol proposals that have flooded the IETF. A small blemish is identified, then the fix is a whole new protocol, or a major piece of surgery. No regard for the wisdom of Chesterton’s Fence or the accumulated knowledge and usefulness embodied in what exists.
Experienced engineers know that rewriting a code module is not something you do lightly. There’s lots of literature out there about why this is a bad idea generally, and some emerging discussion about how AI might just change that.
The reasons not to rewrite a software component still largely apply to a protocol component. The reasons that AI might make it easier to do that safely, less so. Protocols are different.
Just like with a code change, a protocol component that changes will miss use cases that people really care about.
The usual concerns with code apply:
Unlike code changes, you probably don’t have a test case for existing features that you didn’t know about. We found that with HTTP/2, where a number of use cases got lost in the process of “upgrading” HTTP.
In HTTP/1.1, performing client authentication in the middle of request was possible. Losing that capability in HTTP/2 affected few enough people that it was not badly damaging for the ecosystem. It still sucked.
A lot of work was done to try to find these issues, but we did not learn about these problems until fairly late in the process.
Proposing a protocol change means asking a whole lot of other people, many of whom are not invested in your goals, to do that work.
Changing a protocol by replacing a chunk of it, no matter how much care is taken, either asks the entire ecosystem to change with you.
That means asking everyone to move with you. If they don’t, you are not changing the protocol, you are forking it.
The real value of having a protocol like TLS is that a great many things can all talk to each other.
Forking a protocol – and sometimes profiling a protocol, a subject for another post – destroys that. You now have two ways to achieve the same goal, and a choice to join one of two clubs. You can join both, but that means constantly translating back and forth, something that can only get harder over time as protocol semantics diverge.
And yes, in case you were asking, this applies to the entirety of the IETF IoT sphere, which has parallel HTTP, TLS, and other analogues. Ostensibly, these address the needs of highly constrained hardware, but the cost is an ecosystem cut off from the mainstream.
Yes, existing protocols come with baggage or technical debt. Maybe they aren’t perfectly optimized for your use.
The value that an existing protocol carries is that you are sharing the burden of its maintenance with a great many more people. Fixing it, maybe by adding extensions to support your needs, comes with opportunities to improve the protocol even beyond that immediate need. Every change is a chance to work off some of the accumulated cruft.
Major refreshes, like the TLS 1.3 reworking, cleared out a ton of cruft in the process. You get to benefit from the work that others do to improve that protocol too.
It is hard to be a responsible steward for the fabric of the Internet. We do it because it is worthwhile. Ignoring the lessons of the past is not helpful.
Mozilla Open Policy & Advocacy Blog
This is part one of a two-part series in which we explore approaches to protecting children online while safeguarding privacy, security and the open web. Part one covers our concerns regarding age gates, and alternative policy proposals that address the root causes of online harms.
Young people today have unprecedented opportunities to learn, connect, and explore — not just the web and the world, but also themselves. With the increased ubiquity of digital technologies and devices, worries around the relationship between these technologies and young people’s well-being have grown, too. While concerns about the societal implications of new technologies is not a new phenomenon, experts argue that the accelerating speed of deployment of new technologies has outpaced scientists’ capacity to feed into policy recommendations addressing risks. A growing body of research documents the harms experienced by young people online and the challenges reported by parents attempting to mediate their kids’ technology use. At the same time, experts highlight the importance of contextual factors like existing mental health conditions, socio-economic circumstances and parental mediation to understand the real-world effects of digital technologies.
Faced with this complexity, and mounting public pressure, policymakers around the world are urgently seeking ways to improve child safety online. Driven by a sense of time running out and promises of new technical solutions to difficult questions, this has led, across jurisdictions, to proposals to restrict young people’s access to certain technologies or platforms by introducing age assurance mandates.
Privacy and user empowerment have always formed a core part of Mozilla’s mission. As we have said before, we support safer spaces for minors, but we caution against approaches that rely on identity checks, surveillance-based enforcement, or exclusionary defaults. Such interventions rely on the collection of personal and sensitive data and, thus, introduce major new privacy and security risks.
While many technologies exist to verify, estimate, or infer users’ ages, fundamental tensions around accessibility, their effectiveness and effects on user’s privacy, security and free expression remain. Technological approaches must be part of wider efforts to address the root causes of online harms. However, the deployment of age assurance technologies will not solve the complex challenge of preparing young people to navigate an increasingly online world and ensure their wellbeing. That will require more holistic approaches: offering education and support to navigate the web safely, addressing harmful business practices and acknowledging the offline factors shaping children’s lives including social inequality, poverty or disparate access to (mental) health care services.
Ineffective age-gating mandates and the dangerous shift toward VPN restrictions
As jurisdictions around the world gain experience with government-mandated age gates for certain services, evidence is mounting that age restrictions are not an effective policy tool. Avoiding age gates is widespread and trivially easy: In Australia, where minors under 16 year of age have been banned from certain social media platforms since December 2025, the government’s Compliance Update reports that seven out of ten young Australians remain online, often skirting age checks by simply entering a fake birthdate. A recent study on the implementation of the UK’s Online Safety Act found that a third of children have bypassed age gates with fairly trivial steps like faking their birthdate, borrowing someone else’s login credentials, or even drawing on facial hair, and that a quarter of parents have helped their children to bypass age assurance systems. In the US, studies indicate that as far back as 2011, 64% of parents who were aware their child under 13 had a social media account were also ones who helped them create that account.
Confronted with the apparent ineffectiveness of age gates, policymakers around the world seem to be shifting their attention to alleged circumvention tools. While research shows that many young people bypass age barriers by using other people’s devices and accounts or tricking age estimation tools by making themselves look older, virtual private networks (VPNs) are increasingly framed as primarily a “loophole” to age gates. VPNs create encrypted “tunnels” between a user’s device and the internet, protecting all internet traffic from that device and concealing users’ IP addresses. VPNs are an essential privacy and security resource for millions of users worldwide, including young people.
Utah’s recent age verification law holds websites hosting age-restricted content liable for verifying the age of anyone physically located in Utah, including individuals using VPNs or proxies. While the law does not ban VPNs outright, it forces websites to either block known VPN IP addresses or verify the age of every visitor globally. In the UK, policymakers debated age gates for VPNs extensively, but stopped short of restricting VPNs after new evidence confirmed that VPNs are not a relevant pathway for children seeking to bypass age checks. In Brazil, the ECA Digital law empowers the regulatory authority to order technical countermeasures against circumvention tools such as VPNs. These developments suggest a worrying trend: well-meaning but ineffective attempts to protect children risk undermining the fundamental rights to privacy, security, and free expression of all users, as well as the health and openness of the web itself.
We are convinced, however, that there are rights-respecting alternatives policymakers can pursue to empower young people online and improve their safety and well-being.
Moving beyond access bans
We strongly believe that online safety frameworks should be grounded in children’s rights, striking a balance between their right to protection and their right to participate in society, express themselves freely, and access media and information. Such frameworks must also be proportionate and should not undermine the fundamental rights and access to tools like VPNs for all users.
Rather than focusing on limiting access, we believe that policymakers should prioritize interventions that tackle the root causes of online harm. Before considering new instruments, this work starts with ensuring that independent regulatory authorities have the necessary resources to enforce existing online safety frameworks. In Europe, preliminary findings against Meta and TikTok find these companies’ addictive design features to be in breach of the Digital Services Act, underlining the potential of frameworks like the DSA to address key concerns.
The design of online interfaces, and the affordances and constraints they offer, significantly influences users’ interactions, decisions and overall wellbeing. ‘Dark patterns’ or deceptive interfaces are key drivers of harms experienced by users, and especially young people: they can compel people to consent to extensive data collection and processing, resulting in hyper-personalized feeds, personalized ads that may exploit cognitive vulnerabilities and promote unhealthy or excessive consumer choices, and an overall erosion of privacy.
This is why we support proposals like EU Digital Fairness Act (DFA) and the American Innovation and Choice Online Act (AICOA) that could fill regulatory gaps. Specifically, we advocate for the prohibition of harmful design, guided by harmonized definitions of core concepts like “dark patterns”, “deceptive design,” and “addictive design” and anti-circumvention clauses to prevent companies from avoiding regulation through small tweaks. Platforms should be responsible for demonstrating that their design choices are fair, non-manipulative and non-exploitative. And services that are likely to be accessed by children should be required to refrain from enabling certain design features, including excessive notifications, endless feeds and gambling-like features by default, and only with parental consent.
Further, we urge policymakers to adopt a privacy-first approach to online harms. Many of the risks encountered by young people online are related to the collection and processing of personal data. Platforms collect enormous amounts of personal data, including sensitive data, to personalize and target services, ranging from algorithmic recommender systems to online ads. While the systems that target and display ads and curate online content are distinct, both are based on the surveillance and profiling of users.
Such profiling is the basis for young people being targeted with personalized ads and content recommendations, which can segment, exclude, or steer people into inequitable options and towards harmful content. Providers should thus be prohibited from using sensitive personal data (e.g. ethnicity, religious belief, health status, sexual orientation, political affiliation) to personalize content recommendations or ads, and they should be mandated to enable privacy-protective settings by default, including restricting access to users’ location, camera, microphone, contacts, and camera roll. Policymakers should also extend the fairness and transparency obligations to personalization systems and advertising actors, including intermediaries and data brokers.
Additionally, everyone online, including families and young people, should be fully in control of their online experiences and navigate the web according to their preferences and needs. There is a significant opportunity to empower users with easy, effective opt-out rights and granular user controls. In practice, users should have the right to opt out of personalized content and targeting without being penalized with a downgraded version of the service. Some frameworks already strengthen choice – in those cases, we advocate for their robust enforcement.
Across jurisdictions, choice can be strengthened by ensuring that preferences explicitly expressed (e.g. settings selected, feedback signals, customization choices made, survey responses) are respected and “sticky”, so do not get reset without being explicitly requested by the user. Interoperability mandates should let people integrate third-party content moderation systems or recommendation algorithms that better match their preferences and help them break out of the walled gardens of a few dominant companies. Parental controls are another important lever to operationalize user controls: Providers should deploy easy-to-use and effective parental controls that allow families to tailor online experiences to their preferences, across platforms.
We appreciate that this is a long list of complex policy recommendations which are also impacted by broader (geo)political developments. The fact remains that current age assurance approaches are not a silver bullet, and will create more, rather than solve, problems in the long term.
Where policymakers consider age signals as necessary to ensure age-appropriate online experiences, we believe that there are technical approaches better suited to balance users’ rights than those currently pursued. We will explore these developments and approaches in the second part of this series.
The post Beyond technical fixes: Protecting kids online without breaking the internet appeared first on Open Policy & Advocacy.
The Rust Programming Language Blog
The Rust team has published a new point release of Rust, 1.97.1. Rust is a programming language that is empowering everyone to build reliable and efficient software.
If you have a previous version of Rust installed via rustup, getting Rust 1.97.1 is as easy as:
rustup update stable
If you don't have it already, you can get rustup from the appropriate page on our website.
Rust 1.97.1 fixes a miscompilation in an LLVM optimization.
We have backported both an LLVM fix and a disable of the underlying change in Rust 1.97.0 of Rust's generated IR that increased the likelihood of this happening. However, note that the underlying miscompilation has been present since at least Rust 1.87.
If you'd like to help us out by testing future releases, you might consider
running your code's CI or locally using the beta channel (rustup default beta) or the nightly
channel (rustup default nightly). Please
report any bugs you
might come across!
Many people came together to create Rust 1.97.1. We couldn't have done it without all of you. Thanks!
Niko Matsakis
This blog post describes an idea I’ve been kicking around called battery packs. Battery packs are a curated set of crates arranged around a common theme. For example, there’s a CLI battery pack that has everything you need to build a great CLI, an opinionated pack for creating a backend web service, and one for embedded development (based on the Embedded Working Group’s Awesome Rust repository). We’ve also got some smaller ones, such as the error-handling battery pack that shows how to handle errors in Rust. But this is just the beginning – a key part of the battery pack design is that anybody can create one.
Battery packs are meant to address one of the most common things I hear from new Rust adopters. Everyone loves the wealth of high-quality crates available on crates.io. And everyone hates having to spend a bunch of time researching and comparing alternatives. Battery packs can serve as a good set of default choices. And they don’t lock you in. At heart, they’re basically just a list of recommended crates, so you can always swap something out if you find an alternative.
We’ve got a prototype of the battery pack tool working today, so you can try it out if you’re curious. Just run cargo install cargo-bp and then try a few commands! For example,
> cargo bp list
will show you the set of available battery packs, based on a crates.io search (as I’ll explain below, a battery pack is itself packaged and distributed as a crate, but not one that you take a direct dependency on). And cargo bp add will add batteries from a battery pack into your crate, so e.g.
> cargo bp add cli
would let you select and add common CLI libraries. If you want to see a more involved demo, try out cargo bp add embedded, which is derived from the Awesome Embedded Rust repository.
One of the key ideas from battery packs is that anybody can publish one. They are just a crate named X-battery-pack; the dependencies of that crate are your recommendations. Features are designations of common sets of crates frequently used together. The examples are your templates. And so forth.
Letting anybody create a battery pack is in contrast to the previous ideas for an “extended standard library for Rust”1, and it is intended to address some of Rust’s unique challenges. For one thing, it lets people publish battery packs that are tailored to specific requirements. For example, the CLI and backend service battery packs are targeting a “typical computer”. But I could imagine the Rust embedded working group publishing a battery pack with libraries focused on no-std and binary size optimization.
Being open-ended also addresses the “who decides?” question. To my mind, the best people to recommend what libraries you ought to use are other people building systems like yours. This is why I mentioned the Embedded Working Group publishing an Embedded battery pack, for example, as I think they are clearly a set of people who know their space well. But even within the embedded space there are yet smaller groups, and I imagine that sometimes it’ll make sense to get narrower. For example, perhaps a battery pack targeted embassy and its associated ecosystem? Unclear.
If you wanted to create a battery pack, how do you do it? One answer is that you just create a new crate. But a better approach is to use the “battery-pack battery pack”2, which bundles a template:
cargo bp new battery-pack
This will prompt you for the name of the battery pack you want to create and a few other things and make your crate. Then you can just use cargo add dependencies to represent the libraries you want to recommend and publish.
The “batteries” that you can add to your project aren’t always dependencies. They can also be “recipes” or templates. For example, the CI battery pack3 can configure your project with the kind of “super neat-o” github actions you’ve always wanted but never wanted to bother configuring. To use it, select one or more of the templates to install:
cargo bp add ci
I expect this kind of “actions to improve your crate” to become a rich source of things. Right now we’re using a relatively lightweight template system built on minijinja, but I think we’re going to want to expand on this.
Battery Packs also support more than just a flat listing of dependencies/features/templates. You can group dependencies and features into categories and then, for each category, distinguish between “pick at most one” or “pick any number”. For a fun example, try cargo bp add embedded, which is derived from the Awesome Embedded Rust repository. If you run it, you’ll see something like this, which groups the choices thematically and, in some areas like “concurrency framework”, makes it clear that you want to pick one:
──────────────────────────────────────────────────────────────────
▼ Concurrency Framework (pick at most one)
> ○ ✦ embassy [embassy-executor, embassy-sync, embassy-time]
○ ✦ rtic [cortex-m, rtic] RTIC — interrupt-driven real-time
▼ Display & Graphics (pick any number)
[ ] ✦ display-ssd1306 [embedded-graphics, ssd1306] SSD1306
[ ] ✦ display-st7789 [embedded-graphics, st7789] ST7789 col
▼ Popular Drivers (pick any number)
[ ] ✦ display-ssd1306 [embedded-graphics, ssd1306] SSD1306
[ ] ✦ display-st7789 [embedded-graphics, st7789] ST7789 col
[ ] ✦ sensor-bme280 [bme280] BME280 temperature/humidity/pr
[ ] ✦ sensor-lis3dh [lis3dh] LIS3DH 3-axis accelerometer (I
[ ] ✦ usb-device [usb-device, usbd-serial] USB device stack
▼ Hardware Abstraction Layer (pick at most one)
○ ✦ atsamd [atsamd-hal, cortex-m-rt, critical-section-impl, co
○ ✦ esp32 [embedded-hal, esp-hal] ESP32 (Xtensa, WiFi + BT,
○ ✦ esp32c3 [embedded-hal, esp-hal] ESP32-C3 (RISC-V, WiFi
○ ✦ esp32s3 [embedded-hal, esp-hal] ESP32-S3 (Xtensa, WiFi
○ ✦ nrf52832 [cortex-m-rt, critical-section-impl, cortex-m, em
○ ✦ nrf52840 [cortex-m-rt, critical-section-impl, cortex-m, em
○ ✦ nrf9160 [cortex-m-rt, critical-section-impl, cortex-m, emb
○ ✦ rp2040 [cortex-m-rt, critical-section-impl, cortex-m, embe
○ ✦ stm32f0 [cortex-m-rt, critical-section-impl, cortex-m, emb
embedded-battery-pack v0.1.0 ↑↓/jk Navigate | Space Toggle | ←/→
So why am I so keen on battery packs? It’s largely because I’ve heard so many would-be or recent Rust adopters talk about picking crates as a challenge. But I feel they would help with some other problems as well.
What I really want to see is working groups in the Rust Commercial Network banding together to publish battery packs and recommendations. These would cover the dependencies that they’re actually using.
One of the reasons I want to have RCN-recognized battery packs is that they are a natural focal point to then prompt RCN members to fund the maintenance of those crates. I am imagining that for each sponsored battery pack vended within the RCN, there is an associated “ecosystem fund”. Companies or individuals could sponsor this fund to get access to early patches, security disclosures, etc or other perks. The money would be used to support the maintainers of those crates, to implement missing features, and so forth.
Another value-add from battery packs is the ability to drive interop efforts. I think that as soon as we start talking about standardizing, we’re also going to recognize that there are some places where standardization is hard. For example, early conversations within the network service working group (unsurprisingly) immediately identified that while most people are using tokio, some major companies are using their own runtimes internally. It’s not like the need for “async runtime interop” is news. But right now, every crate winds up effectively implementing their own set of little traits to make it work. Sponsored battery packs offer the possibility of a neutral home for that sort of thing.
There are some risks to people using battery packs. The most obvious is that the fact that anybody can publish a battery pack may mean that you just get a ton of battery packs, which doesn’t really help anybody! I’m not so worried about this because I think that there will be a few obvious places that most people go first, and then I think once people are oriented, they’ll get excited to explore what crates.io has to offer and start discovering more niche battery packs.
Battery packs are designed to evolve. I’ve seen it happen a number of times that there is a dominant crate for something, often taking a “traditional approach”, but then somebody else comes along and presents an interesting alternative that gradually takes off. I love that and I don’t want to put it at risk.
One example of evolution around CLI argument parsing. For a time, docopt was a popular way to parse command-line options. Then clap came along and presented a more structured alternative; that was nice, but then structopt came along and connected clap to an auto-derive, so you could just write your data structure and be done. And that was awesome. (That is now the standard in clap.) I want to be sure that, even if there is a CLI battery pack, there’s room for the next clap to come along.
There are a few things about battery pack that I think will help us deal with this. First, they are a “thin abstraction”. You don’t “depend on” a battery pack, you depend on the crates within it. So if a new version comes out that uses clap instead of docopt, that doesn’t impact you at all. Your code keeps working same as it ever did. And of course it helps that anybody can publish a battery pack. You can now have variations on battery packs that are focused around a new approach to help it get started.
Done right, I think that standardized battery packs can also help the ecosystem evolve and pivot. As it is now, knowledge of new crates has to spread by word-of-mouth. But if everybody is aligned around a new approach, adopting that new approach within a battery packs sends a clear signal that your group is aligned that something is the new hotness.
I see always bet on the ecosystem as a key Rust design axiom. It’s the reason we chose a small standard library and a package manager in the first place. It’s also why battery packs are designed to be published by anyone.
But just like plants sometimes need a trellis to grow taller, any successful ecosystem reaches a point where it needs another layer of structure to help it keep growing. Without that, you have this “layer of tacic knowledge” (in the words of a Rust Vision Doc interviewee) that becomes an obstacle for folks. And I think we’ve reached that point with crates.io.
I am hopeful that battery packs can provide that next layer of structure. But at the end of the day, if there’s a better approach, that’s fine too, so long as we find a way to help people find (and fund!) the crates they need. So let’s talk about it!
My first recollection of it was the Rust Platform idea we floated in 2016! ↩︎
Yo dawg… ↩︎
Hat tip to Jess Izen, who proposed and developed the CI battery pack. Neat idea. ↩︎
Oh, and: my apologies to Salt-N-Peppa. ↩︎
This Week In Rust
Hello and welcome to another issue of This Week in Rust! Rust is a programming language empowering everyone to build reliable and efficient software. This is a weekly summary of its progress and community. Want something mentioned? Tag us at @thisweekinrust.bsky.social on Bluesky or @ThisWeekinRust on mastodon.social, or send us a pull request. Want to get involved? We love contributions.
This Week in Rust is openly developed on GitHub and archives can be viewed at this-week-in-rust.org. If you find any errors in this week's issue, please submit a PR.
Want TWIR in your inbox? Subscribe here.
This week's crate is dashu, a pure Rust set of libraries of arbitrary precision numbers.
Thanks to JacobZ for the self-suggestion!
Please submit your suggestions and votes for next week!
An important step for RFC implementation is for people to experiment with the implementation and give feedback, especially before stabilization.
If you are a feature implementer and would like your RFC to appear in this list, add a
call-for-testing label to your RFC along with a comment providing testing instructions and/or
guidance on which aspect(s) of the feature need testing.
No calls for testing were issued this week by Rust, Cargo, Rustup or Rust language RFCs.
Let us know if you would like your feature to be tracked as a part of this list.
Always wanted to contribute to open-source projects but did not know where to start? Every week we highlight some tasks from the Rust community for you to pick and get started!
Some of these tasks may also have mentors available, visit the task page for more information.
If you are a Rust project owner and are looking for contributors, please submit tasks here or through a PR to TWiR or by reaching out on Bluesky or Mastodon!
Are you a new or experienced speaker looking for a place to share something cool? This section highlights events that are being planned and are accepting submissions to join their event as a speaker.
If you are an event organizer hoping to expand the reach of your event, please submit a link to the website through a PR to TWiR or by reaching out on Bluesky or Mastodon!
550 pull requests were merged in the last week
Symbol functionstokens fieldsWorkerLocalmir::Statement to 40 bytes(1, 1) case for arg unificationIterator::count impl for ChunkByAllocators to be used as #[global_allocator]sArc::make_mutchar_to_u32std::io::Write to core::ioString::from_utf8_lossy_ownedVecDeque::retain_back from truncate_frontinstall: Move --debug to Compilation optionssource: incorrect duplicate package warningTomlDebugInfo enum-variants doesn't renamed-L args used in the new build-dir layout-Zno-embed-metadata to -Zembed-metadata=nocargo_compile_with_invalid_code_in_depsrest_pattern_accessible_field and unnecessary_rest_patterndefinition_in_module_rootarbitrary_source_item_ordering: add configurable trait impl item ordering modestests_outside_test_module: put code in backticks in the lint messagesuboptimal_flops false negative with ambiguous float literalsunneeded_wildcard_pattern when rest_pattern_accessible_field is enabledimplicit_saturating_sub's if x != 0 { x -= 1 } rewritesingle_element_loop if the block contains only a final expressionnonstandard_macro_braces by 99.9683% (1.1b → 351K)disallowed_methods rule if the disallowed list is emptytype_mismatch#[doc = macro!()] expansionrender_const_using_debug_impl constructing outdated std layoutsTokenStream::from_str() for doc commentsResponse type closer aligned to JSON-RPCcrate_supports_no_std syntactic heuristicunused_must_use$fooBlockLoc into a tracked struct, take 3This week many new optimizations landed, making this a very good week for performance. The only real regression was a fix for a miscompile that will likely be re-landed in the future.
Triage done by @JonathanBrouwer. Revision range: 3659db0d..5503df87
Summary:
| (instructions:u) | mean | range | count |
|---|---|---|---|
| Regressions ❌ (primary) |
0.3% | [0.2%, 0.4%] | 3 |
| Regressions ❌ (secondary) |
0.9% | [0.1%, 2.5%] | 25 |
| Improvements ✅ (primary) |
-1.2% | [-9.9%, -0.2%] | 195 |
| Improvements ✅ (secondary) |
-3.4% | [-92.1%, -0.1%] | 174 |
| All ❌✅ (primary) | -1.2% | [-9.9%, 0.4%] | 198 |
2 Regressions, 10 Improvements, 10 Mixed; 7 of them in rollups 36 artifact comparisons made in total
Changes to Rust follow the Rust RFC (request for comments) process. These are the RFCs that were approved for implementation this week:
Every week, the team announces the 'final comment period' for RFCs and key PRs which are reaching a decision. Express your opinions now.
unreachable_cfg_select_predicates lint as part of unused lint groupdyn Allocatorbox_vec_non_null-Zpolonius=next on nightly-Znext-solver on nightly by default for testingrepr(Rust) enums by omitting tags in more cases involving uninhabited variants.No Items entered Final Comment Period this week for Cargo, Language Reference, Language Team, Leadership Council, Rust RFCs or Unsafe Code Guidelines.
Let us know if you would like your PRs, Tracking Issues or RFCs to be tracked as a part of this list.
Rusty Events between 2026-07-15 - 2026-08-12 🦀
If you are running a Rust event please add it to the calendar to get it mentioned here. Please remember to add a link to the event too. Email the Rust Community Team for access.
Please see the latest Who's Hiring thread on r/rust
Thank you for your PR, but please edit the description like you are a chainsaw-wielding maniac that just discovered the sentences are young adults who came to the lake at summer camp after sunset.
– workingjubilee on Rust github
Thanks to Theemathas for the suggestion!
Please submit quotes and vote for next week!
This Week in Rust is edited by:
Email list hosting is sponsored by The Rust Foundation
Thunderbird Blog
Our Community Office Hours series is all about connecting with the people behind Thunderbird and our products, and sharing the work that’s happening behind the scenes. These conversations are a chance to learn more about the direction of our projects and to hear directly from the people making it happen.
In this episode, we are talking about Thundermail!
Since we first shared our plans for Thundermail, we have seen an incredible amount of excitement, curiosity, and thoughtful feedback from the community of early bird testers. In this Community Office Hours session, members of the Thunderbird team sit down to talk about the project’s vision, share where things stand today, and answer some of the questions we’ve been getting along the way.
In this conversation, you’ll hear about:
One of the best parts of Community Office Hours is the opportunity to have open conversations with our community. Your questions, ideas, and feedback help shape Thunderbird and its products, and we are excited to keep sharing updates as Thundermail continues to evolve.
Resources for Suggesting Features:
Thunderbird on Desktop and Mobile – https://connect.mozilla.org
Thundermail and Thunderbird Pro – https://ideas.tb.pro
Resources for diving into Thundermail:
Thundermail (and other services) roadmap: https://roadmaps.thunderbird.net/en-US/services/
Subreddit: https://www.reddit.com/r/ThunderbirdPro/
Thunderbird Pro addon repo that is now shipped natively in Thunderbird: https://github.com/thunderbird/tbpro-add-on
Thunderbird Accounts repo: https://github.com/thunderbird/thunderbird-accounts
Webmail client repo: https://github.com/thunderbird/stormbox
Appointment repo: https://github.com/thunderbird/appointment
Thundermail deployment repo: https://github.com/thunderbird/thundermail-deploy
SieveConnect repo: https://github.com/philpennock/sieve-connect
Resources for Getting Help with Thunderbird:
Thunderbird for Android Support Channel (Matrix) – https://matrix.to/#/#tb-android:mozilla.org
Thunderbird Desktop Support Channel (Matrix) – https://matrix.to/#/#thunderbird:mozilla.org
The post Community Office Hours: A Thundermail Update appeared first on The Thunderbird Blog.
Firefox Tooling Announcements
The following changes have been pushed to bugzilla.mozilla.org:
Discuss these changes in the BMO Matrix Room
1 post - 1 participant
The Mozilla Blog
Two years ago Mozilla asked two leading experts on deceptive design, Dr. Harry Brignull and Cennydd Bowles, to look at how Microsoft was treating people who tried to use a different browser on Windows. Their report, Over The Edge, documented a pattern of design choices in Windows, Edge, and Bing that nudged, pressured, and at times tricked people into using Edge.
This morning, the same researchers are publishing Over The Edge 2.0. They retested the same questions across Windows 10 and Windows 11, and added two new dimensions. First, how does Microsoft’s approach change from one jurisdiction to another? They assessed Microsoft’s tactics across four regions: the USA, India, the UK, and Germany, as a representative country in the European Economic Area. Second, they examined how AI features, specifically Copilot, are being used to shape the same outcomes the original report identified.
Their conclusion is short. Microsoft still does not allow users to download, set as the default, or keep using alternative browsers without harmful interference. The patterns are documented, and they meet established definitions of coercion, deception, and manipulation.
There is one bright spot. In Europe, where the Digital Markets Act applies, some harmful design tactics are gone. The Bing ‘All you need is right here’ panel and its trick wording. The nagging Windows 10 ‘You’re almost done setting up your PC’ journey. The Copilot data toggles that default to “On” in the US and India, by contrast default to “Off” in the EEA and the UK. Same Windows. Same Edge. Same Copilot. Different design choices, because the law required them.
That is the heart of the report. Although the EEA product still has obvious harmful design patterns, Microsoft has built a fairer user experience for browser choice. It has shipped that experience to one region. Everywhere else, including the US, the UK, India, and Brazil, harmful design patterns continue. This is an active choice – and one that can be made differently.
The findings are the researchers’ own. Mozilla commissioned the work, and the report discloses that role, but the methodology, the screenshots, and the public journey database at edge.brignull.com let anyone check the evidence for themselves.
We make Firefox because we believe the web should serve people, not platforms. Firefox is independent, built by Mozilla, and designed around what is best for users, not what increases lock-in.
Read the full report here.
The post Over The Edge 2.0: what independent researchers found about browser choice on Windows appeared first on The Mozilla Blog.
The Mozilla Blog
Open source AI has crossed a threshold. For years the debate has centered around whether open models could ever compete with closed ones. This is no longer a debate we should be having. Today Mozilla is publishing its inaugural State of Open Source AI report, built on new analysis and a global survey of 950+ developers, showing that open models are no longer playing catch-up. The performance gap with top proprietary systems like ChatGPT and Claude has narrowed to just 3%, while costs have fallen up to 50x in three years.
But the report also surfaces a harder truth. Open models now power roughly a third of real-world AI usage, but capture only 4% of the revenue. So while the value is there, it just isn’t flowing back into the open ecosystem that made it possible. Meanwhile, the geopolitical picture is shifting fast.
China and East Asia now lead the world in open source AI adoption, at 89%, far ahead of the West, treating open source as a cornerstone of national strategy. Governments elsewhere are responding, with 12 new national AI strategies launched last year, and 47 countries now restrict foreign processing for critical workloads. The EU, Canada, and India are backing open ecosystems with real public investment, treating AI infrastructure as a strategic asset.
For developers, the appetite for open source is there, and cost and privacy rank as developers’ top reasons for choosing models. But wanting to use open models and running them in production are different things. 79% of developers use open models, yet only 51% have deployed them in production, versus 63% for closed models.
As Álvaro Ruiz Cubero of SlashData, which fielded the survey for Mozilla, put it: “this gap indicates that there is not an issue purely of model quality, but of missing infrastructure. Deployment rates for open models barely increase with company size, highlighting a lack of mature tooling and support. At the same time, buyers are prioritising licensing terms (31%) and ownership (26%), signalling a clear shift toward control and flexibility over raw capability.”
Perhaps the most important finding may be the least obvious. The layer that matters most isn’t the model. It’s the agentic harness: the software between people and models that decides what an AI system can see, remember, and do. Changing the surrounding software can affect performance more than switching the model itself. Whoever controls that layer controls how AI behaves in the world.
Right now, that layer is being built with very few guardrails. Users approve AI agent requests by default up to 93% of the time, with so-called “consent fatigue” going largely unnoticed.
“Open source AI has reached a turning point,” said Raffi Krikorian, Mozilla’s Chief Technology Officer. “It’s no longer about expanding access to models; it’s about who has the power to shape, audit, and improve them. Without investment in the infrastructure, tooling, and governance around open models, we risk locking in a system where only restrictive, closed AI can scale – and that doesn’t serve the public interest, or sovereignty over tech policy decisions.”
Open models offer what a subscription never can: owning your infrastructure instead of renting someone else’s. Companies like Microsoft and Uber are already rethinking their reliance on paid, closed AI tools as the bills add up. The economics are shifting and so is the thinking.
If you’re a CTO, a policymaker, a developer or an investor, this is the report for you. Read it, share it, and help build the infrastructure to make openness usable, not just possible.
Read the full Mozilla State of Open Source AI report here.
The post Mozilla’s Inaugural ‘State of Open Source AI’ Report Is Here appeared first on The Mozilla Blog.
Mozilla GFX
HDR video is coming to Firefox for Windows users (and has been available for some time on macOS). This blog post explains how we developed the feature and gives a retrospective on the technical choices we made.
A primer on video playback for the web:
As we began designing Firefox’s HDR support, we had to lay out some assumptions and found many complications:
In general, one of the biggest challenges in working on graphics code in a web browser is a lack of documentation for how to best use features like video playback and desktop compositing in the context of a web browser (e.g. multiple processes, sandboxing, shared memory, sharing external textures, etc). This parallels the rarity of graphics engineers with such experience. Building new features in this space requires a lot of research (and a lot of trial and error). The solution you end up with may not look at all like the one you initially imagined.
On behalf of the graphics team at Mozilla, I want to thank the people who use Firefox Nightly regularly and file bug reports when things aren’t working the way they want. Comments on Experimental High Dynamic Range video playback on Windows in Firefox Nightly 148, Mozilla Connect, and Bugzilla bug reports have guided us to focus on the use-cases that matter to people using Firefox. When we succeed, it’s a great feeling.
We’re working on extending HDR support to photos, apps/games and general web content.
The Rust Programming Language Blog
Another six months have passed since our last development update, and the crates.io team has been busy. Here's a summary of the most notable changes and improvements made to crates.io since then.
Crate pages now have a "Code" tab that lets you browse the contents of published crate versions directly on crates.io. This shows you the exact files that cargo downloads when you add a crate as a dependency, which might differ from the linked repository. This makes it much easier to audit your dependencies, including files that never appear in the repository, like the normalized Cargo.toml files that cargo generates.
The viewer comes with a file tree sidebar with search functionality, syntax highlighting, and GitHub-style line selection, where clicking or dragging line numbers produces shareable #L10-L20 URLs.
Under the hood, the server now builds a zip file for every published version. Since the .crate files that cargo consumes are gzipped tarballs without random access support, a background job re-packs each of them into a seekable zip archive plus a JSON manifest describing the contained files. Both are served from our static CDN. The frontend then fetches only the manifest and loads each file on demand with an HTTP range request. Because of this architecture, browsing crate sources essentially adds no load on the crates.io API servers. Existing crate versions have been backfilled, so this works for old releases too.
The rendering library behind the code viewer is a diff renderer at heart, and that's no accident: a version-to-version diff viewer built on the same infrastructure is currently in the works. This will allow you to review exactly what changed between two published versions, right on crates.io. Stay tuned!
At the end of May, the crates.io team accepted RFC #3946. Crates.io accounts always have been tightly coupled to GitHub: signing in means "Log in with GitHub", and your crates.io identity is your GitHub username. The RFC changes that. It introduces usernames that are native to crates.io and independent of linked GitHub accounts, as a prerequisite for eventually supporting login via other identity providers.
The implementation of crates.io usernames has started, but there is still a lot left to do, most visibly the ability to change your crates.io username. After that is complete, there will be future RFCs and implementation for signing in with identity providers other than GitHub. Since all of this touches authentication and account security, we are deliberately taking it slow and rolling these changes out in small, carefully reviewed steps.
In our January update we introduced the "Security" tab, which shows security advisories from the RustSec database. We have since taken this integration one step further: crates that RustSec has flagged as unmaintained now show a warning banner directly on their crate pages, linking to the corresponding advisory for details and possible alternatives. Thanks to Dirkjan Ochtman for implementing this feature!
Related to this, some popular crates have been largely absorbed into the Rust standard library over the years, like lazy_static, which has been superseded by std::sync::LazyLock since Rust 1.80. Crate pages of such crates now show a friendly "You might not need this dependency" banner describing the standard library replacement, and superseded crates in dependency lists get a small light bulb icon with a similar hint.
The dataset behind this feature lives in the new rust-lang/std-replacement-data repository, together with a documented inclusion policy: standard library replacements only, every entry must cite the stable std, core, or alloc API and Rust version, and crate maintainers get a notice-and-comment window before an entry is added. New entries can be proposed upstream and can benefit other tools too.
The most delightful change of this cycle: the Ferris on our error pages now follows your mouse cursor with its eyes:
Getting a 404 error on crates.io is now slightly less sad.
In our January update, we announced that we were experimenting with porting the crates.io frontend from Ember.js to Svelte. This experiment has concluded successfully: the new frontend reached feature parity, went through a public testing phase in April, became the default at the beginning of May, and the Ember.js app has been removed from our repository.
We designed this change to be invisible for our users, since the new frontend is a 1:1 port of the previous design and functionality. For the team and our contributors, however, it is a big deal: the frontend is now built on a more modern framework, which should make it easier for new contributors to get started. It also allows us to iterate faster, as the source code viewer above demonstrates.
We want to thank the Ember.js team for a framework that served crates.io well for many years, and the Svelte team for making the transition so enjoyable.
These were some of the more visible changes to crates.io over the past six months, but a lot has happened "under the hood" as well:
Search performance: Relevance-sorted search queries previously ranked every crate matching the query, which could take 1-2 seconds for short or common search terms. Ranking is now bounded to the 1,000 matching crates with the highest recent download counts.
Reverse dependencies performance: The reverse dependencies endpoint no longer recomputes the full dependent set on every request. It is now served from a precomputed table kept in sync by database triggers, turning an expensive join into a bounded index scan and greatly reducing the chance of getting a timeout error.
New ARCHITECTURE.md: If you've ever wondered how crates.io actually works, our ARCHITECTURE.md document got a complete rewrite. It is now organized around the high-level systems that make up crates.io and how they fit together, and includes walkthroughs of what happens when you run cargo publish, why a typical crate download never touches our API servers, and how download counts are derived from CDN access logs.
Definition lists: READMEs now render Markdown definition lists, a widely used Markdown extension. Our markdown renderer comrak already supported them, the extension just wasn't enabled yet. Thanks to @mistaste for this contribution!
CDN cache tags: Files uploaded to our static CDN now carry cache-tag metadata, allowing us to invalidate all cached files of a crate or a specific release in a single operation, instead of issuing one invalidation per file URL.
Caching improvements: We removed a global Vary: Cookie response header that was preventing our CDNs from caching public API responses and frontend assets effectively. Per-user responses now use Cache-Control: no-store instead, resulting in better cache hit rates at the CDN edge.
Accessibility: We have made crates.io friendlier to screen readers: decorative icons are now hidden from the accessibility tree, heading hierarchies have been fixed, and lists are marked up as proper lists. ARIA snapshot tests now ensure that regressions can't slip in unnoticed. We plan to continue to improve crates.io accessibility over the coming months.
Git index performance: The background worker's local clone of the git index is now a bare and shallow repository, eliminating roughly 250,000 checked-out files and the full commit history from its disk, improving its performance as we see increased rates of crate publication. The periodic index squashing now goes through the GitHub API instead of generating large git packs locally, which had previously caused out-of-memory failures on the production worker.
We hope you enjoyed this update on the development of crates.io. If you have any feedback or questions, please let us know on Zulip or GitHub. We are always happy to hear from you and are looking forward to your feedback!
Firefox Application Security Team
Welcome to the Q2 2026 edition of the Firefox Security & Privacy Newsletter.
Security and privacy are core principles of Mozilla’s Manifesto and remain at the heart of Firefox’s development. In this edition, we highlight some of the key security and privacy initiatives from Q2 2026, grouped into the following areas:
Note: Some of the bugs linked below might not be accessible to the general public and restricted to specific work groups. We de-restrict fixed security bugs after a grace-period, until the majority of our user population have received Firefox updates. If a link does not work for you, please accept this as a precaution for the safety of all Firefox users.
Private Access Control Tokens (PACT): PACT is a cross-industry initiative designed to tackle one of the web’s most urgent challenges: enabling websites to reliably distinguish legitimate users and authorized automated agents from abusive traffic without compromising user privacy. To introduce the initiative, we published a technical deep dive on Mozilla Hacks alongside a companion Mozilla blog post that explains the vision, motivation, and privacy-preserving design behind PACT.
Qualified Website Authentication Certificates (QWACs): Firefox is prepared to meet upcoming eIDAS requirements under the EU Digital Identity Framework. Qualified Website Authentication Certificates (QWACs), as required by the framework, are supported in Firefox 153 (Bug 2043399) onwards.
Hardening Firefox with Claude Mythos: In a blogpost we shared how our AI-assisted security testing pipeline, powered by Claude Mythos, uncovered and helped remediate hundreds of previously hidden vulnerabilities in Firefox, significantly strengthening the browser’s security while demonstrating the transformative potential of AI to enhance defensive cybersecurity.
Visual Indications for Geolocation Access: In light of some web pages using geolocation for activities that are not related to their maps functionality, Firefox now displays a real-time visual indicator whenever a web page is accessing the user’s geolocation. Starting with Firefox 153, the address bar now provides a real-time visual indicator the moment a website begins accessing a user’s location, providing users with immediate awareness and greater transparency into when and how their geolocation data is being used.
Improving Website Compatibility in Private Browsing: Starting with Firefox 152, Private Browsing Mode now offers users the option to temporarily lower tracking protections for the current tab when stricter tracker blocking could be causing a website to malfunction. Previously, this may have resulted in users turning off privacy protections completely to continue using visited web page. With our new feature, users can quickly restore site functionality of the current tab, preserving users’ overall privacy settings.
Instant fresh start through new Fire Button: Firefox 151 introduced the new Fire Button for Private Browsing, giving users an instant fresh start with a single click. Instead of closing and reopening a Private Window, users can immediately clear all browsing data and continue browsing in a clean session, making Private Browsing faster, more convenient, and just as private.
Advanced Anti-Fingerprinting Protections: Firefox 151 expands our default anti-fingerprinting defenses by ensuring the Available Screen Resolution, Touch Points, and Canvas APIs will provide uniform results for all of our users while also maintaining performance and compatibility. On macOS, for example, these enhancements are expected to reduce the share of users identified as unique by more than 20%, making it significantly harder for websites to uniquely identify and track users using obscure fingerprinting.
Local Network Access Protections: Firefox now requires user permission before websites can access apps and services on a user’s local network or device, helping prevent unauthorized access and sneaky tracking attempts. The LNA feature is rolling out gradually, starting with Firefox Desktop 151 through 153. Android support will follow in upcoming releases.
Firefox CA Root Program: We published Root Store Policy v3.1, introducing stricter transparency, documentation, and audit requirements for public CAs to strengthen trust in the Web PKI.
WebAuthn Related Origin Requests: This feature allows seamless passkey sign-ins across related domains e.g., the same provider using multiple top-level domains. In contrast to other browsers, Firefox UI provides transparency and choice so users are aware and can control when websites request for passkeys from other, related sites.
Hosting Events: We organized and hosted multiple web tech meet-ups in the Mozilla Berlin office, bringing together the developer community to explore the latest advances in web technology, privacy, and security. If you’re in the area, we’d love to have you join us at a future event.
Community Shares: Firefox tracking protection was presented at the SnooSec conference held in the Reddit NYC office. We also had a presentation about existing and upcoming protections against web tracking at the Chemnitz Linux Days conference, and a talk about the latest browser-based XSS protections at OWASP AppSec ‘26 in Vienna.
Web Application Integrity, Consistency and Transparency (WAICT): We are working on WAICT, a new proposal to bring stronger integrity and transparency guarantees to web applications, helping make the web a more trustworthy platform for security-sensitive applications such as end-to-end encrypted messaging. We shared our technical vision in a Mozilla Hacks blog post, including a prototype implementation in Firefox Nightly that works with our WAICT Demo and a draft specification.
Sanitizer API: We are advancing the Sanitizer API to make robust protection against cross-site scripting (XSS) vulnerabilities more accessible. By exploring an implicit sanitizer policy that integrates with Trusted Types, we aim to prevent an entire class of XSS attacks with no application code changes, making secure-by-default web applications easier to build and deploy.
Firefox users will receive these security and privacy improvements automatically. If you’re not already a user, we recommend you give it a try. Firefox helps you shape a more personal internet that puts you back in control - all while supporting the non-profit Mozilla in its mission to keep the web open, safe, and accessible for everyone.
Thank you to everyone who contributes to making Firefox and the web more secure and privacy-focused. You can have an impact too, just by reporting bugs, conducting research, contributing code, or providing feedback.
We look forward to sharing more updates in the Q3 2026 edition.
— The Firefox Security & Privacy Teams
Firefox Tooling Announcements
Welcome to the Q2 edition of the Engineering Effectiveness Newsletter! The Engineering Effectiveness org makes it easy to develop, test and release Mozilla software at scale. See below for some highlights, then read on for more detailed info!
Improved mach startup overhead by 30-50%, as well as a 75% improvement for mach test on Windows and 10s faster configure for subsequent runs
Moved to weekly scheduled dot releases and faster rollouts, allowing us to deliver fixes and uplifts to users faster and more reliably
Created a huge number of dashboards to help developers dig into Mochitest and XPCShell tests
Stood up MacOS worker pools that can run multiple tasks at once using VMs, greatly improving our Mac capacity issues
Can now navigate to about:pdf in Nightly to open and edit arbitrary PDF files, including the ability to set Firefox as your default PDF editor on MacOS
Suhaib Mujahid deployed the initial version of Hackbot, a platform for building and running AI agents to automate parts of the Firefox development workflow.
Evgeny Pavlov ported the “Build Repair Agent” to Hackbot and deployed it for testing. It now monitors Firefox build failures and triggers the agent. When an analysis and a proposed patch are ready developers can be notified by email.
David Lawrence added a new GitHubPullRequests extension that renders a live status panel in the bug modal for any attachment whose content type is text/x-github-pull-request. A new REST endpoint fetches PR metadata (state, author, labels, latest review per reviewer) from the GitHub REST API on demand, and a client-side script populates a table with a “show closed/merged” toggle.[image]
Xavier L’Hour improved the user experience for developers, adding shortcuts to buglist.cgi for all, open, or closed bugs (1764713)
Xavier L’Hour added a new shortcut button to the bug page that allows users to quickly move spam bugs to the Invalid Bugs product (1684509).
Alex Hochheiden has been moving build system logic out of make to pave the way for a new build system backend (coming soon). See Bug 2038789.
Alex Hochheiden landed a 30%-50% (platform dependent) speedup for mach startup. See Bug 1775197.
Alex Hochheiden sped up subsequent configure runs by ~10s by adding caching to the mach taskgraph toolchain step. See Bug 2017746.
Alex Hochheiden reduced mach test startup overhead on Windows by 75%. See Bug 2018327.
Alex Hochheiden has achieved significant code deduplication and simplification by consolidating the Android Gradle configuration into convention plugins. There were also various Gradle configure-cache improvements. See Bug 2007013, Bug 1950099, Bug 2013417, Bug 2017752, and Bug 2017753.
Julien Cristau added support for interactive tasks (aka one click loaners) on Windows and macOS
Andrew Halberstadt implemented mach try support with Github, being used in mozilla/enterprise-firefox-try and coming to Firefox soon.
Andrew Halberstadt implemented the machinery to start making Gecko CI tasks clone from Github.
Ryan Curran brought Firefox CI’s Apple Silicon VM infrastructure into production. Building on the MacOS CI image pipeline established last year, he migrated test suites onto virtual machines and grew the macosx1500-aarch64-vms pool so Taskcluster now routes eligible jobs to VMs alongside physical hardware. This reduces reliance on physical Macs, increases CI capacity, and supports the ongoing migration off of older Intel-based macOS infrastructure
Jonathan Moss migrated Firefox CI’s cloud-based Windows testing from Windows 11 24H2 to 25H2, moving the bulk of Firefox’s Windows test coverage to Microsoft’s latest platform and keeping CI aligned with the Windows version most commonly used by Firefox Desktop users
Florian Quèze created many dashboards to help dig into Mochitests and XPCShell tests
Ryan VanderMeulen landed a set of improvements to mach try chooser. The update adds an exclude filter, a clearer preview pane with removable job rows, an artifact-builds toggle, and a warning when a selection exceeds task-prioritization thresholds. It also fixes a bug where choosing Firefox for Android jobs would unintentionally clear selections for other platforms.
Valentin Rigal and Bastien Abadie created a Code Review Bot prototype for publication of review comments using various source linters on Github
Morgan Rae Reschenberg added support for accessibility review to Code Review Bot
Calixte added about:pdf to use an entrypoint for opening and editing arbitrary PDF files[image]
Calixte added support for playing videos/sounds embedded in PDF files
Connor Sheehan improved the uplift experience by leveraging Lando to manage the assessment forms, train selection, and automatic application, so conflicts are detected earlier. The number of uplifts via Lando has out-paced those via Moz-Phab, and sailed through the rise in uplift numbers (likely due to more sec-bugs getting fixed and uplifted).
Zeid added support for private GitHub repositories in Lando, allowing security patches to be implemented in a private clone of a repo, and pushed to the public one.
Olivier Mehani finalized support for using the new Lando instance for try-pushes. This brings a host of QoL improvements which weren’t backported to the old instance: better UTF-8 support, smarter conflict resolution and improved security and authentication. It is now processing about 1500 pushes / week (old Lando still processes about 50 / week).
Magnolia Liu implemented automatic pushes to Try for uplift requests, for faster feedback in case of issues.
Olivier Mehani added a view of a user’s current and recent jobs on the landing page of Lando when authenticated.
Zeid identified and fixed the causes of some stability and reliability issues in Lando, which were causing increased downtime during deployments and on an ongoing basis.
Olivier Mehani deployed a PoC of reviewer selection on the GitHub pilot, allowing Herald-like mechanisms to GitHub PRs.
Olivier Mehani and Connor Sheehan (with Corey Bryant and Daniel Darnell) migrated the COMM project to GitHub https://github.com/thunderbird/thunderbird-desktop, sharing Firefox’s syncing model.
Donal Meehan drove the Release Management team’s move to a weekly scheduled dot release cadence for Desktop and Android, starting with Firefox 151. This allows us to deliver fixes and approved uplifts to users faster and more predictably. This change is expected to reduce unplanned releases, improve release flexibility, and create a more consistent release rhythm across teams.
Dianna Smith drove the update to the Release Management team’s Desktop major release rollout process, starting with Firefox 152. Instead of throttling to 0% on day 2, it will remain at 25% rollout for two days before moving to 100%, unless any issues arise. This should help us collect uptake and stability signals earlier while still allowing time to catch problems before full rollout.
Pascal Chevrel completed the update to the dictionaries shipped with Firefox Desktop. The update added eleven new dictionaries, covering Croatian, English (UK), Georgian, Persian, Slovenian, Tajik, Tamil, Tibetan, Turkish, Welsh, and Xhosa, and refreshed nine others. This expanded the number of locales with a built-in spellchecker from 30 to 41 beginning in Firefox 152. Special thanks to Francesco Lodolo, Bryan Olsson, and the localization community for reviewing the patches and helping assess the quality of the dictionaries.
Pascal Chevrel delivered a range of improvements to WhatTrainIsItNow, including expanded it to cover weekly dot releases and ESR planned dot releases, added new uplift views including a dot-release uplifts page and a beta uplift graph, and published new APIs that surface train-selection and uplift guidance inside Lando. He also made performance improvements and a steady stream of fixes across the site.
At Pwn2Own 2026, Firefox came through with no successful exploits, thanks to preparation across many teams and individuals. Within Release Management, Ryan VanderMeulen drove pre-event patch readiness and Dianna Smith coordinated the releases during the event, including the 150.0.3 dot release, which mitigated the root cause behind several of the contest entries.
Dianna Smith built out release-health monitoring and alerting in Bigeye, giving Release Management a growing set of automated alerts that surface data anomalies earlier to aid in release health and regression detection. To make the capability easy to extend, she also created a guide for other teams to add monitoring and alerts for the areas they know best. Teams that want an earlier signal on their own metrics are encouraged to use the guide and help grow the coverage.
Ryan Curran built Hangar, a live dashboard for monitoring Firefox CI’s worker pools. It consolidates fleet data from several systems into one view, giving Release Operations a single place to check fleet health and catch problems such as missing or quarantined workers early.
Ryan Curran created the iOS version of BuildWatch, and Andrew Erickson ported it to Android. BuildWatch lets you monitor Firefox CI try pushes from your phone, including live per-platform build status, failure summaries, and one-tap retriggers. It uses only public APIs, so no VPN is required.
Andrew Erickson and Mark Cornmesser developed Fleetbench, a tool for benchmarking Firefox CI workers. It currently measures CPU and ADB/USB I/O performance, helping Release Operations identify slow or outlier hosts before they skew performance test results such as Speedometer and trigger noisy or false regressions.
Andrew Erickson built Pool Classifier, a web app for viewing per-worker success rates across Taskcluster worker pools. It classifies newly completed tasks every 15 minutes, giving Release Operations a continuously updated view of worker health and helping surface problematic workers proactively.
Andrew Erickson created Fleetroll, a command-line tool Release Operations uses to manage and monitor long-running Linux, macOS, and Windows hardware hosts in Firefox CI Taskcluster. It deploys Puppet branch overrides and Vault secrets, audits what is actually applied, and surfaces each host’s Puppet and Taskcluster state in a live dashboard.
Mark Cornmesser built out a set of new worker-metrics dashboards in Yardstick, giving Release Operations clearer real-time visibility into the health of the Firefox CI hardware fleet. These include Linux worker status, Windows worker CPU and disk metrics, and a Windows job pickup and wait timeline, with alerting on key thresholds. The full set lives in the FXCI Hardware Workers folder in Yardstick.
Jonathan Moss expanded cloud cost reporting in Looker, adding Azure support alongside the existing GCP data and a cloud-provider filter on the FXCI task overview dashboard. The team can now break down Firefox CI compute costs by cloud provider, making it easier to track and compare spend across Azure and GCP.
Yaraslau Kurmyza added Azure fast deprovision taskcluster#8790 and concurrency taskcluster#8815 to improve worker scanner performance. This shows ~2x-4x scan time improvements already.
Contributor nitishagar and Yaraslau Kurmyza added patches taskcluster#8514, taskcluster#8784 to support compression in Taskcluster services API and Yarik worked with Fastly to resolve broken brotli support on the WAF edge side. Now services transmit significantly less data.
Yarik added a dedicated service account to log with read only permissions webservices-infra#11197. This allows tc-logview to be used safely by untrusted agents inside containers with narrow short-lived access tokens.
Yarik published queue forecasting dashboard experiments that continuously collects task events and trains models to enable and improve predictions on a task level (how long will it run, and when will it start). With future plans including extending it to the whole task group (mach try)
Florian Quèze added treeherder#9540 “Show task group profile” item to the push action menu
Cameron Dawson, juungo and moijes12 implemented various Treeherder API performance improvements
Heitor Neiva added Git branch labels to pushes in Treeherder
Andrew Halberstadt implemented the ability for Treeherder to display multiple Git branches at once, enabling support for “try like” repositories in Github
Upgrade hg.mozilla.org to Mercurial 7.2.2
Created the mozilla-esr153 and comm-esr153 repositories.
Thanks for reading and see you next quarter!
1 post - 1 participant
Firefox Tooling Announcements
The latest version of the Firefox Profiler is now live! Check out the full changelog below to see what’s changed:
Highlights:
Other Changes:
Big thanks to our amazing localizers for making this release possible:
Find out more about the Firefox Profiler on profiler.firefox.com! If you have any questions, join the discussion on our Matrix channel!
1 post - 1 participant
The Rust Programming Language Blog
The Rust team is happy to announce a new version of Rust, 1.97.0. Rust is a programming language empowering everyone to build reliable and efficient software.
If you have a previous version of Rust installed via rustup, you can get 1.97.0 with:
$ rustup update stable
If you don't have it already, you can get rustup from the appropriate page on our website, and check out the detailed release notes for 1.97.0.
If you'd like to help us out by testing future releases, you might consider updating locally to use the beta channel (rustup default beta) or the nightly channel (rustup default nightly). Please report any bugs you might come across!
When Rust is compiled into object files and binaries, each item (functions, statics, etc) must have a globally unique "symbol" identifying it. To avoid conflicts when linking together different Rust programs, Rust mangles the original name of items to include additional context such as the module path, defining crate, generics, and more. Historically, this mangling was based on the Itanium ABI, also (sometimes) used by C++.
The new mangling scheme resolves a number of drawbacks from the previous one:
Since Rust 1.59, the compiler has supported opting into a Rust-specific
mangling scheme via -Csymbol-mangling-version=v0. Since November 2025, this
scheme has been enabled by default on nightly, and 1.97 is now enabling it on
stable Rust. The legacy mangling scheme can only be enabled on nightly, and the
current plan is to fully remove it.
See the previous blog post for more details.
It's common practice to deny warnings in CI. Historically, doing so is
typically done through RUSTFLAGS=-Dwarnings. With Rust 1.97, Cargo controls
how warnings interact with build success: either silencing them (via allow
level), rendering without failing (default, warn), or denying them (via deny).
As a result of Cargo configuration determining the behavior, using this
feature doesn't invalidate the underlying build cache, meaning that it's easy
to temporarily opt-in. For example, if warnings are adding unwanted noise while
working through fixing errors after a refactor, you can run
CARGO_BUILD_WARNINGS=allow cargo check, temporarily silencing them.
In CI, jobs can instead set CARGO_BUILD_WARNINGS=deny to deny warnings. This
can be combined with --keep-going to collect all errors and warnings rather
than stopping on the first failing package.
See the documentation for more details.
rustc invokes a linker on behalf of users. Historically, rustc has silenced linker output by default if the link completes successfully. This can mask real problems, though, so in Rust 1.97 we are enabling linker messages by default. These are emitted as a warning lint, for example:
warning: linker stderr: ignoring deprecated linker optimization setting '1'
|
= note: `#[warn(linker_messages)]` on by default
Common linker messages that have been diagnosed as false positives or intentional behavior are filtered out by rustc. Several defects have already been fixed as a result of no longer hiding this output on nightly.
Note that currently, linker_messages is a special lint that is not affected
by the warnings lint group. This is intentional as rustc generally doesn't
control linker output as precisely, and it's not uncommon for output to only
appear on some platforms. If you are seeing what you think is a false positive
output from the linker, please file an issue.
To silence the warning in the mean time, you can configure the lint level to
allow. This can be done through Cargo.toml by adding a lints section like this:
[lints.rust]
linker_messages = "allow"Default for RepeatNCopy for ffi::FromBytesUntilNulErrorSend for std::fs::File on UEFI<{integer}>::isolate_highest_one<{integer}>::isolate_lowest_one<{integer}>::highest_one<{integer}>::lowest_one<{uN}>::bit_widthNonZero<{integer}>::isolate_highest_oneNonZero<{integer}>::isolate_lowest_oneNonZero<{integer}>::highest_oneNonZero<{integer}>::lowest_oneNonZero<{uN}>::bit_widthThese previously stable APIs are now stable in const contexts:
Check out everything that changed in Rust, Cargo, and Clippy.
Many people came together to create Rust 1.97.0. We couldn't have done it without all of you. Thanks!
The Mozilla Blog
We don’t put our name on much. So when we do, it means something.
Starting with the 2026/27 season, Firefox is Wrexham AFC’s Official Web Browser Partner and front-of-kit sponsor on the men’s and women’s teams.
More than 160 years old and nearly lost for good, the football club was saved by the community that loved it and has been climbing ever since. Wrexham AFC built something real by doing things their own way and staying close to the community that carries them.
That’s a story we recognize. Firefox has spent more than 20 years as the browser people choose — not the one they’re handed. Backed by a nonprofit, we’ve never had to answer to shareholders, just to the people who use us. Two challengers, never the default, both here because our communities showed up for us.
Our partnership debuts this weekend on the new away kit, when Wrexham travels to Kraków for their opening pre-season fixture. And this is just the start: Expect content, product integrations and fan moments all season long.
“Both Wrexham AFC and Firefox are challenger brands that built passionate global communities by doing things differently and staying true to who they are,” Mozilla CMO John Solomon said. “Firefox has approached the internet with that same philosophy — building it as it should be, with people at the center.”
“We are thrilled to welcome Firefox as our new front-of-kit partner,” said Rob Mac and Ryan Reynolds, co-chairmen of Wrexham AFC. “We love an underdog story and both Wrexham and Firefox know the feeling of having to battle giants. It takes inventiveness, relentlessness and a pristine browsing history. So let’s effing go…”
Football needs Wrexham. The internet needs Firefox.
For more information on our partnership and upcoming 2026/27 season, follow @wrexham_afc on social media. Click here to download the independent browser that puts you first.
About Mozilla and Firefox
Mozilla is a global nonprofit backed technology organization that builds products, invests in startups, and advances policy to keep the internet open, fair, and worthy of trust. Firefox is the independent browser from Mozilla, known for strong tracking protection, open source code, and a focus on user choice, privacy and security.
About Wrexham AFC
Wrexham Association Football Club is based in Wrexham, North Wales, and after an historic, record-breaking three consecutive promotions are competing in the EFL Championship, the second tier of the English football league pyramid. Formed in 1864, they are the oldest Club in Wales and the third oldest professional team in the world. Wrexham have won the Welsh Cup a record 23 times and beaten some of the biggest clubs in the game in the English FA Cup and UEFA European Cup Winners Cup. The Racecourse Ground, home to Wrexham AFC, is the world’s oldest international stadium that continues to host international games.
Wrexham AFC is owned by Rob Mac and Ryan Reynolds. The goal of the owners is to grow the team and establish Wrexham AFC as a Premier League club in front of increased attendances, and in an improved stadium, while making a positive difference to the wider community in Wrexham. For more information, please visit wrexhamafc.co.uk or follow @wrexham_afc.
The post Wrexham AFC and Firefox announce a multi-year, front-of-kit partnership appeared first on The Mozilla Blog.
This Week In Rust
Hello and welcome to another issue of This Week in Rust! Rust is a programming language empowering everyone to build reliable and efficient software. This is a weekly summary of its progress and community. Want something mentioned? Tag us at @thisweekinrust.bsky.social on Bluesky or @ThisWeekinRust on mastodon.social, or send us a pull request. Want to get involved? We love contributions.
This Week in Rust is openly developed on GitHub and archives can be viewed at this-week-in-rust.org. If you find any errors in this week's issue, please submit a PR.
Want TWIR in your inbox? Subscribe here.
This week's crate is apis-saltans, a Zigbee implementation including a coordinator API.
Thanks to Richard Neumann for the self-suggestion!
Please submit your suggestions and votes for next week!
An important step for RFC implementation is for people to experiment with the implementation and give feedback, especially before stabilization.
If you are a feature implementer and would like your RFC to appear in this list, add a
call-for-testing label to your RFC along with a comment providing testing instructions and/or
guidance on which aspect(s) of the feature need testing.
No calls for testing were issued this week by Rust, Cargo, Rustup or Rust language RFCs.
Let us know if you would like your feature to be tracked as a part of this list.
Always wanted to contribute to open-source projects but did not know where to start? Every week we highlight some tasks from the Rust community for you to pick and get started!
Some of these tasks may also have mentors available, visit the task page for more information.
* Protocol - Extend bit-exactness tests to f64 reconstruction targets
* Dofigen - No image tag replacement flag for the generate command
If you are a Rust project owner and are looking for contributors, please submit tasks here or through a PR to TWiR or by reaching out on Bluesky or Mastodon!
Are you a new or experienced speaker looking for a place to share something cool? This section highlights events that are being planned and are accepting submissions to join their event as a speaker.
If you are an event organizer hoping to expand the reach of your event, please submit a link to the website through a PR to TWiR or by reaching out on Bluesky or Mastodon!
598 pull requests were merged in the last week
param_env norm in new solvercore::ffi::c_void as a return typeresolve_ident_in(_local)_module_*#[my_macro] mod foo; (part of proc_macro_hygiene)std::io::cursor::WriteThroughCursorBox::as_non_null()DoubleEndedIterator::next_chunk_backIntoIterator for [&[mut]] Box<[T; N], A>ptr::{read,write}_unaligned via repr(packed)SizeHint and IoHandle to core::iostd::io::Seek to core::ioArrayChunks::try_rfold with DoubleEndedIterator::next_chunk_backfeature(atomic_from_mut)bindeps: register transitive artifact targetsManifestErrorContext--cap-lints=allow is setCompilation::deps_output only taking the last depbuild-dir layout v2chunks_exact_to_as_chunks: Don't report expressions with const parameterschunks_exact_to_as_chunks: Don't report expressions with type paramsmissing_trait_methods: MSRV/unstable awarenessvec_init_then_push: don't lint pushes from a macro expansioninline_modules: ignore cfg(test) modules in test buildsmatch_same_arms: keep arm-level expectations working under an outer allowunnecessary_operation: avoid bad ! suggestionsunnecessary_unwrap_unchecked: don't trigger inside the _unchecked fnneedless_bool suggestion is an operandunnecessary_unwrap_uncheckedinfinite_loop false positive inside gen blocksmanual_c_str_literals suggestion when the trailing backslash is escapedstrlen_on_c_strings incorrect suggestion logicsuspicious_operation_groupings duplicationsMsrv::meets callsis_in_test_function when there are no test itemsStringAdd lint passsuspicious_xor_used_as_powlower_ty in uninhabited_referencemanual_is_variant_and's map() == Some(_) rewritemut_mutredundant_else as a late passtuple_array_conversionsinlay_hints fieldfeat(ide-diagnostics): add diagnostics for invalid union patterns (E0784)internal(query-group-macro): remove the arity testreplace_match_with_if_letsmol_str borsh_non_utf8 test casesgenerate_default_from_enum_variantwalkthrough_create_project file not packagedconvert_tuple_struct_to_named_struct on nested pattern usageextract_variable when selecting unresolved macro callunwrap_return_type, remove_underscore, and promote_local_to_constThis week was dominated by wild swings in benchmarks of the new-solver, which is not enabled by default, yet. Apart from that, we got a very few notable changes, only one unexpected speedup from a bugfix in rustdoc.
Triage done by @panstromek. Revision range: 7dc2c162..3659db0d
Summary:
| (instructions:u) | mean | range | count |
|---|---|---|---|
| Regressions ❌ (primary) |
0.2% | [0.2%, 0.2%] | 3 |
| Regressions ❌ (secondary) |
162.1% | [0.2%, 1116.3%] | 20 |
| Improvements ✅ (primary) |
-1.4% | [-8.4%, -0.1%] | 7 |
| Improvements ✅ (secondary) |
-1.1% | [-8.4%, -0.1%] | 11 |
| All ❌✅ (primary) | -0.9% | [-8.4%, 0.2%] | 10 |
1 Regression, 1 Improvement, 4 Mixed; 3 of them in rollups 17 artifact comparisons made in total
Changes to Rust follow the Rust RFC (request for comments) process. These are the RFCs that were approved for implementation this week:
Every week, the team announces the 'final comment period' for RFCs and key PRs which are reaching a decision. Express your opinions now.
slice_split_onceNo Items entered Final Comment Period this week for Cargo, Language Team, Leadership Council, Rust RFCs or Unsafe Code Guidelines.
Rusty Events between 2026-07-08 - 2026-08-05 🦀
If you are running a Rust event please add it to the calendar to get it mentioned here. Please remember to add a link to the event too. Email the Rust Community Team for access.
Please see the latest Who's Hiring thread on r/rust
if a ptr is dereferenced in a forest and nobody hears it, is it sound?
Thanks to Cerber-Ursi for the suggestion!
Please submit quotes and vote for next week!
This Week in Rust is edited by:
Email list hosting is sponsored by The Rust Foundation
Thunderbird Blog
A few weeks ago, we conducted hour-long conversations with 10 of our users to dig deep into how you manage your preferences and configurations in Thunderbird desktop. While this specific research cycle focused on the desktop experience, our ultimate goal is a holistic strategy that ensures our mobile settings feel like a natural extension of your workspace.
Here is a quick look at what we discovered, what you valued, and how your feedback is actively shaping our design roadmap.
You are incredibly passionate about customization, and appreciate Thunderbird’s robust functionality. Overall, a common thread that stood out was that most of you want to set up your space once and then make small tweaks to your preferences, you want it to look modern, and navigate effortlessly without running into issues with technical jargon.
Here are the key themes that emerged from our conversations:
We don’t want to just make minor fixes, we want to design a better workflow. Based on your feedback, here are the core design actions that will be driving our next phase focusing on general and account settings:
We are hitting the ground running with these insights. Right now, our team is actively:
A massive thank you to everyone who offered their time and feedback for this study! We look forward to sharing more with you soon.
Let us know your thoughts in the comments below!
The post Desktop settings research: what we learned from your feedback appeared first on The Thunderbird Blog.
Ryan Hunt
Well, that went by quick!
I joined Mozilla as an intern in 2016. I wouldn’t believe you if you told me I’d still be here in 2026, working on their WebAssembly engine and contributing to the WebAssembly standards process.
Ten years at one company is a long time in this industry. I’m feeling a bit sentimental, so I thought I’d share how that happened, and why I’m still here.
Mozilla Localization (L10N)
Each year, Mozilla welcomes interns who work alongside our engineering teams on projects that ship to production and improve the experience for contributors around the world. This year, Ayush joined the Firefox Localization team to work on Pontoon, Mozilla’s open source localization platform, where he already tackled several user-facing improvements while learning how large-scale open source software is built.
In this post, Ayush shares the story behind one of his first projects: giving Pontoon’s translation editor its own appearance settings. From understanding long-standing design decisions to balancing accessibility with user expectations, he walks through both the technical implementation and the product thinking that shaped the feature.
You can follow Ayush’s work on GitHub and connect with him on LinkedIn.
Studying Computer Engineering with a Professional Experience Year (PEY) at the University of Toronto’s Faculty of Applied Science gave me a variety of opportunities and companies to choose spending a year interning at. I chose Software Engineering at Mozilla because it’s an open source company that puts people first, which matters to me a lot and allows me to equip my portfolio using snippets and examples from real code used in production.
I joined Mozilla’s Firefox Localization (l10n) team as part of Mozilla Corporation’s Firefox Desktop Engineering Team, based in Downtown Toronto. I officially began my internship on Friday, May 1, 2026, but I unofficially began in mid February. Since my team’s flagship product’s (Pontoon) codebase is entirely open source, I talked to both my manager and Pontoon owner right after signing my offer and got early access to our weekly meetings and some confidential data. I then started to learn as much as I possibly could.
Even before I started learning the codebase, just looking at the Pontoon’s default translation UI was rather interesting because of our editor pane’s glaring white color in dark mode/theme.
Even though I saw the issue (#4001) filed for working on that, I thought that the stark contrast was a stylistic choice because an average user would spend most of their time on said pane editing strings anyway, so I just went on with it.
However, once I officially started to work, I got my onboarding document and saw my starting set of issues. That’s where I came across the very same issue (#4001) on my todo batch, which made me very happy since I could address it and I’d already looked at the surrounding context before working with it.
At first, the user could only change Pontoon’s appearance from their `profile menu` or Pontoon’s `/settings` page. This is where they have the ability to change their appearance to `dark mode`, `light mode`, or keep the `system theme` that matches their device’s preferences.
In general, when a product has a large, established user base that has grown accustomed to a particular interface, it’s important to approach visual changes with care. Even if a redesign is arguably more visually appealing and offers clear accessibility benefits, changing familiar workflows and appearance can still disrupt the user experience.
In fact, according to this Mozilla Research article I read, which explored browser choice design interventions, “It is important that the organizations tasked with designing and regulating current and future interventions (including browser choice screens) are mindful of the design principles we have articulated with this research.”
Even though the relevance of said research is for the browser use-case, the impacts are for a user interface design like in this blog, as the article also mentions “The inertia is a strong force to overcome”, and Pontoon’s inertia dates back over a decade.
This meant that if we were to change the editor pane color, we would have to allow the user to have things as they currently are.
In the update Appearance section of the Settings page, users have the ability to change the main interface as before, but now have the ability to update editor to `dark mode`, `light mode`, or match their `main interface theme` to automatically sync the colors.
The editor theme remains light by default, regardless of the main interface theme.
This UI now matches the dark theme, either by explicitly selecting it or matching the main interface theme.
Since the issue was with `dark interface mode` having a `light editor`, setting the default `editor` to `light` neatly agreed with how the UI looked before the changes were brought in.
These changes neatly allow the user to modify their theme keeping their general preferences in mind. The change is also remembered by Pontoon and stays consistent at every instance the user logs back in.
Furthermore, we now track if the user has interacted with the `editor theme` which gives us knowledge on if we want to eventually change the default editor theme, addressing the concerns of `UI inertia` brought up in Mozilla’s research.
For more information and technical details, please visit: https://www.ayshush.us/mozilla/issue-notes/4001
About:Community
Hi Mozillians, welcome to another Mozilla community roundup!
This month, we’re taking a look at what’s next for Firefox. From an upcoming visual refresh and a peek behind the new design system to hidden features you may never have used before. We’re also highlighting a recent Reddit AMA on the new Firefox product Roadmap and celebrating community contribution that’s making collaboration in Pontoon even better.
Let’s dive in!
✨ Firefox gets a fresh new look. Soon!
Firefox is evolving with a refreshed design that makes the browser feel more modern, approachable, and consistent across desktop and mobile. The refresh also extends to Firefox’s voice and writing style, making product experience feel more human, direct, and unmistakably Firefox. If you’re excited about these changes, make sure to keep an eye out for an upcoming foxfooding opportunity later this month!
Firefox can do all this?
Sreenath from It’s FOSS rounded up 21 Firefox features that many users never discover. From the built-in Eyedropper tool and Picture-in-Picture to vertical tabs and other productivity features, there’s plenty to explore. See how many you’ve already used! We could even turn it into a fun bingo at our next community event.
From the Reddit Community
Firefox leaders recently joined r/firefox for a live AMA to answer questions about the newly launched Firefox Product Roadmap. Community members asked about everything from Android improvements and Containers to Project Nova, PWAs, performance, and future browser development. The conversation generated a wide range of discussions and provided valuable insight into what Firefox users are most excited, and concerned, about.
Community spotlight
Collaboration in Pontoon just got a little easier. Thanks to volunteer contributor Serah Nderi, users can now edit and delete their own comments, while project managers can remove comments for moderation purposes. This long-requested feature helps reduce clutter, improve discussions, and makes collaboration smoother for localization teams.
P.S.
Enjoyed these updates? Subscribe to the Mozilla Community Newsletter and get the latest updates delivered straight to your inbox.
Mozilla Open Policy & Advocacy Blog
During London Tech Week, Mozilla hosted the first UK edition of Mozilla Mornings, our breakfast-discussion series on the digital questions of the moment. We brought together technologists, policymakers, industry, civil society and researchers to ask how the UK can drive forward responsible innovation in privacy-enhancing technologies (PETs) in ways that protect people, strengthen trust and keep digital markets open.
Protecting people’s privacy has always been central to Mozilla’s mission to build a better internet – one where privacy and security are fundamental, people have meaningful control over their data and online lives, and independent actors can compete on a level playing field. Privacy-enhancing technologies (PETs) are an important part of that vision. They help minimise the amount of personal data that needs to be collected and processed while enabling useful functionality. In Firefox, this work includes technologies such as Oblivious HTTP, differential privacy, the Distributed Aggregation Protocol and DNS over HTTPS.
PETs encompass a broad family of technical, architectural and product-design approaches where data analysis, measurement, collaboration, access and computation happen with lower privacy risk.
Advancing both privacy and competition together is key to a healthier internet ecosystem. Advertising illustrates both the challenge and the opportunity. It keeps most of the web free and accessible, but today’s dominant model leans on hidden data collection and opaque systems that work around people rather than with them. Solutions that simply hand more data, more infrastructure or more decision-making power to a handful of large companies do not fix that.
Importantly, PETs should not be viewed as a way to bypass privacy rules. Their value lies in reducing the amount of personal data that needs to be collected, shared or processed in the first place, while preserving useful functionality where appropriate. That is why we have been investing in and building around privacy-preserving advertising, recognising that PETs are not a silver bullet but an important part of a better model.
Responsible deployment of PETs depends not only on the technical design, but also on the governance, assurance, and market context around it. PETs should be grounded in open standards and interoperable architectures. Otherwise, they risk reinforcing walled gardens, limiting choice or creating new dependencies rather than supporting a more open and competitive ecosystem.
The event opened with remarks from the Information Commissioner’s Office (ICO). This included the ICO’s work on PETs, online tracking, privacy-preserving attribution and the questions raised under Regulation 6 of the Privacy and Electronic Communications Regulations (PECR). Shortly before the event, the ICO had published advice to the government on possible online advertising exceptions to Regulation 6 PECR. As we set out in our submission to the ICO’s call for views on online advertising, we support reform that incentivises privacy-preserving practices while keeping consent the default for high-risk practices.
Gijs Kruitbosch, Principal Engineer at Mozilla, then gave a technical demonstration of how Mozilla uses PETs and privacy-preserving design in Firefox, including on New Tab, where relevance can be improved through approaches that reduce reliance on user identifiers and server-side user profiles.
The panel, moderated by Mozilla’s Kirsten Nelson-de Búrca, widened the lens well beyond advertising. Speakers from eyeo, OpenMined, the Open Data Institute and the Information Society Law Centre discussed how PETs are governed and used across sectors, and how their deployment could affect competition as well as privacy. The discussion explored public-interest examples, including federated rare-disease and genomic research that lets analysis happen without data leaving an institution or a country, and emerging routes for external researchers to study platform data.
A recurring theme was that successful deployment depends as much on governance and public trust as it does on mathematics. PETs have the potential to reduce the competitive advantages associated with large-scale personal data collection, but they could also entrench incumbents if the relevant infrastructure is closed, proprietary or expensive to audit. The discussion complicated the familiar trade-off between privacy and competition, arguing that it eases when PETs are built in the open, on shared standards, with interoperable and auditable implementations and real routes for smaller players and new entrants to take part.
The most important questions were the ones we left without tidy answers. Who gets to set standards, and are they set in the open? How do smaller players actually participate, rather than being told they may? What forms of assurance or audit are needed before policymakers can rely on privacy claims? And how should PETs be built into the next generation of AI, where the most sensitive data and the strongest case for protection often sit together? These are the questions we want to keep working on with those who joined us and the wider community.
The post Mozilla Mornings comes to the UK: privacy-enhancing technologies and the questions they raise appeared first on Open Policy & Advocacy.
This Week In Rust
Hello and welcome to another issue of This Week in Rust! Rust is a programming language empowering everyone to build reliable and efficient software. This is a weekly summary of its progress and community. Want something mentioned? Tag us at @thisweekinrust.bsky.social on Bluesky or @ThisWeekinRust on mastodon.social, or send us a pull request. Want to get involved? We love contributions.
This Week in Rust is openly developed on GitHub and archives can be viewed at this-week-in-rust.org. If you find any errors in this week's issue, please submit a PR.
Want TWIR in your inbox? Subscribe here.
This week's crate is deconvolution, a image deconvolution and restoration library.
Thanks to pbkx for the self-suggestion!
Please submit your suggestions and votes for next week!
An important step for RFC implementation is for people to experiment with the implementation and give feedback, especially before stabilization.
If you are a feature implementer and would like your RFC to appear in this list, add a
call-for-testing label to your RFC along with a comment providing testing instructions and/or
guidance on which aspect(s) of the feature need testing.
No calls for testing were issued this week by Rust, Cargo, Rustup or Rust language RFCs.
Let us know if you would like your feature to be tracked as a part of this list.
Always wanted to contribute to open-source projects but did not know where to start? Every week we highlight some tasks from the Rust community for you to pick and get started!
Some of these tasks may also have mentors available, visit the task page for more information.
.transform() and .transform_join() into stage profilingIf you are a Rust project owner and are looking for contributors, please submit tasks here or through a PR to TWiR or by reaching out on Bluesky or Mastodon!
Are you a new or experienced speaker looking for a place to share something cool? This section highlights events that are being planned and are accepting submissions to join their event as a speaker.
If you are an event organizer hoping to expand the reach of your event, please submit a link to the website through a PR to TWiR or by reaching out on Bluesky or Mastodon!
426 pull requests were merged in the last week
check_unusedstable_crate_ids reads lock-free after crate loadingproc_macro thingsio::ErrorKind::TooManyOpenFilesOptionFlatten's iterator methodsstd::io::Error into corefilter_map_next: clean-up, overhaul suggestionschunks_exact_to_as_chunks: Prevent syntactically invalid suggestionschunks_exact_to_as_chunks: Use correct method name in messagechunks_exact_to_as_chunks: Pick iter method depending on mut-nessnon_ascii_literal, invisible_characters: don't suggest a fix on raw stringsConstEvalCtxt in expr_eagernesshigher::Rangemanual_option_zip when map receiver is a lazy evaluated expressionneedless_late_init to cover grouped assignmentsborrow_as_ptr is triggered on generated codetype_mismatchDrop::drop when implementedinline_convert_while_ascii() as unsafeOverall, the week was fairly neutral, with no meaningful shift on most benchmarks on any of our statistics.
Triage done by @simulacrum. Revision range: 8b6558a0..7dc2c162
2 Regressions, 1 Improvement, 7 Mixed; 5 of them in rollups 34 artifact comparisons made in total
Changes to Rust follow the Rust RFC (request for comments) process. These are the RFCs that were approved for implementation this week:
Every week, the team announces the 'final comment period' for RFCs and key PRs which are reaching a decision. Express your opinions now.
{str, [T], Path}::trim_prefix and {str, [T]}::trim_suffixrepr(simd)overflowing_literals lint with repeated negationextern "custom"escape_debug_extNo Items entered Final Comment Period this week for Cargo, Language Team or Leadership Council.
Let us know if you would like your PRs, Tracking Issues or RFCs to be tracked as a part of this list.
Rusty Events between 2026-07-01 - 2026-07-29 🦀
If you are running a Rust event please add it to the calendar to get it mentioned here. Please remember to add a link to the event too. Email the Rust Community Team for access.
Please see the latest Who's Hiring thread on r/rust
I do rather hope anyone using
-Zllvm-target-featuresor any stabilized form thereof would know that they are getting a conversation with the dragon directly and they should mind their words carefully if they do not wish to be barbecued by it and served over a nice plate of iron filings.
– workingjubilee on rust zulip
Thanks to Tomáš Šedovič for the suggestion!
Please submit quotes and vote for next week!
This Week in Rust is edited by:
Email list hosting is sponsored by The Rust Foundation
Firefox Tooling Announcements
The following changes have been pushed to bugzilla.mozilla.org:
Discuss these changes in the BMO Matrix Room
1 post - 1 participant
The Servo Blog
Servo 0.3.0 contains all of the changes we landed in May, which came out to 391 commits (March: 534). For security fixes, see § Security.
We’ve shipped several new web platform features:
<form enctype="multipart/form-data"> (@yezhizhen, #45028)<form enctype="text/plain"> (@yezhizhen, #45111)Plus a bunch of new DOM APIs:
{endings: "native"} (@yezhizhen, #44803)We’ve also fixed some build issues on Windows (@mukilan, #45079), FreeBSD (@delan, @mrobinson, @mukilan, #44600), and for anyone building Servo on NixOS or with Nix (@freyacodes, #45051, #45135).
This is another big update, so here’s an outline:
Security
– memory safety fixes
Work in progress
– execCommand(), Sanitizer, IndexedDB, accessibility, workers
Embedding API
– MSRV, cookies, preferences, diagnostics
For users and developers
– --host-file, --userscripts, DevTools Debugger
More on the web platform
– focus, forms, navigation, SubtleCrypto, WebGPU
Performance
– about:memory, threads, layout, DOM, build times
Stability
– crashes, hangs, static analysis
Servo’s JS runtime, SpiderMonkey 140.10.0, had several memory safety bugs that have been fixed in Servo 0.3.0 with the update to SpiderMonkey 140.10.1 (@jschwe, #44755). For more details, see CVE-2026-7322, CVE-2026-7323, and MFSA 2026-36.
We’re continuing to implement document.execCommand() for rich text editing, under --pref dom_exec_command_enabled (@TimvdLippe, #44735, #44973, #44887).
This release adds support for the ‘backColor’, ‘foreColor’, ‘createLink’, ‘unlink’, ‘superscript’, ‘subscript’, and ‘removeFormat’ commands (@TimvdLippe, #44644, #44682, #44657, #44710, #44677), plus partial support for the ‘insertParagraph’ command (@TimvdLippe, #44909).
We’re also working on the Sanitizer API, under --pref dom_sanitizer_enabled.
With the feature now enabled in servoshell’s experimental mode (@kkoyung, #44701), this release adds support for setComments(), setDataAttributes(), allowProcessingInstruction(), removeProcessingInstruction(), and removeUnsafe() on Sanitizer (@kkoyung, #44734, #44983).
IndexedDB continues to improve, under --pref dom_indexeddb_enabled.
This release brings a more conformant abort() on IDBTransaction (@Taym95, #43950).
All of the features above are enabled in servoshell’s experimental mode.
We’ve made more progress towards accessibility support, including the name from contents algorithm (@alice, @delan, @mrobinson, #44439) and several changes towards building the accessibility tree incrementally (@alice, @delan, @mrobinson, #44766, #45035, #45207, #44768, #44785, #44801, #44767, #45029). The latter is critical for performance in real-world web content.
We’re now working on SharedWorker and ServiceWorker, under --pref dom_sharedworker_enabled and --pref dom_serviceworker_enabled respectively.
This release adds support for new SharedWorker() (@Taym95, #44761), and parts of the ServiceWorker API (@gterzian, @arihant2math, #45082, #44787).
Servo now requires Rust 1.88.0 or newer, up from the old MSRV of 1.86.0 (@sagudev, #44815). We run compile tests with the MSRV, but most of our testing is now done with Rust 1.95.0 (@simonwuelker, #44632).
Breaking changes to the cookies methods in our SiteDataManager API (@longvatrong111, #44708):
SiteDataManager::clear_cookies now takes an additional callback argument, allowing it to be called async – to continue calling it sync, pass None as the callback
SiteDataManager::clear_session_cookies now takes an additional callback argument, allowing it to be called async – to continue calling it sync, pass None as the callback
SiteDataManager::set_cookie_for_url now takes an additional callback argument, allowing it to be called async – to continue calling it sync, pass None as the callback
SiteDataManager::set_cookie_for_url_async has been removed in favour of set_cookie_for_url – to migrate, replace set_cookie_for_url_async(callback) with set_cookie_for_url(Some(Box::new(callback)))
Breaking changes to our Preferences API (@Narfinger, @mrobinson, #44307):
threadpools_image_cache_workers_max, threadpools_indexeddb_workers_max, and threadpools_webstorage_workers_max have been removed in favour of a combined thread_pool_workers_max
threadpools_fallback_worker_num has been renamed to thread_pool_fallback_workers
threadpools_async_runtime_workers_max has been renamed to thread_pool_async_runtime_workers_max
threadpools_webrender_workers_max has been renamed to thread_pool_webrender_workers_max
We’ve also reworked our DiagnosticsLogging API (@mukilan, #44703):
You can now set options with DiagnosticsLogging::toggle_option, and check if they are enabled with DiagnosticsLogging::is_enabled
Each option is a variant of DiagnosticsLoggingOption, a new type that also has useful methods for exposing these options in embedder UI
(Breaking change) DiagnosticsLogging no longer has pub fields representing each option – to migrate, replace field writes and field reads with toggle_option and is_enabled respectively
(Breaking change) DiagnosticsLogging::extend_from_string no longer accepts a help option – this option only existed to support servoshell’s -Z help / --debug=help option, so the code implementing it has been moved to servoshell
servoshell has two new options:
You can now configure the path to a hosts file with --host-file= (singular), as an alternative to the HOST_FILE (singular) environment variable (@jschwe, #44880).
You can now provide a directory of user scripts to run in every document with --userscripts= (@jdm, #44754).
When using the Debugger tab in the Firefox DevTools:
You can now “blackbox” a script by clicking Ignore source (@freyacodes, #44359). This prevents breakpoints from being hit inside that script, and it should also allow you to step through execution in the debugger without pausing inside that script.
The Scopes panel is more accurate now (@atbrakhi, @eerii, #44765).
For developers of Servo itself, please note that per project policy, you must not use the output of large language models or other generative AI tools in your contributions. To help us enforce that, we now have CI checks that reject AI agents as coauthors (@SimonSapin, @delan, #44723).
We’ve also fixed build issues with --features vello (@Gae24, @yezhizhen, #44875, #45036).
We’ve improved the default appearance of <dl>, <ol>, <ul>, <table>, <thead>, <tbody>, <tfoot>, <tr>, <td>, <th>, <dir>, <menu>, and <form> (@avis137, #44837, #44920).
CryptoKey is now serializable, allowing it to be used in structuredClone() and postMessage() (@kkoyung, #45163).
We’ve improved JS error messages in several parts of the DOM (@n0blenote, @jdm, @TG199, @PuercoPop, #44704, #45186, #44656).
We’ve improved the conformance of form submission (@yezhizhen, #44943, #44953, #44954, #44957), tab navigation (@mrobinson, #44684), javascript: url navigation (@jdm, @TimvdLippe, #43490), ‘Refresh’ headers and <meta http-equiv=Refresh> (@jschwe, @mrobinson, #45113, #45116), ‘line-break: anywhere’ (@mrobinson, @SimonSapin, #44609), assign() on Location (@TG199, @jdm, #44298), crypto.subtle.deriveBits() (@kkoyung, #44706), getComputedStyle() (@Loirooriol, #44856), performance.measure() (@shubhamg13, #44675), readAsDataURL() on FileReader (@yezhizhen, #44897, #44924), stream() on Blob (@Taym95, #45133), and ML-KEM in SubtleCrypto (@kkoyung, #45153).
We’ve also landed improvements to GPUSupportedLimits (@sagudev, #45114), GPUTexture (@sagudev, #45154), createBindGroup() on GPUDevice (@sagudev, #45140), and other WebGPU features (@sagudev, #45097).
We’ve fixed bugs related to <svg> with ‘Content-Security-Policy’ (@TimvdLippe, @jdm, #44974), ‘:active’ (@SharanRP, @mrobinson, #43953), ‘:hover’ (@SharanRP, @mrobinson, #43979), ‘align-items’ (@yezhizhen, #44396), ‘border-image-outset’ (@lumiscosity, #45039), ‘padding’ with ‘overflow: scroll’ (@stevennovaryo, #44263), ‘pointerup’ events (@mrobinson, #44666), ‘slotchange’ events (@jdm, #44688), dynamic import() (@Gae24, #44741), and clip() on CanvasRenderingContext2D (@yezhizhen, #44831).
We’ve built a tool that will help us improve ‘about:memory’ by finding untracked allocations (@jdm, @TimvdLippe, @webbeef, #44674, #44980).
Servo now requires fewer OS threads per CPU, after we combined the thread pools for the image cache, web storage, and IndexedDB (@Narfinger, @mrobinson, #44307).
We’ve landed a bunch of layout optimisations:
The fragment tree is now immutable for the most part, with small pockets of interior mutability where mutability is needed. This means that most fragment tree accesses no longer have to incur the runtime cost of borrowing an AtomicRefCell (@mrobinson, @Loirooriol, #44849).
Two steps in the layout process, calculating containing blocks and building the stacking context tree, require traversing the fragment tree. This can be expensive, but we’ve now combined them into a single fragment tree traversal in most cases (@SimonSapin, @mrobinson, #44911, #45210).
Another step in the layout process, calculating scrollable overflow, used to require traversing the entire fragment tree. We’ve effectively eliminated that traversal, by making the calculation both lazy and incremental (@mrobinson, @Loirooriol, #44854).
We’ve improved the caching of fragments, shaping results, and other layout results between reflows (@mrobinson, @Loirooriol, @SimonSapin, #45038, #44769).
We’ve made incremental fragment layout more precise (@Loirooriol, @mrobinson, #44925).
We’ve reduced the memory usage of text shaping (@mrobinson, @SimonSapin, #44609).
DOM attributes are much more efficient in this release:
When scripts write attribute values, we avoid serialising them until the attribute is read back by a script (if ever), speeding up frequent writes to inline styles by up to 25% (@mrobinson, #44931).
When we parse attributes in HTML or read attribute values internally, we avoid constructing Attr nodes until a script actually needs them, reducing memory usage and making garbage collection less likely (@webbeef, @TimvdLippe, @mrobinson, #44209, #45023, #45031, #45060).
We’ve eliminated a traversal of the whole DOM tree whenever an <iframe> is attached to the tree, which is especially noticeable when parsing documents with many <iframe> tags (@mrobinson, #45236).
Stylesheet locks now use AtomicRefCell, which is even more efficient than a parking_lot::RwLock (@mrobinson, #44883).
On OpenHarmony, we now have a real refresh driver for reduced idle CPU usage (@jschwe, @yezhizhen, #44927), and we now cache the font list on disk for faster startup (@RichardTjokroutomo, @d-desyatkin, #44158).
We’ve also reduced allocations, GC rooting steps, and other operations in many parts of Servo (@jschwe, @kkoyung, @mrobinson, @SteveSharonSam, @Narfinger, @jdm, @nodelpit, @simonwuelker, #44961, #44944, #44972, #45231, #45078, #44662, #44679, #44967, #44963, #44933, #44935, #44905).
To improve Servo’s build times, we’re moving more code out of our massive script crate (@Narfinger, @jdm, #44598, #44636, #44823), and reduced the size of our dependency tree (@jschwe, #44818).
Several crashes and hangs have been fixed:
document.execCommand("delete") (@TimvdLippe, #44748)--debug-mozjs builds (@Gae24, @SharanRP, #44745, #45001)We’ve continued our long-running effort to use the Rust type system to make certain kinds of dynamic borrow failures impossible (@Gae24, @MavenRain, @Narfinger, @SteveSharonSam, @TimvdLippe, @elomscansio, @jdm, @kkoyung, @yezhizhen, #44712, #44759, #44879, #45014, #45058, #45061, #45076, #45098, #45110, #45149, #45117, #45184, #45201, #44806, #44930, #44942, #44946, #45233, #45181, #44659, #44660, #44664, #44668, #44992, #45000, #45081, #45009, #45225, #45087, #45244, #45245, #45247, #44663, #44665, #44993, #45040, #45053, #44647, #44671, #44681, #44717, #44733, #44686, #44653).
A special thanks to the following people for landing their first patch in Servo:
Interested in helping build a web browser? Take a look at our curated list of issues that are good for new contributors!
Thanks again for your generous support! We are now receiving 7659 USD/month (+4.2% from April) in recurring donations. This helps us cover the cost of our speedy CI and benchmarking servers, one of our latest Outreachy interns, and funding maintainer work that helps more people contribute to Servo.
Servo is also on thanks.dev, and already 35 GitHub users (+2 from April) that depend on Servo are sponsoring us there. If you use Servo libraries like url, html5ever, selectors, or cssparser, signing up for thanks.dev could be a good way for you (or your employer) to give back to the community.
We now have sponsorship tiers that allow you or your organisation to donate to the Servo project with public acknowlegement of your support. If you’re interested in this kind of sponsorship, please contact us at join@servo.org.
Use of donations is decided transparently via the Technical Steering Committee’s public funding request process, and active proposals are tracked in servo/project#187. For more details, head to our Sponsorship page.
The Rust Programming Language Blog
The Rust team has published a new point release of Rust, 1.96.1. Rust is a programming language that is empowering everyone to build reliable and efficient software.
If you have a previous version of Rust installed via rustup, getting Rust 1.96.1 is as easy as:
rustup update stable
If you don't have it already, you can get rustup from the appropriate page on our website.
Rust 1.96.1 fixes:
It also fixes three CVEs affecting libssh2 (which is compiled into Cargo):
Many people came together to create Rust 1.96.1. We couldn't have done it without all of you. Thanks!
Mozilla Security Blog
Mozilla remains committed to maintaining a secure, trustworthy, and transparent Web PKI. Today we are announcing the publication of Mozilla Root Store Policy (MRSP) version 3.1, effective July 1, 2026.
While previous policy updates focused heavily on certificate revocation, automation, and operational resilience, MRSP v3.1 focuses on a different challenge: ensuring that Certification Authority (CA) operations are sufficiently transparent, understandable, and auditable.
Trust in the Web PKI depends not only on technical requirements, but also on the ability of Mozilla, auditors, and the broader community to understand how CA systems are designed, operated, and assessed. MRSP v3.1 introduces new requirements intended to improve the quality of CA documentation and strengthen independent assurance of the design and effectiveness of controls that protect CA systems.
Certification Practice Statements (CPSes) and combined Certificate Policy / Certification Practice Statement documents (CP/CPSes) are among the most important public documents published by a CA. They describe how a CA conducts its operations and meets industry requirements.
Over the years, we have seen significant variation in the quality, structure, and level of detail provided in CP/CPS documentation. Some documents provide extensive implementation detail, while others rely heavily on incorporation by reference or provide only high-level descriptions of CA practices.
The revised policy will continue to require conformance with RFC 3647, as modified by applicable CA/Browser Forum requirements. Improvements to section 3.3 in the MRSP will establish clearer expectations regarding the content and quality of CP/CPS documentation. The new requirements emphasize that documentation must be explicit, bounded, auditable, and sufficiently detailed to describe the CA operator’s certificate issuance and management activities, while also establishing requirements for version control, accessibility, and ongoing maintenance. The objective is to ensure that a technically competent reviewer will be better-able to determine what commitments the CA has made, how those commitments are implemented, and whether the documented practices support technical, operational, and performance oversight.
Mozilla believes that these new CP/CPS requirements will improve transparency, reduce misunderstandings, support more effective audits, and help reduce the risk of certificate misissuance by ensuring that operational practices are documented accurately, consistently, and in sufficient detail to permit meaningful review.
A second major enhancement in MRSP v3.1 is the introduction of Detailed Controls Reports (DCRs). Traditional WebTrust and ETSI audit reports provide valuable independent assurance regarding compliance with established criteria. However, they generally provide only limited visibility into the specific controls, testing procedures, and operational environments that support those conclusions.
Beginning with audit periods starting on or after July 1, 2027, CA operators with root certificates enabled for TLS website authentication will be required to obtain a DCR. The purpose of the DCR is to provide CA management, auditors, and Mozilla with greater visibility into the controls, testing, and operating effectiveness of CA systems that support compliance with the CA/Browser Forum’s TLS Baseline Requirements and Network and Certificate System Security Requirements. Mozilla generally expects to review DCRs only on an as-needed basis, such as during compliance reviews, incident investigations, root inclusion evaluations, or other oversight activities.
A DCR must include:
Mozilla expects that DCRs will complement existing audit reports and strengthen transparency and assurance by providing additional detail regarding system boundaries, control implementation, testing procedures, and control effectiveness that is not typically available in traditional audit reports. Effective compliance requires more than documented policies and successful audits; it also requires management understanding, oversight, and engagement. By providing greater visibility into CA systems, controls, testing activities, and operational risks, DCRs can help reinforce a strong tone at the top regarding compliance expectations, support informed decision-making and resource allocation, enable earlier identification of weaknesses, and promote a culture of continuous improvement. The intent is not to replace existing audit reports, but to provide additional information that supports effective governance, oversight, and informed trust decisions.
MRSP v3.1 also includes several targeted clarifications and refinements:
Mozilla recognizes that these changes will require preparation by CA operators, auditors, and other ecosystem participants. To support implementation, Mozilla is publishing accompanying wiki guidance regarding both CP/CPS Documentation and Detailed Controls Reports.
As with previous policy updates, these changes were informed by discussions with CA operators, auditors, and members of the Web PKI community. We appreciate the feedback received during the review process and look forward to continued collaboration as the ecosystem evolves.
Mozilla has a longstanding focus on building confidence in the Web PKI through transparency, accountability, and continuous improvement. By requiring higher-quality CP/CPS documentation and strengthening independent assurance, MRSP v3.1 advances Mozilla’s commitment to protecting its users and maintaining their trust in the systems that help secure the web.
The post Improving Transparency and Assurance in the Web PKI: Mozilla Root Store Policy v3.1 appeared first on Mozilla Security Blog.
Firefox Tooling Announcements
The latest version of the Firefox Profiler is now live! Check out the full changelog below to see what’s changed:
Highlights:
Other Changes:
Big thanks to our amazing localizers for making this release possible:
1 post - 1 participant
Thunderbird Blog
Welcome back from the Thunderbird development team!
The past few months have been exceptionally busy across the project. As we approach the midpoint of the year, we’ve been focused on a mixture of delivering user-facing features, investing in long-term architectural improvements, and preparing for the next ESR cycle.
A significant amount of effort has gone into modernizing Exchange support, where the team is now approaching Graph API feature parity with our existing EWS implementation. At the same time, progress has continued on the Account Hub, the Global Message Database, and improvements to the add-ons ecosystem that will help extension developers transition toward a more secure and sustainable future.
Behind the scenes, we’ve also continued the less visible but equally important work of maintaining a large application: adapting to upstream platform changes, improving test reliability, addressing long-standing bugs, and supporting the growing community of contributors who help move Thunderbird forward every day.
This month we’d especially like to recognize one of those contributors, Maxe, whose sustained efforts tackling decades-old MIME bugs have been making a meaningful impact across the codebase.
One of the largest efforts underway in Thunderbird continues to be our modernization of Exchange support.
Over the past several months, the team has pushed through multiple Graph API implementation phases and is now entering the final stretch toward feature parity with our existing EWS implementation. At the time of writing, only a small number of remaining email features separate the two implementations, with completion expected imminently.
Reaching this point has involved considerably more than simply implementing new API calls. The work required substantial investment in shared understanding, protocol abstractions, automated code generation, testing frameworks, request batching, synchronization mechanisms, and interoperability between legacy and modern components. Many of these improvements will continue to benefit future protocol work long after Graph support itself is complete.
A notable development came from our ongoing engagement with Microsoft, and following discussions around Graph API permissions, Microsoft confirmed that approved mail clients such as Thunderbird will continue to be able to obtain user consent for permissions that were previously unavailable to third-party applications. This removed a significant long-term uncertainty around Graph support and helps to ensure Thunderbird users can continue connecting Exchange accounts without requiring administrator intervention.
With email functionality nearing completion, the team has already begun planning the next stage of Exchange support, including calendar integration work that will build upon the foundation established over the past year.
Keep track of our Graph API implementation here.
This month we’d like to highlight Maxe, who has been on an impressive run tackling some of Thunderbird’s oldest and most stubborn MIME issues.
Open source projects often benefit from contributors who quietly and consistently improve areas of the codebase that most people would rather avoid. Over the past several months, Maxe has become one of those contributors for Thunderbird.
What began as a handful of fixes has grown into a sustained effort to tackle some of the oldest MIME-related bugs in our tracker. Many of these issues date back decades, touching parts of the mail stack that have accumulated years of edge cases, historical assumptions, and compatibility quirks.
MIME handling sits at the heart of how Thunderbird interprets messages, attachments, encodings, and content types. While users rarely think about it when everything works correctly, it is often involved when messages display incorrectly, attachments behave unexpectedly, or unusual emails expose long-standing inconsistencies. Fixing these issues requires a deep understanding of both email standards and Thunderbird’s historical behavior.
What has impressed us most is not any single patch, but the consistency. Over the past few months Maxe has continued to identify issues, develop fixes, respond to review feedback, and refine solutions until they work reliably across platforms and message types. Along the way, several fixes have uncovered additional problems and improved behaviour in places that weren’t originally expected.
This kind of work is rarely flashy. It involves patiently navigating decades-old code, reproducing obscure bugs, and developing enough confidence to modify systems that affect virtually every Thunderbird user. Yet these are exactly the sorts of contributions that make open source software better over the long term.
On behalf of the team, thank you Maxe for the energy, persistence, and technical skill you’ve brought to Thunderbird this year. Your work is making a real difference.
The add-ons ecosystem remains an important part of Thunderbird, and over the last few months we’ve continued working toward a safer and more maintainable extension platform.
One significant decision was the postponement of experiment deprecation on the Monthly Release channel for an additional year. Feedback from extension developers made it clear that many maintainers needed more time to migrate away from legacy experiment APIs, and we want to ensure that transition is successful rather than disruptive.
This extra time allows us to focus on expanding official WebExtension APIs, improve migration paths, and work directly with extension developers to understand their priorities. To support this effort, we’re preparing a broader outreach initiative later this year that will gather feedback from experiment maintainers and help guide future API development.
A great deal of this work has been driven by John, who has been balancing ecosystem improvements alongside onboarding new team members and supporting several other strategic projects. Ensuring that extension developers have a sustainable path forward remains a key investment area for Thunderbird.
Over the past several months we’ve continued modernizing Thunderbird’s authentication experience, with a particular focus on OAuth and account setup.
One of the most visible improvements has been the continued rollout of browser-based OAuth flows. Instead of embedding authentication within Thunderbird itself, users can now complete sign-in using their system browser, providing a more familiar experience while benefiting from the security features and account state already present in their preferred browser.
As we expanded support for these flows, we also uncovered an interesting interoperability challenge. RFC 8252, the standard commonly used by native applications, recommends the use of loopback redirects with dynamically assigned local ports. While most providers support this approach correctly, several major providers have historically handled these redirects differently. As a result, we’ve been working directly with providers including Yahoo!/AOL, Comcast/Xfinity, and Yandex/Mail.ru to improve compatibility and ensure Thunderbird users continue to enjoy a smooth sign-in experience as authentication requirements evolve.
We’ve also been simplifying account setup for users of Thunderbird’s growing ecosystem of services. Recent work allows users to launch authentication for a Thundermail account directly from Thunderbird without first manually entering account details. This significantly streamlines onboarding and lays the groundwork for similar experiences with other major providers in the future.
Another important addition has been the introduction of a Thunderbird-specific protocol handler. This enables web-based account dashboards, management interfaces, and enterprise deployment tools to communicate directly with Thunderbird and complete account configuration automatically. For Thundermail users, this creates a much smoother path from account creation to a fully configured desktop client. Looking ahead, the same technology opens the door to deeper integration opportunities for enterprise deployments and other hosted services.
While much of this work happens behind the scenes, it represents an important investment in making account setup faster, more reliable, and more secure for both individual users and organizations deploying Thunderbird at scale.
Behind the scenes, work continues on one of Thunderbird’s most ambitious long-term architectural projects: the Global Message Database.
Recent months have focused on strengthening the foundations needed to connect Panorama’s user experience with the underlying storage architecture. Geoff has resumed significant front-end work following ESR-related priorities, while Brendan has joined the project to help accelerate development and planning efforts. At the same time, Ben has been refactoring portions of the IMAP codebase to establish cleaner interfaces that will simplify integration with the new database architecture.
While much of this work remains infrastructural and therefore less visible to users today, it represents important progress toward a more modern foundation capable of supporting future performance, search, and organizational improvements throughout Thunderbird.
While major features tend to attract the most attention, a significant portion of Thunderbird’s engineering effort continues to be devoted to maintenance and adaptation work required to keep pace with our upstream platform.
This period is traditionally one of the busiest times of the ESR cycle. As Firefox prepares its next ESR release, large volumes of platform changes land in a relatively short period of time. While these improvements benefit Thunderbird in the long term, they can also introduce unexpected regressions, styling inconsistencies, test failures, and compatibility issues that require immediate attention.
One particularly notable example has been Mozilla’s ongoing Nova initiative, which introduces substantial visual and styling changes throughout Firefox. Without intervention, many of these changes would create inconsistencies across Thunderbird’s user experience. Richard (Paenglab) has done exceptional work identifying, triaging, and adapting these upstream changes to ensure Thunderbird continues to present a coherent and polished interface. Much of this work goes unnoticed when done well, which is perhaps the highest compliment for maintenance engineering.
Alongside these adaptation efforts, the team and contributor community have continued landing a steady stream of reliability, stability, and usability improvements across the application. Recent highlights include:
If you would like to see new features as they land, and help us find some early bugs, you can try running daily and check the pushlog to see what has recently landed. This assistance is immensely helpful for catching problems early.
—
Toby Pilling
Senior Manager, Desktop Engineering
The post Thunderbird Monthly Development Digest: June 2026 appeared first on The Thunderbird Blog.
Firefox Tooling Announcements
Bugs resolved in Moz-Phab 2.15.3:
jj util backend name check for Jujutsu integrationDiscuss these changes in #engineering-workflow on Slack or #Conduit Matrix.
1 post - 1 participant
The Rust Programming Language Blog
This is another post in our series covering what we learned through the Vision Doc process. We previously described the overall approach and what we learned about doing user research, we explored what people love about Rust, dug into what it takes to ship safety-crticial Rust, and described some of the major challenges that people face when using Rust.
In this post we walk through what folks have found on their journey to learn the Rust programming language with ups and downs covered.
As a disclaimer, LLMs (Large Language Models) come up in this post because our interviewees brought them up. We're scoping discussion to their use as a learning tool, covering research and example generation, not broader questions about AI (Artificial Intelligence) in software development.
The interviews surfaced several different paths into Rust: curiosity, embedded work, job-market pressure, organizational adoption, and reassignment after a team or company chose Rust. That last path matters because many learners are not evaluating Rust from a blank slate; they are trying to become productive after Rust has already arrived in their work.
"Funny enough, I've advocated for more niche languages than Rust in the past. Rust has pretty much stopped being as much of a niche language as it was, but it's not Java." -- Fractional CTO
Likely as expected, the folks that we talked to reach for a range of resources to learn Rust. Some reach for official documentation, such as The Rust Programming Language Book and find that sufficient to build on what the compiler was already showing them.
"I started with the official Rust documentation because there are a lot of great examples of how features like the borrow checker work." -- Software engineer at an Automotive supplier
Others needed more passes and more formats, sometimes reaching for resources the community maintains, such as Rustlings, The Little Book of Rust Macros, and Learn Rust With Entirely Too Many Linked Lists.
"The first time I went through the chapter in [The Rust Programming Language] on borrow checking, I was like, what is this? I read it again, then I watched a YouTube video of someone explaining the chapter." -- Rust freelance consultant
"Rust book, Rustlings, Zero to Production in Rust, Jon Gjengset tutorials. A bunch of books. It's not a one-pass reading. Can't say how many times I've gone through it." -- Software engineer working on video streaming and storage
These resources have brought up an entire generation of Rust programmers. But, to some, there is a perception that these resources have trouble keeping pace with the language.
"We'd like to use [The Rust Programming Language/'the book'], but we've found that it's out of date, unfortunately. We've looked at the GitHub repo and found it's got a lot of unresolved issues and unmerged PRs" -- Principal Software Engineering work on Rust adoption in a regulated industry
Whether or not this is factually true, Rust's growth has nonetheless put more scrutiny on these materials. Companies evaluating adoption and engineers getting reassigned to Rust teams are looking at them with fresh eyes and finding the gaps that affect their own evaluation.
It's pretty typical for Rust to be the 2nd, 3rd or Nth programming language that someone picks up. They'd end up writing their most familiar language in Rust, whether C++ patterns, Java patterns, or whatever they knew, for months or even years. Eventually they got comfortable enough to start writing idiomatic Rust.
"There's a bit of a drop in productivity compared to C if you're already familiar with it just because you're learning new rules, new syntax." -- Principal Firmware Engineer (mobile robotics)
"In the beginning it was more poking around the code and adding and removing some ampersands and asterisks to try to make sense of
mutand notmutand whatever." -- Senior engineer with 20 years of Java experience in cloud and IoT
We also spoke with someone who found that not having much of a programming background seemed to benefit people picking up Rust. Not having worn-in grooves from other languages may play a role here, and it's worth investigating further.
"I had someone who had never programmed much before start working on the internals of [our Rust project]. She was just fine with getting into Rust. It's more of the senior people that struggle as they need to unlearn practices which may work in other languages, but it's not the 'Rust' way." -- Researcher, Automotive OEM R&D Lab
We heard a lot about learning to work with the borrow checker instead of against it. People get there through different paths, but a few patterns came up repeatedly.
Rust's diagnostics did the teaching on their own, especially around lifetimes.
"If you mess up the lifetimes in a piece of code that you've written by hand, I usually find that Rust's diagnostics are very helpful" -- Researcher working on static analysis of Rust programs
"Whatever's missing, the compiler usually fills in: it tells me 'you need to declare the lifetime of this reference', so I know and can figure it out. That all generally works pretty well." -- Senior Software Engineer
Others felt like they only really internalized the borrow checker after writing a lot of Rust. It took projects, coding challenges, prototyping and so on until at some point it clicked.
"I actually did not understand the borrow checker until I spent a lot of time writing Rust" -- Founder of a startup built on Rust
"Besides the prototyping work, I also did coding-challenge-type stuff to get familiar with Rust for Advent of Code. [..] It eventually clicked to the point where I wasn't fighting with Rust, it was working for me. I had that experience other people describe: when I managed to get my program to fit with Rust, it worked. I didn't spend time debugging." -- Principal Software Engineer, large SaaS provider
Some learners arrive with the assumption that good Rust means zero clones, zero copies, lifetimes threaded through everything. They set the bar at optimal before they've learned how to write idiomatic Rust, and it makes the borrow checker feel harder than it needs to be at the outset.
"On one of my first projects, I was like, 'I don't ever want to copy or clone anything,' so I carefully wove through all the lifetimes and got myself into a bit of a bind. Then I saw someone else just cloning the struct I was working with, and it was super cheap. Sometimes you can just clone and it's going to be okay." -- Researcher at a university
The experienced Rust developers we spoke with consistently said the same thing: clone freely while you're learning, then optimize when you understand the problem. Rust's reputation for performance and correctness feeds this. Newcomers assume anything less than optimal is wrong before they've written a first working program, and clone guilt is how that shows up.
We think it could be an interesting area of future study to check into the patterns Rust programmers employ at different levels of experience and under which circumstances. One member of the Rust Vision doc team that's very experienced with Rust noted that there's kind of an "expected shape" they understand as passing the compiler. This knowledge influences how they approach writing code which wouldn't take that shape and they naturally find themselves understanding when to use so-called workarounds, such as passing around indices into arrays or Vecs.
The Rust programming language is multi-paradigm, and how that lands depends on what you're coming from. We heard some that came from a functional background were delighted with digging into learning how much Rust inherits from that lineage. Some others noted that they and others on their teams struggled to unlearn the object-oriented style they'd come to use heavily in other languages like C++ and Java.
"Developers coming from C++ tend to think object-oriented. I think that's a difference between C++ and Rust." -- Architect at Automotive OEM
"I had exactly that thing, where I would apply all my years of Java and JS thinking, where I could just create some object, not care about it, return it, have it sloshing around between various functions. Found myself reaching for these patterns and then being told 'no, you cannot do that'." -- Principal Engineer at a SaaS company
Developers coming from functional programming had less to unlearn: strong typing, pattern matching, and an expression-oriented style were already familiar.
"My background has been more functional programming, strong typing. That originated for me as a Lisper: once a Lisper, always a Lisper." -- Principal Software Engineer working on Rust tooling for safety-regulated industries
"The languages I primarily used before Rust were things like OCaml. Way back, I came from C and C++, the classic languages, and then I spent quite a long time doing primarily pure functional stuff. These days I've ended up back in what I like to think of as a pragmatic center ground [with Rust]." -- Fractional CTO
We spoke with a university professor that's been teaching Rust generally. In the academic environment, they were able to use proxies for some things such as "traits are like interfaces in Java" because the students had already gone through a set of courses in their first and second years that taught them Java. They introduced concepts slowly throughout the course, choosing to deal with some more complex topics like generics later. The outcome generally was that students had no problem picking up Rust in this setting.
"I couldn't see any big difference on the embedded side. We also teach an embedded class, and we did an experiment. Half of the students' feedback was worse on the Rust class, mostly because they needed to build the project themselves. The C students just got one from [an LLM], absolutely no problem." -- University Professor, on teaching Rust
The C cohort leaned on LLMs for the project in ways the Rust cohort couldn't. We don't yet have a clear answer for why.
What did come through clearly was the Rust cohort's experience with the community. Some students needed to figure out which drivers to use for the embedded project and how to use them. Their professor encouraged them to open issues and ask questions directly on GitHub, and the maintainers responded. Students who had never contributed to open source before were getting answers from the people who wrote the code.
Some experienced folks shared that they saw LLMs as a tool that can help someone come up to speed quickly, either as a research tool or for generating example Rust code to understand concepts.
"I'm optimistic that there's a way to work [LLMs] in that will cut down that learning curve. One of the big things these tools bring is reducing the learning curve in general; these are very good tools to help you navigate a space that you don't know yet." -- Maintainer of large open source Rust crate
"I try [LLMs] out once a month, usually for generating an example or something like this. Just like with Stack Overflow: when you read an example, you should read it carefully and try to understand it. Not copy and paste it, but type it in your own words in code and then check it, because that's where the teeny tiny little mistakes are." -- Founder of startup built on Rust
For some learners, an LLM is just another way to find answers, no different than a search engine.
"So for the most part, picking up Rust - how do I learn? I'll [use web search for] things, I'll ask [an LLM], I'll just poke around and read the code." -- Senior Software Engineer working in a regulated space
One founder went further and claimed that LLMs change who can become a Rust developer. One consulting company founder described hiring high school graduates with no systems programming background and training them as Rust developers, with LLMs filling in the learning gaps that would previously have required years of experience.
"At the beginning, I was worried, but now that we have [LLMs] supporting development, the difficulty of the language doesn't matter. I'm seeing a huge opportunity behind strong runtime languages like Rust. [..] In [Developing Country] we hire 20-25 high school graduates, train them to be Rust programmers, then they enhance our workforce worldwide." -- Founder of a consulting company
We heard this from one organization. This is a claim that the combination of Rust's compiler and LLM tooling can dramatically shorten the path from beginner to working developer. Whether it generalizes depends on questions we can't answer from a single interview: how long these developers stay, what kind of code they can maintain independently, and whether this training/learning model works outside this company's particular structure. If it holds up, the pool of people who can become Rust developers is much larger than the usual hiring profile suggests.
We spoke with a number of folks on teams that are using Rust in larger organizations. Teams wanted to know that everyone would end up at roughly the same level of competence, which led a good number to invest in training courses to get there. Some leaders found that staff was able to ramp well enough by reading The Rust Programming Language, going through Rustlings, and then picking up lower risk and priority tickets to work on. Having a sense of community was also important within companies; it helps people know they are not alone when they are asked to work on Rust after, say, a reorganization happens.
"[..] the idea with the class as opposed to 'just read the Rust book on your own' was that this gives everyone kind of the same baseline going in." -- Principal Firmware Engineer (mobile robotics)
"So typically we're going to have people work through Rustlings, work through The Rust Programming Language. We have them then start to pick up lower risk tickets to work on." -- Principal Engineer at a large SaaS provider
"We've got an internal Slack channel for Rust learning where people can drop questions and others will come in and answer them. That helps build up understanding and community." -- Software Engineer at a large corporation
Some organizations found that while the person they'd hire would need to learn Rust, it was still preferable to the alternative of hiring someone for a critical piece of software written in another language.
"They needed to grow and maintain this C++ codebase. They had a C++ wizard, and they tried for about two years to find someone with the same level of expertise. They ended up hiring people that didn't know Rust and ramping them up, creating FFI bindings from the C++ side so they could work in Rust. And you can feel it: the borrow checker is teaching these people the right way to handle their systems." -- Principal Engineer at an Automotive OEM
The community and helping each other aspect seems to grow bonds as organizations mature.
"Our team is [all about] mentorship. I've mentored people coming up to speed on Rust, and people help each other hugely." -- Principal Software Engineer at a large SaaS company
We identified some cases where people have approached Rust and bounced off of it, for one reason or another. In the below case, someone with a background in a language with fewer guardrails found themselves frustrated enough with Rust to walk away.
"All of that means that that embedded ecosystem is very frustrating to somebody who comes from C and is like, why can't I just get a pointer to this peripheral and then write into the registers. What are you doing to me? [..] My friend never got over that. He looked at it and said, I'm not going to deal with this and walked away." -– A second University Professor
There may be language features that for a particular domain are not seen as comfortable or usable yet, such as async Rust usage in a safety domain. We'd like to map which language features feel off-limits in which domains; async in safety-critical work probably isn't the only case.
"We're not fully sure how async [Rust] will work out in the long run in our domain. [..] People don't feel comfortable yet since C++14 doesn't provide such concepts. [..] It's the chicken-and-egg problem again: we probably need to gain some experience to see whether we can actually benefit from these new concepts in the automotive and safety domains." -- Team Lead at Automotive Supplier (ASIL D target)
We heard in at least one case, that while the language was challenging and there was a near bounce, the tooling helped keep them coming back and trying.
"Well, I think my early impressions of Rust - one is I find C++ so intimidating, and I think a big part of why I was able to succeed at [..] learning Rust is the tooling. I mean, all this makes sense [..] but it's like, for me, getting started with Rust, the language was challenging, but the tooling was incredibly easy." -- Founder of another startup built on Rust
While it might be considered more of a community concern, if there are interactions online and in spaces that point to learners having so-called "skill issues" this feeds into the narrative that Rust must be hard to learn. We may be unintentionally turning away Rust Project contributors and maintainers due to the vibes being put out when new learners show up in certain spaces.
"People are very helpful, but generally the attitude is: if your program is very complicated, it's mostly a skill issue. There's not that much empathy when people get stuck learning, and a lot of people are just pushed away by it. There's probably a huge number of people who silently stop wanting to write Rust, because at some point it gets complicated and the feedback they get is 'you just need to be a better programmer, obviously'." -- Software Engineer at a SaaS Provider
We found a few interesting perspectives collected in the Rust Vision doc survey which we administered with examples of bouncing and coming back:
"I started before 1.0, got stuck very soon when trying to translate patterns from C++ to Rust (due to borrow checking). I tried again after 1.0 and it stuck. [..]" -- Survey Respondent A
Survey Respondent A went on to share in a more detailed response about a perceived weakness in Rust learning materials related to lifetimes and the borrow checker are explained. There was an observation that it's fairly easy to run into more complex situations with lifetimes and the borrow checker. They felt that the current state of this sort of material and tutorials is fairly superficial and can leave learners stuck when they run into those more complex situations.
One respondent that bounced once and came back shared challenges around usage of async. In concert with Rust's memory-safety and the borrow checker, they found some of the nitty-gritty details of async were difficult to learn. While we're aware of the Rust Project's continuous efforts to improve Rust's async story, this is another data point of a user that faced challenges.
Another survey respondent shared how they had multiple times bounced in trying to learn Rust. They returned after a year or so and found Rustlings to be highly motivating. We note that having multiple pathways for folks to learn Rust opens up more possibilities for those that nearly bounced, just like this person.
The thing that stood out most to us was the lack of real, first-hand knowledge of having bounced when learning Rust. While this is an obvious effect of soliciting answers to our survey and opportunities to interview through Rust channels and our networks, this cohort is good future candidate where interviews could start.
Across these conversations, the experience of learning Rust depended heavily on context. Why someone was learning and what support they had mattered as much as the borrow checker. The same kinds of examples kept coming up: a training course that got a team to a shared baseline, a maintainer answering a student's first GitHub issue, and a colleague whose code showed that cloning was okay.
That context is largely something the community has a hand in. With that in mind, here is what we take away from what we heard, and what we still don't know.
Learning materials aimed at unlearning. Syntax barely came up when people described their struggles. People struggled with unlearning habits from previous languages, whether OOP structuring from C++ and Java or the instinct to grab a raw pointer to a peripheral. Most of our learning materials teach Rust from first principles, and that works. What we didn't come across is much written for, say, the engineer with ten years of Java who lands on a Rust team after a reorg: material that names the patterns they'll reach for that won't transfer, and shows what to do instead. The professor we spoke with did a version of this in the classroom, leaning on "traits are like interfaces in Java" and saving generics for later in the course, and the students did fine. Something similar could work outside the classroom too.
Put the "clone freely while you're learning" advice somewhere official. Every experienced developer we spoke with gave the same advice, but learners seem to mostly pick it up by accident, like the researcher who happened to see someone else cloning the struct they had been carefully threading lifetimes through. Saying it early in official materials would take some of the steepness out of the curve. The broader version belongs there too: idiomatic Rust doesn't have to mean optimal Rust, especially on a first project.
Diagnostics are already a primary learning resource: several people told us the compiler taught them lifetimes before any documentation did. Diagnostics reach learners right at the moment they're stuck. When writing new ones, it seems worth keeping the confused newcomer in mind alongside the expert, because for a lot of people this is where the learning happens.
Is "the book" actually out of date? Whether or not The Rust Programming Language or other materials are actually behind, a team evaluating Rust looked at its repository, saw unresolved issues and unmerged PRs, and moved on. As more companies evaluate adoption, more people will look at these materials with the same fresh eyes. Visible issue triage and some communication about what's current and what's planned would address the perception, separately from whatever content work may or may not be needed.
How stuck learners get treated is shaping who stays. We heard about students getting answers on GitHub from the maintainers who wrote the code, and we heard about learners being told their struggles were a skill issue. The first group came away with a lasting good impression of Rust. Some of the second group walked away entirely, and because they leave quietly, it's easy to underestimate how many of them there are. The welcoming side of the community came up unprompted as a reason people stayed, so we know it makes a difference when we get this right.
Every organization we spoke with described essentially the same ramp-up for bringing a team to Rust. Teams that brought groups of developers to Rust described roughly the same approach: get everyone to a shared baseline with a training course or with The Rust Programming Language and Rustlings, start people on lower-risk tickets, and give them somewhere internal to ask questions. Several organizations also found that hiring developers without Rust experience and ramping them up worked out better than continuing to search for rare expertise in another language. None of this is complicated, and teams weighing adoption don't need to invent a training program from scratch.
The biggest gap is the people we didn't reach. Nearly everyone we spoke with stuck with Rust long enough to be reachable through Rust channels, so the stories of bouncing off came to us second-hand: a friend who walked away from embedded Rust, colleagues who quietly stopped after the responses they got. As we wrote in our first post, finding people who decided against Rust takes targeted outreach. If the proposed User Research team comes together, talking with learners who bounced would make a good early project, and learning is probably the area where that research would teach us the most.
We also don't know what to make of LLMs as a learning tool yet. They came up as a search engine, as an example generator, and in one organization's case as something that makes training high school graduates into working Rust developers possible. We saw a classroom where the C cohort leaned on LLMs in ways the Rust cohort couldn't, and we don't have an explanation for it. All of this comes from a handful of conversations, so we treat it as a set of leads to follow up on. Given how quickly the tools are changing, it seems better to study this deliberately than to wait and see what folklore develops.
The folks we spoke with showed that people do get there: with enough passes through the materials and enough code written, it eventually clicks. The opportunities above are mostly about making it work for the people who didn't pick Rust on purpose, and for the ones who would have stuck around if their early experience had gone a little differently.
The Mozilla Blog
Earlier this month, we officially stood up Mozilla.org: a new 501(c)(3) nonprofit created to steward the long term success of the Mozilla Project.
Over the last year or so, I’ve said a lot about how AI is reshaping the web — and how we need to simultaneously stand up for the open internet Mozilla helped build and shape what the internet is becoming in the AI era. This is a huge and urgent challenge.
Mozilla has evolved and grown a great deal in order to step up to this challenge.
We are still a high impact philanthropic foundation and a browser company focused on user choice. But we are also: an email company built around privacy; an open source AI startup focused on developers; a place for people to create and share data on their own terms; and an investor in responsible tech startups.
These are all pieces of Mozilla today, and are all important levers as we try to shape where the internet is headed for the better.
We have created Mozilla.org to pull all of the different pieces of Mozilla together. It will act like a strategic endowment — allocating funding, managing our brands and shaping long term strategy — to ensure every part of Mozilla is well set up to advance the vision outlined in the Mozilla Manifesto. And, if we’re successful, it will help all of the pieces of Mozilla add up to more than the sum of their parts.
This is an important milestone for Mozilla. The challenge of fusing the values of the Mozilla Manifesto into this next era of the internet is huge. This updated structure will make it easier to nimbly direct our resources and orchestrate our actions to step up to this challenge.
At the same time, what we love about Mozilla stays the same. All of Mozilla’s organizations remain under the umbrella of the 501(c)(3) Mozilla Foundation, with the new non-profit operating the Mozilla portfolio of organizations on its behalf. Our mission — and our commitment to nonprofit ownership at the top — remain steadfast.
For more information on the new Mozilla.org non profit including an FAQ, see wiki.mozilla.org/mozilla_org
The post The web is evolving. So are we. appeared first on The Mozilla Blog.
Thunderbird Blog
Over the past several weeks, we have been welcoming early users from our waitlist into Thundermail, a few waves at a time. Many of you are now setting up your accounts, trying things out, and sharing your thoughts with us.
You may have noticed that we are now saying Thundermail more often, and Thunderbird Pro less.
Thunderbird Pro started as the name for our subscription services, including Thundermail, Appointment, and Send. But early feedback made two things clear: people cared most about Thundermail, and “Pro” created confusion about whether Thunderbird itself was becoming a paid or limited product.
So, to clarify things, Thunderbird Pro is now simply Thundermail: the email service from Thunderbird, with features like Appointment and Send included.
The Thunderbird Desktop and Mobile apps remain exactly what they are today: powerful, compatible with any email service, and free.
Every day, members of our team are reading through your survey responses, your messages in the Thundermail Early Bird community chat, your support requests, and every new idea and vote on the board. We discuss what we are hearing, and we sort it into what we can address right away and what we want to plan for. Then we keep working in the open, where you can see what we are up to and tell us when something is not quite right.
Here are some of the things we’ve learned so far:
However, there was one request which came through louder than any other…
Webmail was, by a wide margin, the most requested idea from our community, and whereas we had it in the plans for down the road, many people expected this to be a feature available from day one.
We moved webmail to the top of the list, shifted resources into the work behind it and we are excited to share that an early alpha version of it is coming next month. As with most early releases, it will have some rough edges, but will also allow for a much more interactive user experience for our beta testers. Everyone will have a vote in how it’s shaped for the future.
Our scheduling and secure file sharing tools are still here, and they are still part of your subscription. Our main focus right now is Thundermail and webmail, but we are continuing to care for both with steady improvements along the way.
If you are an Early Bird, we would love for you to visitour ideas board to share your suggestions and vote on the ones you would most like to see. We really do read every single one.
And if you have not been invited yet, you can join the waitlist. More waves are going out soon, and we are looking forward to welcoming you onboard.
Thank you for helping us build Thundermail.
The post Thundermail June 2026 update: what we learned after the first few waves of invites appeared first on The Thunderbird Blog.
The Mozilla Blog
If you’ve been running into endless CAPTCHAS or website login requests lately, you’re not imagining things.
Websites, facing a rising tide of abusive traffic from bots, are adopting increasingly aggressive countermeasures, damaging user’s experience of the web, their privacy and open access to the web.
In this post, we’ll talk about a new initiative we’re launching with Cloudflare, other web browsers, and web stakeholders to address this challenge while keeping the web anonymous by default.
Privacy and access in tension
The fight for privacy on the web has made real progress. Browsers that put privacy first are eliminating third-party cookies, restricting fingerprinting, and hiding IP addresses, pushing back against the trackers.
But every step forward has come with a cost.
Users are seeing more CAPTCHAs, more demands to log in, and more outright block pages than ever before. Building privacy into the browser means dismantling the passive signals, like IP addresses and browser fingerprints that are used to profile users, but are also relied on by anti-abuse systems.
At the same time, sites are facing large increases in bot traffic. The response from websites is understandable; volumetric abuse like credential stuffing and spam can do real damage. But the result is a lose-lose: users face mounting friction and reduced privacy, while sites drive away the legitimate visitors they wanted to serve.
If nothing changes, users will increasingly be forced to choose between their privacy and their access to the web.
Proposals have been made to tackle this dilemma, by asking users to prove to sites that their devices and software are ‘trusted’. These proposals, such as Web Environment Integrity (WEI), transfer control of devices away from users and to a small handful of operating system and hardware vendors. This deprives users of choice and control and gives those gatekeepers control over which devices and software can access the web, the opposite of the open web, which Mozilla is working to protect.
Finding a better way forward
We think there’s a better way forward. It starts from a simple observation: bots cause harm because they operate at scale. To stop that kind of abuse, a site doesn’t need to know who you are, or that your device is restricted to running approved software. It only needs to know whether you’re staying within a reasonable rate limit.
To make a rate limit work, it must be hard for attackers to create new identities and reset their allowance. That’s one reason why sites demand an email address, a federated login or a device fingerprint: obtaining a new one is just costly enough to make the rate limit stick. The challenge is whether we can make rate limits work, without giving sites access to hard-to-change identifiers that also enable tracking.
Some sites naturally have a relationship with their users, like a subscription or a long-standing account. What if one of those existing relationships could quietly vouch for you elsewhere, so a site you’ve never visited could trust that you’re a real person within its limits, without learning who you are or even where the vouch came from?
For example, consider a VPN service. Many websites block VPN traffic entirely due to the high rates of abusive traffic blended with legitimate traffic. What if a VPN service could vouch for each of its subscribers? This would let sites manage a per-subscriber rate limit, meaning users get fewer roadblocks and sites get more of the legitimate traffic they want. Of course, this requires that the vouching system doesn’t enable sites to track VPN users, which would otherwise defeat the very purpose of using the VPN.
Enabling this kind of privacy-preserving vouching is already possible in a limited sense. Apple’s Private Access Tokens, built on a cryptographic protocol called Privacy Pass, let Apple devices receive single use tokens they can later present to websites without those visits being linked together.
However, Private Access Tokens have some critical shortcomings. First, like WEI, they rely on device attestation, the very hardware gatekeeping we are determined to avoid. Second, there’s no easy way to open up the system to let more parties vouch for users without compromising on user privacy, which means concentrating control in the hands of a few. To keep the web open, we need a system where any site can vouch for users, and where other sites can decide who they trust to vouch for users responsibly.
This is a much harder problem, but we think the cryptographic foundations exist to deliver it. Anonymous credentials let one party issue you a credential that you can later present to a site a limited number of times, whilst preventing sites and issuers from tracking its use. It’s even possible to hide which party issued it, proving only that it came from a set of trusted issuers.
A fix is both essential and possible
Building this into a system for the open web, where any site could vouch and any site could set its own limits is challenging, but we believe it’s both possible and essential in order to defuse the tension between privacy and access, while avoiding centralising control in a small number of gatekeepers.
Working with other web stakeholders, including Cloudflare and other browsers, we’ve started designing such a system. For a deeper dive, read our post on Hacks, which goes into more detail about the problem space and the approach we’re working on.
Our goal is simple: fewer CAPTCHAs, fewer unnecessary blocks and fewer demands to identify yourself, without compromising on privacy. This is the kind of web that Mozilla built Firefox to offer: easy to use, private and open to all.
The post Keeping the web open and private in the bot era appeared first on The Mozilla Blog.
Hacks.Mozilla.Org
This is the technical companion to our update on Distilled, “Keeping the web open and private in the bot era.” Here we take a deeper look at the problem space, the design we’re proposing, and the problems still left to solve.
Browse a news site in a private window. Shop at a major retailer with a VPN. Visit a video streaming platform with anti-fingerprinting defenses tuned up. You’ll see the same responses: registration walls, block pages, and endless CAPTCHAs. The message is clear: if we think you might be a bot, you’re not welcome.
Websites have valid reasons for wanting to block bots. Bots enable volumetric abuse, abuse that wouldn’t otherwise be feasible if they had to be carried out by humans. For example: SEO comment spam, credential stuffing and DDoSing. Consequently many sites employ dedicated anti-abuse tooling which aims to keep the bots out whilst minimizing friction for human visitors.
Unfortunately, that tooling is increasingly failing at both tasks. Browser privacy protections are dismantling the passive signals that anti-abuse systems depended on to identify and distinguish visitors. Meanwhile advances in generative AI have rendered CAPTCHAs ineffective: bots now solve them faster and more reliably than humans.
Many sites are switching to more invasive mechanisms and now ask visitors to disclose identifying information, e.g. an email address, a federated login or disabling their VPN. This means greater friction for users, since providing these details on a first visit takes time. It also compromises their privacy, since these details enable the same kinds of cross-site tracking that browser privacy protections were intended to mitigate.
This leaves users with a dilemma. The more effectively they protect their privacy, the harder it is for websites to distinguish them from bots and the worse the treatment they receive. Website operators are also suffering. The additional friction they inflict upon well-behaved visitors harms their site, but many are willing to pay the costs if it mitigates volumetric abuse.
Browser-based AI agents make this tension more acute. Sites may want to allow agents which are acting on behalf of individual users while blocking agents engaged in volumetric abuse. However, with no effective mechanisms to distinguish the two, websites are opting to block both. That hurts users, who should be free to choose the user agent they use to access the web; it hurts new browsers and agents, which struggle to interoperate; and it hurts sites, which lose legitimate visitors.
The consequence is that the web gets worse for everyone. Users get more friction or less privacy or both. Website operators see more volumetric abuse and the friction they add drives away users who would otherwise want to consume their content or services. New user agents struggle to access the same content as conventional browsers.
Some large ecosystem players have put forward solutions that leverage their control of the dominant operating systems and their deep integration with consumer hardware. These rely on device attestation: identifiers and privileged code baked into devices at the hardware level, which let manufacturers prove what software is running on a user’s device. Exposing this functionality to the web means attesting to sites that the user is running approved software with trusted hardware and therefore isn’t a bot. There have been two substantive proposals.
Google’s Web Environment Integrity, abandoned in 2023, was the blunt version. It attested to the user agent itself, as well as the operating system and device in use. Users would have lost control in two ways: once to the attester, which would decide which operating systems and devices could be blessed, and again to the website, which would decide which software to accept. If sites had adopted allow-lists of approved user agents, building a new browser would have become virtually impossible, and sites could have withdrawn access from any user agent they chose.
Apple’s Private Access Tokens, deployed across their ecosystem in 2022, have more subtle issues. Built on the Privacy Pass protocol standardized at the IETF, they get a lot right: a user receives a renewed, limited batch of one-time tokens that can be presented to websites without linking their visits together. This provides privacy for users and has shown rate limits to be an effective tool for sites – both points we’ll return to later in this post.
However, Private Access Tokens rely on device attestation, requiring that the hardware manufacturer be in overall control of the user’s device. Presenting a PAT tells a website you are locked into Apple’s rules for what counts as acceptable software. Due to PAT’s technical design[1], there’s no way to open the system to other sources of scarcity without compromising the system’s privacy properties, meaning that if more widely deployed, access to the web would become tied to having bought expensive hardware from a small, hard to change set of vendors.
Both approaches are ultimately hostile to users and to the openness of the web. Both are premised on parts of a user’s device that sit within the manufacturer’s control and beyond the user’s own. Were they widely deployed, the web would become just another walled garden with centralized gatekeepers controlling acceptable hardware, operating systems and software. As convenient as these solutions are for the players who already dominate the ecosystem, we think there’s a better path.
Bots’ harms arise from their ability to operate beyond human scale. For sites to prevent volumetric abuse they don’t actually need to know the user’s identity or receive cryptographic proof that they’re running approved software. If sites knew their visitors were restricted to a rate limit set by a site, that would be enough.
Rate limits only make sense if they’re tied to something scarce; something an attacker can’t cheaply replicate to evade the limit. Without anchoring to a scarce resource, like the trusted hardware used in Private Access Tokens, attackers can generate as many fresh identities as they need to bypass the rate limit.
However, hardware is just one option for scarcity. Anything a user already has that an attacker can’t trivially spin up at scale will work: email addresses and phone numbers are naturally scarce. A paid subscription costs an attacker the same as a real user. Even maintaining an account on a free service requires some non-trivial work.
What if we could use these scarce signals across the web? We could build an open ecosystem with many parties offering scarcity signals, each site choosing which to accept. By opening up who can provide a signal, and letting sites choose which to accept, we can avoid transferring control to device manufacturers and the resulting harms.
As a concrete example of who might be well positioned to provide such a signal, we can consider VPN providers acting as a subscription service. Sites routinely block VPN users indiscriminately, whether through a deliberate policy choice or through an indirect consequence of rate limiting visitors per IP address. But a VPN subscription is a perfect source of scarcity. If the VPN provider could vouch for its users so that sites could rate limit each user individually – then users would be able to browse the web with less friction and without giving up their VPN usage.
The catch is that building a system that can enable this on the open web whilst maintaining user’s privacy is genuinely difficult. It requires that we take information from one site — that this user holds some scarce thing — and expose it to other sites so that they can use that as the basis for their rate limiting. Letting one site verify a signal from another is the sort of information flow that privacy-preserving browsers have spent the last decade locking down to prevent cross-site tracking.
Our goal would be that no more than the minimum information gets through: a single bit communicating whether the user is below the rate limit set by the site. Leaking anything more – like the source of the scarcity that the rate limit is anchored to – would be unacceptable. Enabling a new cross-site information flow might feel like compromising privacy to gain better access, but reality is more nuanced. If a new system moves sites away from demanding that visitors be identifiable (whether through fingerprinting or login forms), it can be a win for both privacy and access.
The good news is that the cryptographic foundations for a privacy preserving approach already exist. The Privacy Pass protocol, originally developed in 2018 to reduce the friction of Cloudflare CAPTCHAs for Tor users, introduced the core primitive: a token that is unlinkable between issuance and redemption. You prove something to an issuer (e.g. by solving a CAPTCHA), receive some tokens, and later present a token to a website. The website can verify the token is legitimate, but can’t link it to the user it was issued to.
Figure 1: In Privacy Pass, a CAPTCHA provider can issue tokens to a client which can then be used to bypass challenges for future site visits. Even if the CAPTCHA provider and sites collude, they can’t use the tokens to identify the user or their browsing history.
Privacy Pass has gone on to be successfully deployed in systems where the issuer and verifier have a prior trust relationship: Apple uses it to authenticate users of Private Cloud Compute and Private Relay without linking their activity to their identity, Chrome uses it for two-hop IP protection, and Kagi uses it to provide private search. These deployments work in part because a small number of parties have agreed in advance on who issues tokens and who accepts them.
Applying this approach to an open system where any site can act as an issuer brings real challenges. Firstly, even though tokens are unlinkable, knowing a user has access to a specific issuer is a privacy leak on its own, because you can infer that the user meets the relevant issuance criteria. If one site can learn that you have a token from another site, that reveals that you have been to that site, which can be a major privacy problem. This compounds if sites can learn the set of issuers you have visited, since it becomes a fingerprint which can be used to identify you.
Generic techniques exist for proving a statement in zero knowledge: we can prove that a client has a token from a set of acceptable issuers without revealing which specific issuer it is. We’ll call this issuer blinding. The generic approach is often slow, but bespoke approaches tailored to the underlying cryptography can improve this considerably.
Another challenge is how sites using rate limits decide who to trust to issue tokens. If an issuer misbehaves then the site’s rate limits become ineffective, enabling volumetric abuse. However, if we need to prevent the site from learning which issuers a user has access to, the site is only going to know that one of its trusted issuers was used, not which one. This makes mistakes or misbehaviour by an issuer difficult to detect, and makes it hard for sites to evaluate new issuers. Solving this challenge is essential for openness. Without adequate information, sites are likely to lean towards conservative issuer selection. That could lead to less choice between Anchors, which in turn could lead to a new form of gatekeeper being created.
To solve this, sites at least need a way to calculate an aggregate score for each issuer they use. This should roughly correspond to how much of the traffic it considers abusive to have come from users using that particular issuer. Mozilla has long invested in systems like Prio which use multiparty computation (MPC) to protect user privacy whilst enabling aggregate measurements of system behaviour.
Privacy Pass also struggles to handle dynamic adjustments to rate limits. Once tokens have been issued, they’re difficult to invalidate without either revoking all active tokens or risking attacks which can compromise the privacy of users. It’s also beneficial if sites can adjust rate limits on a per client basis, for example by increasing rate limits where they become more confident the client is benign and withdrawing access when abuse is detected.
Anonymous Credit Tokens offer a useful building block to solve this problem. Conventional Privacy Pass schemes rely on issuing a bucket of tokens but ACT works differently by enabling the use of a credential with state. For example, an ACT credential can hold an internal counter. When the credential is presented, the site can check the counter is over some threshold and mutate it, increasing or decreasing the counter whenever the site’s perception of the holder has improved or worsened. Critically, the exact value is never leaked to the site, preventing the site from tracking the holder and ensuring successive presentations of the same credential can’t be linked.
So how can we combine these techniques to build a system which can enable privacy-preserving rate limiting on the open web? In May 2026, we participated in a W3C CG Meeting in collaboration with Cloudflare, Chrome and other web stakeholders in which we started sketching out a design we’re calling PACT – Private Access Control Tokens.
Rate limits need a starting point, a source of scarcity to anchor on. We’ll call an entity that provides such a source an Anchor. To a user who meets the Anchor’s criteria, like having a subscription, an account in good standing, or a verified phone number, an Anchor issues a batch of Endorsement tokens, following the Privacy Pass model. In practice, Anchors could be any website which has access to this kind of signal. An Endorsement conveys scarcity to other sites.
That’s enough for a simple system where access is either granted or denied. But as we discussed earlier, we also want the ability to increase access where a visitor behaves benignly and decrease it where they don’t. The state needed to enforce a rate limit can’t live in the Endorsement, because Endorsements cross trust boundaries between unrelated sites. We need a second object that can hold that state, scoped to the party that maintains it.
We’ll call that the party that handles rate limiting for a site a Moderator and the stateful object a Credential. A Credential is specific to a Moderator and, unlike endorsements, we limit each site to nominating a single Moderator. In the common case the site itself plays the Moderator role, so there’s no new entity or trust boundary. A Moderator can also be a third-party service shared across many sites, allowing those sites to cooperatively share a rate limit.
In the terminology of the previous section, the Anchor is the issuer of Endorsements, and the Moderator both verifies Endorsements and issues Credentials. A Moderator manages rate-limit policy: it decides which Anchors it trusts, accepts their Endorsements, and issues a Credential in return.
Figure 2: (1) Clients acquire Endorsements from Anchors in the course of normal browsing to sites they have relationships with. (2) Clients can exchange Endorsements for a stateful Credential from a Moderator. (3) Credentials can be used to access sites which use that Moderator. Credentials can be updated over time.
Directly revealing which Anchor backed an Endorsement would leak a lot of information about the user. The issuer blinding techniques from the previous section solve this: when an Endorsement is redeemed, the Moderator only learns that it came from one of the Anchors it trusts, but not which one.
When a Moderator covers more than one site, we let Credentials be presented across all of them but partition cookies and storage as we would for any other third party site. The unlinkability of Credential presentations keeps this from creating a new cross-site identifier. The benefit is that good behaviour on one site improves access on every site the Moderator covers, and bad behaviour cuts it everywhere. Websites can already build the same capability with a shared account system, so this doesn’t create a new way to lock users out, but it does provide a new way to grant access without requiring users to give up their privacy.
Enabling Moderators that cover many sites carries a centralisation risk, similar to the concentration we see today in anti-abuse providers. The mitigation is that the choice of Moderator stays with each site, and the choice of trusted Anchors stays with each Moderator. This can’t reverse the centralisation pressure the web already faces, but it ensures this system won’t lead to additional lock-in: a new Anchor or a new Moderator can be adopted without coordinating with a dominant vendor.
The system then has three flows. First, the user receives Endorsements from an Anchor in the course of normal interaction, based on the Anchor’s positive view of the user. This is a relatively rare operation for any given user and Anchor. After all, as our source of scarcity, Endorsements should not be too easy to accumulate.
Figure 3: In the course of normal browsing, clients browse to websites they have a relationship with. These sites can act as Anchors by issuing Endorsements to clients.
Second, when the user arrives at a site that works with a Moderator, the browser spends an Endorsement from an Anchor the Moderator trusts and receives a Credential in return. The presentation hides which Anchor was used, and neither the Anchor nor the Moderator can trace the Endorsement back to where it was issued. The Moderator decides what initial balance the Credential starts with. If the user has no Endorsements from suitable Anchors at all, existing mechanisms (CAPTCHAs, account creation, federated login) could be used to bootstrap a Credential the same way, so the system degrades to today’s experience rather than locking the user out.
Figure 4: When the client browses to a site, it can prompt the client for a Credential from the Moderator it uses. If the Client doesn’t have a suitable Credential, but does have a suitable Endorsement, it can exchange it for a Credential with the Moderator. In practice, the Moderator and the Site might be the same server.
Third, as the user browses, the browser presents the Credential and the Moderator updates the internal state of the Credential. The Moderator can reward behaviour that looks benign and penalize suspicious activity, but can’t track the use of the Credential or identify it if it’s used on other sites the Moderator covers. Revocation falls out of the same mechanism: a Moderator can refuse to return an updated Credential.
Figure 5: The Client can present the Credential on sites which use the matching Moderator. Sites can check if the Credential is in good standing. The sites can then adjust the access the Credential has in response to behaviour. E.g. increasing it when they gain confidence in the client or reducing it in response to malicious behaviour.
In practice, all of this would happen transparently to the user through a WebAPI that sites acting as Anchors or Moderators would call from JavaScript. In an ideal ecosystem, users would accumulate Endorsements through normal browsing, just by virtue of the sites they already visit, and the rest of the flow would happen in the background as they move around the web, leaving users with meaningfully less friction.
AI agents acting on behalf of a user slot into the same flow. An agent can carry its user’s Credentials, in which case the user remains accountable for how the agent behaves. Sites would not need to grant any more access than they would to the user themselves. Alternatively, the operator of an agent can run its own Anchor and vouch for its agents the way other Anchors vouch for human users. Sites retain control over which Anchors they accept, so they can choose how to treat agent traffic without needing a separate detection mechanism.
Several mechanisms combine to keep the information about a user that flows out close to a single bit. Cryptographic unlinkability ensures successive Credential presentations cannot be tied to each other or to the original issuance, so a user’s visits cannot be joined into a history. Each site is bound to a single Moderator, so the set of Moderators a user has Credentials with never becomes a cross-site fingerprint. The Anchor-to-Credential exchange happens in an isolated browsing context, so during ordinary browsing the only thing the site or its Moderator ever observes is a Credential presentation: the site only learns if the user has a valid Credential below the rate limit, or nothing. When the Moderator updates a Credential, it adjusts the credentials state without learning what it is.
The additional privacy given to users from Issuer blinding makes participating in the system more challenging for Moderators. Because the Moderator can’t see which Anchor backed a Credential at issuance, it can’t give a Credential from a strong Anchor more access than one from a weak Anchor: doing so would itself leak which Anchor was used. The initial access has to be uniform across the Moderator’s whole pool of Anchors, which in practice means setting it at the strength of the weakest. However, this is only relevant for that initial access, the Moderator can update credentials according to the holder’s behavior, enabling Credential’s to accrue access over time.
Building an open ecosystem also requires that sites can make effective decisions about the Anchors they choose to trust. Multiparty computation systems like Prio enable aggregate scoring without compromising privacy. When users present Credentials, they can provide an encrypted share which identifies the anchor they used and can be privately aggregated to compute the quality of an issuer.
We think the architecture we’ve sketched for PACT has the right shape, but many of the details still need to be worked out and the entire system needs rigorous privacy and security analysis.
We want to do that work in the open. The IETF is the natural venue for the cryptographic protocols underneath, and the W3C for the WebAPI surface that sits on top. We’ll be bringing draft specifications to these bodies as soon as they’re ready, and we welcome collaborators from across the ecosystem: browser vendors, site operators, anti-abuse providers, and the cryptography community.
If successful, we think we can provide a system which will keep the web open and private, while still giving sites the rate-limiting signal they need.
The ideas described here are the result of collaboration and conversations with many people, including: Watson Ladd, Thibault Meunier, Michele Orrù, Trevor Perrin, Eric Rescorla, Samuel Schlesinger, Martin Thomson, Eric Trouton, Benjamin Vandersloot & Cathie Yun.
[1] PAT requires that the source of scarcity and an independent issuer be trusted not to collude. If they do, they can track users as they interact with the system. This is not suitable in the context of an open system where any party could play those two roles.
The post PACT: Anonymous Credentials for the Web appeared first on Mozilla Hacks - the Web developer blog.
Mozilla Open Policy & Advocacy Blog
Two years ago, the Digital Markets Act was the first of its kind: an ex ante competition framework introducing contestability and supporting innovation. But, as much as its critics try to cast it as a European experiment, it was never alone. In 2019, expert reports across the US, EU, UK and elsewhere studied the competitive dynamics of digital platforms. Today, the conversation has moved on to how to restore competition and choice within digital markets.
That shift was on display at Mozilla’s event in Brussels on 2 June, “Rebalancing Digital Markets: Delivering Competition, Choice, and the Road Ahead.” Regulators and policymakers from the EU, UK, Japan, and Brazil came together to assess what two years of reform have delivered and what still needs to happen. The answer was clearer than expected: ex ante competition regulation works, but only where it has been properly enforced.
The DMA’s browser choice screen obligations have produced real, measurable results. We have written about the data in detail. The headline numbers: Firefox has been selected via a DMA choice screen over six million times in two years, once every ten seconds. Daily active users in the EU have grown to double those in comparable countries. People who choose Firefox through a choice screen are five times more likely to stick with it than those who arrive organically. The core finding is simple: this is what competition looks like when it is allowed to work. When users are given a genuine, well-designed choice, they take it. That was not a given. Earlier choice screens resulting from ex post antitrust processes produced no measurable impact. What changed was rigour and enforcement: working with behavioural economists, testing interfaces carefully, focusing on outcomes rather than checkbox compliance.
The lesson extends well beyond browsers. Desktop remains largely untouched, leaving roughly 310 million desktops and laptops in the EU without an equivalent level of browser choice. Windows users are still frequently steered towards Microsoft’s own services through design choices that override user choice or make switching more difficult than it should be. Ensuring that users of all gatekeeper services can easily change default settings, free from manipulative practices or undue influence, remains a critical test for DMA implementation. Choice screens can help, but they are not a silver bullet. Ecosystem lock-in, interoperability barriers and entrenched defaults continue to limit competition and innovation. The window to address these issues before new forms of digital gatekeeping become entrenched remains open, but it will not stay open indefinitely.
The institutional approaches across jurisdictions may differ, but the direction of travel is increasingly similar. Policymakers are recognising the need to address concentrated power in digital markets, expand user choice, and create conditions for competition and innovation to flourish.
The UK has opted for a flexible, case-by-case model and has already designated Google and Apple in respect of their mobile platforms. Japan scoped its legislation tightly to mobile, moved quickly, and is now seeking to ensure effective compliance for Japanese smartphone users. Brazil is building a framework calibrated to its own market realities.
None of these jurisdictions is simply copying the EU’s DMA. All of them are drawing on their experience and developing ex ante frameworks that work for their people and their economies: what a good choice screen looks like in practice, what regulatory capacity is needed before a law enters into force, and what happens when the large tech companies seek to undermine rather than facilitate choice and competition.
The consistent lesson is unglamorous: the staff, expertise, and industry relationships needed to enforce these rules have to be built before the law arrives, not after.
A clearer consensus is also forming around what enforcement needs to deliver. Rules that look good on paper but do not change what users experience every day are not enough. Technical expertise matters, particularly as AI raises questions that economists and lawyers alone cannot answer. The European Commission’s ongoing proceedings in AI distribution are a test of exactly this: whether regulators can move fast enough to ensure competing AI services are able to reach users before vertical integration entrenches dominance. And regulators need to be clear-eyed about a pattern that has emerged across jurisdictions: gatekeepers framing incomplete compliance as a deliberate policy choice, rather than acknowledging it as a failure to meet their obligations.
These are sophisticated companies, and they will keep testing what regulators will accept. The measure of this generation of digital market frameworks is not whether they passed through legislatures, but whether they can deliver real impact for people.
For Mozilla, that means ensuring the lessons travel: to desktop, to AI, and to every jurisdiction building its own version of these rules. Competition, choice, and true innovation in digital markets should be the norm, not the exception.
The post Building Competitive Digital Markets: From Rules to Results appeared first on Open Policy & Advocacy.
Firefox Nightly
Script to find new contributors from bug list
Firefox Tooling Announcements
The latest version of PerfCompare is now live!
Check out the change-log below to see the updates:
[andra - esanuandra ]
[david - davidmiculit ]
[kala - kala-moz ]
Results Page: Return the scatter series for base and new back to original order (#1046)
Bug: 2044194 Port client-side KDE mode detection from kde-widget into CommonGraph (#1045)
Bug 2034263: Give subtest column in subtest page a max-width (#1047)
[paul - padenot]
Two small followups on modal-work (#1049)
Assorted fixes to improve the app when numbers are very large and not measuring time. (#1050)
Thank you for the contributions!
Bugs or feature requests can be filed on Bugzilla. The team can also be found on the #perfcompare channel on Element. Come and chat!
1 post - 1 participant
Firefox Developer Experience
WebDriver is a remote control interface that enables introspection and control of user agents. As such, it can help developers to verify that their websites are working and performing well with all major browsers. The protocol is standardized by the W3C and consists of two separate specifications: WebDriver classic (HTTP) and the new WebDriver BiDi (Bi-Directional).
This newsletter gives an overview of the work we’ve done as part of the Firefox 152 release cycle.
Firefox is an open source project, and we are always happy to receive external code contributions to our WebDriver implementation. We want to give special thanks to everyone who filed issues, bugs and submitted patches.
In Firefox 152, multiple WebDriver bugs were fixed by contributors:
WebDriver code is written in JavaScript, Python, and Rust so any web developer can contribute! Read how to setup the work environment and check the list of mentored issues for Marionette, or the list of mentored JavaScript bugs for WebDriver BiDi. Join our chatroom if you need any help to get started!
webExtension.install command to support installing web extensions in Firefox enabled in Private Browsing mode. browser.setDownloadBehavior command to allow overriding the download target folder before the temporary file is created.WebDriver:Navigate and WebDriver:Refresh commands to properly report errors when triggering the navigation fails, instead of silently ignoring them.Firefox Tooling Announcements
The latest version of the Firefox Profiler is now live! Check out the full changelog below to see what’s changed:
Highlights:
Other Changes:
--use-env-proxy only when the node version is >= 24 (#6064)Big thanks to our amazing localizers for making this release possible:
Find out more about the Firefox Profiler on profiler.firefox.com! If you have any questions, join the discussion on our Matrix channel!
1 post - 1 participant
The Mozilla Blog
Firefox gives you many ways to make the browser your own, from privacy settings and AI controls to tab management, custom colors, and more. As we continue to improve Firefox, you get more control over how it works for you.
Today, we’re introducing a redesigned settings experience that makes your options easier to find, understand, and manage.
Over time, some settings pages became crowded, related preferences were spread across different sections, and it wasn’t always obvious where to look for a particular option. The redesign makes settings easier to navigate while preserving existing preferences and the flexibility and control that Firefox is known for.
The new settings feature a cleaner layout, modern visual design, improved labels and descriptions, and updated navigation, bringing related categories together. One of the most noticeable updates is the retirement of the long-standing “General” page. Many of the options that were previously there have now moved into more specific areas, including “Appearance,” “Accessibility,” “Languages,” and “Tabs and browsing.”
Some options may have moved, but your existing preferences haven’t changed. The customization options you rely on are still available. If you’re not sure where to find something, the search bar can help you locate it quickly. You can also visit Mozilla Support for more detailed guidance.
This redesign reflects extensive user research, including interviews, usability testing, card-sorting exercises, and analysis of usage data. It was also shaped by feedback from the Firefox community through Mozilla Connect, Reddit, and other channels, along with collaboration across Mozilla teams.
Thank you to everyone who shared their feedback along the way. Your insights helped shape the redesigned settings experience, and they’ll continue to guide future improvements as Firefox evolves.
The post Firefox is easier than ever to customize appeared first on The Mozilla Blog.
The Mozilla Blog
Firefox has been busy introducing updates across productivity, privacy and AI. From Project Nova and browser-wide AI controls to expanded privacy protections and new ways to stay organized, the goal is simple: help you spend less time managing your browser and more time getting things done online.
But building the best browser isn’t just about shipping new features. It’s also about building them alongside the people who use Firefox every day.
Today, we’re introducing the Firefox roadmap to give people a closer look at what we’re building and a preview of where Firefox is heading next.
We’ve always built Firefox in the open and our new roadmap is an extension of that.
“Some of the best ideas in Firefox come directly from the people who use it,” said Ajit Varma, head of Firefox. “The roadmap gives our community a new way to see what’s taking shape, share feedback earlier and help guide where we’re heading next.”
The roadmap highlights what we’re working on across Firefox, including productivity, privacy, performance, AI and the technologies that help make the web better for everyone.
Some items on the roadmap will be things we’ve already shared; others we’ll be sharing publicly for the first time. Together, it’ll provide a clearer picture of where Firefox is headed and the areas we’re investing in to build a better browser.
Here’s a look at what’s rolling out now in Firefox 152, and what we’re building next.
Try out the latest features, explore what’s coming next and tell us what you think on Mozilla Connect.
We’re also hosting a Reddit AMA on June 24 with Firefox product leaders, so bring your questions.
Talk soon.
The post What’s new in Firefox this June, and what’s next on the Firefox roadmap appeared first on The Mozilla Blog.
Thunderbird Blog
The past month was busy; the theme was evolution. We went into this quarter with our own ideas for what we wanted to accomplish. However, our users had better ideas. With the release of Thunderbird’s own mail service, Thundermail, the need for a better account settings import process across our services and apps became vital.
We have also heard from our users about issues they had with syncing, notifications, bugs, and more. As such, we refined our roadmap, because the goal is always a better, safer, more private email client, and delivering that experience is more important than any foregone projects. Our roadmap can change, but our goal to deliver the best cannot.
On Android, our focus turned to our import function through a QR code. This helps transfer settings from Thunderbird desktop to mobile. We also ensured that it would work for our new Thundermail project. This allows our early Thundermail users to quickly move their accounts to the Thunderbird mobile app. Additionally, we merged fixes for importing accounts, getting the avatar monogram to match the account immediately after setup, and new translations.
We also began some exploratory work on one of our biggest upcoming projects. Our users have been clear about a recurring issue that they want resolved. Notifications. Notifications on an email client are tricky, especially one like ours, where we support so many different services. Users report not receiving notifications for newly delivered emails, even when polling or push are enabled, notifications appearing improperly in the notification shade, and many others.
We broke this up into two projects. The first priority will be to work on the quick fixes. These are bugs discovered through reports or our own testing. These fixes won’t require an entire refactor of our sync, notification, or database features, allowing us to address user concerns without launching a big project. But we are intending to launch a big project. We’ll do whatever we have to in order to make notifications work as expected. That’s the second planned project, to consider larger performance improvements, even a full refactor of sync, notifications, and the app database if we have to. Prompt email delivery is essential. We’re going to ensure Thunderbird becomes a trusted client for rapid email syncing and notification.
We’ve also begun investigating potential feature flag options. While we currently have internal feature flags for our debug builds, we’re looking into options that we could host ourselves to ensure privacy, while also allowing us to enable features more quickly, without a full update. This would also allow us to roll back any changes that introduce issues without an app update. We hope it can allow us to launch products more quickly, and potentially even give end users more control over the features they have enabled.
The iOS app is coming along nicely, with the core libraries (IMAP, SMTP, and MIME) set up. We’re working on how to store account data in the local database, as well as starting the email compose functionality. iOS will have a WYSIWYG editor for new messages, just as you’ve come to expect from other email clients. Also, this week has been busy with WWDC and understanding how the new tools and operating system will affect and improve our roadmap, mostly in the user interface.
We wouldn’t be who we are without the open source community at our core. Open source isn’t just how we build software, it’s how we plan, make, and grow. Our community has a say in what we do, which includes making changes, bug fixes, suggesting new features, and voicing complaints – this insight from you is our greatest strength. Over the past month, we’ve heard requests for new features, bug fixes, and changes, and we’ve adapted our plans for the rest of the year to focus on this. And hopefully deliver them in more bite-sized pieces.
First, the community has helped drive our upcoming projects. From notifications to investigations into spam filters, our community is helping drive our objectives. Our priorities and our roadmap have shifted thanks to that feedback. With our community having our back, we’re making the best email client available on iOS and Android, and can’t wait to deliver these improvements. We’re listening and working on these highly requested changes.
A notable contribution from our community came in the form of a fix for our notification widget. The text wasn’t scaling for users with different font size settings. This is a serious accessibility issue. Fortunately, a contributor pushed a fix which ensures the font size scales with the system. We’re grateful for this help, and it’s a perfect example of how we build better together.
Interested in contributing yourself? It can be tough to figure out where to start or how to go about it. Soon starting development on the Thunderbird for Android app will be easier than ever. We’re moving our documentation around and adding templates and examples to make everything from requesting new features to working on bug fixes, and even documenting your changes easier. We’ve also added a new pull request template to help you write a detailed message explaining your pull request. This includes letting us know if you’ve used AI in the development and to what level it was used.
One note to say with contributions, given our limited team size and resources, we are focusing our efforts on the top items on the roadmap first and foremost. Second, we will spend a couple of weeks solving the largest impact bugs. Then we will spend a week or two supporting what contributions help resolve bug reports or align with our roadmap. We are intrigued about how AI can help us in these efforts, but we are also vigilant about the quality of the code. Potential contributors need a strong understanding of what they are contributing. This means being able to change, improve, and support the code they contribute in the future.
We have just three engineers on each of our iOS and Android teams. We’re supported by wonderful designers and product folks, but we also couldn’t do it without your support. So, thank you. We’re doing our best to make a great product, and with your help, contributions, donations, bug reports, feature requests, and enthusiasm, we’re able to do it better than ever. Thanks for checking in with us!
– Danielle G. (she/her), Senior Android Engineer
The post Mobile Progress Report: June 2026 appeared first on The Thunderbird Blog.
Firefox Nightly
Script to find new contributors from bug list
About:Community
Hi Mozillians,
For years, the Mozilla Community Newsletter has served as a monthly touchpoint for contributors and community members across the Mozilla ecosystem. Coordinated by the Customer Experience (CX) team, it helps keep our global contributor and product communities informed, connected, and engaged through updates, contributor stories, announcements, and opportunities to get involved.
While the newsletter has traditionally been distributed directly to community members, we recognize that many of these updates are valuable to a broader audience as well. That’s why we’re bringing our content into a blog post format, making it easier for anyone interested in Mozilla’s mission, products, and community work to stay informed.
Whether you’re a longtime contributor, a Firefox enthusiast, or simply curious about what’s happening across Mozilla, we hope these updates provide useful insights into the people, projects, and initiatives shaping our community.
In this edition, we’re sharing our latest updates from May 2026. It’s packed with the latest community news, contributor highlights, project updates, and opportunities to get involved in Mozilla’s work around the world.
So, without further ado, let’s dive in!
From localizing to Firefox Enterprise
Long-time Mozillian Valery recently shared an open-source project he built to help Firefox Enterprise administrators manage Firefox deployments more easily called Browser Policy Manager or BPM. Beyond the tool itself, this story is a proof of the long-term value of investing in the community. Stories like Valery’s show how community contributions evolve over time and why fostering a strong, engaged contributor community continues to pay dividends across the Mozilla ecosystem.
Firefox Referrals: We want to hear from you
Could Firefox users help grow the community by recommending Firefox to friends and family? That’s the question being explored in a recent Mozilla Connect discussion about potential referral programs. Join the conversation to share your thoughts on what would motivate you to recommend Firefox, and what a referral experience should (or shouldn’t) look like.
Web Serial finally arrives in Firefox 151
After years of community interest and requests dating back more than a decade, Firefox 151 now includes support for the Web Serial API. Developers can use Firefox to communicate with and manage serial-connected devices such as ESP boards, Raspberry Pi Picos, 3D printers, CNC machines, and other microcontrollers directly from the web. It’s a long-awaited milestone for the maker and hardware community, and we’re excited to finally see it land in Firefox.
Community spotlight
In this community spotlight, we feature Baurzhan Muftakhidinov, whose work has helped bring Firefox and many other open source projects to Kazakh users. His story is a testament to the power of persistence, community, and the importance of keeping smaller languages visible online.
P.S.
Enjoyed these updates? Subscribe to the Mozilla Community Newsletter and get the latest updates delivered straight to your inbox.
Mozilla Open Policy & Advocacy Blog
Mozilla Champions the Reintroduction of the American Innovation and Choice Online Act (AICOA)
Today, only a handful of tech companies shape the online experience for the more than 300 million internet users in America. This concentration of power is exactly why we need legislation that advances competition and user choice. It’s all the more urgent as AI transforms not just the tools that people use, but also magnifies the competitive inequities underlying the web itself.
The American Innovation and Choice Online Act (AICOA) is bipartisan legislation designed to curb harmful gatekeeper behaviors of the biggest tech platforms. The bill does so by prohibiting dominant platforms from unfairly preferencing their own products, discriminating against tech competitors, and preventing interoperability — all practices that stop the best product winning and stifle consumer control. The goal is straightforward: companies should compete based on the quality of their products, not by leveraging anticompetitive tactics.
As the builder and operator of the Firefox browser and the browser engine Gecko, Mozilla has firsthand experience with the impact of the exclusionary practices AICOA seeks to prevent. For example, deceptive design tactics deployed by operating systems make it difficult for people to install and keep Firefox as their preferred browser. Browsers are the portal through which people access the open web, and users should define that interaction. AICOA would help limit the ability of operating systems to steer users toward affiliated products through deceptive design choices. Ensuring meaningful user choice online is not just about variety; it reflects values and individual preferences. Openness and innovation thrives when the web is built around platforms that serve people, not the other way round.
Browser engines, while lesser-known, are among the most complex and consequential pieces of infrastructure on the modern internet, impacting user-focused innovations in privacy, security, speed, and more. Gecko is one of only three widely used engines and the only independent browser engine. The importance of that competitive counterweight cannot be underestimated. When platform owners favor their own vertically integrated products, independent challengers face barriers that have nothing to do with product quality and everything to do with a monopolized market.
It’s important to recognize that antitrust reform can make the internet more private and secure than it is today, as we’ve consistently emphasized. For example, in 2021, Firefox was at the forefront of developing technology against cross-site tracking, but could not release the technology to Firefox users on iOS because of app store rules preferring Apple’s own browser engine, blocking alternatives like Gecko.
We’re champions of AICOA and look forward to working with members of Congress to push this legislation forward and tackle longstanding anticompetitive practices. Mozilla thanks Senators Grassley and Klobuchar for their leadership in advancing competition. A thriving tech ecosystem requires an open, fair, and competitive market where innovative services can compete on merit and people can control their own experiences online.
The post A Handful of Companies Control the Web. AICOA Can Change That. appeared first on Open Policy & Advocacy.
Pascal Chevrel
A while back, I stumbled onto something that turned into a rewarding side-project at Mozilla.
Firefox ships with a built-in spellchecker, but it only activates if a dictionary for your language is bundled with the browser. Coverage had grown organically over the years — driven largely by localizers and community members adding support for their own languages. Dictionary work was actually very active in the early years of the Mozilla project, but like many things in a large open-source codebase with a lot to manage, it had quietly received less attention over time, for no particularly good reason. So I decided to change that.
I put together a full inventory dashboard of every third-party dictionary shipped in Firefox Desktop, cataloguing sources, upstream health, and — critically — licensing.
Licensing turns out to be the main bottleneck. Firefox is open-source software, so any dictionary we ship has to carry a licence compatible with the Mozilla codebase. Some excellent dictionaries exist for languages Firefox supports, but their licences don't allow direct inclusion. In those cases, the dictionary can still reach users — but only as a Firefox extension they have to find and install manually, rather than something that just works out of the box.
The goal of the inventory wasn't to point fingers at anything. It was to make the full picture visible, so that anyone who wanted to help would know exactly where to start.
Once the inventory existed, the work was really about connecting the right people. Mozilla's localizer community already had the expertise and motivation — what was sometimes missing was a clear entry point. I took care of all the patches myself, so that localizers wouldn't have to deal with the technical side of things. This work was done in coordination with Mozilla's Localization drivers team, who own the dictionary infrastructure and reviewed and merged the changes.
We expanded the number of locales shipping with a built-in dictionary from 30 to 41. This shipped last week with Firefox 151.0.3, and these improvements also benefit Thunderbird users, since both applications share the same dictionary infrastructure.
New dictionaries added: Croatian, English (UK), Georgian, Persian, Slovenian, Tajik, Tamil, Tibetan, Turkish, Welsh, and Xhosa.
Updated dictionaries: Bulgarian, Danish, French, Hungarian, Indonesian, Latvian, Polish, Romansh, and Swedish.
Part of the reason for doing this work publicly — building the inventory, filing the bugs, making the gaps visible — was to give people with the right expertise a reason to step in themselves. That's exactly what happened.
For Turkish and Russian, the existing open-source Hunspell dictionaries had become outdated — vocabulary and linguistic rules that hadn't kept pace with how the languages are actually used today. Selim (our Turkish l10n lead) and Valentin (our Russian l10n lead) each decided to take matters into their own hands.
Selim forked the TDD Turkish dictionary and updated it with modern vocabulary, better circumflex support, and performance improvements — the result is hunspell-tr-moz, now shipping in Firefox 151.0.3. Valentin built a new modern Russian dictionary from scratch, ru-spelling-dictionary, released under MPL-2.0. It's currently available as a Firefox extension — if you use Russian, Valentin would appreciate feedback on the quality before it's integrated directly into Firefox.
Both projects are public and open-source.
The licence question is also quietly resolving itself for a couple more locales. The maintainers of the Kabyle and Asturian dictionaries have agreed to relicense their work to allow direct inclusion in Firefox. Once that's done, those communities will join the list too.
There are still gaps in the inventory. Some are licence issues that may resolve over time. But for many of the remaining locales, the honest answer is that we simply haven't looked hard enough yet. Dictionaries are often individual passion projects or work coming out of linguistics circles — they exist, but finding them takes investigation. If you know of a dictionary for a language Firefox doesn't currently support, that's exactly the kind of lead worth following up on.
Mozilla is still a place where a motivated contributor can find a corner of the project, do meaningful work, and have a real impact — without needing to be a browser engineer or a Mozilla insider.
The inventory dashboard is public. If you're a localizer, a linguist, or a dictionary maintainer and you want to help bring spellchecking to more Firefox users, the gaps are clearly documented. And if you maintain a dictionary that could be included but licensing is an obstacle, that's a conversation worth having.
See you in May 2027 for the next update.
Firefox Tooling Announcements
The following changes have been pushed to bugzilla.mozilla.org:
Discuss these changes in the BMO Matrix Room
1 post - 1 participant
The Mozilla Blog
For a limited time, where the VPN is available, users can get unlimited VPN bandwidth in Firefox – up from the 50 gigabytes monthly limit — plus access to over 25 country locations to browse from. Don’t have Firefox yet? Try it now.
Firefox’s free built-in VPN usually gives eligible users 50 GB of free bandwidth each month. From now through Aug. 31, we’re making that unlimited, so you have more room to browse privately while you travel, work from public Wi-Fi or connect from somewhere new. Not only will users get unlimited bandwidth, but we’re also unlocking access to 28 country locations to browse from during this period. The VPN returns to its standard 50 GB monthly limit and a standard location set on Sept. 1.
Whether you’re using the airport Wi-Fi, booking a last-minute flight or trying to access websites away from home, here are a few ways Firefox’s VPN can help while you travel:
When you’re traveling, public Wi-Fi is often part of the deal. But those networks can make it easier for others to spy on your traffic and see which websites you’re visiting. Firefox’s built-in VPN adds another layer of privacy by helping mask your IP address and makes it harder for others on the network to see your browsing activity.
The web can feel a little funky when you go abroad. Sites may load in another language, show a different local version or have trouble recognizing where you usually browse from. Maybe you need to schedule your monthly pharmacy prescription, but the site isn’t loading the way it normally does from home. Or maybe you’re trying to order a new dress to your apartment but the address isn’t registering properly.
With Firefox’s built-in VPN, you can switch your browsing location back to your home country so the sites and services you rely on feel a little more familiar while you’re away.
Firefox can recommend a VPN location based on what’s fastest and most convenient. But you can also choose from more available locations, whether you want to browse closer to home or see how the web looks somewhere else.
The full set of countries available during this summer period include: Australia, Austria, Belgium, Bulgaria, Canada, Chile, Colombia, Denmark, Finland, France, Germany, Ireland, Italy, Malaysia, Mexico, Netherlands, New Zealand, Portugal, Singapore, Spain, Sweden, Thailand, Norway, South Africa, United Kingdom and United States.
Some sites don’t always work smoothly with VPNs. If one is giving you trouble, you can turn the VPN off for that website right from the panel. You can also add sites to a list in advanced settings if you don’t want them to connect through the VPN.
Wherever you’re logging on this summer, Firefox’s built-in VPN gives you an easy way to add another layer of protection while you browse. More control for summer browsing, right where you need it. As it should be.
The post Browse more privately all summer with Firefox’s free built-in VPN appeared first on The Mozilla Blog.
Niko Matsakis
only bounds are going to be the most impactful change to Rust that you’ve never heard of. They are currently being designed and developed by the Arm team (David Wood, Rémy Rakic, et al.) as part of the Sized Hierarchy and Scalable Vector Extension project goal. This post explores the feature and aims to answer a particular question about the design (the scope of bounds, I’ll explain). But before I dive in, I want to give a bit of context.
Sized bound by default todayIn today’s Rust, every type parameter (except for Self) has a default bound called Sized:
// So this function...
fn identity<T>(t: T)-> T{t}// ...is actually short for
fn identity<T>(t: T)-> TwhereT: Sized,// <-- Added by default!
{t}A type T implements Sized if the compiler can compute the size of a T value at compilation time. This is true for almost every type, with a few notable exceptions. Consider [u32], which refers to “some number of u32 instances”. We know that a single u32 is 4 bytes, but without knowing how many u32 there are, you can’t know the size of [u32]. This means you can’t have a value of type [u32] on the stack (how big should the stack frame be?).
?SizedHowever, if you have a function like by_ref, that just takes the value by reference (i.e., by pointer), you shouldn’t need to know how big the [u32] value is, because you’re not manipulating it directly. You can have a type parameter U that doesn’t require Sized, but you have to explicitly “opt out” from the default bound:
fn by_ref<U>(t: &U)whereU: ?Sized,// <-- Opt out from the default
{}As a fun bit of historical trivia, this system was introduced way back in 2014 to accommodate Dynamically Sized Types. Before that, &[u32] was actually a built-in, indivisible type; we even wrote it like [u32]/& for a time.1
Sized vs ?Sized isn’t enough for everything we needThe Sized vs ?Sized design has served us reasonably well but it is also showing its limits. It turns out that “value has a statically computable size” vs “each value has a distinct size computable at runtime” doesn’t cover all the things you might want. For example, extern types are types whose values have no known size, even at runtime. And then Arm’s Scalable Vector Extension want to describe SIMD types where every value of the type has the same size (unlike str and [T], where each value can have a different length) but where that size is not known until runtime.
Sized hierarchyRather than just Sized or ?Sized, what we really want is to have a richer hierarchy. The current plans look something like this:
flowchart TD
subgraph S["Sizedness traits"]
Sized[["Sized (default)"]] -- extends --> MetadataSized
MetadataSized -- extends --> MaybeSized
end
where
trait Sized means that all values have the same size and that size can be computed knowing only the type.trait MetadataSized means that values can have different sizes and that size can be computed given the metadata attached to a reference to the value. Examples include [T] or dyn Trait.trait MaybeSized is implemented for all values and tells you nothing about the value’s size.Two caveats:
?Sized notation doesn’t scale to this hierarchyBut now we have a kind of problem. The ?Sized notation was predicated3 on the idea that users should specify the default bound they are opting out of – i.e., the ? is meant to say “I don’t know if this is Sized or not” (unlike the default, where you know it is Sized). But “opting out” from a bound doesn’t work so well with a multi-level hierarchy. When you write ?Sized, does that correspond to T: MetadataSized (but not T: Sized)? And what if we want to insert another level in between T: MetadataSized and T: Sized later? Then we either have to change what T: ?Sized means (to refer to the new bound) or we have to have T: ?Sized drop two levels down the hierarchy. Even more annoying, what do we do while that middle rung is unstable? Surely T: ?Sized shouldn’t refer to an unstable trait… what if we decide to remove it
only boundsThe new proposal is to write T: only MetadataSized or T: only UnknownSized instead of T: ?Sized. An only bound combines two things:
T: only MetadataSized means that T must implement at least MetadataSized.T: Sized bound.The name only comes from the fact that T: Sized implies T: MetadataSized. So the default of T: Sized already means that T: MetadataSized for free; but when you write only MetadataSized, you are saying “I don’t need the full hierarchy, just MetadataSized will do”.
only bounds work like normal bounds: ask for what you needA nice feature of only bounds is that they work more like a regular bound. Whereas a ? bound is saying “I don’t need this”, an only bound is saying what you do need. So e.g. if you are writing a function that just has references to values of type T does not care what their size is, you can write
fn by_ref<U>(u: &U)whereU: onlyMaybeSized,{}If you are writing a function that does need to compute the size of values of type V, you can ask for that capability:
fn checks_size<V>(v: &V)whereV: onlyMetadataSized,{std::mem::size_of_val(v)}only bounds allow for new levels to be added laterA nice feature of only bounds is that, later on, we can add new levels to the hierarchy, and they work normally. For example, suppose we wish to add something like Aligned where the size is not known at compilation time but the alignment is. We could change the hierarchy to
traitSized: AlignedtraitAligned: MetadataSized// <-- new!
traitMetadataSized: MaybeSizedtraitMaybeSizedand functions with U: only MaybeSized (like by_ref) and with V: only MetadataSized (with checks_size) would continue to have the same requirements. But new functions could be written with T: only Aligned that would use the new bound. And there is no conflict with stabilization; code that writes T: only Aligned can be considered unstable until that middle hierarchy is finalized.
only bounds compose normallyLike any other bound, only bounds are combined with other bounds to form the overall requirements. So it is possible to write e.g. T: only MetadataSized + Sized. This is equivalent to T: Sized and therefore equivalent to the default and therefore kind of pointless, but you can write it. Similarly, given that trait Clone: Sized, if you write T: only MetadataSized + Clone, that is kind of pointless too: you might as well write T: Clone, which would be equivalent. We plan to have a warn-by-default lint for that.
only to other “default bound families” (speculative)The final strength of only bounds is that they allow us to introduce whole new families of default bounds. One example is the idea of introducing a Move bound. Note that this is a distinct feature and is not covered under the current RFC.
All types in Rust today are “movable” and “forgettable”, meaning that you can memcpy the value from place to place so long as you stop using the previous location and you can recycle the memory where it is stored without running the value’s destructor. There is one notable exception – when you pin a value, it can no longer be moved, and you must run its destructor before its memory is reused – but otherwise this is a hard-and-fast rule. And that’s annoying!
The problem is that not being able to guarantee that a destructor runs blocks a lot of unsafe code patterns. For example, scoped tasks a la rayon depend on a destructor for safety. In sync code, this works because we’ve decided it’s UB to unwind a stack frame without running the destructors of values stored there, and so if you put a local variable on the stack, you can be sure its destructor will run. But that doesn’t work in async code! And there are times when unwinding without running destructors would be nice.
The solution is to introduce a second family of default traits. Unlike the Sized family we saw before, this family defines fine-grained capabilities about how values of that type can be used:
flowchart TD
subgraph A["Accessibility traits"]
Forget[["Forget (default)"]] -- extends --> Leak
Leak -- extends --> Destruct
Destruct -- extends --> Access
Move[["Move (default)"]] -- extends --> Access
end
Copy -- extends --> Move
The meaning of the traits are as follows:
Forget, the default, says that you can recycle the memory for a value without running its destructor.Leak says that you can skip running a destructor for a value, but only if you never reuse the memory where the value resides.Destruct says that if you have a value of this type, you can reuse the memory where it resides by running its destructor.Copy, which already exists, says that you can memcpy the place and keep using the original place; it’s not really a default, but I included it because it is relevant.Move, another default, says that you can memcpy the value to a new place if you stop using the original.Access is the root of this family. It indicates a value that can be “accessed in place” (basically, any value at all).This introduces new checks into the compiler:
a = b where b is not used later), we will check that the type implements Move (whereas today, it is always allowed).Destruct.Some implications:
T: only Destruct, then you must destruct it before your function returns. You can’t move it (because you don’t know if it implements Move) and you can’t leak or forget it either.T: only Move, then the only thing you can do with it is move it somewhere else. You can’t drop it (because you don’t know if it implements Destruct).T: only Access, because you wouldn’t be able to move it nor drop it, and hence you could not return. But you could have such a value (say) in a static.only bounds could work in the presence of multiple familiesThe spur for writing this blog post was a question in a lang team meeting on how only bounds ought to work given the existence of multiple “families” of default traits, as I described above. Although the current RFC is looking only at the Sized traits, we expect to look at the “access family” in a future RFC, so we want to be sure we are not making any decisions that won’t scale to cover both.
The way I imagine it working is like this. Each default trait is associated with one or more “families”. When you have an only bound, it “opts out” from all default traits in each family that the trait is associated with:
T: only Move opts out from Forget, Leak, Destruct – but not Sized.T: only Destruct opts out from Forget, Leak, and Move – but not Sized.T: only MetadataSized opts out from Sized – but not Forget or Move.T: only MaybeSized opts out from Sized – but not Forget or Move.You may also want to “opt back in” to some defaults. For example, T: only Move + Destruct is a sensible thing to do. It means values that can be moved and destructed but not leaked or forgotten.
Option::map requires only Movemap is an example of a function that only needs Move. You need to be able to destructure self (which moves the optional value out into a local variable v and then invoke the closure op, which again moves the wrapped value v:
impl<T: onlyMove>Option<T>{fn map<U: onlyMove>(self,op: implFnOnce(T)-> U,)-> Option<U>{matchself{Some(v)=>Some(op(v)),None=>None,}}}One interesting thing is the result type U. Using only the stuff I wrote in this blog post, it needs to be only Move, because the result will be moved into the Some value and so forth. But in-place-init would allow for this definition to omit the U: only Move bound because we could statically guarantee that the Option will be constructed in place and never moved after that.
Option::or requires only Move + DestructThe a.or(b) method on Option returns a if it is Some and otherwise returns b. This is an interesting one because the value b may not be used and therefore requires only Move + Destruct bounds.
impl<T: onlyMove>Option<T>{fn or(self,alternate: Option<T>,)-> Option<T>whereT: Destruct,// <-- because it may be dropped
{matchself{Some(v)=>Some(v),// drops `alternate`
None=>alternate,// moves `alternate`
}}}Rc requires MaybeSized + LeakThe Rc type is an example where we would want to relax bounds from both families:
struct Rc<T: onlyMaybeSized+onlyLeak>{}I believe the proper minimum bounds for Rc are:
only MaybeSized because while it can store MetadataSized or Sized things, it doesn’t have to, it can also store things of an non-computable size (although it does raise the question of how they would be freed, but that’s an allocator concern).only Leak because Rc values can form cycles and thus we can’t ever guarantee the destructor will be run. Interestingly, Rc<T> can implement Forget even if its contents don’t.The post may be a bit confusing here. The current RFC is looking only at the proposed “Sized” traits. The Access family is a speculative future extension that we are exploring but at a much earlier stage.
only with any trait?In the beginning, the plan would be that only can only be used for well-known, default traits (e.g., Move, Sized, etc). In the future though there are some thoughts to generalizing it.
An alternative that was proposed is to have the opt-out be per-type-parameter. So you might write something like
fn foo<T: MetadataSized+?default>which would “opt out” from all defaulted bounds. Obviously we’d have to bikeshed the syntax, but ignore that for now. The question is whether opting out of all defaults is better than opting out of a single family. I prefer the per-family option for two reasons:
T: only Move demonstrate that you might very reasonably wish to opt out from a single family but retain the default Sized bound. I think it’s likely that there will be many functions that want to opt out of Sized or Forget but not both.
Move: Sized to get the same effect, but I think that would be a mistake. The fact that a value’s size must be computed dynamically doesn’t inherently mean it can’t be moved.Sized xor Forget but not both?Because the Forget, Move, and similar traits mostly apply to owned values. The examples we saw with Option<T> were quite typical. And when you are moving values of type T around, you need that T to be Sized.
Rc wanted to opt out of both families with only Leak + only MetadataSized, right?Yes, that’s true, and I think that particular combo will be common. I don’t think that’s an argument for the ?default approach on its own, though, particularly since that case would not be much cleaner or shorter…
impl<T: ?default+Leak+MetadataSized>Rc<T>{}…what I think that argues for is actually trait aliases and shorthands.
Yes! I think that a future RFC could extend only bounds to allow you to define trait aliases with “only bounds” as supertraits:
traitRefCountable=onlyLeak+onlyMetadataSized;// Equivalent to:
// trait RefCountable: only Leak + only MetadataSized {}
// impl<T> RefCountable for T where T: only Leak + only MetadataSized {}
You could then use an only RefCountable bound to define Rc<T>:
impl<T: onlyRefcountable>Rc<T>Without the only, T: Refcountable would just be a regular trait bound and would not opt-out from any defaults.
Yes, we could! You could define an alias like Value:
traitValue=onlyAccess+onlyMaybeSized;Since Access and MaybeSized are both implemented for all types, this effectively becomes part of both families:
flowchart TD
subgraph All["All default families"]
subgraph A["Access family"]
Forget[["Forget (default)"]] -- extends --> Leak
Leak -- extends --> Destruct
Destruct -- extends --> Access
Move[["Move (default)"]] -- extends --> Access
end
subgraph S["MaybeSized family"]
Sized[["Sized (default)"]] -- extends --> MetadataSized
MetadataSized -- extends --> MaybeSized
end
Access -- extends --> Value
MaybeSized -- extends --> Value
end
Then you can do T: only Value and opt out from both families at once.
Ay, there’s the rub. If we wish to add a new family in the future, let’s say for values that don’t live in the same memory space (T: only Distributed…?), then Value would be “out of date” because code written against Value would still be assuming uni-memory-space values. But we could make Value into an edition-dependent alias or something like that, as has been discussed.
Value later?Yes! We can introduce a root trait at any time. So we can add the Sized-ness family first, then the Access family, and then see how we feel. Maybe we find people are very commonly opting out of both– in which case, some aliases are useful, or perhaps a Value variant.
The only way we might “regret” it is if, in practice, people usually just opted out of both and then opted back in to what they want specifically. But we already know that T: only Move will be common and clearly T: only Value + Move + Sized is more awkward in that case, so I don’t consider that very likely.
Destruct and not Drop?That name comes from the const trait RFC. There are a few reasons to move away from Drop. The first is that it is possible to have a destructor even if you don’t implement Drop: Drop really refers to user-provided logic in the destructor, but the compiler adds its own logic (“drop glue”, it’s sometimes called) to drop all the fields in the value. The second reason is that the Drop trait itself needs some revision, so moving away from that name lets us have other ways to specify custom logic (e.g., pinned self, or by-value, etc etc).
const traits anyway?Quite beautifully! In fact, the proposal from Arm for SVE is to introduce the idea of T: const Sized being “a type whose size can be computed at compilation time”, which I find quite elegant. Similarly T: const Destruct was proposed by the const RFC as a way to say that a value has a constant destructor.
T: only Move + Destruct. Couldn’t we have Destruct imply Move so that I can just write T: only Destruct?My original proposal for introducing linear types had Destruct extending Move. This would mean that the Option::or proposal could simply do U: only Destruct and not U: only Move + Destruct. However, Alice Ryhl and others pointed out that there are immovable types that must nonetheless be destructed, so it doesn’t make sense to combine those.
The Project Goal has a lot of details. The latest updates are available on the tracking issue. If you like watching videos, I recommend David Wood’s Rust Nation talk.
I want to close with a meta-observation and a big shout-out to the Arm team. I think they are showing how awesome open-source can be. The Arm team’s primary motivation is adding support for Scalable Vector Extension. This helps Rust make full use of Arm processors. This is, in and of itself, a laudable goal, and valuable to Rust: One of Rust’s assets, in my view, is that it gives you access to all the power your processor has to provide, and that should include unique extensions.
But rather than add the feature as a kind of special-case extension to Rust, the Arm team is going further and driving a general purpose improvement, one that will unlock a bunch of other features (extern types and, to some extent, guaranteed destructors; guaranteed destructors themselves unlock scoped async threads and better Wasm integration). I love that.
In fact, I recall that in one of my blog posts I proposed writing "" as the way to spell &str. I kinda wish we had done that just for the sheer wackiness of it (fn foo(name: "")). ↩︎
I prefer names that refer to the operations that can be performed on the values, so e.g. instead of MetadataSized I would prefer SizeOfVal, since it means that you can invoke the std::mem::size_of_val function on it. ↩︎
Little logic pun there for you. ↩︎
Firefox Tooling Announcements
Bugs resolved in Moz-Phab 2.15.2:
moz-phabpatch --raw leaks a global logger level, causing order-dependent test failuresDiscuss these changes in #engineering-workflow on Slack or #Conduit Matrix.
1 post - 1 participant
The Mozilla Blog
Your browser tabs say a lot about your life: work projects, vacation plans, shopping carts and all the rabbit holes in between.
Add the world’s biggest soccer tournament to the mix, and your browser is suddenly juggling scores to check, streams to watch, lineups to scan and group chats to keep up with. And since many matches kick off during the workday, there will be lots of temptation to just sneak a peek at the action between meetings.
Firefox is built to be your ultimate second screen. When the tournament is on, keep Firefox open to follow the action, keep up with the conversation, and stay on top of everything else happening online – whether you’re watching from the couch or checking in on your mobile device on the go.
You’ll find a World Cup widget, custom wallpapers, and game-day multitasking tools. Plus, Firefox is teaming up with Trevor Noah, a soccer superfan, as he hosts live watch parties for the tournament moments everyone will be talking about.
When the action is happening fast, keeping up should be as easy as opening a new tab.
Firefox’s World Cup widget gives you the latest tournament updates every time you open a new tab (and you can turn it off anytime). With key match information always within easy reach, it’s easy to stay on top of the action without bouncing between apps or having to browse around.
You can follow your favorite teams and even customize Firefox with wallpapers that bring big fan vibes to every new tab. To get started, open a new tab and click the Customize icon at the bottom of the page.
With picture-in-picture in Firefox, you can detach a video from its tab and pin it anywhere on your screen so you can keep watching while working on other stuff.
Open two tabs side by side in one window with split view. That way, you can keep live updates on one half and stats, searches or chats on the other.
Remember what we said about your tabs representing your life? While 99 tabs of fandom can make it feel more chaotic, your browser doesn’t have to.
With tab groups in Firefox, you can create separate groups for:
Match days are better with good company. This summer, Firefox is teaming up with Trevor Noah to be his second screen sidekick for his World Cup watch party on YouTube.
Hosted live throughout the tournament, the series will feature Trevor alongside some of his best friends plus celebrity guests as they react to matches, highlights and the internet moments coming out of each day’s games.
Trevor is a longtime Firefox user whose comedy and commentary have explored how technology shapes everyday life. That makes this collaboration feel especially fitting for Mozilla, a company built around the idea that the internet should work better for everyone.
“I’m grateful to our incredible brand partner Firefox for helping make this series possible and for believing in the power of sport to bring people together,” said Noah.
“Events like this are some of the biggest shared experiences on the internet,” said John Solomon, Chief Marketing Officer at Mozilla. “While many people stop their lives for the World Cup, those that can’t follow them while working, traveling, connecting with friends and family, and doing everything else they need to do online. Firefox is built for moments like this, and Trevor is a fitting partner. He’s a longtime Firefox user who believes, like we do, that technology should work for people, helping them stay connected to the moments, information and communities they care about most.”
Make Firefox your World Cup sidekick this summer. Follow the tournament with the World Cup widget, multitask like a pro with picture-in-picture, split view and tab groups, and get into the spirit with custom wallpapers, all in the browser that helps you get more out of every match.
The post Make Firefox your World Cup sidekick this summer appeared first on The Mozilla Blog.
Will Kahn-Greene
Bleach is a Python library for sanitizing and linkifying text from untrusted sources for safe usage in HTML.
Bleach 6.4.0 includes two security fixes, a fix to tinycss2 dependency requirements, and some other things.
See the changes here:
https://bleach.readthedocs.io/en/latest/changes.html#version-6-4-0-june-5th-2026
I haven't used Bleach on a project in years, but I still had some time to maintain it. That changed about a year ago when I got re-orged into a new role and I haven't had time to do any Bleach work since then.
To recap, Bleach sits on top of html5lib which hasn't been actively maintained in years. It is dangerous to maintain Bleach in that context.
We vendored html5lib so we could make adjustments to the library to keep Bleach going. This is not a sustainable approach, but it was ok for the short term.
Over the years, we've talked about other options:
find another library to switch to
take over html5lib development
fork html5lib and vendor and maintain our fork
write a new HTML parser
etc
None of those are feasible for me.
Bleach has been a solo-maintained project for a while now. The world is crazy and it's much harder to build a team of trusted maintainers now than it was (or at least, it sure feels that way). I don't see any possibility of increasing the maintenance team or passing it to someone else responsibly.
Switching contexts from my regular work to Bleach is really hard. Bleach is complicated, the problem domain is complicated, and there's a lot of nuanced context. I can't just switch gears, spend 15 minutes on Bleach to do something, and then switch back to the rest of my day. I periodically get nag messages about this which are entirely valid, but there's nothing I can do about it. It doesn't feel great.
Then in 2025, Emil, a long-time Bleach contributor, built justhtml which gives us an easy migration path off of Bleach. He even took the time to write a migration guide.
In 2019, when I stepped down the first time, I wrote a post on stepping down.
In 2023, when I deprecated the project, I wrote a post on Bleach 6.0.0 and deprecation.
From the first commit on 2010-02-18 to today's final commit on 2026-06-05, the Bleach project lasted 16 years, 3 months — 5,951 days, or about 16.29 years.
There were 64 releases.
There were roughly 960 commits.
From 80 roughly contributors
Top 3:
Will Kahn-Greene: 462
James Socol: 182
Greg Guthe: 133
Roughly 5,040 lines of Python code excluding the vendored html5lib.
I was maintainer from October 2015 to now--that's a little under 11 years.
It feels weird to end a project that's outlived many of the Mozilla sites and Python web frameworks it was designed to protect.
This is the end of the project.
Bleach. Last release.
If you're still using Bleach, I think you have three options:
End your project. Maybe you don't need to be maintaining your thing anymore? Use Bleach as your reason to exit and do something different with your time on Earth.
Switch to the sanitizer API. Rework your project to use the sanitizer API.
Swap Bleach out for justhtml. Emil provided a migration guide for switching from Bleach to justhtml.
Good luck with whatever option you choose!
Many thanks to James who created Bleach and gave it a set of first principles that guided our choices for 16 years.
Many thanks to Greg who I worked with on Bleach for a long while and maintained Bleach for several years. Working with Greg was always easy and his reviews were thoughtful and spot-on.
Many thanks to Emil who was a contributor to Bleach for a long while and created justhtml providing Bleach users a migration path.
Many thanks to Jonathan who, over the years, provided a lot of insight into how best to solve some of Bleach's more squirrely problems.
Many thanks to Sam who was an indispensible resource on HTML parsing and sanitizing text in the context of HTML.
Many thanks to all the users and contributors of Bleach!
For more specifics on this release, see here: https://bleach.readthedocs.io/en/latest/changes.html#version-6-4-0-june-5th-2026
Documentation and quickstart here: https://bleach.readthedocs.io/en/latest/
Source code and issue tracker here: https://github.com/mozilla/bleach/
Firefox Nightly
Script to find new contributors from bug list
The Rust Programming Language Blog
If you want to financially support the development of Rust, please consider donating to the Rust Foundation Maintainers Fund.
A few months ago, the Rust Foundation announced the Rust Foundation Maintainers Fund (RFMF). Since then, the Rust Project has been closely cooperating with the Rust Foundation to determine how exactly this fund will be used to support Rust maintainers. This resulted in the acceptance of RFC #3931, which established the Funding team and the Maintainer in Residence program.
The primary goal of the Funding team is to ensure that maintainers who work on Rust and its toolchain will be properly supported. We will talk to Rust Project members to figure out their funding situation, meet Rust team leads to learn about their maintenance needs, approach companies to find opportunities for them to invest into Rust by supporting Rust maintainers, coordinate various funding efforts and ensure that the beneficial effects of funded maintenance are visibly promoted, with the help of the Content team.
Maintainer in Residence is a new program dedicated to financially supporting existing Rust Project maintainers1. Each Maintainer in Residence will be funded to maintain one or more critical parts of Rust, such as the compiler, the standard library, Cargo, Clippy or one of many other projects that the Rust Project develops and maintains. The funded work will include activities such as performing large-scale refactorings, code reviews, unblocking new features, issue triaging, mentoring other contributors and more, and will be split between priorities guided by the teams they are supporting and priorities of their own choosing within the Project. Where applicable, Maintainers in Residence are also encouraged to propose, champion, and drive forward Rust Project Goals.
The goal of this program is to provide stable and long-term funding so that maintainers can focus on important work that ensures the long-term health of Rust. The funding team will select Maintainers in Residence based on funding availability and maintenance needs within the Rust Project, and help ensure that they are successful. We expect that this will usually be a (near) full-time position, but that will depend on the nature of the work and the area of maintenance.
This program extends our existing support for Rust maintainers, such as the program management program and the compiler-ops program. An important development is that we now have a centralized mechanism for gathering donations from both individuals and companies, and a dedicated team that will help direct those funds to specific maintainers. You can find more details about the funding team and the Maintainer in Residence program in the RFC.
We expect to hire the first Maintainer in Residence in the upcoming months and announce it on this blog, so stay tuned!
If you are an individual who wants to help Rust succeed and thrive, you can donate to the RFMF through GitHub Sponsors2. Companies who would like to invest in better maintenance of Rust can also donate through GitHub Sponsors or they can contact the Rust Foundation directly.
The important thing is that all proceeds from this fund will be directly used to support Rust Project maintainers. We currently expect that to happen primarily through the Maintainer in Residence program, but it can also be done in the form of smaller-scale grants or other mechanisms, as determined by the Funding team. We will figure this out on the go, as this is also quite new for us.
We really appreciate each donation, however small, because with more money we can hire more maintainers to ensure that we can continue to develop Rust and that important improvements are not blocked on maintenance tasks. This is especially important at this time, where Rust is starting to get used more and more in the industry in various application areas, which increases the need for sustained maintenance. The importance of multiple funding sources is underscored by an unfortunate trend we currently observe, where key Rust maintainers are losing their funding for Rust work due to budget shifts. The Rust Foundation Maintainers Fund is designed to provide stable funding for Rust maintainers that is less dependent on sudden shifts in the job market and the IT industry.
As with most things, there is no one-size-fits-all solution, so there are multiple ways to support Rust financially. The RustNL Maintainers Team recently hired several Rust Project maintainers. Previously, we wrote about how you can support specific individuals working on Rust. And there are also Rust Project Goals in search of funding. We welcome all efforts that can help support Rust Project maintainers, who often do work that is near invisible and thankless, while at the same time incredibly important and necessary, on a volunteer basis.
Thank you for considering sponsoring the development and maintenance of Rust! You can find more information about funding Rust on our Funding page.
This program was inspired by the Developer in Residence concept used by the Python Software Foundation (PSF), with which we led several helpful discussions. Thank you, PSF! ↩
Note that the fact that GitHub Sponsors is currently enabled on the rustfoundation GitHub organization, and not the rust-lang organization, is an implementation detail that might change in the future. All donations raised on this Sponsors page will be routed to the Rust Foundation Maintainers Fund and will be spent on directly supporting Rust Project maintainers. ↩
Firefox Nightly
Script to find new contributors from bug list
Tom Ritter
WebGL exposes the details of your graphics hardware (specifically, the string that describes the rendering engine) in 2 ways. There are three levels of protection that browsers have taken to protect this data.
gl.getParameter(gl.VENDOR) and gl.getParameter(gl.RENDERER) - these are the 'simple' names. At some point in the past, someone argued that it wasn't enough information, and therefore we have a second APIlet ext = gl.getExtension('WEBGL_debug_renderer_info'); and then gl.getParameter(ext.UNMASKED_VENDOR_WEBGL) and gl.getParameter(ext.UNMASKED_RENDERER_WEBGL)The unmasked values are intended to be the more detailed ones, so always make sure you're comparing apples to apples. Another axis is that WebGL can render with Hardware or Software. This isn't a guarentee which one you'll get, but you can hint towards one or the other and the browser may or may not respect it. Here are your values:
Alright, now let's talk about what browsers do about it. There's no point in talking about Vendor, Renderer, and Unmasked Vendor - they don't really show as much detailed info, it's all about Unmasked Renderer. There are three levels:
Safari and Tor Browser give constant values.
Firefox 'rounds'.
Chrome (and Brave, and I assume all-ish other Chrome-based browsers) give the exact value.
Firefox actually is purusing constant values, this week. I wrote this document for our QA team to test it. (You can get a sense of the internal sausage making it takes to launch a privacy feature from it.) I don't know if you can see the dates but I made it May 20th. The problem is this - websites use this data legitimately to adjust behavior so that users get the best experience possible. I found one example where they detect a buggy graphics stack; and a couple of examples where they adjust rendering so things are more performant for users with lower end machines - a problem Apple has less to worry about because they only support certain machine models!
A common response to this seems to be ambivalence, and I would suggest that is a bit elitist. Yes, if you're caring about the details reveal by a particular Web API you probably have a computer where you don't need to worry, but making the web work well for everyone is important for equitable access to improving everyone's human condition.
We have been bucketing WebGL Renderer since 2021. While many of our (supported, on-by-default) fingerprinting protections are part of Enhanced Tracking Protection - rolling out first in PBM/ETP Strict before making it to ETP Standard/Normal Browsing Mode - the bucketing is on by default, for everyone, and is not disabled if ETP is disabled.
How much of a difference does it make? A lot! Here is the distribution of the raw values. 83,705 distinct values.
Compare that to the bucketed data. 131 distinct values.
Now this data is from Firefox, so I cant say conclusively what the distribution of data is in other browsers, but... yeah. To claim Chrome (of all browsers!) is doing this better than us is pure FUD. We're making a big impact in how fingerprintable you are today and we're trying to improve it even further.
Nick Fitzgerald
Structure-aware fuzzing can better exercise the system under test (SUT) by
crafting inputs in the format expected by the SUT, rather than throwing
pseudorandom bytes against it. That is, it avoids “shallow” inputs that the SUT
will reject early (for example, syntactically invalid source text when fuzzing a
programming language’s compiler) and only produces inputs that go “deep” into
the SUT (e.g. programs that type-check and exercise the mid-end optimizer and
backend code generator). The Rust fuzzing ecosystem is largely built around
cargo-fuzz and the libfuzzer-sys crate, which provides two methods for
structure-aware fuzzing:
Generating structured inputs from scratch with the arbitrary crate
Mutating existing inputs from the fuzzer’s corpus in a structure-aware
manner, thereby producing new structured inputs, via the
fuzz_mutator! hook
While the two methods are not technically mutually exclusive, combining the two can be difficult and engineering resources are finite. So:
If we are only implementing one approach, is generation or mutation better?
To help answer this question, I implemented structure-aware generation and
mutation of guaranteed-valid WebAssembly (Wasm) instruction sequences. This
task is small enough to be easily understandable but large enough and real
enough to (hopefully) be representative and applicable to other domains, or, at
the very least, interesting.1 To evaluate their effectiveness, I
used Wasmtime as the SUT, libfuzzer-sys as the fuzzing engine driving
everything, and then compared code coverage over time when using mutation-based
fuzzing versus generation-based fuzzing.
Additionally, there are many ways we can generate pseudorandom WebAssembly instruction sequences. In this experiment, I’ve evaluated three methods:
Unconstrained instruction sequence generation followed by a fixup pass to ensure validity
Generating valid instructions in a forwards, bottom-up manner (from operands to operators)
Generating valid instructions in a backwards, top-down manner (from operators to operands)
In contrast, while there are surely many ways to mutate a given WebAssembly instruction sequence into a new, valid instruction sequence, I’ve only implemented one method: perform an arbitrary instruction insertion, deletion, or replacement, producing a new but probably-invalid instruction sequence, and then run the same fixup pass mentioned previously to ensure validity. This is the direct mutation-based equivalent of the first generation-based method.
Before continuing further, I want to disclose that I am the author of
wasm-smith and mutatis, and a maintainer of Wasmtime, arbitrary,
libfuzzer-sys, and cargo-fuzz. That is, while I am familiar with Wasm,
fuzzing, fuzzing Wasm, and both the arbitrary and mutatis crates, I may also
be propagating my own biases into these implementations.
A generation-based fuzzer uses a generator to create a pseudo-random test cases from scratch, feeds these into the system under test, and reports any failures to the user:
fn generation_based_fuzzing<T>(
// A test-case generator.
generator: impl Fn() -> T,
// A function to run the system under test with a
// generated test case, returning a result that
// describes whether the run was successful or
// not.
run_system_under_test: impl Fn(&T) -> FuzzResult,
) {
loop {
// Generate an input.
let input = generator();
// Run the input through the system under test.
let result = run_system_under_test(&input);
// If the system crashed, panicked, failed an
// assertion, violated an invariant, or etc...
// then report that to the user.
if let Err(failure) = result {
report_to_user(&input, failure);
}
}
}
On the other hand, mutation-based fuzzers are given an initial corpus of inputs and create new inputs by mutating existing corpus members. They run each new input through the SUT, report failures the same as before, and if the new input was “interesting” (for example, exercised new code paths in the SUT that weren’t previously covered in any other input’s execution) then the new input is added into the corpus for use in future test iterations:
fn mutation_based_fuzzing<T>(
// A corpus of test cases.
corpus: &mut Corpus<T>,
// A function to pseudo-randomly mutate an existing
// input into a new input.
mutate: impl Fn(&T) -> T,
// A function to run an input in the system under
// test, returning a result that describes whether
// the run was successful or not.
run_system_under_test: impl Fn(&T) -> FuzzResult,
) {
loop {
// Choose an old test case from the corpus.
let old_input = corpus.choose_one();
// Pseudo-randomly mutate that old test case,
// creating a new one.
let input = mutate(old_input);
// Run the input through the system under test.
let result = run_system_under_test(&input);
// If the system crashed, panicked, failed an
// assertion, violated an invariant, or etc...
// then report that to the user.
if let Err(failure) = result {
report_to_user(&input, failure);
}
// If the input was interesting, for example if
// it executed previously-unknown code paths,
// then add it into the corpus for use in a
// future iteration.
if result.input_was_interesting() {
corpus.insert(input);
}
}
}
The two approaches are not mutually exclusive and hybrid generation- and mutation-based fuzzers exist.
More resources:
Structure-unaware fuzzing will generate pseudorandom byte sequences and pass them directly to the SUT. If the SUT expects some sort of structured input, e.g. the source text for a programming language, it is likely that these byte sequences are invalid and will be rejected early by the SUT’s frontend. For example, when fuzzing a compiler, the input is rejected as syntactically invalid by the parser or rejected as semantically invalid by the type checker. This can be useful when hardening a tokenizer, parser, or type checker, but is less useful when hunting for misoptimization in the mid-end or bad instruction encoding in the backend because the inputs are unlikely to make it that far through the compiler’s pipeline.
Structure-aware fuzzing will produce inputs that match the SUT’s expected input format. Returning to the compiler-fuzzing example, structure-aware fuzzing lets us generate valid programs for the compiler, so we can exercise more of the mid-end and backend, rather than just the frontend.
Structure-aware fuzzing is often generation-based: for example using
grammar-based fuzzing to generate pseudorandom strings from a given language
grammar or language-specific tools like csmith and wasm-smith that
generate C and WebAssembly programs respectively. But structure-aware fuzzing
can also be mutation-based: libFuzzer’s custom mutator
example
implements a structure-aware mutator for zlib-compressed strings, where the raw
input is decompressed, the decompressed data is mutated, and then the mutated
data is recompressed to provide the new raw input. The mutator is aware of the
SUT’s zlib-compressed input structure.
More resources:
google/fuzzing on structure-aware
fuzzingrust-fuzz book on structure-aware
fuzzingarbitrary CrateThe arbitrary crate helps Rust developers write custom structure-aware
generators for fuzzing. It provides building blocks and abstractions for
translating a raw byte sequence (usually from a fuzzing engine) into a
structured type, effectively interpreting the raw bytes as a “DNA string” or set
of predetermined choices for its decision tree. The library also provides a
derive(Arbitrary) macro to automatically implement its functionality for a
given type.
Because arbitrary is effectively implemented by combining decision trees, it
is extremely easy to create imbalanced trees and unintentionally bias the
distribution of generated test cases.
mutatis CrateThe mutatis crate is, at a high-level, performing the same role for
authoring structure-aware mutators that arbitrary plays for generators. That
is, it provides Rust developers with abstractions and combinators for creating
custom structure-aware mutators. It also provides a derive(Mutate) macro to
automatically implement its functionality for a given type.
mutatis is designed to resist bias via a two-phase design: first, it
enumerates all of the candidate mutations that could be applied to a test case,
and only afterwards chooses a particular random mutation from the candidate set
to actually apply.
WebAssembly is a virtual instruction set designed to be safe, portable, and
fast. It is a stack machine where an instruction’s operands are popped off a
stack during execution and results pushed. It has sandboxed linear memories,
global variables, and local variables (the latter two effectively being two
kinds of virtual registers). The following instruction sequence computes a * 3
and stores the result into memory at address p:
;; []
local.get $p
;; [p]
local.get $a
;; [p, a]
i32.const 3
;; [p, a, 3]
i32.mul
;; [p, a*3]
i32.store
;; []
The range of all three generators and the mutator is the same universe of
WebAssembly programs. They are all implemented on top of the same Module and
Inst types, and, given enough time, none is capable of producing an
instruction sequence that another cannot. This helps ensure that our comparison
is apples-to-apples. However, due to their different implementation techniques,
they do produce different distributions of WebAssembly programs within that
universe, and produce test cases at different speeds from one another, which
ultimately affects how efficiently they exercise the SUT.
All of the generators are built on top of the arbitrary crate. The mutator
is built on top of the mutatis crate.
The Module type is our structured fuzzing input. It describes a WebAssembly
module containing a variable number of linear memories, a variable number and
type of globals, and one function with a variable number and type of parameters
and results and a variable instruction sequence:
/// A WebAssembly module of the shape:
///
/// (module
/// (memory ...)
/// (memory ...)
/// ...
///
/// (global ...)
/// (global ...)
/// ...
///
/// (func (export "run") (param ...) (result ...)
/// ...
/// )
/// )
pub struct Module {
num_memories: u32,
globals: Vec<Global>,
param_types: Vec<ValType>,
result_types: Vec<ValType>,
instructions: Vec<Inst>,
}
The Inst type is an enum of all the WebAssembly instructions the
implementations support, which is all of the integer, float, SIMD, memory,
local, and global instructions. Control-flow, threading, table, and GC
instructions are not supported. Here is a subset of Inst’s definition:
/// A WebAssembly instruction.
pub enum Inst {
Drop,
LocalGet(u32),
GlobalGet(u32),
// ...
I32Const(i32),
I32Add,
I32Sub,
I32Mul,
// ...
I64Const(i64),
I64Add,
I64Sub,
I64Mul,
// ...
F32Const(f32),
F32Add,
F32Sub,
F32Mul,
// ...
F64Const(f64),
F64Add,
F64Sub,
F64Mul,
// ...
I32WrapI64,
I64ExtendI32S,
I64ExtendI32U,
// ...
V128Const(i128),
I8x16Add,
I8x16Sub,
// ...
I32Load(u32),
I64Load(u32),
// ...
I32Store(u32),
I64Store(u32),
// ...
MemorySize(u32),
MemoryGrow(u32),
}
There is an Inst::operand_types method that returns the types that the
instruction pops from the stack, and an Inst::result_type method that returns
the type of the value that the instruction pushes onto the stack, if
any. Finally, the Module::to_wasm_binary method encodes the module into
WebAssembly’s binary format, so it can be fed into Wasmtime. These methods are
used, directly or indirectly, in every generator and mutator implementation.
arbThe arb generator leverages derive(arbitrary::Arbitrary) on our structured
input types to generate a pseudorandom instance of Module, unconstrained by
validity. The module’s instruction sequence is almost certainly not valid at
this point: it likely is missing operands for instructions, producing more
results than the function’s signature describes, producing results of types that
don’t match the function signature, accessing globals and locals that don’t
exist, etc… Having produced an instance of Module, it next calls the
Module::fixup method to mutate the Module so that it is valid.
The fixup method works by abstractly interpreting the instruction sequence to
track the types of each value on the stack at every program point. Whenever an
instruction’s operand types don’t match the types on top of the stack, it
generates dummy values of the correct type. When the instructions produce more
values than the function’s signature proscribes, it emits drop instructions.
impl Module {
pub fn fixup(&mut self, mut make_value: impl FnMut() -> i64) {
// ...
// The fixed-up instructions.
let mut fixed = Vec::with_capacity(
self.instructions.len(),
);
// The types on the stack at any given program
// point. Similar to the Wasm spec's appendix's
// validation algorithm.
let mut stack: Vec<ValType> = Vec::new();
for inst in mem::take(&mut self.instructions) {
// Special-case `drop` because it is
// polymorphic.
if matches!(inst, Inst::Drop) {
if stack.is_empty() {
fixed.push(
ValType::I32.make_const(make_value()),
);
} else {
stack.pop();
}
fixed.push(inst);
continue;
}
// First clamp entity indices to valid
// ranges.
let Some(inst) = self.fixup_inst_immediates(
&mut make_value,
has_mutable_global,
inst,
) else {
continue
};
// Then make sure that the stack has
// operands of the correct types for this
// instruction.
self.fixup_stack(
&mut make_value,
&mut fixed,
&mut stack,
&inst,
);
// Finally, apply the effects to the stack.
let len_operands = inst.operand_types(
&self.globals,
).len();
stack.truncate(stack.len() - len_operands);
stack.extend(inst.result_type(
&self.param_types,
&self.globals,
));
fixed.push(inst);
}
// ...
self.instructions = fixed;
}
fn fixup_stack(
&mut self,
mut make_value: impl FnMut() -> i64,
fixed: &mut Vec<Inst>,
stack: &mut Vec<ValType>,
inst: &Inst,
) {
let needed = inst.operand_types(&self.globals);
let n = needed.len();
if stack.len() >= n {
if (0..n).all(|i| {
stack[stack.len() - n + i] == needed[i]
}) {
// All needed operands are on the stack.
return;
}
} else {
if stack.iter().enumerate().all(|(i, ty)| {
*ty == needed[i]
}) {
// A prefix of needed operands are on the
// stack; make constants for the tail that
// are missing.
for ty in &needed[stack.len()..] {
fixed.push(ty.make_const(make_value()));
stack.push(*ty);
}
return;
}
}
// Otherwise, just make constants for all the
// needed operands.
for ty in needed {
fixed.push(ty.make_const(make_value()));
stack.push(*ty);
}
}
// ...
}
The fixup method also makes sure that for all instructions that have an
immediate referencing some entity, the referenced entity is valid. For example,
for a local.get $l instruction, it ensures that local $l actually exists or
else rewrites the local to one that does exist.
impl Module {
// ...
fn fixup_inst_immediates(
&mut self,
mut make_value: impl FnMut() -> i64,
has_mutable_global: bool,
mut inst: Inst,
) -> Option<Inst> {
match &mut inst {
Inst::LocalGet(l) => *l %= self.param_types.len() as u32,
// ...
Inst::I32Load(m)
| Inst::I64Load(m)
| Inst::F32Load(m)
| Inst::F64Load(m)
| Inst::V128Load(m) => {
if self.num_memories == 0 {
return None;
}
*m %= self.num_memories;
}
// ...
_ => {}
}
Some(inst)
}
}
After calling fixup, the arb generator invokes Module::to_wasm_binary to
get the encoded Wasm program.
bottom_upThe bottom_up generator also uses abstract interpretation to track the types
of values on the stack. It generates instructions in forwards order, from
operands to operators. It begins with an empty stack, filters candidate
instructions down to just those that would be valid given the types currently on
the stack, randomly chooses one, updates the stack types accordingly, and
repeats the process. This is the same approach that wasm-smith uses. After
generating instructions this way, it then makes sure that the final types on the
stack match the function signature’s results, similar to the end of fixup.
impl Module {
pub fn bottom_up(u: &mut Unstructured<'_>) -> Result<Self> {
// ...
let max_insts = u.int_in_range(1..=MAX_INSTS)?;
let mut instructions = Vec::new();
let mut stack: Vec<ValType> = Vec::new();
for _ in 0..max_insts {
if stack == result_types && u.ratio(3, 4)? {
break;
}
// Choose a random instruction whose operand
// types match those currently on the stack.
let inst = choose_inst_bottom_up(
u,
&stack,
¶m_types,
&globals,
num_memories,
)?;
// Apply this instruction's effects to the
// stack.
apply_inst(
&inst,
&mut stack,
¶m_types,
&globals,
);
instructions.push(inst);
}
// ...
Ok(Module {
param_types,
result_types,
globals,
num_memories,
instructions,
})
}
}
fn choose_inst_bottom_up(
u: &mut Unstructured<'_>,
stack: &[ValType],
param_types: &[ValType],
globals: &[Global],
num_memories: u32,
) -> Result<Inst> {
// Build up all the valid candidate instructions.
let mut candidates: Vec<Inst> = Vec::new();
// Producers are always okay: [] -> [t]
candidates.push(Inst::I32Const(0));
candidates.push(Inst::I64Const(0));
candidates.push(Inst::F32Const(0.0));
candidates.push(Inst::F64Const(0.0));
candidates.push(Inst::V128Const(0));
if !param_types.is_empty() {
candidates.push(Inst::LocalGet(0));
}
// ...
let top = stack.last().copied();
let second = stack.get(stack.len() - 2).copied();
// Drop needs 1 operand of any type: [t] -> []
if top.is_some() {
candidates.push(Inst::Drop);
}
// i32 unary: [i32] -> [...]
if top == Some(I32) {
candidates.push(Inst::I32Clz);
candidates.push(Inst::I32Ctz);
candidates.push(Inst::I32Popcnt);
// ...
}
// i64 unary: [i64] -> [...]
if top == Some(I64) {
candidates.push(Inst::I64Clz);
candidates.push(Inst::I64Ctz);
candidates.push(Inst::I64Popcnt);
// ...
}
// ...
// i32 binary: [i32 i32] -> [...]
if top == Some(I32) && second == Some(I32) {
candidates.push(Inst::I32Add);
candidates.push(Inst::I32Sub);
candidates.push(Inst::I32Mul);
// ...
}
// i64 binary: [i64 i64] -> [...]
if top == Some(I64) && second == Some(I64) {
candidates.push(Inst::I64Add);
candidates.push(Inst::I64Sub);
candidates.push(Inst::I64Mul);
// ...
}
// ...
// Choose a random instruction from the
// candidates.
let mut inst = *u.choose(&candidates)?;
// If the instruction has immediates, generate
// them here, as they were hard-coded during
// candidate selection.
match &mut inst {
Inst::I32Const(v) => *v = u.arbitrary()?,
Inst::I64Const(v) => *v = u.arbitrary()?,
// ...
Inst::GlobalGet(g) => {
*g = u.int_in_range(0..=(globals.len() as u32 - 1))?;
}
// ...
Inst::I32Load(m)
| Inst::I64Load(m)
| Inst::F32Load(m)
| Inst::F64Load(m)
| Inst::V128Load(m)
| Inst::I32Store(m)
| Inst::I64Store(m)
| Inst::F32Store(m)
| Inst::F64Store(m)
| Inst::V128Store(m)
| Inst::MemorySize(m)
| Inst::MemoryGrow(m) => {
*m = u.int_in_range(0..=(num_memories - 1))?;
}
_ => {}
}
Ok(inst)
}
After constructing a Module via bottom_up, we don’t need to call fixup
because the module is already valid by construction, so all that’s left is
invoking Module::to_wasm_binary to get the encoded Wasm program.
top_downThe top_down generator is very similar to bottom_up, but instead of
generating instructions forwards, from operands to operators, it generates them
backwards, from operators to operands. Instead of maintaining a stack of the
types of values generated thus far by the instruction sequence prefix, it
maintains a stack of the types of values expected by the instruction sequence
suffix. This is the approach that rgfuzz by Park, Kim, and Yun
takes.2
impl Module {
pub fn top_down(
u: &mut Unstructured<'_>,
) -> Result<Self> {
// ...
let max_insts = u.int_in_range(1..=MAX_INSTS)?;
let mut instructions = Vec::new();
let mut needed = result_types.clone();
for _ in 0..max_insts {
if needed.is_empty() && u.ratio(3, 4)? {
break;
}
// Choose a random instruction in a
// top-down manner.
let inst = choose_inst_top_down(
u,
needed.last().copied(),
¶m_types,
&globals,
num_memories,
)?;
// Pop the result type from `needed`, if
// any, as it's been satisfied.
let ty = inst.result_type(
¶m_types,
&globals,
);
if ty == needed.last().copied() {
needed.pop();
}
// Add operand type demands.
match &inst {
Inst::Drop => {
// `drop` is polymorphic; choose
// a random type.
needed.push(u.arbitrary()?);
}
Inst::GlobalSet(g) => {
needed.push(globals[*g as usize].ty);
}
_ => {
needed.extend_from_slice(
inst.operand_types(&globals),
);
}
}
instructions.push(inst);
}
// Fill remaining needed types with
// constants.
for ty in needed.iter().rev() {
instructions.push(
ty.make_const(u.arbitrary()?),
);
}
// Instructions were generated backwards, so
// reverse.
instructions.reverse();
Ok(Module {
param_types,
result_types,
globals,
num_memories,
instructions: prefix,
})
}
}
fn choose_inst_top_down(
u: &mut Unstructured<'_>,
target_ty: Option<ValType>,
param_types: &[ValType],
globals: &[Global],
num_memories: u32,
) -> Result<Inst> {
let mut candidates: Vec<Inst> = Vec::new();
match target_ty {
Some(I32) => {
candidates.push(Inst::I32Const(0));
candidates.push(Inst::I32Add);
candidates.push(Inst::I32Sub);
candidates.push(Inst::I32Mul);
// ...
}
Some(I64) => {
candidates.push(Inst::I64Const(0));
candidates.push(Inst::I64Add);
candidates.push(Inst::I64Sub);
candidates.push(Inst::I64Mul);
// ...
}
Some(F32) => {
candidates.push(Inst::F32Const(0.0));
candidates.push(Inst::F32Add);
candidates.push(Inst::F32Sub);
candidates.push(Inst::F32Mul);
// ...
}
// ...
None => {
// Nothing needed. `drop`, `global.set`, and
// stores add demand.
candidates.push(Inst::Drop);
if globals.iter().any(|g| g.mutable) {
candidates.push(Inst::GlobalSet(0));
}
if num_memories > 0 {
candidates.push(Inst::I32Store(0));
// ...
}
}
}
let mut inst = *u.choose(&candidates)?;
// If the instruction has immediates, generate
// them here, as they were hard-coded during
// candidate selection. Same as `bottom_up`.
match &mut inst {
// ...
}
Ok(inst)
}
Similar to bottom_up, after we’ve constructed a Module via top_down, we
don’t need to call fixup because the module is already valid by construction.
All that’s left is invoking Module::to_wasm_binary to get the encoded Wasm
program.
mutatemutate is, as the name implies, a mutator rather than a generator. It is the
direct equivalent of the arb generator, but for mutation: it uses
derive(mutatis::Mutate) on Module and Inst to automatically generate
custom mutators for these types, rather than authoring them by hand. After
producing a new Module by mutating an old Module, that new Module probably
represents an invalid Wasm program, in the same way that
derive(arbitrary::Arbitrary) produces Modules that are probably invalid. And
mutate also uses the same approach that arb does to resolve this problem:
the fixup method.
But first, a mutator-specific wrinkle is that fuzz_mutator! gives us a mutable
byte slice to mutate, not a Module. We address this gap by deriving the
serde crate’s Serialize and Deserialize traits on Module and Inst,
deserializing a Module from the mutable byte slice, mutating that deserialized
Module with mutatis, and then reserializing it back into the mutable byte
slice. We use the postcard crate here, but could just as easily use
bincode, JSON, or protobuf.
use libfuzzer_sys::{fuzz_mutator, fuzz_target, fuzzer_mutate};
fuzz_mutator!(|
data: &mut [u8],
size: usize,
max_size: usize,
seed: u32,
| {
// With probability of about 1/8, use default
// mutator.
if seed.count_ones() % 8 == 0 {
return fuzzer_mutate(data, size, max_size);
}
// Try to decode using postcard; fallback to
// default input on failure.
let mut module: Module =
postcard::from_bytes(&data[..size])
.ok()
.unwrap_or_default();
// Mutate with `mutatis`.
let mut session = mutatis::Session::new()
.seed(seed.into())
.shrink(max_size < size);
if session.mutate(&mut module).is_ok() {
if let Ok(encoded) = postcard::to_slice(
&module,
data,
) {
return encoded.len();
}
}
// Fallback to the default libfuzzer mutator if
// serialization or mutation fails because, for
// example, `data` doesn't have enough capacity.
fuzzer_mutate(data, size, max_size)
});
Finally, the fuzz target itself deserializes the Module from the raw bytes,
calls fixup, encodes it to a Wasm binary via Module::to_wasm_binary, and
then passes that into Wasmtime.
fuzz_target!(|data: &[u8]| {
let Ok(mut module) = postcard::from_bytes::<Module>(data) else {
return;
};
module.fixup(|| 0);
let wasm = module.to_wasm_binary();
// ...
});
We pair each of our generators and mutator with libfuzzer-sys and feed the
resulting test cases into Wasmtime. All fuzzers start with an empty corpus.
The most important metric for a fuzzer is its bug-finding ability, but that can be difficult to measure directly. For example, Wasmtime is actively fuzzed 24/7 with more-complete fuzzers than those implemented here, so, as expected, I have not found any bugs via these benchmarks. Therefore, instead of reporting a found-bugs count, the benchmark harness reports two alternative metrics:
Coverage over time: Coverage is the cumulative code paths exercised by the fuzzer. A fuzzer cannot find bugs in code paths it does not cover. This is the most important metric reported.
Executions over time: An execution is one iteration of the fuzzing loop. This is basically measuring how fast the fuzzer can produce test cases. All else being equal, more executions is better, but all else is rarely equal. It is easy to generate poor test cases very quickly: just return an empty sequence of Wasm instructions every time. Unfortunately, that exclusively leads to useless executions. Therefore, this metric is really only useful when comparing two implementations of the same algorithm, and I’ve omitted its results in the next section.
Additionally, I report results for both 24 hours of fuzzing and 5 minutes of
fuzzing. The expected behavior of long-term fuzzing, e.g. 24/7 fuzzing in
OSS-Fuzz, can be extrapolated from the 24-hour results. The 5-minute results
show the expected behavior of short-term fuzzing, e.g. when using
mutatis::check or arbtest.
Discussion of short-term fuzzing is somewhat rare, so I feel its motivation deserves explanation. I find short-term fuzzing useful in the following scenarios, for example:
That is, short-term fuzzing is useful for the same reasons and in the same scenarios as property-based testing.3
As recommended in Evaluating Fuzz Testing by Klees, Ruef, Cooper, Wei, and Hicks and adopted in Fuzz Bench: An Open Fuzzer Benchmarking Platform and Service by Metzman, Szekeres, Simon, Sprabery, and Arya, the benchmark harness tests the statistical significance of its results with a Mann-Whitney U-test. The harness performs 20 trials per fuzzer, the same number of trials as Fuzz Bench.
arb has 1.00 ± 0.00 times more coverage than bottom_up (p = 0.01)
mutate has 1.01 ± 0.00 times more coverage than arb (p = 0.00)
top_down has 1.00 ± 0.00 times more coverage than arb (p = 0.00)
mutate has 1.02 ± 0.00 times more coverage than bottom_up (p = 0.00)
top_down has 1.01 ± 0.00 times more coverage than bottom_up (p = 0.00)
mutate has 1.01 ± 0.00 times more coverage than top_down (p = 0.00)
bottom_up has 1.01 ± 0.01 times more coverage than arb (p = 0.04)
mutate has 1.47 ± 0.02 times more coverage than arb (p = 0.00)
top_down has 1.06 ± 0.02 times more coverage than arb (p = 0.00)
mutate has 1.45 ± 0.01 times more coverage than bottom_up (p = 0.00)
top_down has 1.05 ± 0.02 times more coverage than bottom_up (p = 0.00)
mutate has 1.38 ± 0.02 times more coverage than top_down (p = 0.00)
The mutate fuzzer performs best. It vastly outperforms all the others at 5
minutes of fuzzing (36-49% more coverage), and while the rest narrow that gap
after 24 hours of fuzzing, mutate maintains its lead (1-2% more coverage).
The comparison between arb and mutate is as apples-to-apples of a comparison
as it gets between idiomatic test-case generation and mutation in Rust:
derive(Arbitrary) and derive(Mutate). They use the same fixup method to
ensure that the resulting Wasm instructions are valid. The fuzzer built with
mutatis and test-case mutation provides better coverage over time than the
fuzzer built with arbitrary and test-case generation. When writing
structure-aware fuzzers, I used to reach for arbitrary; in the future, I
will reach for mutatis instead.
The top_down fuzzer performs second-best, and is best of the generation-based
fuzzers. This aligns with results from the rgfuzz paper, which found that
top-down Wasm instruction generation resulted in better instruction diversity
than bottom-up generation. This result is intuitive, they point out, because
Wasm instructions tend to have more operands than results, which means that more
candidates are filtered out from consideration when generating instructions in
forward order from operands to results (bottom-up) than when generating them in
backward order from results to operands (top-down).
Subjectively, none of the approaches feel significantly more-complicated nor
easier to implement than the others. All approaches require a stack of types,
representing the generated Wasm’s operand stack, at some point in their
implementation. Some require it during instruction generation (top_down and
bottom_up) while others require it during fixup (mutate and arb). Adding
support for new Wasm instructions is roughly the same in all of them: add a new
variant to enum Inst and define its operand and result types. top_down and
bottom_up additionally require adding a line for the new instruction in their
choose_inst_{top_down,bottom_up} functions, but this could be avoided with
some targeted macro_rules! sugar.
The fixup method fixes instructions in a forwards order; as future work, it
would be interesting to implement a backwards_fixup method that fixes
instructions in a backwards order and see if mutate and backwards_fixup
outperforms the current mutate and forwards fixup the same way that
backwards generation (top_down) outperforms forwards generation
(bottom_up).
fixup makes an attempt to reuse stack operands when it can, rather than
synthesize dummy constants or drop already-computed values, but the attempt is
somewhat half-hearted. Dropping operands introduces dead code, which is not very
interesting for exercising deep into the compiler pipeline. Dummy constants are
not that interesting either. Therefore, another potential line of follow-up work
would be to investigate ways to maximize operand reuse and minimize drops and
dummy constants inserted while ensuring validity. That could include storing
values to memory or globals instead of droping them when possible. It could
even include liberating ourselves from the stack-focused paradigm we’ve had thus
far.
WebAssembly is a stack-based language and so it is natural that our approaches have focused on producing stack-y code. But, in practice, optimizing WebAssembly compilers like Wasmtime’s use a static single-assignment intermediate representation, and erase the operand stack early in their compilation pipelines. Therefore, from these compilers’ point of view, the following two WebAssembly snippets are identical:
;; `x = a + (b * c)` in a "stack-y" encoding and
;; without temporary locals.
local.get $a
local.get $b
local.get $c
i32.mul
i32.add
local.set $x
;; `x = a + (b * c)` in a "non-stack-y" encoding
;; that uses temporary locals for every operation.
;;
;; Equivalent of
;;
;; temp0 = b * c
;; temp1 = a + temp0
;; x = temp1
local.get $b
local.get $c
i32.mul
local.set $temp0
local.get $a
local.get $temp0
i32.add
local.set $temp1
local.get $temp1
local.set $x
Producing code that uses many temporaries in this manner might be easier than code that doesn’t, but, more importantly, it may enable better reuse of already-computed subexpressions, emit less dead code, and ultimately produce more interesting data-flow graphs that better exercise the deep innards of the compiler.
A final vein of interesting follow-up work to mine would be comparing
arbitrary-based generators and mutatis-based mutators for structured inputs
that are not programming languages and when the SUT we are fuzzing is not a
compiler. Do we see these same results when, for example, producing PNG images
to fuzz an image-transformation library?
Here is the source code for this experiment, including the three generators,
one mutator, raw benchmark data, and benchmarking harness. The README
includes instructions on running the benchmarks yourself.
WebAssembly’s stack-based instructions encode an expression tree
— local.get $a; local.get $b; local.get $c; i32.add; i32.mul is
isomorphic to a * (b + c) — so the experiment should be relevant and
applicable to any other generator or mutator for a programming language with
expressions, even if it might not appear so at first glance. ↩
Ignoring its rule-guided bit, which is orthogonal and could be
applied to bottom_up as well. ↩
Structure-aware fuzzing and property-based testing are basically the same: convergent evolution from different communities. ↩
Andreas Farre
I’ve spent a lot of time at Mozilla working on session history, the machinery that keeps track of where you’ve been so the back and forward buttons do something sensible. It’s one of those parts of the browser that sounds simple from the outside and turns out to be anything but. Once you add iframes, nested iframes, and the subtle rules about when a navigation creates a new entry versus replacing the current one, the state you’re reasoning about gets large and hard to hold in your head.
For years my main tool for understanding that state was reading code and printing things to a log. That works, but it’s slow, and it never quite shows you the shape of the thing. So I built a way to see it: a new DevTools panel in Firefox Nightly called Session History Diagrams.
The panel is available in Firefox today, behind a pref. It’s been there in some form since Firefox 150, growing more stable with each release. To turn it on, set devtools.application.sessionHistory.enabled to true in about:config, then reload DevTools. The new panel lives under the Application tab, next to Service Workers and Manifest, and it draws the browser’s session history as a diagram that updates as you navigate.
Since Firefox 153 it also works over remote debugging. Connect to a device from about:debugging and you can watch the session history of a page running on Android, the same as you would on the desktop.
I didn’t invent the idea of drawing this. The HTML spec already has a notation for it, called a Jake diagram after Jake Archibald, and that’s where I started. It’s a tabular notation where columns represent steps in session history, and rows represent navigables (the top-level browsing context plus any iframes). Background colors identify documents, a fresh color marking a new document loaded in that navigable, and the current step is shown in bold. It’s a genuinely useful way to capture multi-navigable interactions that are otherwise hard to describe in prose.
These diagrams don’t have to be drawn by hand. Domenic Denicola, one of the HTML spec editors, built a Jake diagram generator that turns a description of a navigation sequence into a rendered diagram. That’s where I first started playing with a more dynamic approach to the visualization. The thing I missed the most was being able to build a history up step by step rather than describe a finished sequence all at once. So I wrote rejake, a small tool that draws diagrams in the same style1, but lets you construct the history one step at a time.
But rejake, like the spec’s diagrams and Domenic’s generator before it, was stuck with a limitation the spec itself admits to, that they only work with a single level of nesting. That was exactly my problem. Real pages nest iframes inside iframes, and the bugs I was chasing usually lived down in that deeper nesting, precisely where the diagram stops being able to help. And however I drew them, I was still typing the history out by hand. It’s a short step from there to wanting the diagram to draw itself from the browser’s actual session history instead2.
So the panel extends Jake diagrams to handle arbitrary nesting. Every column is a step in the session history. Every row is a frame, listed in pre-order from the frame tree: top-level document first, then its first iframe, then that iframe’s children, and so on. The current entry is highlighted in blue, and the diagram updates live as you navigate.
The recording above is an ordinary bit of browsing, a handful of pages visited one after another. The top row tracks the page you’re actually looking at, and the current position is the one in blue. The interesting part is everything underneath that top row.
Some of those pages didn’t just load a single document. They pulled in nested frames of their own, and the diagram stacks those below the page that owns them. None of that is visible in the address bar or anywhere in the page chrome, and the frames come and go as you move through the history. Normally you’d have no way of knowing they were ever there. Here you can read straight off the diagram which frames a given step carried, when each one entered, and when it dropped away again.
I built this for myself, working on Gecko’s session history internals, where being able to watch the diagram change while reproducing a bug turns opaque state into something I can point at. But it turns out I’m not the only one who hits this wall. Plenty of people working elsewhere in Gecko, anywhere near navigation, end up reasoning about the same state, and now we all share one picture of it.
If you build single-page applications, or work with the History API or Navigation API, you’ve probably run into the same kind of confusion from the other side. A push where you expected a replace, a missing history entry, an iframe that accumulated entries unexpectedly. These are hard to reason about without seeing the state directly, and that’s exactly what the diagram gives you.
Session history isn’t a Firefox-specific problem either. Every engine implements the same part of the HTML spec, and Jake diagrams come from that shared spec. The panel only ever shows Firefox’s state, but the rules are the same everywhere, so if you work on another engine it can still be a useful reference for how one implementation behaves. It’s often the only practical way to surface an interoperability difference, which might be a bug in any of the engines, but stays hidden until you can actually see it.
A big thanks to Nicolas Chevobbe, whose assistance was invaluable in getting the DevTools integration right. The work, including what’s still to come, is tracked in Bug 2015726. There’s a fair bit still on that list, like marking whether a step was a push or a replace, surfacing back/forward cache state, tying the diagram into the Network and Inspector panels, and more, all heading toward fuller DevTools support for Navigation and Session History.
Which, naturally, meant re-implementing the whole of Session History along the way. ↩
Getting nerd-sniped by Jan Jaeschke definitely contributed as well. ↩
Olivier Mehani
Like many, docker and and compose have become my go-to tool to create software that can be conveniently deployed to production with a limited amount of headache. However, many tasks, and sometimes whole services, pertain only to the development side of the workflow, and need to stay there.
Moreover, some tasks, such as time-consuming provisioning tasks, are only on-demand one-offs. They shouldn’t run at all most of the time, but they should slot into the dependency graph correctly when needed.
tl;dr: I realised that docker compose supports profiles, which allows services to be enabled conditionally, along with the depends_on.[].required option, to ignore them when they are disabled. Profiles are also useful to package actions and triggers to run on demand, so they are not started by default.
We can start with a simple setup where our long-running main service depends on an init service to perform preliminary steps. This can be setup with depends_on the compose.yaml.
services:
main:
image: debian:latest
command: "sh -c 'while : ; do echo main; sleep 10; done"
depends_on:
init:
condition: service_completed_successfully
init:
image: debian:latest
command: sh -c 'echo init; sleep 10'
Even when run ning the main container, we get the right dependency (and delay). So far so good (though up will show the output from all containers.
But what if we have another, much more time consuming, initialisation step?
services:
[...]
opt-init:
image: debian:latest
command: sh -c 'echo opt-init; sleep 100'
Perhaps we are lucky, and while it needs to run once, we don’t need it to run everytime (think: database setup).
Docker compose can use profiles to select when services are started. It will then only be started when this profile is selected. Services without explicit profile will always be started, but any service with one or more profile listed will only get started iff that profile is selected.
We can make the opt-init service part of the opt profile. We can also make the main service dependent on it, so it is started beforehand.
services:
main:
[...]
depends_on:
[...]
opt-init:
condition: service_completed_successfully
opt-init:
[...]
profiles:
- opt
This works well enough when the opt profile is specified but… Oh no! If the profile is not specified, the dependency on the opt-init isn’t resolvable, and none of the stack can spin up with just docker compose up
Fortunately, this is easily solved with the required attribute of the depends_on objects.
services:
main:
[...]
opt-init:
condition: service_completed_successfully
required: false
And that’s really all there is to it: with the right profile, the optional dependency is started in the desired order, but its absence is otherwise transparently ignored. Both docker compose up and docker compose --profile opt work as desired.
Profiles afford us another useful trick: on-demand tasks not started by default. This can be handy for maintenance tasks (data cleanup, garbage collection, …) or test scripts (running test workload, sending message, …). Those are handy during development, but would not be necessary, or take a different form, in other deployments.
services:
[...]
say-hello:
image: debian:latest
profiles:
- hello
command: echo hello
depends_on:
main:
condition: service_started
Conveniently, when explicitly running a service, it is not necessary to request a matching profile, keeping the command line lean: docker compose run say-hello.
So here we are. Compose profiles allow us to control which services get started, and mark some as conditional. This, coupled with the ability to mark some depends_on rules as not required is a good way to seamlessly prevent heavy or otherwise time consuming services from starting when not needed, while retaining proper dependency ordering when enabled.
For completeness, the full, final, compose.yaml looks as follow.
services:
main:
image: debian:latest
command: "sh -c 'while : ; do echo main; sleep 10; done'"
depends_on:
init:
condition: service_completed_successfully
opt-init:
condition: service_completed_successfully
required: false
init:
image: debian:latest
command: sh -c 'echo init; sleep 10'
opt-init:
image: debian:latest
profiles:
- opt
command: sh -c 'echo opt-init; sleep 100'
say-hello:
image: debian:latest
profiles:
- hello
command: echo hello
depends_on:
main:
condition: service_started
The post Optional Docker services and dependencies first appeared on Narf.
The Servo Blog
Servo 0.2.0 contains all of the changes we landed in April, which came out to yet another record 534 commits (March: 530). For security fixes, see § Security.
We’ve shipped several new web platform features:
"webgl") on OffscreenCanvas (@niyabits, #44159)Plus a bunch of new DOM APIs:
Servo’s support for text in Chinese, Japanese, and Korean languages has improved, with correct wrapping in the layout engine (@SharanRP, #43744), and CJK fonts now enabled in servoshell’s browser UI on Windows, Linux, and FreeBSD (@yezhizhen, @CynthiaOketch, @nortti0, #44055, #44138, #44514).
Navigating to a JSON file as the top-level document now renders the JSON with an interactive pretty-printer (@webbeef, @TimvdLippe, #43702).
April was a big milestone for Servo, with some automated tests failing because they had hard-coded cookie expiry dates set to April 2016 plus ten years. Surprise! We’re still here. Here’s to the next 100 years of Servo (@jdm, #44341).
This is another big update, so here’s an outline:
CryptoKey now zeroes buffers containing key material after use (@kkoyung, #44597).
With only a few exceptions, you can only access DOM APIs in another document if that document is in the same origin.
But if that document is in the same site with a different port number, Servo currently allows these accesses even though it shouldn’t.
We’ve fixed some (but not all) of these incorrect accesses, specifically those that involve binding a Window or Location method in this document with a this from the other document (@yvt, @jdm, #28583).
We’ve fixed a bug where localStorage and sessionStorage were usable in sandboxed <iframe> and shared with every other sandboxed <iframe>, rather than throwing SecurityError (@Taym95, #44002).
We’ve fixed a bug where localStorage and sessionStorage were shared between all <iframe srcdoc> documents, rather than isolated using the origin of the containing document (@niyabits, #43988, #44038).
We’ve fixed a bug where IndexedDB was usable in sandboxed <iframe> and data: URL web workers (@Taym95, #44088).
We’ve fixed a bug where pages in some IP address origins can evict cookies from other IP address origins (@officialasishkumar, #44152). Only evicting cookies was possible, not reading or writing them.
We’ve fixed an out-of-bounds memory read in texImage3D() on WebGL2RenderingContext (@simartin, #44270), and fixed some undefined behaviour in servoshell’s signal handler (@Narfinger, #43891).
IndexedDB is now enabled in servoshell’s experimental mode (@arihant2math, #44245).
As always, embedders can enable it with Preferences::dom_indexeddb_enabled (@arihant2math, #44245, #44283).
IndexedDB now uses Servo’s new “client storage” system, which is based on the Storage Standard and will allow us to have a unified on-disk format and quota management for all web platform features that persistently store data (@gterzian, #44374, #43900). We’ve also made key range queries more efficient (@arihant2math, #39009), landed improvements to IDBDatabase, IDBObjectStore, IDBCursor, IDBKeyRange, IDBRequest, and to the handling of transactions, keys, values, and exceptions (@Taym95, #44128, #43901, #44009, #43914, #44161, #44183, #44059, #44215, #42998, #43805).
We’ve made more progress on the IntersectionObserver API, under --pref dom_intersection_observer_enabled (@stevennovaryo, @jdm, #42204).
We’re continuing to implement document.execCommand() for rich text editing (@TimvdLippe, #44529), under --pref dom_exec_command_enabled.
This release adds support for the ‘bold’, ‘fontName’, ‘fontSize’, ‘italic’, ‘strikethrough’, and ‘underline’ commands (@TimvdLippe, @jdm, @mrobinson, #44511, #43287, #44432, #44410, #44194, #44030, #44039, #44041, #44075, #44234, #44250, #44331, #44390, #44137, #44293, #44312, #44347).
All of the features above are enabled in servoshell’s experimental mode.
Servo can now build a very basic accessibility tree for web contents, under --pref accessibility_enabled (@alice, @delan, @lukewarlow, #42338, #43558, #44437, #44438).
This includes text runs, plus nine other non-interactive accessibility roles (@alice, @delan, #44255).
We’ve also fixed a crash when reloading pages with accessibility enabled (@alice, #44473), and made accessibility tree updates more efficient (@alice, #44208).
We’ve started implementing the Sanitizer API, under --pref dom_sanitizer_enabled (@kkoyung, #44198, #44290, #44335, #44421, #44452, #44481, #44585, #44594).
We’ve also started implementing SharedWorker, under --pref dom_sharedworker_enabled (@Taym95, #44375, #44440).
We’re working on the WakeLock API too, under --pref dom_wakelock_enabled (@TG199, @rovertrack, #43617, #44343).
servoshell for Android now has a revamped browser UI, including a new history view (@espy, #43795), the apk is 30% smaller (@jschwe, #44278, #44182), and we’ve fixed the black screen bug when closing settings or switching back from another app (@yezhizhen, #44327). You can now close tabs on OpenHarmony too (@Narfinger, #42713).
As for servoshell on desktop platforms, we’ve fixed some focus- and IME-related bugs (@mrobinson, #43872, #43932), and on Windows, we now install a normal shortcut without the strange behaviour of an “advertised” shortcut (@yezhizhen, #44223).
When using the Inspector tab in the Firefox DevTools, the Rules panel now includes declarations in ‘@layer’ rules (@arabson99, #43912).
When logging expressions in the Console tab, and when hovering over symbols in the Debugger tab, you can now get more information about the contents of functions, arrays, objects, and other values (@atbrakhi, @eerii, #44172, #44173, #44022, #44233, #44196, #44181, #44064, #44023, #44164, #44369, #44262).
When using the Debugger tab, you can now use the Scopes panel to inspect local and global variables (@eerii, @atbrakhi, #43792, #43791), you can now debug web worker scripts (@atbrakhi, #43981), and we’ve started implementing blackboxing, aka the Ignore source button (@freyacodes, #44142).
We’ve also landed some initial support for the Style Editor tab (@rovertrack, #44517, #44462).
We’re working towards re-enabling our automated DevTools tests in CI, which should make the feature more reliable (@freyacodes, #44577), and we’ve landed a small build reproducibility fix too (@jschwe, #44459).
For developers of Servo itself, please note that the Cargo ‘release’ profile is no longer #[cfg(debug_assertions)] (@jschwe, @mrobinson, #44177).
If you’ve been using ‘release’ as a “faster ‘debug’ with assertions” build locally, consider switching to ‘checked-release’ or ‘medium’.
The pull request template has been updated (@mrobinson, #44135). ‘Testing’ and ‘Fixes’ should go at the bottom of the PR description, and ‘Testing’ is about automated tests, not how you tested the PR locally.
We’ve made more progress on the new dev container, which will provide an alternative to our usual procedures for setting up a Servo build environment (@jschwe, @sagudev, #44126, #44111, #44162, #44641, #44109). Keep an eye out for that in the book!
In the meantime, did you know that you can use Lix or Nix to build Servo on Linux with a lot less hassle, even if you’re not using NixOS?
For now at least, head to the NixOS page in the book to learn more.
We’ve also fixed a regression that made --debug-mozjs and MOZJS_FROM_SOURCE builds take much longer to complete on Linux when not using Nix (@jschwe, #44346).
We’ve fixed building Servo with the ‘jitspew’ feature in mozjs, allowing you to set IONFLAGS to enable JIT logging (@simonwuelker, #44010). We’ve also fixed build issues on Windows and FreeBSD (@zhangxichang, @mrobinson, #44264, #44591).
With this second monthly release of the Servo library, we have some quick notes about API stability and semver compatibility:
The ‘servo’ package follows Cargo’s rules for semver compatibility. 0.1.1 is compatible with version 0.1.0, but 0.2.0 is a breaking update.
Until we integrate semver analysis into our release process, each monthly release will have a breaking version number, while non-breaking version numbers may be used for LTS updates.
In general, dependencies of ‘servo’, like ‘servo-base’ and ‘servo-script’, do not use semver. Any release may include breaking changes.
We’ve fixed a build failure affecting embedders with a new or updated Cargo.lock (@jschwe, #44093), and landed several other changes to help us with the Servo library release process (@jschwe, @mukilan, #43972, #44642, #43182, #43866, #44086, #43797).
Breaking changes:
WebView::animating now takes &self instead of self, so you can call it without cloning the handle (@JavaDerg, #44253)
Servo::site_data_manager now returns &SiteDataManager instead of Ref<'_, SiteDataManager> (@sabbCodes, #44116)
WebViewDelegate::play_gamepad_haptic_effect and stop_gamepad_haptic_effect have been removed (@mrobinson, #43895), but they have not worked since February 2026 – use GamepadDelegate instead
You can now load a URL with custom request headers by calling WebView::load_request (@Narfinger, @longvatrong111, @mrobinson, #43338).
You can now retrieve cookies asynchronously by calling SiteDataManager::cookies_for_url_async (@longvatrong111, #43794).
The synchronous version of that method, SiteDataManager::cookies_for_url, was previously not callable because CookieSource was not exposed to the public API, but we’ve fixed that now (@TG199, #44124).
You can now clear session cookies without clearing permanent cookies by calling SiteDataManager::clear_session_cookies (@longvatrong111, #44166).
When intercepting requests with ServoDelegate:: and WebViewDelegate::load_web_resource, we now include a destination and referrer_url in the WebResourceRequest, which can be helpful if you’re implementing ad blocking (@webbeef, #44493).
You can configure Servo to write all of its storage to a unique directory for that session by enabling Opts::temporary_storage (@janvarga, #44433).
Note that these unique directories currently persist after Servo exits, so it’s an isolation feature, not a privacy feature.
WindowRenderingContext::new and SoftwareRenderingContext::new now return an error if the given size is less than 1x1 (@freyacodes, @mrobinson, #44011).
We’ve improved our API docs for WebView, WebViewBuilder, WebViewDelegate, ServoDelegate, PromptDialog, WebResourceLoad, WebXrRegistry, Preferences, and servoshell’s EXPERIMENTAL_PREFS (@simonwuelker, @TG199, @sabbCodes, @jdm, @rovertrack, #43892, #43787, #44171, #43947).
We’ve also improved our API docs for Opts, OutputOptions, DiagnosticsLogging, PrefValue, servo::opts, and servo_config (@mukilan, #43802).
Tab navigation now works across <iframe> boundaries (@mrobinson, #44397), and Ctrl+Backspace (or ⌥⌫) now deletes a whole word in input fields (@mrobinson, #43940).
Tab characters are now rendered correctly in <pre> (and other elements with ‘white-space: pre’), with proper tab stops (@mrobinson, @SimonSapin, #44480). Spaces are now rendered correctly in 2D <canvas>, instead of twice as wide as they should be (@mrobinson, #43899).
<a href> now correctly resolves the URL with the page encoding (@sabbCodes, #43822).
We’ve improved the default appearance of <input type=file> (@sabbCodes, #44496) and <textarea placeholder> (@mrobinson, #43770).
All keyboard events, mouse events, wheel events, and pointer events, other than ‘pointerenter’ and ‘pointerleave’, now bubble out of shadow roots (@simonwuelker, @webbeef, #43799, #44094). ‘error’ events on Window now report the correct filename (source in onerror) and lineno (@Gae24, #43632).
console.log() and friends now support printf-style formatting directives, although for now %c is ignored (@TG199, #43897).
file: URLs are now considered secure contexts, so they can now use features like crypto.subtle and crypto.randomUUID (@simonwuelker, #43989).
Exception messages have improved in Location, StaticRange, and the HTMLElement family of types (@arihant2math, @MuhammadMouostafa, @treetmitterglad, #44282, #43260, #43882).
We’ve improved the conformance of fetch algorithms (@yezhizhen, #43970, #43798), focus and tab navigation (@mrobinson, #43842, #44029, #44360, #43859, #44535), form submission (@TG199, #43700), JS modules (@elomscansio, @Gae24, #43741, #44179, #44042), page navigation (@TimvdLippe, #43857), <svg viewBox> (@yezhizhen, #44420), ‘attr()’ (@Loirooriol, #43878), ‘:focus’ (@mrobinson, #43873), ‘font’ (@RichardTjokroutomo, #44061), ‘@keyframes’ (@simonwuelker, #43461), ‘@property’ (@Loirooriol, #43878), ‘load’ events (@jdm, @arabson99, #43807, #44046), fetchLater() (@TimvdLippe, #43627), axes and buttons on Gamepad (@log101, @rovertrack, #44411, #44357), copyTexImage2D() on WebGLRenderingContext (@simartin, @mrobinson, #43608), texImage3D() on WebGL2RenderingContext (@simartin, #44367), environmentBlendMode on XRSession (@msub2, #44155), mark() and measure() on Performance (@shubhamg13, @simonwuelker, #44471, #44199, #43990, #43753), and PerformanceResourceTiming (@shubhamg13, #44228).
We’ve fixed bugs related to console logging (@sabbCodes, #44243), ‘animation’ (@mrobinson, #44299), ‘box-shadow’ (@yezhizhen, #44474, #44457), ‘display: contents’ (@Loirooriol, @mrobinson, #44551, #44299), ‘display: inline-flex’ (@SimonSapin, #44281), ‘display: table-cell’ (@Loirooriol, #44550), ‘display: table-row-group’ (@Veercodeprog, #43674), ‘overflow-x: clip’ and ‘overflow-y: clip’ (@Messi002, #43620), ‘position: absolute’ on grid items (@nicoburns, #44324), ‘word-spacing: <percentage>’ (@sabbCodes, #44031), removeChild() on Document (@rovertrack, #44133), and URL.revokeObjectURL() (@simonwuelker, @jdm, #43746, #43977, #44035).
We’ve fixed some big inefficiencies in Servo. appendChild() with nested shadow roots is no longer (@yezhizhen, @webbeef, #44016), and we’ve halved the time it takes to load the ECMAScript spec by fixing the processing of ‘id’ and ‘name’ attributes (@simonwuelker, #44120, #44127, #44117).
Servo makes its first TLS connection in each session 30–60 ms faster (@jschwe, #44242), and we’ve instrumented the Servo and servoshell startup processes to find more opportunities for optimisation (@jschwe, #44443, #44456).
Like most browser engines, Servo is a multi-threaded (and sometimes multi-process) system requiring a great deal of IPC messages to keep everything connected. Two key components of this system are the constellation thread, which manages the engine as a whole, and the script threads (or web processes), which render the web pages. Sending these messages can be expensive though, so to reduce unnecessary IPC traffic, we’ve landed an optimisation that allows script threads to selectively receive only the relevant messages from the constellation (@webbeef, #43124).
We’ve reduced the memory usage of each Attr, Text, and CharacterData node in the DOM by 16 bytes (@mrobinson, @Loirooriol, #44074), and fixed a memory leak when deleting <video controls> or <audio controls> (@Messi002, #43983).
Our about:memory page is more accurate now too, with new tracking of libc memory allocations on macOS, improved tracking of libc memory allocations on Linux (@jschwe, #44037), and more accurate tracking of PathBuf and types in tokio, http, data_url, and urlpattern (@Narfinger, #43858).
Less memory usage isn’t always better in browser engines though, because there are many kinds of caches and other optimisations we can do to make browsing the web faster, at the expense of increased memory usage. For example, we can greatly speed up prototype checks for DOM objects by storing a number in each object that identifies the concrete type, at the expense of making each DOM object 64 bits larger (@webbeef, #44364).
Layout can now reuse fragments in later reflows, in many cases that involve block layout or ‘position: absolute’ (@mrobinson, @lukewarlow, @Loirooriol, #42904, #44231). We’re also working on reusing shaping results in later reflows, and making inline layout more efficient (@mrobinson, #44370, #43974, #44436).
We’ve landed several changes that should reduce the binary size of Servo (@rovertrack, @mrobinson, @nicoburns, @Narfinger, #44227, #44221, #44303, #44338, #44428, #44134).
We’ve also reduced clones, allocations, borrow checks, GC rooting steps, and other operations in many parts of Servo (@rovertrack, @Narfinger, @Loirooriol, @yezhizhen, @simonwuelker, #44008, #44544, #44271, #44279, #43826, #44052, #44139).
Several crashes have been fixed:
--debug-mozjs builds (@jdm, #44386, #44573, #44581)offsetHeight (@mrobinson, #44560)u64 --pref (@yezhizhen, #44079)We fixed a crash in servoshell when pressing keys like Ctrl+2 or ⌘2 with not enough tabs open (@mrobinson, #44070).
DOM data structures (#[dom_struct]) can refer to one another, with the help of garbage collection.
But when DOM objects are being destroyed, those references can become invalid for a brief moment, depending on the order the GC finalizers run in.
This can be unsound if those references are accessed, which is a very easy mistake to make if the type has an impl Drop.
To help prevent that class of bug, we’re reworking our DOM types so that none of them have #[dom_struct] and impl Drop at the same time (@willypuzzle, #44119, #44501, #44513).
We’ve improved our static analysis for GC rooting (@officialasishkumar, #44489), and we’ve continued our long-running effort to use the Rust type system to make certain kinds of dynamic borrow failures impossible (@sagudev, @TimvdLippe, @Narfinger, @elomscansio, @Gae24, @rovertrack, @yezhizhen, @nodelpit, #43174, #43524, #43928, #43943, #43942, #43944, #43946, #43952, #43975, #44018, #44175, #44241, #44368, #44406, #44441, #44422, #44475, #44478, #44484, #44476, #44490, #44477, #44494, #44497, #44498, #44495, #44505, #44506, #44507, #44508, #44509, #44510, #44512, #44482, #44527, #44528, #44531, #44534, #44542, #44533, #44543, #44553, #44547, #44563, #44562, #44565, #44558, #44583, #44606, #44605, #44608, #44602, #44584, #44620, #44590, #44254, #44628, #44629, #44638, #44626, #44081).
Thanks to a wide range of people, we’ve also landed a bunch of cleanups and refactors (@delan, @alice, @Skgland, @atbrakhi, @eerii, @sabbCodes, @jdm, @thebabalola, @CynthiaOketch, @kkoyung, @TimvdLippe, @rovertrack, @webbeef, @arabson99, @yezhizhen, @simonwuelker, @mrobinson, @nicoburns, @longvatrong111, @niyabits, @treetmitterglad, @foresterre, @mukilan, @elomscansio, @freyacodes, @StaySafe020, @TG199, #43772, #44006, #43860, #44121, #44160, #43884, #44154, #44569, #43939, #44003, #44110, #44122, #43824, #44635, #44103, #43978, #44092, #44114, #44277, #44454, #44274, #44237, #44232, #44167, #44214, #43820, #43825, #43810, #43838, #43841, #43847, #43875, #43876, #43889, #43893, #43896, #43881, #43906, #43913, #43908, #43917, #43910, #43921, #43924, #43925, #43907, #43923, #43916, #43909, #43911, #43957, #43969, #43967, #43915, #43954, #43963, #43959, #43955, #44067, #44068, #44071, #44084, #44265, #44115, #44358, #43848).
Thanks again for your generous support! We are now receiving 7349 USD/month (+2.5% from March) in recurring donations. This helps us cover the cost of our speedy CI and benchmarking servers, one of our latest Outreachy interns, and funding maintainer work that helps more people contribute to Servo.
Servo is also on thanks.dev, and already 33 GitHub users (−4 from March) that depend on Servo are sponsoring us there. If you use Servo libraries like url, html5ever, selectors, or cssparser, signing up for thanks.dev could be a good way for you (or your employer) to give back to the community.
We now have sponsorship tiers that allow you or your organisation to donate to the Servo project with public acknowlegement of your support. If you’re interested in this kind of sponsorship, please contact us at join@servo.org.
Use of donations is decided transparently via the Technical Steering Committee’s public funding request process, and active proposals are tracked in servo/project#187. For more details, head to our Sponsorship page.
Frederik Braun
This is a blog post about standards, their proliferation and the issues that may arise. My first involvement with standards was just as a reader. To better understand complicated code or unexpected behavior in a protocol. After a while, I also got involved and helped clarify certain things to ensure implementations align on the same behavior in edge cases. Eventually, I found myself co-editing a specification - Subresource Integrity (SRI) which was published as a W3C Recommendation in 2015. The core idea behind SRI is that you include third-party JavaScript combined with a SHA2 digest of the expected file. If the browser does not find the downloaded URL to match the expected digest, the script will not execute. This allows using a fast CDN for JavaScript without giving them full control over the scripts on your page - essentially reducing the security risks.
The standard format for these digests is e.g.,
sha(size)-(base64 encoding of the digest).
While computing the hash digest is rather straightforward, base64 comes in two
encoding alphabets: First, a-zA-Z0-9/+ and secondly the url-safe variant
which uses a-zA-z0-9_-. The specification examples all used the former.
Only approximately ten years after publication, in 2025, we still found a bug. As part of a compatibility report against Firefox not properly supporting a website, we found that the core issue was actually with a different browser. The other browser liberally accepted both types of encoding, which resulted in websites expecting support for base64 and base64url interchangeably. The page did not work in Firefox, because it did not accept all hashes a website wanted the browser to check, revealing a minor security issue.
The real fix would have been that the standard clarifies that the base64url variant is incorrect and the other browser engine changes their behavior.
But due to (somewhat unrelated) issues around proliferation of standards, web compatibility and the unfortunate market dominance of certain browsers, we went the other road. To support existing web content, we changed the standard to acknowledging that both types of encoding are considered valid representations.
This example shows, that it can take multiple years for subtle differences to appear. Interoperable specifications can establish a shared understanding along a "happy path", but not necessarily in adversarial settings. In addition, standards need to continuous maintenance and active stakeholders who ensure that implementations remain interoperable and secure over time.
Originally, a specification is at first just a write-up, an idea how something could be better: How it should behave, how it works, what the data structures, the algorithms and the interactions of them look like. Anyone can come up with a grammar, a parser and a resulting data structure.
For a standard, this specification needs a shared agreement that is also widely and consistently implemented. This will work best with iterative co-design of the spec, the implementations and intense discussions of corner cases. Some may go further and use shared test suites.
This will lead to Interoperability (interop), but still requires constant maintenance and observation of the ecosystem beyond individual implementations. While interop is asymptotic and requires a shared agreement over time, security demands understanding - a broader reach that requires the inspection of limitations and subtle boundaries.
This deeper level of understanding is often missing when implementations consider syntax "simple enough" without reading the spec. The base64 SRI example is just one example, but there are more:
Many people have written their own parsers for text-based languages. You may have seen code that parses HTML with regular expressions. Other great examples of "easily" parsed languages are maybe XML, JSON, or YAML.
But these implementations often make different assumptions, leading to subtle incompatibilities or even security flaws.
More practical, let's look at an issue with JSON, to demonstrate the impact of handling input that is ostensibly simple. Let's examine this JSON string and the resulting data structure:
{
"test":0,
"test":1
}
When parsed into an object obj, what do you think will obj.test return?
Most JSON parsers are so liberal that they will happily consume two dictionary
keys with the same name "test". One implementation may simply assign obj.test
twice: First with 0 and then overwrite it with 1.
Another one might check for existing keys
and reject the second "test" key silently, keeping the first one.
The lack of rigor in the original description of JSON as a "subset of JavaScript" was already acknowledged and raised as problematic in the JSON RFC (which came much later in 2017). But still to this day, many implementations allow input with duplicate dictionary keys and show divergent behavior.
While the examples with SRI and JSON are relatively harmless, real parser differential bugs were leading to code execution, authentication bypasses and more1.
Perfect interoperability is not created through a specification, it needs constant maintenance. The ambiguity can only be removed through long-term commitment and regular feedback from implementations and users.
The same is true for security: The SRI bug persisted for ten years and nobody noticed how implementations disagreed and corner cases were overlooked. They only aligned due to a real, user-facing issue.
But these examples are not a warning sign, they are scar tissue that shows how the internet is made. Standards can only mature through vigilant maintenance.
The bug reports, the spec issues being filed, the shared test cases, sometimes even the random forum complaints. All of these help to remove ambiguity and allow internet standards to mature.
In the end, standards are not secure because they are written down. They are secure because people continue to question, understand, and maintain them.
The Rust Programming Language Blog
The Rust team is happy to announce a new version of Rust, 1.96.0. Rust is a programming language empowering everyone to build reliable and efficient software.
If you have a previous version of Rust installed via rustup, you can get 1.96.0 with:
$ rustup update stable
If you don't have it already, you can get rustup from the appropriate page on our website, and check out the detailed release notes for 1.96.0.
If you'd like to help us out by testing future releases, you might consider updating locally to use the beta channel (rustup default beta) or the nightly channel (rustup default nightly). Please report any bugs you might come across!
Range* typesMany users expect Range and related core::ops types to be Copy, but this is not the case: they implement Iterator directly, and it is a footgun to implement both Iterator and Copy on the same type so this has been avoided. RFC3550 proposed a set of replacement range types that implement IntoIterator rather than Iterator, meaning they can also be Copy. The standard library portion of that RFC is now stable, introducing:
core::range::Rangecore::range::RangeFromcore::range::RangeInclusiveA Rust version in the near future will also add core::range::RangeFull and core::range::RangeTo as re-exports from core::ops (these do not implement Iterator and already implement Copy), and core::range::legacy::* as the new home for the current ranges. Range syntax like 0..1 still produces the legacy types for now, but will be updated to core::range types in a future edition.
With these stabilizations, it is now possible to store slice accessors in Copy types without splitting start and end:
use core::range::Range;
#[derive(Clone, Copy)]
pub struct Span(Range<usize>);
impl Span {
pub fn of(self, s: &str) -> &str {
&s[self.0]
}
}
The new RangeInclusive also makes its fields public, unlike the legacy version which avoided exposing the exhausted iterator state. This isn't a concern with the new type since it must be converted to begin iteration.
Library authors should consider making use of impl RangeBounds in public API, which accepts both legacy and new range types. If a concrete type is needed, prefer using new ranges as this will eventually become the default.
The new macros assert_matches! and debug_assert_matches! check that a value matches a given pattern, panicking with a Debug representation of the value otherwise. These are essentially the same as assert!(matches!(..)) and debug_assert!(matches!(..)), but the printed value improves the possibility of diagnosing the failure.
These new macros have not been added to the standard prelude, because they would collide with popular third-party crates that provide macros with the same name. Instead, they should be manually imported from core or std before use.
use core::assert_matches;
/// [Random Number](https://xkcd.com/221/)
fn get_random_number() -> u32 {
// chosen by a fair dice roll.
// guaranteed to be random.
4
}
fn main() {
assert_matches!(get_random_number(), 1..=6);
}WebAssembly targets no longer pass --allow-undefined to the linker which means that undefined symbols when linking are now a linker error instead of being converted to WebAssembly imports from the "env" module. This change prevents modules from linking unless all linking-related symbols are defined to catch bugs earlier and prevent accidental issues with symbol naming or similar.
Undefined linking-related symbols are often indicative of build-time related bugs or misconfiguration. If, however, the old behavior is intended then it can be re-enabled with RUSTFLAGS=-Clink-arg=--allow-undefined or by editing the source code and using #[link(wasm_import_module = "env")] on the block defining the symbol.
This change was previously announced on this blog, and now takes effect in Rust 1.96.
assert_matches!debug_assert_matches!From<T> for AssertUnwindSafe<T>From<T> for LazyCell<T, F>From<T> for LazyLock<T, F>core::range::RangeToInclusivecore::range::RangeFromcore::range::RangeFromItercore::range::Rangecore::range::RangeIterRust 1.96 contains fixes for two vulnerabilities for users of third-party registries.
CVE-2026-5223 is a medium severity vulnerability regarding extraction of crate tarballs with symlinks.
CVE-2026-5222 is a low severity vulnerability regarding authentication with normalized URLs.
Users of crates.io are not affected by either vulnerability.
Check out everything that changed in Rust, Cargo, and Clippy.
Many people came together to create Rust 1.96.0. We couldn't have done it without all of you. Thanks!