Gedit Technology

@geditblog

B2B Services around gedit and libgedit

This article is also available in the B2B Services section on the gedit website.

Several business-to-business services are possible around gedit:

  • Development of a new plugin.
  • Development of a new text editor or Integrated Development Environment (IDE) based on the libgedit.
  • Code maintenance.
  • Training.
  • Support.
  • Creation of Long-Term Support (LTS) versions.
  • Developer Experience (DX) guidance.
  • […] Come with your own ideas to collaborate with the gedit project.

Target audience

  • Operating System / Linux distributions: for your installed-by-default text editor.
  • GTK-based desktop environments: to maintain a text editor or IDE, and to adhere to your Human Interface Guidelines (HIG).
  • Scientific sector: to build developer tools.
  • Education sector: to build easy-to-learn developer tools.
  • New programming languages / development platforms: you're designing and implementing a new programming language, and you need developer tools for your users.
  • Older programming languages users: you're a big organization and you have a lot of legacy code. You want better developer tools to be more productive.
  • Markup or domain-specific languages: to better promote your language, you would like first-class support for it.

The libgedit shared libraries

gedit is not just a general-purpose text editor application, there is a “libgedit” underneath!

A lot of gedit features are implemented as re-usable code, as a set of shared libraries. So new apps - text editors and IDEs alike - can be built on top. There is an ongoing effort from the gedit project to make more code re-usable.

An example of an IDE based on the libgedit is Enter TeX.

The libgedit is in turn based on the very flexible GTK graphical toolkit.

GTK 3 or GTK 4

The libgedit currently targets GTK 3. If you want to develop with GTK 4, there is the GtkSourceView library (but it doesn't contain all the libgedit features). Another possibility is to first port the libgedit to GTK 4.

The plugin system

gedit has a powerful plugin system mechanism, to extend the application. You can leverage it for prototyping additional features, or as the final solution that requires less efforts than creating a new specialized text editor.

You can also combine the best of both approaches:

  • First implement a feature that is based on libgedit.
  • Then wrap your feature in a gedit plugin so it can readily be used.
  • Finally, as an option, create a custom text editor app, re-using the same implementation of your feature(s), integrating everything well together.

Other developer tools

The text editor part is essential, it is the central feature of an IDE. But other developer tools can be developed as well.

For instance, Devhelp can be used for browsing and searching API documentation. Almost all its code is re-usable; like for gedit, there is a libdevhelp toolkit under the hood.

Advice to not start from scratch

A little advice: please don't create a new text editor or IDE from scratch, base your work on existing, high-level libraries like the libgedit. Even if it looks simple on paper, developing a feature-full text editor is a lot of work.

Use the libgedit from your preferred programming language

libgedit and GTK can be used from a wide range of programming languages, and the support for additional languages can be implemented too. This is thanks to GObject Introspection. See the list of language bindings for the GTK project.

Open-source or proprietary software

libgedit and GTK are licensed under the GNU Lesser General Public License (LGPL), which allows to develop proprietary software on top.

gedit plugins need to be distributed as free/libre software, under the GPL license.

Who to collaborate with

  • Sébastien Wilmet, the current maintainer and main developer of gedit and libgedit.
  • You? If you're or work for a consultancy company specialized in GNOME, GLib or GTK, and want to be part of this project, don't hesitate to get in touch!

Allan Day

@aday

GNOME Foundation Update, 2026-05-29

Welcome to another update about everything that’s been happening at the GNOME Foundation. As has become my custom, this post covers a two week period, this time from 18 May until today, 29 May. As usual the Foundation continues to be busy, with events, infra, governance, and accounting activities all happening simultaneously. Read on for more information!

Events

Linux App Summit (LAS) 2026 was held in Berlin over the 16-17 May weekend. I’ve heard quite a few reports now, and everyone seemed extremely positive about the event. Kristi wrote a nice summary if you want more details.

The GNOME Foundation had two team members on the ground helping with running the event, which we co-organize with KDE. I’d like to take this opportunity to give a big thank you to the event’s sponsors: openSUSE, Tuxedo, Nextcould and Codethink. This event wouldn’t be possible without your support.

In addition to LAS, work is continuing on arrangements for GUADEC 2026. The deadline for travel sponsorship applications has now passed, and the Travel Committee has met to decide who will be funded. Notifications will be going out soon.

Board Elections

The process is officially underway for this year’s Board elections. Terms on our Board of Directors are two years in length, and each year half the board seats are open for election. This year we have five seats being contested.

The 2026 election has a slightly different schedule to previous years. In the past, there was no gap between the candidacy period, in which people can announce their intention to run, and the voting period. This meant that there was little opportunity for last-minute candidates to participate in discussion prior to voting taking place.

To address this, we’ve added a one week discussion period to the schedule, which will run between 8 and 15 June, between the candidacy and voting periods. This will hopefully give us opportunity to have more structured and inclusive debate amongst the candidates. We are still figuring out what that might look like, so if people have ideas or want to help, let me know in the comments.

GNOME Fellowship

We are currently in the very final stages of confirming and announcing the successful candidates for the inaugural round of the Foundation’s Fellowship program. Expect an announcement very soon.

Got a Concern?

Last week we introduced a new policy for handling of concerns about the Foundation, which is now part of the project handbook.

The new policy covers how to report concerns about people who are working for the Foundation, either in a paid or voluntary capacity. It also covers more general concerns about the Foundation.

The main goals of the policy are to:

  • have a documented reporting procedure for those who have concerns relating to the Foundation
  • clarify how concerns will be responded to
  • provide reassurance for those reporting concerns, including that concern reports are welcome, are taken seriously, and will never result in retaliation

We hope that this policy will make it clear how you can inform us of a concern if you have one. We also want to emphasise that we want to hear concerns, so we can address them. Please do use the new reporting procedure.

Finance/Accounting

Work has continued on the finance and accounting operation over the past two weeks. Highlights include:

  • Our transition to a monthly rather than quarterly close reached a significant milestone this week, with the completion of our April finance reports within three weeks of the previous month end. This is probably the fastest ever turnaround for our finance operation, and is a huge win for us in being able to effectively manage our finances.
  • Following input from the board, corrections have now been sent to the accountants for our audit and annual tax filing.
  • Applications are still open for our Director of Finance and Operations part-time contract. Candidates have until 4 June to submit.
  • Finally, as I mentioned in my last update, we are in the process of retiring a number of finance platforms as we consolidate and streamline our operation. This week saw another platform retired, which brings the total number of eliminated platforms to four.

Infrastructure

Our infrastructure experienced a DDoS attack last weekend, which Bart and Andrea have been dealing with. Thankfully it seems that services weren’t too badly affected, and we’ve already improved our protection against similar attacks in the future.

Also on the infra side, Bart wasn’t at LAS this year, but he did spend some time writing two great posts about Flathub’s internals: How does Flathub even work? and Why are Flathub downloads so slow sometimes?. They’re a fascinating read if you’re interested in Flathub.

That’s it from me! As always, thanks for reading, and see you in two weeks.

Sophie Herold

@sophieherold

Updates from the Circle Committee

We want to share some updates and future plans from the GNOME Circle Committee with you. The Circle Committee is responsible for reviewing and accepting apps and other components into GNOME Circle as well as maintaining the review criteria.

The biggest issue for us, and for maintainers who submitted apps, has been the considerable backlog of unreviewed apps. There are cases where apps have been waiting for feedback for years. We are very sorry for those who have been affected by this. We are trying to finally address this issue now.

AI Policy

As a precaution, we have adopted the AI policy that already applies to GNOME Shell extensions. With the rise of low-quality machine-generated software, there is a risk that GNOME Circle could be overwhelmed by new submissions of this nature, further clogging the review queue.

This policy is a starting point, and for now, it will only be enforced on new submissions. We are open to receiving feedback on the AI policy from existing Circle maintainers. In a quick survey, 62% of Circle maintainers reported not using LLMs at all, 34% reported using them for small questions or snippets, and only 3% are taking larger parts of code from them. No maintainer selected the option for no longer writing code themselves.

Meanwhile, Flathub has also tightened their AI policy. Since GNOME Circle apps are usually expected to be published on Flathub, this policy indirectly applies as well.

Closing New Submissions

To make sure that we are focusing on submissions that haven’t been addressed for a long time, we will stop accepting new submissions for now. We will reopen submissions when we have made a considerable dent in our backlog.

New Handling of Issues

This is mostly a technical detail, but from now on, we will close issues that are awaiting feedback from the maintainer. When providing an answer, maintainers should just reopen the issue. This gives us a better overview of which issues are awaiting further processing by us.

Early Reminder About the Use of Outdated SDKs

Every GNOME release cycle, we spend a considerable amount of time on getting GNOME Circle apps to update their SDK on Flathub before the SDK becomes unmaintained. To avoid this in the future, we will try something new: We will remind maintainers much earlier that they are using an outdated SDK – months before it reaches EOL, rather than just a few weeks. This means apps using the GNOME 49 SDK will soon be part of an automatically generated reminder issue. To get the reminder, please make sure that you have provided a GNOME GitLab account in your project’s .doap file.

Growing Benefits

We are steadily growing the benefits for GNOME Circle apps and their maintainers. Among the benefits that were added are:

  • Having a student work on your project for Google Summer of Code or Outreachy.
  • Hosting your help pages on help.gnome.org.
  • Getting a tag for your project on GNOME’s Discourse.

Of course, all contributors and maintainers of GNOME Circle projects are still eligible for GNOME Foundation membership. You can check our full list of benefits.

Call for Reviewers

We are in constant need of more reviewers in the Circle Committee. If you have already reached out to us, and we haven’t gotten back to you, please give it another shot. You can find us in #circle:gnome.org.

Gedit Technology

@geditblog

News, April-May 2026

Here are the noteworthy news about the gedit and Enter TeX text editors. (Some sections are a bit technical).

A single package for gedit and its core plugins

Before, users needed to remember to install the gedit-plugins package in order to benefit from additional plugins such as Word Completion or Bookmarks.

Now, all core - or “official” - plugins are part of the main gedit module. So, Word Completion, Bookmarks and others have been copied into the gedit module, and gedit-plugins is now empty/archived.

Note that there are also third-party plugins which are available in other repositories.

New version on the Microsoft Store

gedit 50.0 is now also available on Windows.

The re-implementation of document loading continues

It's now a recurrent topic. Progress has been made, but more work is necessary to fully replace the GtkSourceFileLoader internals.

This time, here is a general description of the methodologies used along with the current state of affairs, without entering into too much technical details. If you're an experienced developer, it'll most probably ring a bell to you :-)

The typical “divide and conquer” methodology is followed: the problem is divided into several smaller ones. The sub-problems are solved individually, providing utility functions and small classes. Then things are built on top, with several layers, gradually solving bigger and bigger problems.

It all looks nice, except that an iterative process is also needed, to revise the solutions already built because of unforeseen problems (it happens all the time in computer science).

The current progress status is that some utilities need to be reworked in order to be a better fit for the above components, and also to fix a couple of unforeseen problems. Thankfully it's not a “back to the drawing board” situation, as the utilities that lie at the bottom layers don't need any change.

For the curious reader, here are the two unforeseen problems encountered:

  • iconv() can output nul bytes even for UTF-8 as the target charset. Embedded nuls are not valid UTF-8 according to g_utf8_validate(). So an additional pass of validation is necessary, to escape invalid UTF-8 bytes.
  • With the new implementation, at most two copies of the whole file content needs to be present in memory. I stumbled across a corner case where three copies would be necessary, so it needs to be fixed. The three copies were: (1) the content containing a single chunk (or at least a big chunk) of invalid bytes, (2) the invalid chunk escaped, and (3) the content as inserted into the GtkTextBuffer.

    It's just a matter of escaping the invalid bytes directly / at a different place.

    (Note that this is a concern only for the work-in-progress branch; current stable versions of gedit doesn't suffer from that issue).

Enter TeX goodness

The Projects feature has been re-implemented in a better way (mostly under-the-hood changes).

The module is again on GNOME Damned Lies, so there are many translation updates. Thank you, all translators around the globe! In the past I contributed to the French translation, and I directly saw that GNOME translation teams do very high quality work. So, thanks again :) !

Other stuff

As usual there are some other work items fixed or where progress has been made. Among others:

  • The comment/uncomment actions have been fine-tuned.
  • A prototype has been created in order to improve the main “sandwich” menu in gedit.

Code re-use and Business-to-Business opportunities

There is a lot of potential with the libgedit modules (or also with GtkSourceView for GTK 4), to grow the project into something like the GStreamer multimedia project (but at a smaller scale) where there is a lot of B2B activity with consultancy companies helping to create new products for other businesses.

Especially with libgedit-tepl, the name “Text editor product line” means that it's possible to create other text editors or IDEs products (more easily).

An example that I have in mind is Arduino and its specialized IDE. But there are new programming languages or development platforms that pop up regularly, so there are opportunities to collaborate with them and develop great developer tools for them.

I'll talk about it more in the future, but if you read this and are interested to develop such a business model for gedit and/or GtkSourceView, talk to me!

Laureen Caliman

@lcaliman

My Current MR and GSoC Project Start

Ongoing Work

Back in March, I started to tackle this MR for GNOME Crosswords. It allowed me to learn a lot about navigating the codebase, adhere to naming conventions, and collaborating with others involved in Crosswords.

Crosswords Editor features a section for users to input metadata, such as author name, puzzle URL, and date. Originally, the MR was to convert and store the user’s manually typed date to ISO8601. This means storing the entered date in the form YYYY-MM-DD, while the user just needs to type in a date using the GDate struct.

Upon code review, the scope expanded to move from manual typing to selecting a date on a drop-down GTK calendar widget. I was able to implement this concept by following the architecture of an existing widget (edit-shapebg.c/h/blp) which placed shapes on the crossword puzzle. Then I connected the new calendar widget to the metadata so the chosen date from the now drop-down calendar will be stored in ISO8601 format yet display respective to the locale of the person using g_date_strftime.

Project Start

With the start of GSoC, I have spent a lot of time this week reading, white-boarding, and began setting up some structure to adding vocab-style crosswords to the libipuz library.

The basic constraints and workflow that I mapped are:

  • A user can input up to 30 words (strings), with a 25 character capacity.
  • Upon hitting enter or a button to generate, a Depth-First Search (DFS) algorithm starts to piece the words together on a blank grid. The algorithm can backtrack to tear apart words and rearrange them until a valid graph is formed
    • I’m going with DFS at the moment to prioritize the longest-to-shortest word seeding the graph in order to increase possibilities of connections
  • Every time the user generates the graph, it will be treated like its own isolated creation, recalculating the layout from scratch to allow for cleaner undo/redo states and easier addition/deletion of words
  • I don’t want words being placed right next to each other unless it was intentional for a 2+ word answer, which each word would be separated by thick lines
    • Words should only connect through intersections
      • The frontend does not mutate the grid but will be able to pass actions to the backend and return a new state
      • For instance: say a user starts off their grid by typing “CAT” and then “DOG.” These words don’t have common characters, so they cannot be connected. However, instead of erasing the user’s desire to incorporate “DOG,” the string gets tucked away and saved in a GArray

      • Now our user types and enters “CADET.” The UI takes our list and passes it to the algorithm to start chewing on and deal onto the board anew. This time, allowing for “CAT,” “DOG,” and “CADET” to appear, without the user having to retype “DOG”

I went back and forth with myself on how to represent the puzzle’s state in memory and disk. To write a custom GObject, or not.

The Argument Against: Standard IpuzCrossword objects have a handle on grids and JSON saving, and one could write a new function that takes an array of strings and return a standard IpuzCrossword.

The Argument For: Revisiting the “CAT,” “DOG,” and “CADET” conundrum; say an educator is creating a puzzle for his/her/their class, and they have just one word out of 20 that don’t connect to current existing possibilities of intersecting words. If they save their puzzle, it transforms to a standard IpuzCrossword, and the floating word are permanently abandoned.

At the current moment, I want the puzzle to be its own vocab-style workspace. Therefore this would allow for the floating word to instead sit in the sidebar and wait for further instruction for a future inserted word that works, or to get deleted.

Of course, as I gain more insight about the library, states, and optimization within the codebase, this approach is subject to refinement.

Benjamin Otte

@Company

Snapping

With the release of 4.23.1, GTK’s renderer will come with a new feature that we’ve called snapping.

How does it work?

Snapping is enabled by calling gtk_snapshot_set_snap(). If enabled, it will slightly adjust the placement of rectangles when drawing so that they align with the pixel grid and don’t cover half a pixel.

GSK_RECT_SNAP_ROUND

Content drawn with GTK is scaled automatically by the desktop’s scale factor. But with the arrival of native fractional scaling, it is no longer possible to know if content is aligned to the pixel grid.

While that is usually not a problem, there are a few cases where it is:

Sprite grids

Gameeky is a learning game that plays on a grid. Unfortunately, on a fractionally scaled machine, it can end up looking like this:

A screenshot of a Gameeky game without snapping. A distracting grid can be seen between the sprites.

Once those sprites are snapped to the pixel grid by rounding to the nearest pixel, the same image looks like this
A screenshot of a Gameeky game with snapping. No grid is visible anymore and it looks like a single image.

Sharp images

Often Applications want to display images in a way that matches the pixels of the image 1:1 with pixels of the monitor. This is a challenge on a fractionally scaled display. Not only is it important to get the scale factor right, it’s also important to align the pixels correctly, or they will appear slightly blurry.

The use case is not just image viewers that want to offer a 1:1 zoom factor, but all applications that redirect drawing, from game emulators to viewers like Boxes or Connections.

Hardware optimizations

And finally, there are optimizations like graphics offload that rely on content being aligned to the pixel grid or the hardware cannot optimize them. So it is important to snap content to the pixel grid for those cases.

Why don’t we just always snap to the grid?

There is one big problem with automatic snapping: smoothness. Because snapping only works on full pixels, doing slow animations causes content to jump from one pixel to the next. And that causes jitter.

The main situation where one can see this is smooth scrolling, like in this example:

Summary

The next GTK release will offer a new way to tame the effects of fractional scaling.  Please try it out and let us know how it works!

Michael Calabrese

@mccalabrese

Synchronizing Timeline Ticks with GES Framerates in Rust

While working on my GSoC project (rewriting the Pitivi timeline in Rust), I ran into an issue getting precise UI ticks that map to the absolute nanosecond timestamps of the video frames. Initially I hardcoded NTSC fractional math (24000/1001) to calculate the boundaries of frames.

This led to issues with truncated timestamps, and had a glaring issue with other framerates (like 30fps). I needed a more robust solution that could handle any framerate and provide accurate tick positions.

I assumed that I could extract the framerate directly from ges::Timeline, however there is no direct getter in the Rust bindings. After some digging, I discovered that the framerate is actually stored in the gst::Caps of the timeline's video stream as a gst::Fraction.

My Approach

The steps I used:

  • Enumerate the timeline tracks (timeline.tracks())
  • Filter for the video track (checking ges::TrackType::VIDEO)
  • Read the track's restriction_caps
  • Extract the gst::Fraction

My helper

I wrote a helper function to extract the framerate from the timeline:


pub fn get_fps(&self, timeline: &ges::Timeline) -> Option<(i128, i128)> {
    timeline
        .tracks()
        .into_iter()
        .find(|track| track.track_type().contains(ges::TrackType::VIDEO))
        .and_then(|track| {
            let caps = track.restriction_caps().or_else(|| track.caps())?;
            let structure = caps.structure(0)?;
            let fps = structure.get::<gst::Fraction>("framerate").ok()?;

            // Extract the safe numerator and denominator
            Some((fps.numer() as i128, fps.denom() as i128))
        })
}

// ... inside the timeline injection logic:
if let Some((fps_num, fps_denom)) = self.get_fps(timeline) {
    self.fps_num.set(fps_num.max(1) as i32);
    self.fps_denom.set(fps_denom.max(1) as i32);
} else {
    // Default to 23.976 if we can't find a valid framerate caps
    self.fps_num.set(24_000);
    self.fps_denom.set(1_001);
}

I make some assumptions here, such as only one video track existing, and that the framerate is always present in the caps. This solution made the tick spacing and labels line up with the timeline’s actual frame boundaries at any framerate.

Nick Richards

@nedrichards

Fuzzy Time Everywhere

I do not always want to know what time it is. This is a slightly awkward position for someone who keeps making clocks, but there we are. Quite often the useful answer is not 17:42. It is “quarter to six”, “nearly lunch” or “you should probably start thinking about leaving”. The precise time is useful when catching trains, baking things and joining calls; the rest of the time it can be a bit much.

So I have been working on fuzzy time for a while. The first version I made was for the Pebble, which remains one of those devices that makes later technology feel slightly ashamed of itself. A small always-on screen, good battery life, physical buttons and just enough personality. It’s not tokyoflash after all.

The current versions are Fuzzy Time GB, a Wear OS watch face, and Fuzzy Clock GB, a GNOME Shell extension.

Fuzzy Time GB watch face showing a fuzzy time phrase

The Android version is quite a funny object internally. It is a Watch Face Format v2 face, so the APK has no app code:

android:hasCode="false"

The face itself is declarative XML. Since writing thirty-six thousand lines of watch face XML by hand would be a cry for help, there is a generator which writes the cases out from the same fuzzy time rules. For every hour and every five-minute bucket it emits the condition, text and separate interactive and ambient versions.

That sounds excessive until you look at the details; and then it still sounds excessive. There are lots of pernickety things that give this the correct GB locale to my ears. “Five Past Midnight” is a real phrase. 23:58 should say “Midnight”, and if the date is visible it should be tomorrow’s date. 11:58 should say “Noon”. “O’Clock” wants different spacing and weight from “Twenty-five To”. Ambient mode wants smaller, quieter text. A round watch face leaves less room than you think it does. The watch face has a few small choices rather than a settings cathedral: warm white, cool white, soft green, dim amber; system font or Arvo; optional radial complication slots above and below the text. The range complications are deliberately arcs around the edge rather than little widgets in the middle. They can show useful things, but they should not make the face stop being mostly words and calm black space.

Fuzzy Time GB GNOME Shell extension

The GNOME version is the same idea on a different surface. It finds the existing clock label, listens to the same wall clock, respects the existing “show date” and “show weekday” settings, and changes the text. I have wanted to build something like this for years, partly because of Emmanuele Bassi’s word clock extension. That extension was great, but not quite the thing I wanted, so eventually I got around to making my own.

One of the few design decisions left that I helped on in main GNOME (which is much better now) is that the shutdown and logout dialogue only updates its timing every so often. It could update every second; the computer is quite capable of counting. But it’s much more pleasant when the number doesn’t twitch constantly while you are trying to decide whether you meant to press the button.

You can build both projects from source. I may choose to distribute them in a more structured fashion in future. The Android one is a minimal Wear OS watch face, and the GNOME one is a normal Shell extension that currently supports GNOME Shell 45 to 50.

Shivam

@dedacc

Journey Starts : Gitg Port to GTK4

About Me 

Hello Everyone! I am Shivam, I am currently pursing my engineering in Electronics. I have been selected for GSoC 2026 for the port of GNOME-Gitg from GTK 3 to GTK 4. I am starting this blog in order to document my journey of porting Gitg. I have been contributing in GNOME from several months and in awe with the supportive and helpful nature of the community.

Project

As many of you probably know, Gitg is still using GTK3, which means it misses out on a lot of the improvements and features that came with GTK4. The main goal of this project is to port Gitg from GTK3 to GTK4 and then gradually modernize the application.

The scope of the project itself is quite large, and that’s honestly one of the most exciting parts for me. Working on this port will help me understand the application interacts with different libraries and components behind the scenes.

At the same time, I hope that this work will help the new contributors like me easier to get started contributing to the various GNOME projects 🙂

Conclusive Goal

The final goal of this project is to get Gitg building and running completely with GTK4 dependencies. At the moment, the application still fails to compile, which is expected since many GTK3 APIs are still present throughout the codebase.A separate GTK4 branch already exists where parts of the migration work have been started, and several components have already been adapted to GTK4. This project will continue building on top of that existing effort and gradually move the remaining parts of the application to the newer toolkit.

I would also like to sincerely thank the contributor(s) who have worked on the GTK4 porting work earlier. Their efforts created the foundation for this project, and I’ll be continuing from the work they have already done.

Thank You For Reading!

 

 

PS:- I would also like to thank Alberto Fanjul for mentoring me in this project and Felipe Borges for this time and support.

I realized that A cheap VPS is a good front

I have a server at home. It runs a Kubernetes cluster and a few services. I want to expose them to the Internet, so I can e.g. share public links from my Nextcloud, or synchronize my Kobo reader with Grimmory. But I don't want to expose my home IP to the world, and I want to have some reasonable protection against unsophisticated DoS attacks.

I realized that I can achieve that with a cheap VPS that acts as a front, HAProxy, and Wireguard.

I rented a tiny VPS for €4/month at Infrawire (1 vCPU, 2 GB RAM, 25 GB NVMe). I installed a Debian 13 on it, because I want that front server to be as stable and low maintenance as possible, and installed the Debian-packaged HAProxy onto it. I also installed Wireguard. The VPS has a publicly accessible IP, so it will be my Wireguard server: my server at home can reach the VPS to establish a tunnel, the opposite is not true.

On my k3s node, I've installed Wireguard as well. I configured Wireguard on the VPS and my k3s node to establish a tunnel between the two. I've also bound the sshd on my VPS to the wireguard address. Infrawire offers a console so I can unstick myself if I locked me out of my own server (e.g. by misconfiguring Wireguard on any side, or if my server at home had any failure).

I pointed all my DNS records to the VPS. The HAProxy is a "dumb" tcp forwarder, so I can keep operating like before on my cluster. In particular, HAProxy doesn't do TLS termination. My certificates are fetched on my cluster by cert-manager like before, using the http-01 challenge and Let's Encrypt. I could also move to dns-01 challenges, but http-01 just works and lets me switch to a registrar without an API if need be.

That way, I don't need a fixed IP at home, and I don't have to do any port-forwarding from my home router to my k3s cluster. Even better: the VPS has an anti-DDoS protection included, and I can also configure HAProxy to refuse too many connections from a same IP, I can make it close TCP connections that take too long to establish, and more. If my VPS gets hammered, I can still access my services from within my home network.

Michael Catanzaro

@mcatanzaro

Single-Click Code Execution Exploit for Evince, Atril, and Xreader

CVE-2026-46529 is an argument injection vulnerability in Evince, Atril, and Xreader caused by missing shell quoting when composing a command line. The reporter, João Medeiros, has published a GitHub repo for the CVE and a blog post with the story of how he discovered the flaw and developed the exploit. He also created an Atril security advisory and an Evince issue report.

The vulnerability is fixed in:

  • Evince 48.4 (fix commit) (I originally reported that it is fixed in 48.2, but there was no successful release for that tag)
  • Atril 1.28.4 and 1.26.3 (fix commit)
  • Xreader 4.6.4 and 3.6.7 (fix commit)

If you use one of these PDF readers, update immediately. Or at least please be seriously paranoid about clicking on links in PDFs until you do update.

This vulnerability also affects Papers, but it’s probably not urgent to update Papers. (No, not because it uses Rust. Keep reading!)

The Flatpak sandbox could have drastically reduced the danger of this attack, limiting the compromise to only files that you had previously opened in the PDF reader. Sadly, Evince and Papers both use sandbox holes that render the sandbox totally meaningless. (Atril and Xreader are not available on Flathub.)

The Vulnerability

When you click on a link in a PDF, Evince may execute itself to display the link. Normally the command line used would look something like this:

/usr/bin/evince --named-dest=/home/foo/hello.pdf

But an evil PDF may trick Evince into executing a command that is quite different than expected:

/usr/bin/evince --named-dest= --gtk-module=/home/foo/evil.so /home/foo/hello.pdf

Oops. The first part of the command is always going to be /usr/bin/evince, but the evil PDF is nevertheless able to unexpectedly load a GTK module into Evince. The fix is to quote the untrusted input using g_shell_quote() to ensure it cannot “break out” of its intended context:

/usr/bin/evince --named-dest='/home/foo/hello.pdf'

Or:

/usr/bin/evince --named-dest=' --gtk-module=/home/foo/evil.so /home/foo/hello.pdf'

Much better: now the threat is neutralized. g_shell_quote() is safe to use even if the untrusted input itself contains quotes. (However, beware: this only works because GLib is parsing the command line itself, and GLib is not a real Unix shell. It’s not safe if the input is going to be passed to an actual Unix shell. It might not even be theoretically possible to do that safely, because it’s valid for filenames to contain entirely arbitrary characters!)

All GTK 3 apps support the --gtk-module command line argument for injecting a shared library into the application. The library may of course then execute whatever code it wants via its library constructor. But GTK 4 no longer has standard GTK command line flags, so this does not work for GTK 4 applications like Papers. It’s still possible to tell a GTK 4 app to load a GTK module, but only via environment variables, not via command line flags, and I don’t see any opportunity for the malicious command to set environment variables. It’s probably not possible to exploit this vulnerability in Papers: although it has the exact same vulnerability as the other PDF readers, the impact is different.

The Exploit

So far this looks like a pretty typical security bug. OK, so if you trick the user into downloading an archive (or perhaps a git repo) that contains both a malicious PDF and also a malicious shared library, then you can trick the PDF reader into loading the shared library and thereby execute arbitrary code. That’s a pretty bad foreseeable exploit, sure, but at least the attacker is at considerable risk of arousing suspicion if the user is trying to download a PDF and also receives a shared library. You’d have to try pretty hard to hide the library in a forest of other boring files if you want the attack to look convincing and unsuspicious. Right?

Nope.

João used Claude Opus 4.7 to develop a sophisticated script for building malicious polyglot PDFs that are simultaneously both valid PDF files and also valid ELF binaries, so the attacker only needs to trick the victim into downloading one evil PDF file. When the victim clicks on a link in that PDF, the PDF reader will dlopen the PDF itself. The PDF/ELF polyglot’s library constructor will then execute arbitrary code. Much less suspicious, and much scarier. Polyglot files are not entirely novel, but I’d still say this required substantial creativity and expertise from the AI, and substantial persistence from the human. Needless to say, very nice job to both Claude and João.

You can easily build your own malicious PDF using the provided script and sample GTK module. The script in the Evince and Atril issue reports requires that the attacker predict the absolute path that the malicious PDF file will be saved to; however, João’s blog post and GitHub repo refine the exploit to remove that requirement.

Thoughts on AI Vulnerability Reports

A human inspecting this code should have been able to find the parameter injection vulnerability, but that requires considerable time and effort, so unsurprisingly nobody did. We’re probably in for a rough time in the short term as the volume of AI-generated vulnerability findings remains temporarily very high and attackers have a much easier time crafting working exploits. But in the long term, I expect we are going to be much more secure than we were before, so this will be worth it.

A human working alone would have almost certainly stopped and moved on after finding the vulnerability. Claude allowed taking the investigation much farther. It’s highly unusual for a GNOME vulnerability report to come with a working exploit. This is a dangerous change. Perhaps it will be a one-time event, but I suspect we will be seeing more frequent exploits in the future.

Silver lining: the exploit helps us better appreciate the severity of the issue. It’s often hard to assess how bad a vulnerability is. If not for the weaponized exploit, I would have thought this bug was not very scary, and would have treated it as not a big deal. We would have fixed it, perhaps or perhaps not with a CVE ID, surely without any blog post or fanfare, and probably without distro security updates. But since there is an exploit, we instead had no doubt that this vulnerability was dangerous, and were able to handle it accordingly.

Several GNOME projects have begun outright prohibiting all AI-generated contributions, including issue reports, with no exception for vulnerability reports. Such policies are misguided and unacceptable. I can sort of understand why some projects might (misguidedly) wish to prohibit AI-generated code contributions. OK, fine. But blocking AI vulnerability reports will make GNOME less safe. AI-assisted vulnerability reporting is the new industry standard for good reason: it is highly effective.

Some humans are not good at preparing AI-assisted vulnerability reports and will spam maintainers with low-quality reports. Sometimes they will be outright bogus, although more often there may be valid underlying bugs with exaggerated severity claims or bad proof of concept demos. This is annoying, but bad issue reports are a cost we are just going to have to accept and deal with.

The quality level of AI vulnerability reports reviewed by conscientious humans — as well as AI assessments of AI vulnerability reports — is now often quite encouraging. But just like humans, AIs may also miss things, especially subtle distinctions that may be highly relevant. Although I’m quite impressed with these AIs, we still need experienced humans to review and manage reports. Please don’t abuse the technology by submitting vulnerability reports that you do not understand or have not validated. And certainly please do not allow an AI agent to interact with an issue tracker on your behalf!

For Security Geeks

This was my first time scoring a vulnerability using CVSS 4.0 rather than CVSS 3.1. It’s also the first time I wasn’t terribly confused about how to set the parameters, because the scoring guide contained answers to all of my questions. Nice. My CVSS vector for CVE-2026-46529 is CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:A/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N, the base score is 8.4, and I’m pretty sure my choices for each parameter are good. By comparison, using CVSS 3.1 I came up with CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H and base score 7.8.

Bart Piotrowski

@barthalion

Why are Flathub downloads so slow sometimes?

It's probably not your fault.

On a cache miss, there are two things a reverse proxy (which Fastly is to us) can do. It can make the client wait until the proxy itself fetches the requested content and then serve it, with subsequent requests being served from the cache. From a user's perspective, it means staring at "hung" process, and people tend not to be understanding when a program is stuck seemingly doing nothing.

Instead, the proxy can stream the response from the origin, caching it at the end. This makes the client receive the data right away, although it's not without drawbacks.

In a streaming setup like Flathub's, an all-MISS path adds some upstream latency before the first byte, but also limits the download speed to what the slowest link can deliver. As we don't run servers in the same datacenter or on a single backbone network, the hop from Fastly through the caching proxy to the master server incurs a penalty that may affect how quickly the data gets back.

In order to cache files larger than 20MB, Fastly expects customers who use streaming misses to use segmented caching. Anything larger than that gets broken down into smaller chunks. When Fastly wants the data from us, it will add a Range header specifying which bytes we should respond with. Fastly will then serve the request after reconstructing the file from various chunks. Our caching proxies also use the value of the Range header in the caching key to avoid requesting the full file over and over again from the master server as well.

While great for caching, many concurrent range MISSes can turn what would be a sequential file read into scattered, random reads. It wouldn't matter with SSD or NVMe, but as the repository is stored on HDDs, when combined with streaming misses, it can turn cold transfer speed into min(network bottleneck, ZFS random-read bottleneck).

Counterintuitively, you may improve your download speeds by aborting the ongoing Flatpak operation and starting it again. While the initial request was slow, there's a non-zero chance it went through all the caching layers and it will become a cache hit in the meantime.

Flatpak

Let's talk Flatpak. When installing or upgrading applications, Flatpak will try to use delta files. A typical delta is an update file that contains only the difference between versions. There are also from-scratch deltas, which effectively are an archive with all files required to install an app from scratch, thus the name.

Flathub generates a single upgrade delta and a from-scratch delta for the latest version. Delta generation is an expensive process in terms of disk reads and writes, but also disk space. Because our ZFS setup isn't exactly the fastest, generating more delta files also affects how quickly we can publish an update. Yes, in theory we could be doing this out of band but we don't. In hindsight, Titanic wasn't unsinkable after all.

What happens if you are not updating often enough? A lot of suffering. Flatpak will download each missing file between the version you are on and the one you want to upgrade to, separately. This is an almost certain cache miss causing even more random seeks on the master server. At this point Flatpak would be better off downloading the from-scratch delta but it can't. The behaviour is controlled by OSTree, which doesn't offer any knobs to affect it. It is the right choice if the goal is to limit the bandwidth used by the client to fetch updates, but an incredibly bad one for anyone on a reliable connection; downloading a single large file is almost always faster than fetching multiple smaller ones.

What do? Some brave soul could fix OSTree to apply a better heuristic on when to use from-scratch deltas for upgrades, or at least make it expose an API that lets Flatpak choose. For the rest of us mere mortals, we can only update regularly or wait patiently for the update to finish.

Status update, 21st May 2026

I often write about how when stuff works well, you take it for granted.

It’s true for technology: when’s the last time you hit a compiler bug in GCC? Once upon a time these were a common thing and you had to choose your C compiler wisely. Yet I haven’t recently seen an article that says “GCC is going great” .

It’s true for people too. When someone does an excellent job maintaining an open source project then, they do occasionally get some gratitude, but — if you do a bad job, it’s amazing how quickly the negative comments pile up in the issue tracker, many of which taking subtle or not-so-subtle digs at the project owners. Maybe we created this situation for ourselves by having a prominent “report issue” button but no corresponding “send flowers to the maintainer” button.

On that note, a hat tip to Carlos Garnacho for all his work on the Localsearch extractor sandbox which recently got a shout out for its “extremely strong” design.

(It’s worth noting that Localsearch also stopped using GStreamer to parse media files altogether, which the discussion in that thread missed. We love GStreamer but it isn’t the right tool for metadata scanning. The 3.9 and 3.10 series use libav/ffmpeg instead, but given that US software patent laws make it tricky for USA folk to distribute that, the plan is to move to using MediaInfoLib)

Fairphone 5

It’s coming up to two years since I switched to a Fairphone 5. The real proof of this device will be in 2033 when I manage ten years of using the same phone.

Meanwhile, I recently had some issues with it not charging via the USB-C port. I thought it might be a bit tricky to fix, but it really is easy: buy the replacement part (about 20€), take off the back cover, remove a few small screws and switch over the whole USB port + speaker unit.

I hear some fellow Android users complaining about Alphabet/Google’s intrusive AI integration. Apparently the power button is now the AI button? I use the stock Android, and I know vendors have their hands tied somewhat by Alphabet/Google, so its worth noting that disabling the AI integration on the Fairphone 5 is a single config setting.

I’d be interested to know more about the kernel version as it is old as hell. I guess this is a vendor/Android thing, and hopefully most of the many known vulnerabilities in this old version of Linux are mitigated by sandboxing higher up in Android. If you’re a high risk cybercrime target then I would definitely not recommend using the vendor Android OS on this device. (Probably best to avoid Android altogether if this is your situation!)

So its not perfect, but I just wanted to shout out again that there are some good people doing good work here. If only all smartphones were built like this one.

Korg Minilogue XD

One reason I’m not writing much about open source software is that I’m spending a lot of my time outside work making music in various guises, these days mainly as part of soon to be huge Galician disco revival group Muaré. This band needs a website, so in future I don’t have to link you to Instagram, but you know how the world is at the moment. We do at least have a Bandcamp page.

When it comes to music gear, I seem to be a Yamaha guy. It’s amazing actually that the same company that made my trombone also makes excellent digital pianos, and if and when I need a motorbike, Yamaha also sells those.

When it comes to synths though I’ve been really enjoying the Korg Minilogue XD. It’s cheap, built like a tank and its ten years old so there are plenty of second hand models around. It’s not fucking Behringer (please don’t give money to Behringer). It’s simple and sounds great.

But most impressively, it support plugins via a freely available SDK. You can develop your own custom digital oscillators and effects for this thing and deploy them over USB. Of all major pro audio manufacturers, Korg are the only company I know to support this. So even though the hardware is now 10 years old, it can still learn new tricks, and there is an active scene of both free and commercial plugins for the platform. Perhaps the most active commercial outfit is Sinevibes. There is, of course, reddit. The SDK is not truly open source (and few things in pro audio ever are) but it’s free from any licensing fees, and the whole thing is sat here in a Git repo. Pretty good.

If I’d had more time to prepare I might have a video here of some cool Minilogue XD tunes I made. But I guess you’ll have to wait til next month for that. Until then!

Richard Hughes

@hughsie

LVFS Sponsorship Announcement: HP

Some more great news: I’m pleased to announce that HP has also agreed to be premier sponsor for the Linux Vendor Firmware Service (LVFS) as part of our sustainability effort.

list of vendors sponsoring the LVFS service

With the industry support from HP (and our existing sponsors of Lenovo, Dell, Framework, OSFF and of course Linux Foundation and Red Hat) we can turbo-charge the growth of the LVFS even more. Thanks!

GIMP

@GIMP

GIMP on MS Store now requires Windows Build 20348

We received some informal reports in the Microsoft Store reviews telling us that GIMP updates were deleting user data! In particular, plug-ins, themes, brushes, user settings and other add-ons were not being kept during an update from the store.

To resolve this, the updated version of GIMP from the Microsoft Store will require Windows 11 or at least Windows Server 2022 (Windows NT Build 10.0.20348.0).

Windows 10 is still supported, but only when GIMP is installed from our .exe installer.

Note that we are no longer able to support versions of Windows older than Windows 10, and can no longer support 32-bit systems running Windows.

Installing the update will prevent future data loss during upgrades.

Technical details

This was being caused by the way MSIX (the format of apps distributed on Microsoft Store, GIMP included) is designed, which prefers sandboxed user data (like Flatpak and Snap). We have been trying to work with this requirement when running from MSIX, but were not successful.

So, to fix this packaging limitation, we were given the special “restricted capability” unvirtualizedResources (specifically virtualization:ExcludedDirectory), to preserve user data on %AppData%\GIMP, just as the .exe installer does. This is similar to what we do on other sandbox distributions such as Flatpak and Snap.

After the update, GIMP user data will be copied on upgrades.

The update does not affect images or other resources stored outside your GIMP folder.

How to migrate the previous user data

This restricted capability is only available on Windows NT Build 10.0.20348.0 and later. So, users with older Windows versions will need to use the .exe installer.

Note: the binaries inside the .exe installer and the .msix are exactly the same.

For users who were using the MS Store version until now and are switching to the .exe installer, here are instructions in order not to lose your configuration data from the MS Store version:

  1. Manually locate your config dir at %LOCALAPPDATA%\Packages\ (you may just copy-paste this string into the Windows Explorer).
  2. Search a folder starting with GIMP (with random numbers and letters after that) at this location.
  3. From there, navigating down to LocalCache\Roaming\ where you will find a folder named GIMP.
  4. Copy this GIMP folder into %APPDATA% (again, just copy-paste %APPDATA% into the Windows Explorer to find the correct location).
    Note that maybe the folder %APPDATA%\GIMP\ already exists, in particular if you also used the .exe installer at some point. If so, it is up to you to decide whether you want to override the data already present with the ones from the MS Store GIMP.

This procedure is a one-time thing as a workaround to this exceptional issue. You won’t have to do it again in the future!

Martin Pitt

@pitti

Leaving Red Hat

In December 2016 I left Canonical with one sad and one happy eye, with lots of good memories. Now it’s time to revisit some more! Starting at Red Hat back then was quite a cultural shock, of course. I got used to the new headwear fashion quickly: But never really to the rest of the formal dress attire: The drinking habits I was familiar with, and I quickly learned enough Czech to get “dvě piva prosím”:

Andy Wingo

@wingo

soot, solar, sedimentation, sin, & 'centers

Good evening, friends. Tonight I have a few loosely-knit stories.

soot

A couple years ago, my house was heated by a condensing gas boiler. It was awful from both an environmental and a geopolitical perspective: environmental, as I would emit somewhere around 2.5 tons of CO2 equivalent per year to heat my home, which compares poorly to the target total CO2e emissions of 2 tons per year per person; and geopolitical, because although France gets 40% of its gas from Norway, with whom we have no beef, all the rest is a problem in some way. (Algeria, 10%, is the least of my worries; the 20% for Russia and the US respectively are the most, followed by 10% for the Gulf states.)

Still, natural gas is better than fuel oil, which we had at my former rental house. It is a lamentably visceral experience to call up the fuel provider and say, yes, s’il vous plaît, can you drive a diesel-powered tanker truck out to my house, unroll the hose, and pour out 1500 liters of toxic fuel oil into a tank under my garden. Yes, I will just burn it all. Sure, see you again next year.

Some friends of mine recently had their fuel boiler die, which is itself an experience: one of them came over to visit, completely covered in soot, saying that the chimneysweep (whom he also has to call every year) said that his boiler is on its way out, that the chimney is completely clogged, and now because of the cleaning his basement is also covered in soot; awful. What to replace it with? Apparently despite the prohibition on new fuel-oil boiler installs, it might be possible to just install a new one; or they could hook up to natural gas from the street; or they could install a heat pump. Which to do?

To all these questions there is a moral answer, which we can phrase in terms in CO2 emissions and localized PM2.5 pollution, and it is always and everywhere to stop burning things. But fortunately we don’t need to rely only on moralism: electrification is just better, in essentially all ways. Owning and operating an electric car is a better experience than a petrol car. Induction stoves are better than gas; I know, I did not believe this for the longest time, but I was wrong. The experience of using a heat pump is pretty much equivalent to gas, so it’s a harder sell, but it is a relief to no longer have a pressurized methane tube connected to my house.

In the end, I think my neighbors are going to go for the heat pump, despite the 20k€ price tag, labor included. (Oddly, I think the deciding factor was that my neighbor confessed to having had a long chat with an AI chatbot, after which she felt she had a good understanding of the proposed solution and its tradeoffs; make of that what you will!)

solar

In late November I got some brave lads to install nineteen solar panels on my roof. Each of these magic rectangles can make up to 500W of power in optimal conditions, but my house faces south, with the roof inclined east and west, so it’s unlikely that I will ever hit the full 9.5 kW of potential power.

graph of consumed and produced electricity in december, showing a more or less constant 2 kW use, with a tiny hump of production in midday, peaking at, like, 150W

December was... very dark. The panels produced a total of 145 kWh over the month, but I used 1250 kWh of electricity, essentially all to run the heat pump. I live in a basin that is mostly covered by low clouds from November to February, and slanty photons couldn’t make much headway through the fog. The house is well-insulated (20-25 cm of wood-fiber exterior insulation on sides, 40 under the roof, though it is an old house with a few less-insulated bits), so it’s not that I am leaking lots of heat, and I have a combination of low-temperature floor heating and low-temperature radiators, so it’s not that I’m running the heat pump inefficiently to generate a too-high output temperature; it’s just, you know, cold in winter. A typical day would be between 1 and 5 degrees C. Cold; cold and dark.

Things got a little better in January: 285 kWh produced, though the heating needs are higher than in December, with 1450 kWh total consumed. In February we grew to 419 kWh produced, for 850 kWh consumed. In March we equalized, with about 850 kWh produced and consumed, but although the bulk of my consumption in this month is for heating, the “need” to heat overnight meant that I consume from the grid overnight, but feed in to the grid during the day. I have a small battery (7 kWh), but it’s not enough to store the “excess” electricity generated in a day; I should probably arrange to have the system heat only during the day in these months, to avoid taking from the grid.

graph of consumed and produced electricity in may, showing the battery never emptying, little power use, and a huge hump of production peaking at 7 kW

With practically no heating needs now, as you can imagine, I am just feeding a lot of excess to the grid. We’re halfway through May, just coming through a cold snap (the peasant lore is that we just passed the saints de glace, the date you need to wait for to plant crops that aren’t frost-hardy), but still we’ve produced more than twice as much as we’ve consumed (550 kWh vs 220 kWh), and essentially all the excess goes to the grid. The 7 kWh battery is quite enough to cover night-time electricity needs.

I didn’t know before, but often a solar panel installation doesn’t work when the grid is down. This is because the inverters that convert the DC from the panels to AC for the house need to match phase with the grid, and if the grid’s phase signal is down, they stop. It’s also for safety, so that line workers can repair downed lines without worrying that every house is a live wire. I spent a little extra to install a cutout that allows the house to run in “island mode” if the grid is down. We almost never have that situation here, though, but it seemed prudent that if we were going all-in on electricity, that perhaps we should take precautions.

When you buy a solar installation, you can either have little DC/AC inverters attached to the back of each panel (microinverters), or feed DC from all panels wired in series (they call them strings; there may be 2 or 3 of them in a home setup) to a central inverter. I have the latter. The panels happen to be assembled locally by MaviWatt, though surely the cells themselves are from China. My panels are installed on top of the ceramic roof tiles with little clips and an aluminum structure. (It used to be that sometimes panels would replace tiles and become the roof. That’s not done so much any more here.) Installation is, like, 60% of the price of solar. Often you need scaffolding, though my installers just used ladders; perhaps living in the mountains where I am, there are more people used to doing ropes and rock-climbing and such. I don’t think they took as much care of themselves as they should, though.

My inverter is made by Huawei (SUN2000), as is my battery and the cutout (“backup”) box. Some batteries have their own microinverter, allowing them to consume and produce AC, but this one is DC, hence the need to have the same brand as the inverter. It sends all my electricity usage data to China or something, so that it can send it to the app on my phone. It’s not ideal from an geopolitical perspective but it is good kit.

sedimentation

Although we haven’t hit the height of summer yet, I would like to offer a few observations that have precipitated out of solution.

Firstly, at least in my house, the baseline load without heating is pretty low: 200 or 300 watts or so. (I didn’t know this before looking at Huawei’s app.) We have a recently renovated, not tiny, but otherwise normal sort of house with, you know, the usual lot of modern conveniences, idle chargers plugged in here and there, and also my work computers and such, and it all runs on less than a handful of the old 60W bulbs. That’s interesting.

As far as actual load, there are only a few things that count: heating, when it’s cold; it can easily average 2 kW on a cold day. Plug in the electric car (I don’t have a wall box yet, just with the mains plug), that’s another kilowatt. I hardly drive, though, so it’s not a huge load. Using hot water is perhaps the most surprising thing: it can cause a spike up to 6 kW, over a short time, despite the heat coming from the heat pump; probably there is some tuning to do there. The oven and stove are little tiny blips. There’s the kettle, but it’s also a little blip. Nothing else matters: not the dishwasher, not the washing machine, nothing. You can leave the lights on all day and it just doesn’t matter.

Call me naïve, but I had hoped that solar would help my electricity usage in winter. This is simply not the case. Though the heat pump is efficient, there does not appear to be a magical energy solution for December, which is the bulk of my energy usage. My electricity bill is fixed-rate: 20 cents per kWh used. Using 4000 kWh or so from the grid over winter costs me 800€; annoying. I don’t have a natural before-and-after experiment as we added on to the house as we were renovating, but for context, in my previous poorly-insulated rental house that was half the size of this one, we’d pay 2000€ or so per year for heating oil. Perhaps I can lower the 800€ via variable-rate metering, to let the battery do some arbitrage, but there are some fundamental constraints that can’t be finagled away.

When I got my solar panels, I was resigned to never getting peak power, as they are on two different sections of the roof. It turns out that doesn’t matter: firstly, because 9.5 kW is a lot of power, as you can appreciate from the numbers above. I could never do anything with 9 kW. But secondly, because power isn’t equally valuable at different times of the day: by having east and west roof pitches, I can start producing earlier and continue producing later than if I had, say, a flat roof with panels tilted to the south. And the morning and the evening are the peak hours both for my house and for the grid, so that lets me consume more of my local production both when I need it and when the grid is under higher stress.

I was interested to hear that Alec Watson of Technology Connections had reservations about residential rooftop solar. I found a video in which he explains his perspective, which has a delightfully socialist character. His beef is partly due to the net metering scheme in some parts of the US, in which each kWh fed to the grid makes your meter run backwards; Watson finds it unfair, because it lets those wealthy households who have the capital to install solar to opt out of paying for the grid, which is a social good. In some cases, these households actually capture a part of what consumers pay for the grid, unlike industrial producers who are paid wholesale rates that don’t include transmission. Also, he finds it less efficient overall to install solar panels on houses rather than in bigger solar parks; each euro that society allocates to solar would go farther if we pooled them together.

Both points are interesting, but I would offer a couple responses. Firstly, at least in Europe, net metering is not really a thing; we have smart meters and I hear from friends in Portugal that there can even be a charge for grid injection at some times, if the grid is overloaded. France’s case is a bit weirder; I wouldn’t have gotten as large a system as I did, but there was a government program to offer a fixed buyback rate of 7 cents per kWh, stable for 20 years, if you installed more than 9 kW of panels. But given the lack of solar in December, I still pay the grid when I need energy the most.

Putting solar panels on roofs is indeed less efficient than putting them on a field. But, we are not in a situation of scarce solar panels: China could make another 350 GW of panels this year if there were demand. An incentive like the 7-cent buyback rate encourages capital allocation to solar, effectively calling these panels into existence. The bank loans me 20k€ at 4%, and the elimination of 3000 kWh that I would have bought from the grid in a year plus the 9000 kWh that I sell to the grid covers the cost entirely, and I get a life insurance policy on the remaining principal. It’s not a great investment financially but it doesn’t cost me anything either.

sin

As a person with a conscience, I have always experienced questions of energy as questions of sin; to leave a light on is not simply inefficient but a moral failing. Each kilometer a car travels on fossil fuel carries with it a quantum of guilt and must be justified in some way, otherwise a moral stain attaches.

Solar panels and electrification changes all this. 8 or 9 months out of the year, I live in a world of abundance: the electrical generation capacity that I have called into existence is free, clean, and much, much more than I need. Owning and operating a car still has externalities, but the emissions and cost aspects are entirely gone. It’s a funny feeling, and disorienting.

I grew up in the south of the US, where everyone has air conditioning. I came to see it as sinful, too; burning things and making emissions just so you could be a bit more comfortable. I haven’t lived in air conditioning since then, but it does get hot in summer, and I would be more comfortable if I could pump heat out of my house. Now I can. I have excess power available right when air conditioning (or, in my case, floor cooling) is needed. On a societal level, solar plus air conditioning is going to be a key part keeping our cities liveable while we ride out higher temperatures.

‘centers

It is with a sense of dissonance, then, that I have been experiencing Datacenter Discourse™: there is a lingering language of sin proceeding from an environmentalism born in penury, in a world in which every kilowatt-hour is precious and scarce. If China has unallocated capacity for another 350 GW of panels this year, why stress about a few GW of datacenters?

Of course, there are many aspects to these AI datacenters, but today I am just thinking about energy. Given that each GW of datacenter places extra demand on a grid, equivalent to 3 million times my home’s baseline load, or maybe 300 thousand of its winter load, if society wants this kind of datacenter to be a thing, it needs to add that amount of clean energy to the grid, with adequate battery storage to even out supply. We should, as a society, require this via legislation, because the market seems only too happy to use natural gas or even coal if it is marginally cheaper. At least if the datacenter boom busts, we’d be left with more clean energy production.

Conversely... and I don’t think I’m going too far here, but causing new fossil generation to come online in 2026, or even prolonging the life of existing generation, should result in the state confiscating all property of those responsible. (I have moderated my previous position, which was hanging.) Such people are not fit to live in society, so society should not allow them to own things.

Anyway. I think that those of us that wish “AI” were not a thing are losing the battle, and that we should prepare to fall back to more defensible positions; otherwise we risk a rout. A requirement to bring additional clean capacity online in sufficient amounts should be a baseline ask when it comes to datacenters. We have the productive capacity in the form of solar panels, at an affordable price, more than enough space in terms of the existing cropland that is inefficiently turned into ethanol to burn, batteries are a thing, and we just lack the political will to turn what could be into what is.

And as for AI datacenters themselves: there are enough aspects to argue about as it is. We do ourselves a disservice by weighing down the Discourse with outdated ideas of what is and isn’t possible.

Sjoerd Stendahl

@sstendahl

Graphs 2.0 is out!

After two years of development, Graphs 2.0 is finally out!

This will be a shorter blog, as the changelist of the new features have been discussed in the previous post in more detail, you can check this out here in more detail if you’re interested: https://blogs.gnome.org/sstendahl/2026/04/14/announcing-the-upcoming-graphs-2-0/

For a quick overview, a quick reprise of the most interesting features can be found here in bullet-point format:

    • New Data Types with proper equations: In this new release, we finally have proper support for equations, where equations are manipulated analytically (e.g. a derivative on y = 12x² will result in y = 24x, and be rendered accordingly), limits are rendered infinitely, and equations can be changed after importing them. To accomodate this change, we now have three different data types. Equations, Imported Data, and Generated Data. Imported Data is regular data that you import from file. Generated Data behaves the same as Imported Data, but you generate the dataset using an equation. For generate data you can also change the equation after the fact, and rerender, or change limits or amount of generated datapoints.
    • New Style Editor: We revamped the style editor. One major change is that you can now easily import new styles based on matplotlib-style themes, and you can also export your style and share it with others. If you have a nice style you want to share with us, open an issue on the GitLab page and let us know. We’re looking to expand the default choice of styles :). Furthermore, when editing  a style, you finally see a preview of how this affects the canvas. This way you don’t need to guess, or go back and forth when finetuning a style.
    • UX-changes: Whilst the general look-and-feel of Graphs is still mostly the same, we did make some UX-changes with how we handle settings. Mainly, instead of showing a modal popup dialog, settings that affect the canvas itself (i.e. item and figure settings) are now shown in the sidebar instead. The reason for this, is that the popup dialog hid the figure that you’re editing, making it difficult to see how your changes actually affect your canvas.
    • Improved data import. We revamped the data import completely. First of all, we have made the codebase here much more modular, making it easier to write new parsers for other filetypes. We now added support for SQLite Database Files, Microsoft Excel Sheets and .ods files from LibreOffice Calc. It’s also now possible to import multiple files at once, and finetuning the settings for each file individually. Another nice feature here is that you can now import multiple datasets from the same file without having to reimport them. Finally, we added proper-support also for single-column imports where x-data can be generated using your own equation
    • Error bar support: Graphs now has proper support for error bars. The error bar style can be set globally in the new style editor, or individually for each item.
    • Reworked Curve Fitting: The curve fitting logic has almost been completely rewritten. Whilst it mostly still works the same, the confidence band that is shown is now calculated properly using the Delta-method, instead of using a naive way using the limits of the standard deviations. We also added support to show the residuals to verify your fits, and added more useful error messages when things go wrong. The results in the curve fitting dialog now also show the root mean squared error as a second goodness-of-fit figure. The parameter values themselves in the curve fitting dialog are no longer rounded (e.g. 421302 used to be rounded to 421000) and finally custom equations in the curve fitting dialog now have an apply button, greatly improving the smoothness when entering new equations
    • Proper Mobile Support: With this release, we now officially feel confident in stating proper mobile support. The entire UI is tested on real mobile phones using PostMarketOS, and everything works properly. Labels are now also ellipsized earlier on narrow displays, so that the UI remains useable.
    • Reworked Figure Exports: Figure exports are reworked. Instead of simply taking a snapshot of the current canvas, you can set the actual size in pixels when exporting a figure. This is vital when trying to create reproducable figures for e.g. publications. Of course, you can also easily still use the same canvas that you see in the application.
    • Quality of life changes: We haven’t even gone through each individual change we made in the blog, but here’s a quick fire-round of more quality-of-life changes:
      • It is now possible to have multiple instances of Graphs open at the same time
      • The style editor now also has the option to draw tick labels (so the numeric values) on all axes containing ticks, this is not supported by default in Matplotlib, so we had to add our own parameter here
      • Graphs now inhibits the session when unsaved data is still open, so you get a warning if you close your computer with unsaved data.
      • Added support for base-2 logarithmic scaling
      • Graphical fixes for the drag-drop animations, which used to look somewhat glitchy
      • Panning and zooming are now done consistently on all axes when using multiple axes
      • Data can now be imported by drag-and-drop into Graphs
      • The subtitle now also shows the full file path for Flatpaks
      • Limits can now easily be clicking on the numbers near the axes
      • The custom transformation has gained the following extra variables: x_mean, y_mean, x_median, y_median, x_std, y_std and counts
      • Warnings are now displayed when trying to open a project from a beta version
      • The many code refactors and reimplementations from Python to Vala makes the application more robust, and significantly more performance. Especially when working with larger datasets.

This release took a long time to get right, but we’re happy to get the new features to the public. Graphs is handcrafted by human hands, which takes more time than LLM-based slop. But the longer manual process does allow us to think through changes, and make intentional decisions with human care. I am very proud to say we are able to deliver something intentional where we can deliver the polish that both Graphs, as well as the users deserve. As always, thanks to anyone involved which includes everyone who has been providing feedback, reported issues, contributed with code, or helped in any other possible way. And of course especially to Christoph who has been maintaining Graphs with me and is responsible for a large part of the architectural changes that made this release possible.

Go get the new release from Flathub here!

p.s. On a more personal note with a shameless plug, I will be speaking at GUADEC 2026 about my journey into app development, and how to get into this world as an outsider without a CS degree. Be sure to check that out if you are interested in starting with applications, and want to know how it is to join a project in the GNOME ecosystem, it’s a lot less scary than it may sounds 🙂 

I’ll be joining on-site, so say hi to me there if you have any questions or are up for a chat :). Otherwise the whole event will be livestreamed as well, and you can always reach me at sstendahl@gnome.org. 

Allan Day

@aday

GNOME Foundation Update, 2026-05-15

Welcome to another GNOME Foundation update post! Today’s installment covers highlights from what’s happened over the past two weeks.

LAS 2026

Linux Apps Summit 2026 starts tomorrow! The organizing team, which includes members from both GNOME and KDE, has been hard at work and is on the ground in Berlin making final preparations. The schedule looks great, and it promises to be a well-attended event.

The talks are being streamed this year, so make sure to watch our social media for details, and tune in live to hear the talks.

GUADEC 2026

Preparations are continuing for July’s GUADEC. The call for Birds of a Feather sessions is currently open. If you want to hold an informal discussion or working session, please fill out the form before 5th June.

Applications are still open for travel funding for GUADEC. The deadline for submissions is 24th May – that’s just over one week.

Board meeting

This week the Board of Directors had its regular meeting for May. A summary:

  • The Board authorized the closure of a bank account which we are no longer using.
  • I gave an update on operations over the past month, and got feedback from the Board
  • Felipe Borges from the Internship Committee joined, to give a report. The Board discussed how we can best support the committee.
  • Deepa gave a finance report, which included numbers from January and February. The main news here was that our finances are running close to what was projected for this year’s budget.
  • The Board discussed the draft of the report from the audit we recently underwent, as well as the draft of our latest annual tax filing. This is a routine part of the Board’s work, as it is required to perform a review before these documents are finalized.

Office transitions

Our long-running effort to enhance our internal accounting processes has continued over the past two weeks. A notable development has been the retirement of several finance platforms, which have been effectively replaced by the new payments platform that we adopted in January. This platform reduction will reduce operational complexity, as well as workloads. It is still ongoing – we have an additional two more platforms that are currently in the process of being retired.

Another highlight has been the launch of a search for a new member to join our finance and operations team. This is a part-time, contract-based role, which has been shaped in close consultation with Dawn Matlak, who is supporting our finance and accounting operations on a temporary basis, and has already been factored into our budget projections.

We are looking for someone at director level who brings substantial nonprofit finance experience — including audit preparation and compliance experience — which reflects how much the Foundation’s operational and regulatory requirements have grown, particularly in the run up to and following our audit last March, and will provide in-house expertise which will reduce our reliance on external consultants. You can read the full posting here.

Thanks for reading, and see you in two week’s time!

Bart Piotrowski

@barthalion

How does Flathub even work? The CDN and caching layer

There is one specific way in which the non-corporate open source projects typically document how their infrastructure work: not at all, and Flathub is no different. The full picture likely lives only in my brain, and while it could be sorted out by anyone (especially in this LLM age, yay or nay), why should it only be me thinking at night about all the single points of failure?

Like any system that evolved naturally, it's all over the place. It's tempting to tell its history chronologically, but even then, it's difficult to find a good entry point. Instead, this post focuses on what happens when users call flatpak install; later entries will cover the website and, finally, the build infrastructure. Buckle up!

CDN, caching proxies, the master server

The secret of making computers work well is to have them not do anything at all, and that's the story behind serving Flathub's OSTree repository. Content-addressed objects are extremely cacheable as they are immutable, offloading the effort to the CDN provider.

When the client connects to dl.flathub.org, you can be certain it hits some layer of cache. Almost all the heavy-lifting is done by Fastly. At the peak, when both EMEA and North America are awake and at computers, 50 million requests per hour are cache hits served by Fastly's infrastructure, with a modest 20 million being misses passed down to our servers. There would be no Flathub without Fastly; Fastly does it completely for free, not even for fake Internet points as we are incredibly bad at highlighting what our sponsors do for us.

image

image

You can't do enough cache, and so various Fastly servers talk to Fastly-managed shield server which caches the most requested objects to avoid spilling over too much to us. For legit cache misses, the request will be served by one of 8 caching proxies we are running at different VPS providers. We use a consistent hashing director at Fastly which will pick the backend based on the path being requested. In the past, we used a dumb round-robin but as a result, each caching proxy had its own independent copy of the working set, wasting disk space and producing a higher miss rate against the master server. Hashing by URL behaves like one big cache instead of N copies.

These days, the caching proxy fleet consists of 3 servers at Mythic Beasts, 2 servers at AWS, another 2 at NetCup and a single server at DigitalOcean. We don't collect overly detailed metrics, but on average, each proxy serves around 1 TB/month back to Fastly and pulls roughly 5 TB/month from origin. With only 100 GB of disk space per proxy against a multi-TB working set, we're not so much caching the long tail as smoothing it. In the ideal world, we would be retaining much more data at this layer, but it's not the world we live in.

Each of these servers is running the latest stable Debian release. The requests are served by the usual nginx setup with proxy_cache enabled. There is some custom Lua code for invalidating certain paths after publishing new builds finishes (spoilers!). Vanilla nginx doesn't support the PURGE method, and third-party modules like ngx_cache_purge have not seen any maintenance for over 10 years. In the end, it was more maintainable to write Lua code to calculate the caching key of a URL and then run os.remove to "purge" it from the cache.

There's also a systemd timer for refreshing the Fastly IP allowlist. We used to expose these servers publicly, but a vision of everything crumbling down due to a DDoS attack kept me awake at night so this had to change.

On the far end of this setup sits a lonely physical server living in one of the Mythic Beasts' datacenters. This is The Server holding the entire Flathub repo on an equivalent of RAID10 in ZFS world: two 2-disk mirror vdevs on which ZFS stripes data across. There is more nuance to this setup, but the ultimate advantage is that we can tolerate a disk failure in each of the mirrors, while being less taxing to resilver after a swap. The entire reachable data set is around 4TB of data, with the remaining 6TB unused. There will be more about the repository maintenance later on!

Ironically, it's the only server running Ubuntu. At the time, it was the easiest way to have support for ZFS readily available. We could re-provision it to Debian, but on the other hand, what for? It works fine that way. It has survived at least 2 major upgrades between LTS-es; if it ain't broke, don't fix it.

The master server itself has to be partially public as it's where new builds are being uploaded. It no longer exposes the raw Flathub repository for the same reason caching proxies don't. This is accomplished with Tailscale and a lightweight ACL config ensuring caching proxies can talk only to the HTTP server running on the main repo server and vice versa (for issuing PURGE requests). Yes, all involved parties have public IP addresses assigned so this could technically be pure WireGuard setup but I prefer to make this someone else's concern, especially given how generous Tailscale's free plan is.

Flathub CDN topology

It's not much, but it's honest work. For how little we have, the file-serving half of Flathub's infrastructure works unreasonably well. Stay tuned for part 2!