[go: up one dir, main page]

Preventing token theft

When you log into a service you’re given an authentication token. Each further request to the site includes that token, allowing the server to figure out who you are and ensuring that you have access to your data. Depending on site policy, this token may either be stored in memory (and so vanish if you restart your browser) or disk. The token is the proof of your identity. As far as the site is concerned, anyone with your token is you. These tokens may be traditional browser cookies, but they may also be stored in either site local storage or (if you’re not using a browser) in some other storage location.

In recent years we’ve seen infostealer malware (like LummaC2) gain the ability to exfiltrate user tokens, allowing attackers to gain access to the user’s data without needing to retain access to the user’s machine. This attack is viable even if the site has strong MFA requirements, so passkeys don’t help. Encrypting the tokens on disk doesn’t prevent the malware from scraping them out of the browser’s RAM or obtaining whatever key is used to encrypt them. This feels like a pretty hard problem to solve.

But that hasn’t stopped people from trying! Dirk Balfanz wrote an IETF draft describing a mechanism for using self-signed certificates for TLS authentication. This uses the mutual authentication feature of the TLS protocol that requires both sides prove their identity to each other. In regular TLS, the remote site presents a signed certificate that tells you who it is. When performing mutual authentication, you then present a certificate to the remote site telling it who you are. These client certificates are largely unused outside enterprise environments because they’re a huge pain to deploy. It’s not so much that this has sharp edges, it’s that it’s entirely made of sharp edges. Managing certificate deployment to your devices is hard. Browsers get confused if the certificates change under them. You have one certificate and it lives forever, so sites you present it to can track your identity. Users are prompted to choose a certificate to authenticate with, and if they pick the wrong one everything breaks and is hard to recover. I’ve deployed this and I did not have a good time.

But Balfanz’s idea was simple. Rather than require certificates to be deployed, browsers would simply generate a certificate on the fly. The goal wasn’t to prove the device or user’s identity in any global way - but it would associate a TLS session with a specific certificate. You could then, for example, include a hash of the certificate in the cookie, and if someone tried to use that cookie without presenting that certificate then the cookie could be rejected. If the browser used a hardware-backed private key for the certificate then it would be impossible for an attacker to steal it. Sure, you could still steal cookies, but you wouldn’t be able to use them.

This was written almost 15 years ago, and seems simple, elegant, and functional. It didn’t happen. Part of the reason for that is that, well, it wasn’t quite so simple. One problem was privacy related. Cookies are only sent after the TLS session is established, so anyone monitoring the network doesn’t know anything about the user identity. A naive implementation of this approach would have meant the client certificate being sent before session establishment, and now user identity can be tracked (no longer an issue if this was implemented on top of TLS 1.3, but this was a log time ago). This was avoided by reordering the client handshake, but that meant having to modify the TLS specification and implementations would have to be updated to support this. Another was that figuring out the granularity of the certificates was difficult. You’d want to use different certificates for every site to avoid them effectively becoming tracking cookies, but you need to provide the certificate before cookies are set, and you don’t know what origin the site is going to set in its cookies. If you generate a certificate for a.example.com and a different one for b.example.com, and a.example.com sets a cookie for *.example.com and includes the certificate you used for a.example.com, that cookie isn’t going to work on b.example.com and things are broken. This meant supporting it wasn’t as straightforward as it seemed - you’d need to ensure that your cookie scope was compatible with the certificate scope. You could probably make this work well enough by aligning it with the Public Suffix List, but there was still some risk of expectations not being aligned.

And, perhaps most importantly, TLS session resumption (replaced by pre-shared keys in TLS 1.3) somewhat defeats the purpose of the exercise - clients store state that allows them to re-establish a TLS connection without performing certificate exchange (this reduces overhead if a connection gets interrupted or you switch to a new network or anything along those lines), and anyone in a position to steal cookies could steal that state as well.

The followup attempt was channel IDs. This simplified the implementation somewhat - rather than certificates, a raw public key would be sent, along with proof of possession of the private key in the form of a signature over a portion of the TLS handshake. This was required even in the event of session resumption, which avoided having to worry about theft of session secrets. The timing of the exchange was after the encrypted session had been established, so user identity couldn’t be leaked that way either. Cookies could then be bound to this identifier. Unfortunately it didn’t really deal with the problem of scoping keys in a way that would match cookie requirements, and the spec suggests that the right way of handling this is to scope keys to TLDs, which would enable user tracking across sites (Chrome’s implementation apparently restricted it to eTLD+1, which would match the third party cookie policy and avoid the tracking risk).

Chrome added support for this, but it was removed in early 2018. The discussion of some of the pain points in that message is interesting, explicitly calling out problems with connection coalescing across domains and the incompatibility with zero-RTT TLS1.3. The overall consensus at the time seems to be that trying to solve this entirely at the TLS layer has too many rough edges, and a different approach should be taken.

And so almost 7 years after the initial draft for origin bound certificates, we come to token binding. This ended up being a rather more complex endeavour, covering 3 different RFCs describing how it impacts TLS, how to incorporate it into HTTP, and how to manage all the various parties involved in the process. The short version is that it’s pretty similar to channel ID, except that there’s also a documented mechanism for allowing tokens to be bound to one party and consumed by another, avoiding any need for widely scoped keys. Token binding effectively solved all the issues in the original proposal, but at the cost of somewhat more complexity.

The RFC was finalised in October 2018. Chrome removed its (incomplete, draft) support for token binding in November 2018. Edge carried support until late 2024. Despite getting all the way through the RFC process, it’s functionally dead.

The process up until this point had been largely initiated by Google, with Microsoft contributing significantly to the token binding standards. The work had been focused on identifying a generic solution to the problem rather than tying it to any specific authentication flow. The next step was in a different direction - rather than trying to fix this for the entire internet, how about we try to fix it for OAuth?

RFC 8705 is titled “OAuth 2.0 Mutual-TLS Client Authentication and Certificate-Bound Access Tokens”. This is basically the 2011 approach, but (a) with an explicit definition of how the certificate should be incorporated into issued auth cookies, and (b) with a proviso that well uh if you’re going to use tokens issued by your IdP to authenticate to someone else then well you’re going to need to use the same cert for both. This is probably fine for the company-owned-laptop case where you’re actually fine with multiple sites being able to tie identities together (that’s kind of the point here!), and also works for “I am using an app and not a browser”, but doesn’t work for more generic scenarios. It also doesn’t seem to take the session resumption case into account at all? Support for RFC8705 seems poor, as far as I can tell of the big players only Auth0 implements it. In theory it works fine with self-signed client certs but in reality that’s going to be almost as difficult to support across multiple platforms as just issuing proper client certs in the first place, so deployment is going to be kind of a pain. But the good news is it doesn’t rely on any TLS extensions or custom browser behaviour, so at the client side it works fine with any browser.

Which brings us on to RFC 9449, “Demonstrating Proof of Possession”. This goes even further than RFC8705 in terms of reducing the burden of deployment - it works fine with existing browsers, and it doesn’t even require any certs. The client generates a keypair and provides the pubkey when requesting the cookie. The cookie contains the pubkey. Every request to the service now provides the cookie with the pubkey and also provides a signature over the URI and HTTP method. If the signature matches the pubkey in the token then clearly the signature came from the machine the token was issued to, and everything is good.

This does come with some downsides, though. The first is that it uses browser interfaces to generate the keys (typically crypto.subtle.generatekey()) and as far as I can tell there are no browsers that guarantee that that key is going to be generated in hardware even if it’s marked non-exportable, so anyone able to steal the cookies can also steal the keys. The second is that the signature only covers the URI and HTTP method, and not the message content or any other headers, so anyone able to exfiltrate a valid signature can replay it against the same URI with different message content. The recommended way to handle this is to reject any signatures that weren’t generated within the last few seconds, which is a wonderful additional way to allow clock skew to give you a Bad Day. And the third is that every single request has to be separately signed, which is not intrinsically a problem because computers are fast and have multiple cores, but if you’re trying to solve the first problem by sticking the key in a TPM then you’re dealing with something that’s slow and single threaded and that’s maybe acceptable if you’re using client certificates (because there’s going to be one signature per session and you can use the same session for multiple requests) but probably not if you’re dealing with a user opening a browser that restores previous tabs and each of those is a webapp that fires off 100 requests in parallel.

In case it wasn’t clear, I don’t like DPoP. It doesn’t feel like it actually solves the underlying problem that we see in the real world (malware running in a context where if it can grab the tokens it can grab the keys), it adds a massive amount of overhead, and it has baked in replay vulnerabilities. I don’t know why it exists and I’m incredibly suspicious of vendors telling me that it fixes my problems, because if they’re telling me that then I’m going to end up assuming that they either don’t understand my problems or they don’t understand their technology, and neither of those is good.

Still. Then we get to the thing that prompted me to write this - Chrome’s announcement that they had launched device-bound session credentials. This is interesting because it’s a Chrome feature that’s explicitly intended to counter on-device malware, which was one of the things that was out of scope in 2018 when token binding was being removed. Since this is entire web level it doesn’t have to be an RFC, and so is instead defined by W3C. I’m going to handwave all the complexity and say that it’s basically a way to register a public key when a cookie is issued, and then prove possession of the private key when it’s time to renew the cookie. By making the cookies shortlived and having support for rotating them in the background, user impact is basically zero and while it’s still possible for an attacker to exfiltrate and use a cookie they’ll only be able to do so for a short window before it needs to be refreshed - something the attacker can’t do, since they don’t have the private key. This avoids the DPoP overhead because you only need to do signing once per cookie per cookie lifetime, and not on every single request. I don’t like this due to the window where exfiltrated tokens can be used, but it feels like a strict improvement over the status quo. An extension called device-bound session credentials for enterprise allows pre-enrollment of device keys, so even though the actual runtime DBCE flow doesn’t involve certificates, certificates can be used for device registration in enterprise environments and you can make sure that auth cookies only go to trusted devices. Unfortunately this is Chrome-only, and so we’re going to need to wait for it to be backported to all the random app frameworks for it to have widespread support on mobile or for almost everyone’s desktop app that’s actually three websites in an Electron wrapper. Mozilla’s current position is that they’re not in favour of it, so I guess we’ll see where Safari lands in terms of broad uptake.

The last thing on my list is another client cert/OAuth binding, this one still in draft state at the time of writing. This one is aimed primarily at the use of agent-driven tooling, where you have something running in the background using a whole bunch of tools that are each acting on your behalf. Authenticating to all of them separately isn’t a fun time, but giving broadly scoped access tokens to a non-deterministic agent and trusting that it’ll never post them somewhere public also isn’t a fun time. The key distinction between it and RFC8705 is that it’s aimed at connections rather than sessions, which avoids the worries about session resumption. This is done with TLS Exporters, which in TLS 1.3 should be unique to the connection even over session resumption (TLS 1.2 may reuse some of the same key material for exporters over session resumption, so it’s recommended to enforce 1.3 for this). By providing a new signature alongside the cookie on every new connection, the client proves that it still has access to the private key. This is a very new spec and I haven’t had much time to work through it yet, but my naive understanding is that unlike RFC8705 this would require some additional client support to be able to regenerate the client signature on every TLS reconnection.

This doesn’t avoid all the problems that RFC8705 has, including how to scope certificates. For the agentic use case that probably doesn’t matter - all these tools are acting on behalf of the same user, it’s fine if all the sites involved know they’re the same user. But it doesn’t solve the general purpose user use case, and right now DBSC seems like the best we have there.

But. Part of me still wonders whether Dirk Balfanz’s approach was the right one. Yes, there’s risk associated with TLS session resumption, but in the worst case you could just switch that off for high risk setups. The cookie scope argument is real, and also in cases where it could violate privacy the site owner could already choose to broaden their cookie scope and violate your privacy, and in cases where it breaks things you could just not make use of it. The other problems are largely fixed by TLS 1.3, and then we’re just left with “Browsers handle client certificates badly” to which my answer is “Yes, and we should fix that anyway”.

Despite having a pretty good answer to this solution over a decade ago, the closest we have to actual deployment is something that offers strictly worse security guarantees. And tokens keep getting stolen, and compromises keep occurring, and for the most part people shrug and get on with things.

Michael Catanzaro

@mcatanzaro

Your _get_type() function is not G_GNUC_CONST: Part Two

This blog post is a sequel to Your _get_type() function is not G_GNUC_CONST.

GNOME developers have long used G_GNUC_CONST, which expands to __attribute__((const)), to annotate GObject _get_type() functions, despite knowing that it is incorrect to do so. const functions by definition have no side effects, but _get_type() functions actually have a side effect the first time the function is called: they initialize the type. Why apply an incorrect annotation to these functions? Because it makes the code faster.

Although this was long known to be incorrect, it worked fine in practice… until now. Regrettably, Sam James has discovered that GCC 16 may optimize away the type initialization, resulting in crashes. This is our fault for providing the compiler with wrong information about our code, so it’s time to audit your use of const attributes to remove them from _get_type() functions. Most GNOME programs use these attributes only for _get_type() functions, but if you use it in more places, then check to make sure those functions are actually const, as defined by the GCC documentation.

Sadly, there is no suitable replacement attribute for _get_type() functions. Two decades ago, Behdad requested a new idempotent attribute for expressing the desired semantics, but nobody has implemented it.

Nathan Willis

@n8willis

Conferring notes (aka SCALE|LGM|WAVE|ATypI|LAS|Grapholinguistics)

File under: brief half-year catch-ups, me not remembering when I last updated the back-end of this blog

I’m writing this from a terrible AirBnB in Reading, having just wrapped up a four-day visit to my old Typography & Graphic Communication alma mater and happy to have an excuse to put off re-packing. I’ve been fortunate in the first six months of this year to have time to participate in a number of in-person conferences after a few years of not being able to travel, and when I was looking back at the calendar I noticed that these events have been evenly split between FOSS events and type-related events, so I decided it’s worth jotting down a few of the thoughts that presented themselves before they fade away entirely. Here’s a recap:

SCALE: The Southern California Linux Expo (which, for the record, I refuse to bacronymize in a mixed-case form, so don’t at me about the capital A). I’ve been a regular for yikes years at this point, and have helped coordinate the “libre graphics track” for the past several, but this time I actually had to present a workshop as part of that. It was on the subject of learning Scribus if you’re coming from the world of LibreOffice / Microsoft Word / Google Docs / Etc. Turns out that’s an ambitious subject, scope-wise, and we didn’t get through everything I wanted to.

But for me, that highlights the fact that shifting from one Do Real Work application to another is always a monumental effort. Sure, you can fire up GIMP and do some cropping of images in a matter of minutes. But you can’t drop a full production workflow without a lot of spare time, because every individual sub-task now involves a different order of operations (or different operations), a different cycle of tool-switching, and a different set of cross-checks and QA. I don’t think we give this problem enough attention, especially when the well-meaningers on social media (and yeah, that includes Mastodon, as fully as whatever platform you don’t like) entice people to give it a try like it’s no big thing.

It also reiterated for me how important document templates are. Those are fairly forgettable 99% of the time, but what ships in the template choosers of LibreOffice, WhateverOfficeOnline, and in more specialist apps is what people end up using when they need to get things done. The typography in most FOSS templates is pretty awful: the fonts are weak, the hierarchies are non-existent, the alignments are haphazard, and the optical balance is even more non-existent than the hierarchies. I suspect that this can only be improved with a long, slow, many-person grind. I don’t know where to start.

WAVE and ATypI: Both of these are type-related or type-adjacent events, so I don’t have much that feels relevant to say about them here. WAVE is quite specialized, since it focuses on human writing: just as many of the presenters are linguists as anything else, and the type people made a stronger showing this year than they did in the original event a couple of years back but aren’t the majority. It is genuinely a wake-up call to sit down and learn about a written language that does not operate in the way that your own does, and to hear exactly how many people use it every day (as well as to see photos to remind you that all of this happens in the present, when it’s incredibly easy to write off those concerns as belonging to an earlier era in printing and just presume that Computers Fixed All That. Or that OpenType and Unicode did. Or that FOSS did.).

I do think, however, that most of the FOSS projects I’m involved with keep a stout set of blinders on about non-majority language systems and scripts, and that that’s deeply problematic. Indeed, it was not long ago that I mentioned IRL that GNOME could do a valuable bit of good for the global user community by finding and supporting scripts other than Latin/Greek/Cyrillic — in contrast to today, where the attitude is “oh, those users will figure it out for themselves like they always do aren’t they great over there” and/or “Noto Fixed All That.” It’s not hard to say that the next release of your project will also support, say, Arabic, and to at the very least be deliberate about bringing people into the room to find and test the fonts you need to determine if things are working and look good. Yes, you do have to do actual tests. Yes, I mean you.

ATypI, in contrast, is very much a “type production” event. For me, this year the bit that stood out was behind-the-scenes stuff that eeped out around the seams and got more widely discussed. Like, there was evidently initial interest from some on the local organizing side that there would be a verrrry small list of presenters: less than two dozen, total, for 3–4 days (depending on how you count workshops and exhibits). Far less than two dozen. There are two big gotchas there. First, you have to contend with gatekeeping. All of the well-known people are likely to be the ones with The Exciting Announcement to announce and, in theory, they’re a big part of the draw. But that keeps all the new community members out. Second, it crashes the economic viability of attending the event. The locale this time was Stanford, which (despite being perhaps the world’s only Junior University, look it up) is outrageously expensive, even if you already live outrageously in nearby northern California. And any time people can’t justify the cost of travel, yes you’re gatekeeping again (particularly of the hallway track), but you’re also twisting the dial on your conference further from “I Should Go” over to “Going Is Just Paying Money To Be In The Audience Of A YouTube Video That I Can Watch Any Other Time” … which is a hard dial to reverse.

LGM: The Libre Graphics Meeting seems to be back on its feet and in good form after several post-pandemic years of bumping into things. Massive props. I had a lot of side-project stuff I brought along in disorganized form, although I attended every session. I also quasi-roundtabled a session to talk about how the now defunct “Planet LibreGraphics”, may it rest unpeacefully, used to be the clear answer to the perpetual question of “how do we maintain momentum and connection the rest of the year?”

But whereas the old Planet site was a garden variety class-M aggregator of individual blogs’ RSS or Atom feeds, I’m of the unprofessional opinion that a true community aggregator today has to account for different types of inputs and outputs and user modes (sideputs?). E.g., many projects don’t announce new releases on their project blog anymore; they tag a release on GitHub. That’s a different input. A lot of people don’t post long-form content anymore, but do a lot of microblogging. That might suggest having an ActivityPub output … but it would be a bot, and it would incur a TON of overhead and put scores of messages out all day long, which you definitely couldn’t use on the traditional RSS output. So you probably need to handle those differently, maybe batching the Mastodon bits into a once-per-day blog output?

Ultimately, I’m not sure; there are a lot of these details. We’re way out in the high-cosmic-ray environment of the trans-neptunians here, or some other metaphor. Ping me if you find the question interesting, because I want to talk a lot more about it.

LAS: It was my first time going to the Linux Application Summit, as the kids call it these days, so I have more thoughts to get down about that one. For starters, it wasn’t what I expected, because I expected there to be more people there who develop Linux applications. I covered a lot of growth of post–CD-delivered-and-RPM/Apt-updated packaging efforts when I was toiling as a not-so-young FOSS journalist, so I do think I have the right grounding in systems like Snap, Flatpak, FlatHub, AppKit, immutable-image OS approaches and other user-code–confinement technical building blocks. Except Kubernetes; I never cared about that and never will.

Anyway, all of that stuff (hand-waving) is very much where LAS takes place. It’s really good to see that there are people from multiple application ecosystems talking about how they handle the current set of unsolved problems. Selfishly, one of the sessions that stood out most to me was Carlos Garnacho’s talk about the data-search layer that he’s been working on. The gist there is that it’s for searching local data … which you might think you can already do, but you’d be wrong. You can sort of do filesystem-level text searching, but that doesn’t handle complex stuff, and it really, really doesn’t let you handle per-application searching well. I’ve got a keen interest in what people do with their “big data exports” — we FOSS people like to look down our noses at the public and tell them that they should take all of their content out of The Bad Services and walk into the sunlight. But there’s not anything they can do with it when they get here (or there, depending on how you feel), and they’re the ones holding the bag. The TinySparql and LocalSearch stuff, I think, holds the potential to improve on that in a big way.

I was also quite interested to learn more about how the KDE ecosystem does its builds. I don’t do … builds, at least not in the sense that KDE and GNOME do (nor, who else, who else … Enlightenment, maybe?). But I have been forced against my will to get up to speed on some things like GitHub Actions, and the session about KDE’s build architecture did make me want to go back and re-examine some stuff I’ve built. That being the other sense of “build.” I was also very interested in Evangelos Paterakis’s talk about the gritty realities of picking up an abandoned project, reviving it in fork-form, and getting hit with the consequences of that decision from both upstream and downstream.

For most people, what matters in these sort of pseudoplumbing projects in what’s going to be accessible in end-user applications whenever they land on the next platform releases. That’s why I thought I might encounter app developers at LAS. Instead, much of the session content was about enhancing the the plumbing layer themselves: what’s going on with portals, how sandbox/confinement techniques either fail-open or fail-closed, and where the confined-application model is still leaking.

I suggest checking out Sebastien Wick’s talk about the portal situation if nothing else; the slides are available now, even if the video is not. Whenever the videos go up, I also suggest that everyone watches the session about forking a defunct project … not because it’s something everyone should do, although let’s face it, projects appear and fizzle all the time. But mainly because the social aspects of how a revived fork does or doesn’t catch on are things the FOSS community doesn’t say out loud. Not bad or shameful things, just sharp corners.

All that said, I think that what really needs to happen is for application developers to actually go to this event. I know you think you don’t need to. But the platform layer is another one of those things about which you can easily say “Didn’t Somebody Already Handle That?” and be incorrect for a long period of time before it bites you. Moreover, every time I’ve ever asked the developer of a Mac or Windows desktop app what they know about developing Linux apps, they tell me a story about running into giant potholes, missing documentation, and mismatched API expectations. The presence of Linux app developers at a plumbing-layer conference will not instantly fix that, but I do know that a lot of those Linux developers hit the same roadblocks.

LGM made a massive improvement to the ecosystem of creative-arts apps in FOSS specifically because it involved getting users, app developers, and subsystem developers into a single space. That’s not a magic trick, and the general Linux app universe would benefit from repeating the technique.

Grapholinguistics in the 21st century, also known as /gʁafematik/: This is the one I just came from, and I don’t quite have it all simmered from ingredients into stew just yet. Apart from being hosted in an initially un-airconditioned spot at the university in the hottest UK week since 1666, I don’t think I have any complaints. I introduced speakers and acted as moderator for Q&A blocks, and apart from that I visited with most but not all of the rest of the quantitative type-research clique. You know where to find us.

One takeaway, perhaps, that will be generally useful is that I spent much of the inter-session time when I was on moderator duty trying to gauge the level of nervousness of the upcoming speaker and attempting to defuse it. I don’t know how successful that actually was, but I do think there were sessions elsewhere where it might have made a difference. I don’t know; perhaps that’s projecting.

Anyway, before the Internet runs out of bits, I should wrap up. I do need to be sure to express my appreciation to the GNOME Foundation, who chipped in with some travel expense assistance for LAS, as well as to my friend and mentor-in-a-few-very,-very-limited-capacities-I-can’t-emphasize-that-part-enough Sri Ramkrishna for pinging me about it and then reminding me. I’m definitely glad I went.

At the moment, I’m halfway through reconfiguring the blog site here into static format (fighting Unicode support in the old platform), so don’t count on commenting to work. If you want to reply to anything I said here, try me on Mastodon.

This Week in GNOME

@thisweek

#255 Curated Updates

Update on what happened across the GNOME project in the week from June 19 to June 26.

Third Party Projects

Alexander Vanhee reports

I have overhauled Bazaar’s curated page. Vendors, such as distributions, can now make use of several widget types to showcase the apps they want to promote to their users. One of these widgets displays articles, which can be used to recommend apps or share general news about the OS in a place where users will naturally discover them.

(The data shown is only for illustrative purposes.)

jjjjjj0 reports

I published last week the first release of EdiTidE.

It’s a simple source-code editor, something between the GNOME Text Editor and GNOME Builder. Think of it as an alternative to Notepad++ in terms of features.

It works fully sandboxed, and is quite convenient to quickly open a project and browse the code in.

It has a bunch of settings for customization (like for replacing the menubar by a hamburger button, to make it more GNOME-ish), and can be enhanced with extensions (in Python).

Get it on Flathub Contribute to translations

Tanay Bhomia reports

Whisp Update: Smart Text Expansions, 4k Downloads, & Donations!

Whisp just crossed 4,000 downloads on Flathub! Thank you all for the incredible support. Donations are officially live! If Whisp helps your workflow, you can now support its solo student development via Ko-fi or GitHub Sponsors on the website.

In v1.3.4, we’ve also added a major new feature to remove friction from your workflow: Smart Text Expansions.

Typing :: anywhere in a note now opens a lightning-fast, completely keyboard-navigable GTK popover to instantly insert dynamic data:

::today / ::date(5) for dynamically calculated dates. ::roll(d20) for D&D dice rolls. ::random(str, 20) for instant secure passwords or placeholder text.

Links: https://flathub.org/en/apps/io.github.tanaybhomia.Whisp | https://github.com/tanaybhomia | https://tanaybhomia.github.io/Whisp/

Sjoerd Stendahl announces

This week I’ve released several updates to Lockpicker, a new tool to recover passwords from their hash. The most obvious change is that the console-output has been replaced by friendly widgets, giving a much more convenient overview. The status overview also spots a progress bar to see how many candidates have been tested. And the ordering of the sidebar should be more intuitive. The logo has also been updated to look a bit more proper for now.

Lockpicker now also has support for sessions. You can pause a session, or run multiple in parallel. Sessions persist over reboots, so you can pick up any time it’s convenient. Finally word lists and rules can now imported into the application, and be chosen from a dropdown menu.

Get it on Flathub here!

francescocaracciolo announces

Newelle (AI assistant and agent for Gnome) updated to 1.4.5

This new release features:

🖼 Image generation support (supporting an integrated stablediffusion instance or cloud models)

💬 New chat redesign: a more minimal and space efficient layout

🐞 Minor improvements like support for STDIO MCP Servers

Get it on Flathub

Anton Isaiev announces

RustConn 0.17 Released

This is the first release since RustConn turned one, and it landed just two weeks after 0.16. Despite the long feature list, the goal hasn’t changed: RustConn is still a simple address book and orchestration layer over your connections, nothing more.

The headline this cycle is Workspaces. With a dozen sessions open across split panes, you can now save the whole set as a named workspace and reopen it in one click - every connection, tab order, split layout, and tab group restored. Reboot the laptop, come back, and your working set is exactly where you left it. No more leaning on clusters and re-clicking reconnect.

A few other highlights, mostly user requests:

  • Simple Sync - opt-in bidirectional sync of connections, groups, templates, and snippets across devices. Passwords stay in each device’s keyring, never in the sync file.
  • Native PKCS#11 / YubiKey SSH auth - hardware-token keys offered directly, no SSH-agent workaround, works through jump hosts too.
  • Built-in port knocking and fwknop SPA - open a firewall before connecting, pure-Rust, no external CLI.
  • Security hardening - clipboard auto-clear, SSH passwords zeroized from memory, and a couple of command-injection paths closed off. Homepage: https://github.com/totoshko88/RustConn Flathub: https://flathub.org/apps/io.github.totoshko88.RustConn Snap: https://snapcraft.io/rustconn

Gitte

A simple Git GUI for GNOME

Christian says

Gitte, a simple Git client for GNOME built with GTK4, libadwaita and Relm4, just got its 0.8.0 release! 🎉

The headline feature this time is cherry-picking: you can now grab one or more commits straight from the commit log and apply them onto your current branch. Diffs also learned to handle the awkward cases gracefully. Binary files and overly large diffs are now clearly marked as such across all diff views, and there’s a new “filtered files” option that lets you configure paths which are treated like binary files and kept out of diffs. In the working copy view you can filter what’s shown to new, tracked or all files.

You also get more space when you need it: you can maximize the diff view with Ctrl+M, and in the commit graph that shortcut cycles between maximizing the graph, the diff and the normal layout.

A few new things can now be configured per repository: you can set merge-/pull-request and issue URLs manually for each one, and pick a default location for new and cloned repositories (thanks to René Fouquet!).

On the UI side, the revert dialog and the commit detail box got an overhaul.

And of course there’s the usual pile of fixes: bad SSH-signed commits are now shown as bad instead of “key unavailable”, commit messages now conform to the Git specification, and signature verification finally works under Flatpak, where the verification temp file is now written to a path that’s also reachable from the host.

Under the hood there’s a fresh Chinese (zh_CN) translation (thanks to Dawnchan030920), new just / bacon developer commands including recipes to build and run as a Flatpak (thanks to Bahrom Magdiyev), and the Nix flake can now build and run Gitte directly with nix build / nix run (thanks to bitSheriff).

Get it on Flathub, for macOS or have a look at the Code.

Gir.Core

Gir.Core is a project which aims to provide C# bindings for different GObject based libraries.

Marcel Tiede reports

New GirCore C# bindings got released in version 0.8.0. The most prominent feature is GTK-Template support which required some breaking changes. Please read the release announcement for details. Further changes included support for GNOME SDK 50, several under the hood fixes and improvements to make working with GirCore easier.

Bouncer

Bouncer is an application to help you choose the correct firewall zone for wireless connections.

justinrdonnelly reports

Bouncer 50.1.0 is here, and it’s a big one!

Thanks to user submissions, this release includes new and updated translations and improved accessibility. There are also some subtle bug fixes alongside some more noticeable new features.

Bouncer now uses more modern Adwaita widgets throughout, replacing basic labels and buttons for a more polished look and feel. The dashboard has also been redesigned as a tabbed interface, with a new Networks tab where you can change the firewall zone for a saved network or make Bouncer’s forget it altogether.

As always, Bouncer is available on Flathub!

Shell Extensions

Christian W reports

On the road a lot of frequently use VPNs? Show External IP extension does what it says and displays your external IP in the Toolbar, including Country flag. It sends a system notification if your IP has changed. Shows also IP history with Export and an image of the approximate location.

This extension is handy for those who work at different locations or with different VPNs to quickly see your public IP and country.

Search for “Show External IP” in Extension Manager Download here: https://extensions.gnome.org/extension/5368/show-external-ip-thisipcancyou/ Github: https://github.com/cwittenberg/thisipcan.cyou

Aryan K announces

Medialine is a GNOME Shell extension that shows your currently playing media right in the top bar, in a minimal and elegant way.

It detects any MPRIS-compatible player (Spotify, Chrome and even PWAs) and displays the track inline in the panel. Click the indicator to open a rich popup with album art, a live seekable progress bar, and full playback controls — shuffle, previous, play/pause, next, and repeat. Supports multiple playing media in a compact view.

The highlight is basically the support for PWA, it properly recognises the PWA icons and opens the correct window when clicked. Also has dynamic background color for the pop-up, based on the album art of the playing media.

Get it today from: https://extensions.gnome.org/extension/10076/medialine/ Homepage: https://github.com/funinkina/medialine

Miscellaneous

Evangelos “GeopJr” Paterakis 🏳️‍⚧️🏳️‍🌈 reports

This week I launched a small unofficial website to share a few demo test builds of some GTK, Granite and libadwaita apps on Android. Give them a try!

That’s all for this week!

See you next week, and be sure to stop by #thisweek:gnome.org with updates on your own projects!

Jussi Pakkanen

@jpakkane

Pystd standard library, similar-ish functionality with a fraction of the compile time

I submitted talk proposals about Pystd, the from-scratch written standard library for C++ (custom design, not a implementation of the ISO specification) to a bunch of conferences. Unfortunately all of them were rejected, so it's blog posting time.

A controversial opinion

Pretty much everybody agrees that compiling C++ is slow, but I personally feel that it is intolerably slow. Other people might disagree, and that is fine, but let's look at some numbers.

Compiling a helloworld executable in C takes approximately 0.02 seconds. Compiling a C++ exe that does the same thing using #include<print> takes a second if optimizations are disabled and up to 2.3 seconds with them enabled. This is approximately a 100x slowdown. I'm using a Ryzen 7 3700X processor, so not the latest and greatest but not too shabby either. I have talked about this slowdown with some people in person and weirdly often their answers have been "that's not a problem, two seconds is insignificant". Even if you accepted this (which personally I don't) the big problem comes from scaling because the slowdown factor is approximately linear. Let's assume that in a less extreme case the slowdown was only 20x instead of 100x. In this case a program that should be done in 0.1 seconds takes 2 seconds, and therefore a program that should compile in one minute would take on the order of 20 minutes to compile.

Why is compilation so slow?

C++ is actually very fast to compile, the slowdowns come mostly from the way the standard library is implemented. This is actually fairly easy to test yourself by running the following shell snippet.

echo '#include<vector>' | g++ -x c++ -E - -std=c++23 | wc

The -E flag tells the compiler to stop after preprocessing. The output is the source code that is fed to the compiler proper. Instead we pass it to wc and find out that merely including vector expands out to 29 000 lines of code. The number is not directly comparable to "human written code", but still, almost 30k lines of code just to get a growable array of elements seems a bit much. And vector is actually one of the lighter headers. Memory is 55 thousand lines (especially bad, since 99% of the time people just want unique_ptr), print is 65 thousand lines and filesystem is 80 thousand lines.

The unfortunate reality is that if you include even a single standard library header, your compile times tank and there's nothing you can do about it.

Just say no

Pystd was originally just a project for me to implement low level primitives (hash maps etc) for scratch for the fun of it. Pretty quickly I came to the three design priorities:

  1. Compilation time
  2. Simplicity of implementation
  3. Performance

I'm not aware of an existing standard library where build time minimization would have been a design priority. Those that are fast, like the standard libraries of C and Go, seem to mostly follow from the simplicity of their respective languages.

At the time of writing building Pystd and all tests from scratch using a single core takes 4 seconds. This consists of 45 individual processes (mostly compiles, a few links). Enabling optimizations balloons the build time to 9 seconds. Using all 16 cores brings it down to 1.9 seconds.

What we have thus far

If we ignore test code, Pystd has 6500 lines of headers and 5600 lines of source in total. Even adding these two together yields a line count of roughly one third of std::vector's (preprocessed) implementation. Functionality provided by Pystd includes:

  • vector, string, validating u8string, string views, spans
  • Hash map, ordered map (using a B-tree)
  • sort (approximately as fast as stdlibc++), stable_sort (faster than stdlibc++)
  • Random selection of things in the ISO algorithm header
  • Optional, expected, variant, unique_ptr
  • Functionality roughly equivalent to Python modules argparse, pathlib (including the ** operator), regular expressions (using pcre) and tempfile

Note that all of these are "complete" as it were. Typically they only contain the most commonly used subset of functionality. That might be a fairly small.

Performance

There is an earlier blog post about the performance. The numbers for converting the CapyPDF library are as follows:

  • Compile times dropped ~80%
  • Unstripped binary size reduced by ~75%
  • Stripped binary size reduced by ~30%
  • Runtime performance became ~25% faster (yes, faster, not slower)

Regression can be prevented

Two typical issues people raise when hearing something needs to be "fast to compile" are the following:

  1. What even is "fast"? It is highly subjective thing that depends on each person and the computer they are using.
  2. Even if something is fast now, it can not remain fast. New functionality gets added all the time, so the code is destined to become ever slower and eventually it is just as slow as the default standard library.

The boring solution to both of these issues is the same: a predefined time budget. Pystd has a requirement that compiling a source file that includes any single public header must take at most 0.15 seconds. This limit was originally 0.1 seconds and it worked perfectly with GCC, but Clang's process startup time is longer than that. The test script that validates the performance is here. It must pass even on a Raspberry Pi.

Interestingly the tester script was not originally single-threaded. I parallelised it only because it was the single slowest part of Pystd's compile-test cycle taking over a second.

This is the requirement that all new functionality in Pystd must meet. If the code you want to add compiles too slow then either you rewrite the whole package to compile faster or you submit a patch to the upstream compiler to make it run faster.

Try it yourself

The code for Pystd is available in the usual places. Beginners might want to try using the sample project instead.

The code works on Linux and macOS. It does not support MSVC, because the implementation uses pack indexing, which MSVC has not implemented yet.

Lennart Poettering

@mezcalero

Mastodon Stories for systemd v261

On June 19 we released systemd v261 into the wild.

In the weeks leading up to that release (and since then) I have posted a series of serieses of posts to Mastodon about key new features in this release, under the #systemd261 hash tag. In case you aren't using Mastodon, but would like to read up, here's a list of all 27 posts:

I intend to do a similar series of serieses of posts for the next systemd release (v262), hence if you haven't left tech Twitter for Mastodon yet, now is the opportunity. My series for v262 will begin in a few weeks most likely, under the #systemd262 hash tag.

In case you are interested, here is the corresponding blog story for systemd v260, here for v259, here for v258, here for v257, and here for v256.

Asman Malika

@malika

The Day I Learned That “Remove” Doesn’t Mean Remove

When I started contributing to Flatseal through the Igalia Coding Experience program, I expected to spend most of my time writing code.

I quickly discovered that I was wrong.

Over the past few weeks, I’ve been working on two seemingly unrelated areas of Flatpak permissions: conditional permissions and USB permissions. On paper, they looked like straightforward tasks. One involved understanding a relatively new Flatpak feature, while the other appeared to be a bug fix in Flatseal.

What I didn’t expect was that both tasks would teach me the same lesson: understanding the semantics of a system is often harder, and more important than writing the code itself.

Flatseal is a GNOME application that provides a graphical interface for managing Flatpak permissions. It gives users visibility into what applications can access and allows them to modify those permissions without manually editing override files.

At first glance, permissions seem simple. An application is either allowed to do something or it isn’t.

The reality is far more nuanced.

One of the first areas I explored was Flatpak’s conditional permissions. These permissions allow an application to request access only when certain runtime conditions are met.

Initially, I thought of permissions as static declarations. An application requests access to a resource, and Flatpak either grants it or denies it.

Conditional permissions challenge that assumption.

Instead of saying “always grant this permission,” an application can say “grant this permission only if a particular condition is true.”

The override file showing a conditional permission: if:x11:!has-wayland grants X11 access only when Wayland is unavailable.


The more I learned about the feature, the more I realized that permissions are not always permissions. Sometimes they are fallback mechanisms. Sometimes they are compatibility layers. Sometimes they exist purely because different systems support different capabilities.

This raised an interesting question for Flatseal.

If a permission only applies under certain conditions, how should that be represented to users? Should it be visible? Should it be editable? Should the interface expose the raw configuration or explain the effective behavior?

Those questions had less to do with coding and more to do with understanding the intent behind the feature.

While I was still thinking through those questions, I moved on to USB permissions.

That was when things became even more interesting. Flatpak exposes two related USB concepts:

      • Allowed USB devices
      • Blocked USB devices

These are represented internally through enumerable-devices and hidden-devices.

At first, this seemed straightforward. A device is either allowed or blocked. Then I started experimenting with flatpak override.

Flatseal’s USB section


I discovered that the same USB device can appear in both lists at the same time. Not only is this valid, but it is an expected configuration.

The key detail is that the hidden list always wins.

A device can be present in the allowed list and the blocked list simultaneously, and Flatpak will still treat it as blocked.

That discovery completely changed how I thought about the problem.

The original bug I was investigating involved removing USB devices through Flatseal’s interface. My initial assumption was simple: if a user removes a device from the allowed list, then the device should no longer be allowed.

      • But what does “remove” actually mean?
      • Does it mean deleting an override?
      • Does it mean explicitly blocking the device?
      • Does it mean restoring the original application defaults?

The answer depends on where the permission came from in the first place.

An original permission declared by an application is different from a permission added by the user. Removing those two things should not necessarily produce the same result.

What looked like a small UI interaction suddenly became a question about semantics.

I found myself spending more time reading source code, experimenting with Flatpak commands, and discussing expected behavior with maintainer and my mentor, Martin Abente, than writing actual implementation code.

At one point, I realized that I had been approaching the problem from the wrong direction entirely.

I was asking: “How should I implement this?”.  The better question was: “What behavior is Flatpak trying to express?”

Only after answering that question could I determine how Flatseal should behave. That shift in mindset has probably been the most valuable lesson of this experience. As developers, we often think of programming as the process of building things.

Increasingly, I’m learning that a significant part of software engineering is understanding things.

      • Understanding why a feature exists.
      • Understanding the assumptions behind a design.
      • Understanding what users expect to happen.
      • Understanding what maintainers intended years before you ever opened the codebase.

The code is often the final step. The real work happens before that.

Looking back, neither conditional permissions nor USB permissions taught me the most important lesson. The lesson was that software behavior is defined by semantics, not implementation details.

A user clicks “remove” and expects a particular outcome.

A maintainer designs a feature with a specific meaning in mind.

A system applies rules according to semantics that may not be obvious from the UI.

Bridging those perspectives is where much of the engineering happens. And that’s exactly what has made contributing to Flatseal such a rewarding experience.

I started this journey expecting to write code. Instead, I learned how to ask better questions.

Ironically, that has probably made me a better engineer than the code itself ever could 😀

Jakub Steiner

@jimmac

Flatpak.org Rewrite

The Flatpak website has been running on Middleman for years and time hasn't been kind. Touching the project resulted in seeing 42 vulnerability warnings. The gem itself hasn't seen an update in ages, and the dependency list is rather large.

Flatpak Pixel art - a package delivery guy carrying a box with an open truck with warning lights

Very recently, which happens to be 4 years ago, we've done a rebrand to reflect the fact that Flatpak has hit it big. There were a few hints of the old baggage, but overall it didn't feel like a website from 2007. But it was just lipstick.

For the layout, I reached for the same named-grid approach we designed for gnome.org. Instead of fighting with a rigid single column you get a multi-column grid with named areas. This allows you to escape the column to span across the whole width or do side-by-side layouts with overflow.

Most of the navigation is left to the footer. Another pattern we've embraced elsewhere and has worked well. A major simplification came from the great work of Kolja (razze) on Flathub -- all the distro setup instructions are maintained in one place.

Most benefits remain on the backend — keeping the site up to date won't be such a chore. Shoutout goes to Bart, AsciiWolf and Sebastian, thank you for caring. On to the next one!

Christian Hergert

@hergertme

Asynchronous State Machines with Fibers

Writing state machines gets a bit of a bad reputation because they are often implemented in complex manners which are specific to the problem domain. I think that makes people shy away from writing them when they are truly beneficial, including myself.

Where they often go awry is when you have some sort of work that needs to be done asynchronously. This is exceedingly common in UI programming like GTK applications but just as easily found in daemons.

Because of this, I see people explicitly avoiding the state machine, or worse, implicitly avoiding its correctness by open-coding a solution across a dozen callbacks.

With DexLimiter and DexFiber I find I can write these state machines better.

You can use the limiter with a max-concurrency of 1 to get an “asynchronous Mutex” of sorts. No lock management necessary.

static void
password_daemon_init (PasswordDaemon *daemon)
{
  daemon->limiter = dex_limiter_new (1);
}

Imagine, if you will, that a limiter is a mutex plus a callback/closure which fires as soon as a slot is free. That means we need a little state to send to our transition callback.

/* Define our closure state for a transition */
DEX_DEFINE_CLOSURE_TYPE (StateTransition, state_transition,
  DEX_DEFINE_CLOSURE_OBJECT (PasswordDaemon, daemon),
  DEX_DEFINE_CLOSURE_VALUE (PasswordDaemonMode, target))

That is a nice wrapper around defining a struct with a new and free function.

Now we can request a transition of the state machine. Since our DexLimiter is an asynchronous mutex (with a single runnable slot), the fiber will not be spawned until it is the highest priority.

DexFuture *
password_daemon_transition (PasswordDaemon     *daemon,
                            PasswordDaemonMode  mode)
{
  StateTransition *transition;

  transition = state_transition_new ();
  transition->daemon = g_object_ref (daemon);
  transition->target = mode;

  return dex_limiter_run (daemon->limiter, NULL, 0,
                          password_daemon_transition_fiber,
                          transition,
                          state_transition_free);
}

That makes our transition code very clean when you combine the fiber with g_autoptr() and dex_await() to await the completion of futures. So a state machine might look like the following:

static DexFuture *
password_daemon_transition_fiber (gpointer user_data)
{
  TransitionState *state = user_data;
  GError *error = NULL;

  switch (state->target)
    {
     case PASSWORD_DAEMON_MODE_HANDOFF:
       if (state->daemon->mode != PASSWORD_DAEMON_MODE_INITIAL)
         return invalid_transition (state->daemon->mode,
                                    state->target);

       if (!password_daemon_enter_handoff (self, &error))
         return dex_future_new_for_error (&error);

       break;

      case PASSWORD_DAEMON_MODE_LOCKED:
        ...

      case PASSWORD_DAEMON_MODE_UNLOCKED:
        ...
    }

  return dex_future_new_enum (state->daemon->mode);
}

static gboolean
password_daemon_enter_handoff (PasswordDaemon  *daemon,
                               GError         **error)
{
  GSocket *control;

  if (!(control = dex_await_object (create_socket (), error)))
    return FALSE;

  ...
}

What I find nice about this is enter/leave transition components can be customized for the state machine transition. That leaves room for transitions between states which require specialization for correctness.

This is much cleaner than ad-hoc callbacks chained together because you can await in the transition fiber for asynchronous work to complete and the state machine itself is preserved. No shoving temporary state in your class instance. No testing hell to see if you caught all the failure cases. No pain with sequencing or order of main loop processing.

Hopefully that shows you can use libdex to write more correct and cleaner state machines by keeping the majority of the implementation in one place.

Hylke Bons

@hbons

Icon for ChiPass

Icon for ChiPass

Week 23

This week's icon is for whitequark's project:
ChiPass: "Store and autofill passwords"

Check out all weekly app icons created so far over here and follow my icon creation adventures as they happen (including sketches) on the Fediverse.

Need icons?

I love designing icons and am happy to contribute them free of charge when your project is Free and Open Source. Funded by community sponsors (every little helps!).

This Week in GNOME

@thisweek

#254 Commit Graph

Update on what happened across the GNOME project in the week from June 13 to June 20.

Third Party Projects

Sjoerd Stendahl says

This week I released Lockpicker.

Lockpicker is a tool to recover passwords from a hash. Essentially it functions as a front-end for hashcat, making it possible to do password recovery without the hassle of hashcat syntax in a GNOME native graphical interface.
 It’s mainly useful for anyone that is curious about password recovery, security or as a companion app for CTF-challenges.

You can check it out on Flathub.

Luiggi R. Cardoso reports

Draft’s v2.3 is out now!

Now Draft has a new backend and cleaner code, making it easier and faster to add features.

This new version brings:

  • Markdown-inspired styling to make it simpler to organize your notes and snippets;
  • Zoom functionality to make the text larger and easier to read;
  • Now the app fully follows your accent color, making the interface seamless with your desktop.

Download it on Flathub | Contribute on GitLab | Weblate will be back soon!

JumpLink reports

Learn 6502 Assembly (learn assembly programming on a virtual retro console) just shipped 0.7.0. The app now speaks Hebrew, fully translated by Menachem (@naattxx), who also tested and contributed the new right-to-left support. That makes Hebrew our first RTL language and prepares the app for more. This release also welcomes a new Indonesian translation by Arif Budiman, alongside the usual round of small fixes and improvements.

tanay says

Whisp has surpassed 1,000 downloads this week within 15 days(I am in Awe, Thank you so so much). The application now includes several quality-of-life improvements, including the List module, tree-style lists for better organization, note pinning, and keyboard shortcuts for faster navigation.

Development is now moving into Phase 2, focused on expanding Whisp beyond note-taking. Planned features include a built-in Math Engine for calculations, unit and measurement conversions, and Image-to-Text functionality powered by OCR.

Thank you to everyone who has tried, reported issues, and contributed feedback. Community support has been invaluable in helping shape the project.

Project website: https://tanaybhomia.github.io/Whisp/ Source code: https://github.com/tanaybhomia/Whisp

If you’d like to support development, UPI donations are now available through the website, and COFI support is planned soon.

Gitte

A simple Git GUI for GNOME

Christian reports

Gitte, a simple Git client for GNOME built with GTK4, libadwaita and Relm4, just got its 0.7.0 release! 🎉

The headline feature this time is a visual commit graph in the commit log: branches are drawn with color-coded lines, so commits off the mainline get a distinct per-branch color and it’s much easier to follow how history actually branches and merges. Stashes and pending changes show up as commits in that graph too, giving you a complete picture of your repository at a glance. You can also drop multiple commits at once now.

Diffs got a lot smarter as well. Gitte now detects renames, and the word-based diff has been overhauled to highlight intra-line changes more often. There’s also a new shortcut (Ctrl+D) to switch between staged and unstaged in the working copy view.

There’s more control over your lists and directories: you can toggle whether Gitte recurses into untracked directories, and the branch and tag lists now have a configurable sort order, with global defaults available in the preferences.

This release also comes with a broad UI overhaul. The main layout has been greatly reworked (special thanks to Brage Fuglseth!), and the preferences dialog is now a standard Adwaita preferences dialog. The columns now each have a distinct background color, the sidebar was reorganized, and the commit-graph and “rebase in progress” banners were revamped. On top of that come nice new action buttons in the sidebar, better spinners if an operations takes a bit longer, pane widths kept in sync across all views, and the usual round of consistent title casing and spacing fixes.

Under the hood there’s a performance pass too: file diff lists now show at most 100 files (30 expanded by default, with an option to force loading everything), more operations run asynchronously, and the diff view isn’t reloaded when it doesn’t need to be.

And of course there’s the usual pile of fixes: diff lines are redrawn on theme switch, scroll position is preserved while staging and unstaging, long labels break correctly, tag badges got a better color, and the window size is now saved correctly — including when quitting with Ctrl+Q.

Get it on Flathub, for macOS or have a look at the Code.

GDM Settings

Customize your login screen.

Mazhar Hussain says

It has been almost 2 years since I last posted about GDM Settings on TWIG. While there have been some releases during that time, the development wasn’t happening really consistently. I’m really sad to announce that the development of GDM Settings is on hold for now. I’m not sure when (or if) I’ll pick up the development of this app again.

See issue#324 for information.

Thank you all for the support! It really meant a lot. ❤️

Shell Extensions

Christian W reports

macOS-style text capture now also for GNOME: select any screen area, OCR it, copy instantly, optionally translate with this new extension https://extensions.gnome.org/extension/10209/snap-text-extractor/

Tomáš Gažovič says

RSS Feed extension is back after a long time and ported to latest GNOME. Same old feeds, but with a modern look. If you like your news directly from shell you can give it a try https://extensions.gnome.org/extension/948/rss-feed Project page: https://github.com/todevelopers/gnome-shell-extension-rss-feed

Miscellaneous

balooii reports

For the two people out there that might be interested in trying out GIMP 0.54 from 1996 on your modern Linux system: Now you can, I created a Flatpak just for fun. It’s the last version using Motif before it’s creators decided to kick off a little toolkit called GTK… https://gitlab.gnome.org/balooii/gimp-0.54

GNOME OS

The GNOME operating system, development and testing platform

Bilal Elmoussaoui reports

GNOME OS is now using oo7 by default as a replacement of gnome-keyring-daemon, oo7-portal for the XDG Secrets portal implementation and oo7-cli as a replacement for secret-tool.

That’s all for this week!

See you next week, and be sure to stop by #thisweek:gnome.org with updates on your own projects!

Hari Rana

@theevilskeleton

Draft-Driven Blogging

From 2021 to 2023, I was really motivated to write articles regularly, but that is no longer the case. Most of my energy goes into programming nowadays. Whenever I try to write about a complicated topic in a digestible manner, I quickly lose motivation and don’t publish it.

For half a year, I’ve been trying to write an article about the thought process that went through when making the month view in GNOME Calendar accessible, as well as the implementation details. However, explaining complicated technical details into something that is simultaneously digestible to non-developers interested in accessibility and free and open-source calendar application developers requires me to withdraw my knowledge and assumptions, consider the perspective of someone who is not knowledgeable in this topic, and then recall the events that led me to take certain decisions, which demands a lot of energy.

Due to a lack of motivation, I want to try a different approach. I am calling this approach “draft-driven blogging”. Instead of publishing articles once they are complete, I will publish the draft publicly. This draft may contain keywords, incomplete sentences, random notes, empty sections and other characteristics that are only found in drafts. I will then iteratively improve the draft until it is considered finished.

This approach makes sense to me in terms of publishing and getting things done. I tend to seek perfection, which is great for maximizing quality, but it comes at the cost of motivation. Without external pressure, I am not motivated to fix something if it is not already publicly available. Seeing an unfinished blog post publicly is simply appalling. As it’s ugly, it motivates me to fix and complete it. So, instead of writing and publishing the ‘perfect’ article, I publish the ugly draft and complete it out of spite. Maybe “spite-driven blogging” is a better term for it?

Of course, communication is important. With drafts, I will add a disclaimer stating that the article in question is a draft. The published date will be the date of the last edit, and all previous drafts will be deleted. To avoid spamming RSS feeds and notifications, I will try to republish drafts infrequently.

It’s all an experiment; it might work well, or it might not. I might keep this approach or just pretend that I never tried it in the first place. We’ll see.

Hylke Bons

@hbons

Icon for Hex Colordle

Icon for Hex Colordle

Week 22

This week's icon is for Krafting's project:
Hex Colordle: "Find the color"

Check out all weekly app icons created so far over here and follow my icon creation adventures as they happen (including sketches) on the Fediverse.

Need icons?

I love designing icons and am happy to contribute them free of charge when your project is Free and Open Source. Funded by community sponsors (every little helps!).

Status update, 17th June 2026

This month I’m mostly listening to music by Nu Genea, Danalogue and Noon Garden.

I’m going to tell you about a big change I’m proposing for folks using Freedesktop SDK to build operating systems. And I’m also going to talk a bit about the GNOME Foundation elections. Maybe I’ll do that first.

GNOME Foundation elections

The GNOME Foundation is a democratically organised not-for-profit that grew from the GNOME open source project around the year 2000. Anyone who contributes to the GNOME open source project can be a member of the Foundation, which allows (among other things) periodically voting in a Board of Directors who govern the Foundation. You probably know all of this.

Back in 2001 the Foundation was a lively active space. Check out the election results from 2001: 11 candidates were selected out of 25 candidates, some of whom made pretty wild campaign promises such as banning all mentions of proprietary software.

When I became more involved in GNOME, ten or fifteen years ago, the Foundation seemed pretty boring and sensible and not many people volunteered to be directors. Here’s a mail from 2017 of someone complaining about not enough candidates and low voter turnout.

This year’s election has nine eight candidates for five seats, and a lively debate. Two years ago a big explosion happened in the community, and we are still dealing with the fallout and, in many cases, still piecing together what actually happened. (It seems like the explosion had been building for a long time and maybe the boring and sensible days of 2017 weren’t as boring and sensible as they appeared from outside.) I am not impressed by the tone of some of the discourse, everyone involved in the campaign believes in what they are doing and deserves respect, but it does make me optimistic about the future of the GNOME Foundation. Questioning things is healthier than ignoring them.

My quiet theory is that the dynamics of open source have changed fundamentally now that LLMs are a mainstream technology. Code is less of an asset than it ever was. A lot of work in the desktop space since 1997 has been simply keeping pace with the rest of the industry: Apple introduced glassy window bars and so we had to have them in GNOME too, Apple introduced “retina” displays and now we need fractional scaling, Apple introduced the App Store and now we need Flatpak, and so on. All of these are huge engineering efforts requiring a lot of new code.

Now the industry is out of ideas. Apple this year are announcing AI integration and more glassy window bars.

So if code is not the asset, what is? The people, as it always ways. And in an increasingly hostile and untrustworthy internet, where you can’t even trust websites any more, a resilient autonomous and trusted structure like the GNOME Foundation, with a battle-tested democratic structure, and strong moderation capabilities to keep out the increasingly automated and vociferous trolls, is a very valuable thing indeed. (No wonder the trolls see it as a threat).

It’s hard to imagine a parallel universe where there’s no KDE eV and no GNOME Foundation, but I suspect we would miss both of them. Clearly all of the candidates believe in the Foundation enough to run for election. Remember that it’s an unpaid position with a lot of responsibility and minimal benefits. Being a director is a personal sacrifice. So thanks to everyone who keeps it working.

freedesktop-sdk.bst:public-stacks/runtime-gnu.bst

Onto the more technical material, I guess. The Freedesktop SDK is a widely used Flatpak app runtime that powers thousands of apps on Flathub. You probably know that, too.

Flatpak aimed from the beginning to be distro-independent, and consequently the Freedesktop SDK isn’t a repackaging of Debian or Fedora or Alpine Linux, but something more like a DIY Linux From Scratch build. As an app user you don’t notice any of this, because it’s very well executed and apps just work. Again, it’s hard now to imagine a parallel universe where the main Flatpak runtime was Fedora in a trenchcoat, but perhaps that would have impeded the success of Flatpak. (Of course Canonical still built their own app store technology, but I suspect that Canonical re-inventing things is part of every parallel universe).

So Freedesktop SDK has build instructions for common Linux tools, utilities and libraries, and they are so good that most BuildStream projects end up junctioning Freedesktop SDK to reuse them. (I covered this in more detail back in April). In theory this brings a virtuous cycle: we use FDSDK in industry and that funds maintenance and improvement of the build instructions, which in turn benefits the Flatpak runtime which doesn’t have any source of funding of its own.

I’ve been working on a slightly tricky intersection between these two worlds, which I call “Choose your own userland”. It makes a relatively small change to a stack element in Freedesktop SDK, but one which has big consequences for BuildStream projects that junction the project. (And no immediate consequences for Flatpak users, but you could see it as future-proofing).

A stack element is a group of elements. Freedesktop SDK provides various “public stacks” with useful element groupings. Most of these are related to build systems, like public-stacks/buildsystem-autotools.bst which includes everything you need to run builds with the crusty old GNU Autotools build system. Then there’s this special one: public-stacks/runtime-minimal.bst, which as of today includes the following:

  • Root filesystem symlinks
  • C/C++ platform libraries like GNU GLIBC,
  • The GNU Bash shell (and its dependencies Readline and ncurses)
  • GNU Coreutils, and all their dependencies

This stack is a recent addition, announced in the release notes of last year’s FDSDK 25.08 major release:

[BREAKING CHANGE] It’s now possible to create more minimal runtimes thanks to rework of bootstrap-import.bst. This adds a new stack public-stacks/buildsystem-make.bst which is essentially same as the original bootstrap-import.bst. There is also a new stack public-stacks/runtime-minimal.bst that is intended to provide a minimal environment that you can shell into. More info in the related issues: #1728 (closed), #1523

My selfish motivation for this change is I want to build embedded systems that don’t include GNU Bash and Coreutils at all, using BusyBox to provide the shell and utilities instead. This is hard today with FDSDK because every element unconditionally depends on Bash and Coreutils, so how can I remove them from my final system? But coincidentally, in the desktop world we are also seeing GNU Coreutils replaced with uutils/coreutils, a reimplementation in Rust which is already the default in Ubuntu since 25.10. So there’s another reason we might not want to hardcode a specific coreutils implementation in the lowest level stack.

The idea implemented in my branch came from Abderrahim and is delightfully simple: just drop Bash and Coreutils from the runtime-minimal stack, and have elements opt into them explictly.

On hearing the idea, I wasn’t sure how this would work, so of course I was effectively nerd-sniped into trying it. The result is as we’d hoped, it allows you to build systems with alternative coreutils. The FDSDK includes some example VMs, and here’s an example of one of them booting with uutils/coreutils (taken from MR!31779):

So the approach works. My main concern was the amount of churn we would cause if we change the meaning of runtime-minimal.bst. Of course, we often still want GNU Bash and GNU Coreutils, so my branch adds an additional public-stacks/runtime-gnu.bst element that brings in a GNU userland. I added Bash and Coreutils into all the public-stacks/buildsystem-*.bst elements too as we still want them at build time. That means that for most elements the change is actually transparent. You just need to ensure that each output explicitly includes a shell and utilities of your choice as runtime-depensd.

To test things further I tested the changes in branch of gnome-build-meta. It was pretty boring working through various build failures to get to a new dependency graph, but I came out the other side still convinced that this change is a good idea. (You can see my gnome-build-meta branch here, bearing in mind half the changes are actually dealing with differences between FDSDK 25.08 and ‘master’).

There was some lively discussion on the MR and I’m still not entirely clear if this change is going to land, much as I like it. One sticking point is a fear of landing big changes and not having enough people to deal with the fallout, and as an open source maintainer I certainly know that feeling, so I have more testing planned still.

Another complaint is that this change reneges on the promise from 25.08 about public-stacks/runtime-minimal.bst, that it “is intended to provide a minimal environment that you can shell into.“. You can’t shell into anything if there’s no shell, of course, so I can’t argue with the premise. But I am missing why this is a big deal. I’ve always had a bad time in BuildStream build shells because I just want to edit a file for testing and dammit there’s no Vim or even Vi or even Nano… in fact we don’t even seem to have less?! So I’ve always wanted a way to overlay elements with debug tools in my shell, and it turns out that bst shell should be able to stage the specified element on top of a base element” is a feature request that’s been open since 2018.

If you use FDSDK as a junction and you like the idea then I’d appreciate comments on the MR. (If you hate the idea, I’m sure you’ve already switched tabs and are half way through posting an irate comment ;-). I am of course prepared for an outcome where this change doesn’t land, and it may indeed lead to some separation of “Linux OS & compiler bootstrap using BuildStream” and “Base Flatpak runtime” into different projects. My gut feeling is that this would be a bit like trying to carve a single grape into two pieces, i.e. there are still few enough people who actually want to maintain build instructions that it makes more sense to collaborate in the same repo.

Thanks as always for reading!

Michael Calabrese

@mccalabrese

Pitivi Timeline Ruler | Standalone Beta Progress

Hello GNOME, This is a progress report on the Pitivi Timeline Ruler Rust rewrite.

Progress

We are rewriting the Pitivi Ruler in Rust and gtk4 snapshot logic to improve performance and memory safety. At its current stage the ruler is being constructed as a standalone widget in a personal repo that can be found here:

Pitivi Timeline Ruler

GTK_DEBUG=interactive cargo run --example sandbox

Any feedback on the code is greatly appreciated!

Structure

I am structuring this around rendering a single interval of ticks between two major ticks once, then stamping that across the duration of the project, as seen in draw_single_interval().

With the ticks stamped across the timeline I am then rendering a cache of Pango labels for the timestamps that are stamped across the visible window. I made the decision to use a BTreeMap for the cache for ease of iterating chronologically through the stamps and for dropping out-of-bounds keys. The logic for this cache handling primarily lives in update_label_cache().

After some feedback from members of the video editing community, I made the decision that minor ticks should always be clean multiples of frames in the time period. This logic is implemented in calculate_minor_divisions().

Next Steps

The primary goal I am focusing on next is implementing the gesture handling, including click and drag actions. Once gestures are implemented I am going to begin moving a lot of the math to traits so that I can write a mock_env and a live_env to start writing some unit testing.

Jakub Steiner

@jimmac

Things That Last

One of the great annual trips we do with a bunch of friends is a train trip to Jakuszyce, a tiny stop in neighbouring Poland, and ride along the contour line through one of the most beautiful places in the Jizera Mountains. There's only one proper climb from Smedava to Knajpa, the rest is fast. A joyride. Catching up on our lives on the train and a joyride home is the best combo.

Forever Young Bike

I tend to think of myself as a friend of repairs, of making things last. I have sadly had to retire our washing machine after a good 25 years. The dishwasher before that served us more than 15. A boiler and heater had to go this year after about 20. I had my previous car for 13 years and felt like I was bailing too soon, even though there were quite a few issues with it at the end. None of those things ever felt new to me by the end. They most certainly showed their age.

But the bike. The bike I ride every year on that trip, the one leaning against the wall in the shed right now — it still feels like my "new bike". I replaced the tires and brake pads last year and the thing screams. It is such a joy to ride. It feels current, alive, like something I just picked out. Until a friend sent me a photo his Google Photos app reminded him of. A very young version of myself is sitting on that exact bike. Fifteen years ago. Nothing has aged except me. Bikes just don't age like we do.

Hylke Bons

@hbons

Bobby joins GNOME Circle

Excited that Bobby has been accepted as a GNOME Circle app!


Screenshot of a SQLite table opened in Bobby

Who’s Bobby?

Bobby is a viewer utility. It displays tables from SQLite files. The most deployed database format in the world. That’s it.

Whilst hacking on the backend of Auroras I was missing an easy way to check my data tables. There are many database management tools, but they seemed too heavy for my use case.

GTK4 and Libadwaita

Releasing something smaller first also was a chance to refamiliarise myself with modern GNOME app development. It was my first serious project using Rust.

GTK is still the rock UI toolkit it has always been. Libadwaita makes it easy for your app to look beautiful and is a lot of fun to use.

The main challenge was hooking up the database backend to the ListModel required to be displayed by the new ColumnView. The struggle was worth it though as it enables Bobby to have lazy loading of rows, smooth scrolling, and a limited memory footprint.

After that using any other widget should be a breeze!

Future

I will keep the scope of the app small, but there are a few features I want to add in the future:

  • Encrypted file support
  • Updating values in place
  • Search

Get Bobby on Flathub and always sanitise your database inputs!

Jussi Pakkanen

@jpakkane

Beware of Star Trek managers, especially when bearing MBAs

Almost exactly three years ago the Oceangate submarine implosion happened. The disaster came about when a billionaire called Stockton Rush created his own unclassified submarine to go sightseeing on the Titanic. Ignoring all advice from experts he created a "macgyveresque death trap" that eventually killed him and sadly also 4 innocent people. The whole thing was a massive display of stupidity and arrogance with unfortunate outcomes. We are not going to go into the actual event any deeper, but those interested can find lots of material online.

Instead we are going to look more deeply into one often overlooked points of Stockton Rush's character. Apparently he felt like he was something of a "new James T. Kirk" (link1 paywalled, link2). Liking Star Trek is not that unusual. I'm guessing that more than 99% of the readers of this blog are fellow Star Trek fans. The problem lies elsewhere, but to understand it we first have travel back in time.

A brief overview of the British navy during the Napoleonic wars (by a non-historian, so probably inaccurate)

The original concept for Star Trek was, approximately, The Adventures of Horatio Hornblower in Space! The Enterprise is basically a British warship sailing through the vast ocean of outer space. The command structure mirrors this, where you have a captain, navigator, ship's doctor and so on. The Next Generation leaned into this even further by having a first officer and so on. The original Star Trek never went into detail on how the main cast got to their current positions, just that there was an Starfleet Academy they went to.

In the Napoleonic era of Hornblower things were quite different. Anyone who wanted to become a captain pretty much had to be from the upper classes. They had to obtain a letter of recommendation so that they could join a vessel as a midshipman at the age of 13 or so. They were expected to be able seamen by this time and then spent the next six to seven years working on the ship rigging sails and doing all manner of random jobs. This went on for six to nine years depending on circumstances, after which the person could take a formal examination to become a lieutenant. The test was not trivial, many people could not pass even after trying multiple times.

A lieutenant then had to work successfully for several years before obtaining the rank of captain. Even that did not guarantee a commission. Some captains never commanded a ship simply because there were not enough of them to go around. All in all becoming a ship's captain was a long and difficult journey. In a surprisingly non-British turn of events it was not possible for aristocrats to sneak past the gates. Getting a midshipman position was obviously easier with connections, but the lieutenant's test was something they had to pass on their own.

All of this is to say that every captain of the time was an expert with decades of working experience on many different positions aboard the ship.

What does a captain actually do?

[Note: I have not fact checked this portion at all. Feel free to consider it fanfiction.]

The year is 1808 and we are aboard a British warship about to leave for a mission of great importance. The captain gives the order to set sail. Whistles are blown, bells are rung and sailors springs into action. Every single man, with one exception, is either doing manual labour or directly supervising their underlings. That exception is the captain, who seemingly stands around doing nothing (at least if you ask the crew). This is not so.

What he is doing is crucial. He is observing the state of the ship and her crew. This includes things like overall crew morale, any aberrations from normal operations that could cause problems, thinking of workflow improvements and so on. In a sense he has to sense the ship itself. This only works because of two things. First of all he has personal experience doing the exact work he is observing. If you have not personally "been there", you can't really know if a crew is working well or not. You need a "gut feeling" to be able to sense this. Secondly the captain does not have any manual labour so he can focus all of his mental energy on observing the ship's state. He is preparing for all the unexpected things that may occur in the future. This can only happen if your brain is free from menial tasks.

This is exactly what most books on business and project management advocate. It is a time tested way of improving your chances of success. A highly skilled commander can take an average team of people and lead them to victory. It is the basic plot of most military and sports movies.

Getting back to the present

Now take a typical modern day billionaire-via-inheritance and show them Star Trek at an impressionable age. Do they see the advantages of education, hard work and ethics? The foundation upon which Gene Roddenberry carefully built the show? Hell no! What they see is this (TNG screenshot used because TOS did not have a suitable maritime episode).

And then they think: "Wow! I want to be exactly like that! Parading around in a funny hat while everyone obeys my orders without question is my life's mission from now on. And I get to have sexy space sex with hot sexy space ladies of sex whenever I want. This appeals to me even more profoundly than Atlas Shrugged." Some of them might go on to watch Master and Commander and shed tears upon realizing that they cant publicly flog employees for failing to salute their superiors. Yet. Expect this to be made legal in Silicon Valley any day now.

Liking Kirk is not in any way a bad thing. Wanting to "be just like Kirk" is, because in the real world running a business like Kirk runs the Enterprise is a terrible way to do things. An example will illustrate this nicely. Let's imagine a random episode where the Enterprise has gotten into trouble. Eventually Kirk will call for Scotty and tell him: "You need to <babble> <babble> <babble>." Scotty will then reply with a varying level of scottish accent something like: "I cannae change the laws of physics, captain". Kirk will then say the same thing again, just more aggressively and in a close up shot. Scotty replies with "Well in that case I can get it done in sixty minutes." Kirk counters with: "You have five." And thus the problem is solved. Kirk gets a commendation for incredible valour under stress while Scotty, who did all of the actual work, is never mentioned.

In "Kirk style" management the Big Boss tells his underlings what to do. If they try to give any sort of feedback, the Boss ignores everything and just repeats his original orders again and again until the other party yields. The only reason underlings ever resist The Vision is that they are lazy and it is the job of the manager to put them in their place. This seems like a bit from a comedy show, but I have unfortunately worked under bosses like this. Either you try to talk at least some sense into them, fail, get labeled as a "not team player", watch the project crash and get blamed for the failure, or you try to do the impossible task given to you, fail watch the project crash and get blamed for the failure.

Another major problem with Kirk is that due to the way tv shows and movies need to be structured, he is actually an obsessive gambler. The stakes must always get higher and the ways to get out of trouble must become ever crazier. Kirk will break any and all laws and regulations he sees fit and then, once he has succeeded, no disciplinary action is taken. The ends justify the means. Idolising this sort of behaviour leads to thinking that wild one-in-a-million gambits will succeed at least 99 times out of a hundred. And even if it fails, you can get out of it by betting everything on an even bigger gamble. The real world does not work like that. Reality is not a story and you are not its hero. It does not owe you eternal, or even eventual, success. Had Stockton Rush survived his death trap, he would most likely have faced criminal charges and, if convicted, gone to jail.

The myth of the existence of the professional manager

Let's make one last detour in the 1800s and assume that the 7th Earl of Sidcup or some such really wants to get his idiot son instated as a captain. He and contacts the appropriate naval officers.

"My offspring needs to become a captain of a ship post haste!"

"Well first he has to become a midshipman and ..."

"Phah! None of that nonsense. It's way too slow and not becoming of my statute. Also my son is 35 years old so the post of a midshipman would be beneath him."

"I see. Well what sort of prior naval experience does he have?"

"None."

"Has he even ever been out to sea?"

"Not to my knowledge. But that does not matter. He is highly skilled in using the abacus excelius to compute annual budgets."

"By himself?"

"Of course not. That is what secretaries are for. He just gives them orders. That he can do. And that is all that matters. Same as in sailing."

This person is unlikely to get his wish with this line of reasoning. On the other hand in modern business life this is common. For example when startups get VC money, a common requirement is that they need to get a "proper manager" as a CEO. Typically this means the investor's friend, and more often than not an MBA.

Contrary to common belief, having an MBA does not make you incompetent at managing a technical company (though there is a strong positive correlation). It is entirely possible to be a good manager on a field you have no personal experience in. You just have to have a lot of humility, listen (actually, properly listen) to your employees and let the people with hands-on experience make the most technical, product and development decisions. In other words you have to be the enabler, not the maverick decision maker. People with these sorts of personality traits are rare and typically their career choices steer as far away from getting an MBA as possible.

The absolute worst thing happens if the CEO in question combines the (lack of) skills of an MBA with the attitude of Kirk. That leads to incompetent decisions based on willful ignorance, executed with the fury of an egomaniac who refuses to even entertain the notion that they might be wrong. Further, any person inside the organisation who dares to point out potential flaws in the plan will soon find themselves outside said organisation. Disagreement is treason. Treason shall not go unpunished.

In the 1800s the British navy could be said to be the best in the world. It seems plausible that one component of this success was the requirement that the officers running their ships had to have actual experience operating the ship. Not looking at other people operating it. Not pretending to read about operating it for a test. Actually doing it. If we look around how MBA wielding sociopath CEOs are enshittifying absolutely everything about the tech industry, bringing this requirement back into active use starts to feel awfully tempting.

Epilogue: Why doesn't everything immediately explode?

A reasonable counterpoint to everything written above would be that if managers truly are that bad, shouldn't all those companies be bankrupt by now? In an ideal world they would be, but there are opposing forces that keep them going.

The first one is that all corporations have inertia. If you took an established major company and actively started to mismanage it to death, it would still take years for things to eventually collapse. 

The second one is a dirty little secret. Many employees care more about the product they work on than "corporate visions" that seem to stem from overuse of peyote. They don't blindly obey idiotic commands but instead try to make things silently work within the system. Basically this means that corporations thrive despite their mad kings, not because of them. I know several people who have worked in these kinds of organizations and this is not as rare of an occurrence as one might imagine.

I have also experienced it personally. Years ago I was at a company, whose CEO (who, to the best of my knowledge, did not have an MBA) wanted to change the company's product so that it would do a specific thing X. Everybody thought this was a horrible idea and tried to reason with him using solid business and technical reasons (which turned out to be 100% correct). That failed. Spectacularly. This lead to an eternal series of secret meetings. The participants were main developers and all managers except the CEO. The only item on the agenda was "How can we make the CEO think that we did what he ordered while doing the exact opposite".

Eventually we did succeed, but boy was that a surreal couple of weeks.

Arun Raghavan

@arunsr

Notes from the PipeWire Hackfest 2026: Part 2

(these notes are being posted in two parts to make the length more manageable, part 1 is here)

Continuing from where we left off, about topics discussed at the PipeWire hackfest in Nice…

DSP features

We discussed a number of features related to digital signal processing blocks which are typically realised on specialised hardware (often a DSP core that can directly interface with physical audio inputs and outputs on your laptop/phone/…).

There is currently no standard way for the firmware running on these DSPs to signal what features can be realised directly on DSP. We also would want to allow such features, if exposed from PipeWire, to be realisable on CPU.

Now we do have a way to hide away signal processing in a specific node, which is the filter-graph parameter on the audioconvert node that wraps all audio nodes.

We could extend this mechanism to allow the internal node (say the ALSA node implementation), to expose what filtering it can perform “in hardware” (i.e. the software running on DSP). This would allow the audioconvert to delegate some or all processing to the internal node, with fallbacks available on the CPU.

We would need a number of pieces to do this, including:

  • Some standard definition of filters and associated parameters, so different implementations could have a standard “API” to express any given filter.

  • The DSP block would need to expose what features it has and how they might be used. We could imagine extending the ALSA UCM configuration to do that.

  • The audioconvert node would need to have a way to push down filter-graph params to the internal node, and negotiate what work it is doing vs. what is being delegated

This is a non-trivial effort, but gives us some sketch of what might be possible.

More DSP features

In addition to standard filters, we spoke about two topics that have come up commonly in the past.

The first is some way to expose the processing graph in the DSP, so PipeWire and other userspace daemons have a better view of what is happening on the DSP. With the ability to push dynamic topologies to DSP, there was some renewed interest in exposing and using the ASoC DAPM widget graph. As always, the devil is in the details.

The second thing that came up is speaker calibration. There is a lot of processing and tuning that goes into driving speakers on modern devices as much as possible without destroying them. Some of these are one-time parameters decided at product design time, and some of these translate to runtime parameters based on voltage and current feedback from the speaker amplifier.

For some systems (like Qualcomm platforms), speaker calibration might be run on each system start to perform dynamic tuning. We had some discussion of how this might tie in with the rest of the system for both determining the parameters (separate startup daemon vs. in-process initialisation), as well as uploading parameters to the speaker (some ALSA UCM extensions to load parameters on PCM open but before start, or preloading parameters into ALSA kernel controls and having the driver feed them in at the right point).

Volume limits

A way to set a limit on the maximum volume for a given device has been a common user request ([1] [2]). We discussed the possibility of creating a per-route property (with a fallback to the node, if there are no routes), which WirePlumber could manage to provide users a simple interface to control.

Since the hackfest, Wim has already done some work on this, and we need to bubble this up as a more user-accessible setting.

Performance

A number of performance-related topics were discussed.

The first was an option of a combined DSP mode, where instead of one port per channel, a node would expose one port for all the channels of the stream (but continue to run in the configured “DSP” format/rate). This would improve stream performance for non-JACK-like use-cases, especially in resource-constrained environments.

On the WirePlumber side, there was a discussion about using LuaJIT instead of standard Lua. There are some compatibility issues to be determined there (such as language version supported, etc.), but there might be some quick performance wins to be made if this is feasible.

There is a plan to move some of the WirePlumber core to Rust, and that might be a good time to also port over some of the more standard functionality that tends not to change from Lua to Rust (though that could happen in a Lua->C transition and does not really need to wait on a Rust port).

Declarative Session Management

Another interesting, and broader, thread is the imperative nature of WirePlumber scripts – that is, policy decisions and associated action are often interwoven. It might be helpful to be able to make a clearer split where all policy decisions are first run, and then decisions are translated into actions at one go.

There are some historical choices that make this hard – for example, changing the profile of a device might create and destroy nodes, which makes it hard to be able to make decisions that are independent of the action. There were some ideas around redoing the profile concept such that all nodes are always exposed, but nodes could get a new state to signal availability (and profiles that would allow availability to change). That might make a declarative system possible to implement.

We also discussed the possibility of a “transaction” system. Something that would allow a client to submit a set of objects (think links between nodes), and then “commit” that transaction. This would also help reduce the number of roundtrips between PipeWire and WirePlumber, and generally help performance.

Bluetooth

Being colocated with the BlueZ face-to-face meeting, we had representation from the BlueZ community, so we were able to dive into a number of topics related to Bluetooth, primarily LE Audio.

The first topic was Auracast, the LE Audio system for broadcast audio, allowing listeners to tune into public broadcasts in a space, or to have a device stream audio to multiple headsets concurrently for shared listening. George had a demo system showing an implementation of Auracast with PipeWire, WirePlumber and BlueZ.

We had some discussion of where this feature should live, and the consensus was that we would probably want a separate daemon to manage Auracast settings and loading up the appropriate nodes (either for receiving or sending) based on users’ preferences.

This led to a more general discussion about the current split of the Bluetooth implementation in PipeWire being SPA modules, which include streaming and some policy, and a lot more policy living inside WirePlumber. We could, and likely should, move all of this into higher level PipeWire modules instead, which could make these easier to work with overall.

There was also a discussion about the complexities of LE Audio, and the state of the current user experience with actual devices:

  • Device interop is not always great, as the spec is new, the BlueZ implementation is still being completed, and device implementations seem of variable quality
  • Reliable pairing/feature detection is hard, partly due to how BlueZ exposes the ability to talk to devices in Bluetooth Classic or Bluetooth LE modes
  • Pairing left/right pairs currently needs individual pairing, which does not seem to be needed by other implementations (Android for example)
  • Inter-device synchronisation might need some work as well

While there is much work to be done here, the pieces are coming together for first-class LE Audio support on Linux-based systems.

Audio analytics

We also spoke about “analytics” – using local neural networks to implement things like text-to-speech, speech-to-text, language translation, or other forms of processing.

These pose an interesting problem, because they look like a standard-ish audio stream on one side, but are effectively a sparse stream on the other side if we are talking about text. Even conversion between languages does not look like a standard filter, because the underlying model might consume a varying amount of data before generating an output, and the input and output lengths are not tightly correlated.

While it should be possible to implement such a system with PipeWire, it is not quite clear whether we should. As the application space in this area becomes more mature, it may become clearer what the right place in the stack is for these features.

Click detection and elimination

We spoke about detecting and eliminating clicks at the stop or start of a stream.

If an application is playing back audio, and suddenly stops (i.e. feeds silence, or just nothing), then the sudden drop in the signal might cause a click to be output. If you think of the corresponding waveform as representing the physical displacement of the speaker, then the drop to zero is like a sudden brake to a halt, which isn’t possible, and manifests as a jolt that you hear as a clicky noise. The same analogy holds for resuming from a pause, but in the opposite direction.

The solution is usually to smooth out the end of the sound by fading out, but most applications do not do this, so this problem manifests quite clearly for most browser or application streams if you listen closely.

Wim described a number of experiments he has done for detecting such abrupt changes in audioconvert, but he was not happy with the results. We discussed some of these approaches, and what might work as acceptable tradeoffs to capture the most common cases while still trying to respect the integrity of the signal being sent by the application.

(sorry about the vagueness here, I missed taking more detailed notes)

Miscellanea

The rest of the discussion covered disparate topics that I don’t have long form notes on:

  • Hardware profiles: Shipping hardware-specific configuration for PipeWire and WirePlumber is hard. We discussed some approaches using context properties and conditions, but this is an area that needs more work.

  • Data loop management: PipeWire allows splitting work across data loops so different nodes in a graph can be assigned to different threads. This is currently an all-or-nothing system, where either all nodes go to a single data loop, or every node must be manually assigned a specific data loop. There was some desire to have the ability for there to be a default data loop to make the manual management less cumbersome.

  • ACP -> UCM: PipeWire inherits the ALSA card profile configuration from PulseAudio, which has been helpful in making the migration path smoother on most hardware. There was always some desire to have a single configuration system (probably ALSA UCM) for all hardware, but this likely needs some work on what we can express in UCM configuration, but we also need to clean up how we translate our UCM handling code (George has an RFC for this).

Thanks

That’s it, thank you for reading if you made it this far, and a shout out to George, Mark, and others organising the event!

It was great to see continued interest and so much exciting work that is yet to come. I hope to see more of the community in the next edition of the hackfest.

Arun Raghavan

@arunsr

Notes from the PipeWire Hackfest 2026: Part 1

(these notes are being posted in two parts to make the length more manageable, part 2 is here)

The PipeWire community organised a hackfest in Nice, France, colocated with Embedded Recipes, the GStreamer hackfest, and a number of other events.

In attendance were members of the upstream community, as well as folks interested in PipeWire from Collabora, Red Hat, Qualcomm, Stream Unlimited, Texas Instruments, and Valve. In some cases these were the same person wearing upstream and professional hats, as some of us often do! :)

It was two days of fruitful and deep technical discussions, and lovely evenings hanging out in the Côte d’Azur. Shout out to George Kiagiadakis and Mark Filion for putting this together!

A photo of the waters in Nice from a rooftop
Beautiful view of the Côte d’Azur

The topics were disparate and can be somewhat esoteric for folks who are not familiar with the Linux audio space. I will try to strike a balance between providing context and summarising the finer details we discussed. Please feel free to write in if I missed or can expand on anything.

Multistream nodes

A recurring topic for the last couple of years has been supporting multistream nodes. The PipeWire API currently offers a pw_stream interface that can offer a node with single input or output (closer to the PulseAudio API), and the pw_filter interface that provides a lower-level freeform API to individually manage ports on a node (closer to the JACK API).

The stream API while convenient, can be a bit unwieldy for realising concepts such as loopbacks and filters, because each set of inputs and outputs needs to be implemented as an individual node. If you’ve ever loaded the loopback module, for example, you would have noticed that there are two nodes created for each instance.

Wim has created a version of the API that allows a node to provide multiple streams, which allows us to keep the conveniences of the stream API, but more easily express ideas like the loopbacks, filters, etc. Each stream is effectively a group of ports on the node, and nodes can have an arbitrary number of input and output streams.

The code on the PipeWire side is ready. The primary idea is there will be a PortConfig param per stream, and this is where the format of the stream, and other metadata expressed on port groups (which is essentially what a stream is) will live.

We discussed what is needed in WirePlumber to make sure the linking logic adapts to this concept, and Julian will be implementing that in the coming weeks.

Settings

PipeWire has a generic metadata system based on the JACK API that is used for storing metadata (allowing you to attach a key/type/value, optionally attached to an object). This is also used by WirePlumber to provide its settings system (see wpctl settings), along with some key features such as a schema and persistence.

We discussed that it might be nicer to have the concept of settings as a first-class citizen, and possibly even standardise some settings for desktop wide usage (such as common processing elements). There was consensus that:

  • A new settings interface (instead of extending metadata) would make sense
  • The API should be asynchronous, and can fail
  • A schema for valid settings and their types could be exposed as a well-known metadata key
  • Implementors of the interface would perform validation

Security

We spoke about the current state of security for applications using PipeWire. For context, PipeWire has a fine-grained permissions model where each client can have selective access to what objects are visible to it, and what actions it may perform. There is also a less granular system, where a “manager” application can connect to the manager socket for full access. We broadly think about restricted security for sandboxed applications (primarily Flatpak).

One scenario is sandboxed PulseAudio applications getting full access via the pipewire-pulse server on the host. The discussion on this concluded that there is a way for pipewire-pulse to forward enough security-related information from sandboxed applications for us to apply sandbox restrictions to them, and we need to make that system work.

There was a discussion that it might be reasonable for our default policies to apply for all applications connecting to the regular PipeWire socket to be restricted (this does not prevent malicious applications from accessing the manager socket, but helps applications not do bad things erroneously).

This might be disruptive to introduce as a default change, so we might implement it via an opt-in setting so that there can be some broader testing and refinement of default permissions before flipping the switch for all users.

There are a number of mechanisms related to how security context properties are relayed, and how those properties are used by WirePlumber to determine permissions. We need to document and verify the expected behaviour here.

Flatpak and Portals

Relatedly there was a discussion about how things should fit in with Flatpak, and Sebastian Wick from the Flatpak team joined us briefly on the second day.

There was some discussion of making sure the PulseAudio socket is provided to the sandbox in a similar way to the PipeWire socket, such that some additional security properties can be assigned from the host in a way that the sandboxed client cannot override.

We agreed that we needed the ability for applications to specify with some granularity what permissions they require (via portals), and for us to grant only that (with user intervention, if needed). Broadly this is:

  • Playback (optionally enumeration of sinks)
  • Capture (optionally enumeration of sources)
  • Default visibility of only the application’s own nodes

We also spoke about how we might want to associate PipeWire objects with applications. With Flatpak moving to using a cgroup for each application, this should become easier. We may also want to be able to have a way to associate a stream with a specific window (to, for example, share a window and its audio), which should be possible.

It was also noted that for some classes of applications, we may want a way for users to allow some of these permissions at install time (for example, a remote desktop application asking permission on every start can be annoying). This is already possible with Flatpak manifests (which are static, but we might need to add some more options here), and there is a potential entitlement system being discussed (for server-driven overrides to be distributed for malicious applications, for example).

Encapsulation and Collections

One topic that came up last year is the ability to encapsulate a group of nodes such that they appear as a single node to other applications in the system. This could be useful for:

  • Collapsing all the output from an application so it appears to be providing a single stream
  • Grouping all the filters for a sink or source node, and making it appear as a single node with all the processing hidden away

One piece to making such a system possible is to have a first-class notion of this group. Julian has an implementation of such an entity, called a “collection”. This is currently implemented on top of PipeWire metadata, but we agree that this is likely worth having an explicit PipeWire interface for.

Once that is in place, we discussed the possibility of having a smarter “proxy” node that can act as the interface that translates from the “outside” of the encapsulated region to the “inside”, so that format selection, volume changes, etc. can properly be proxied to the underlying device, for example.

Tooling improvements

It was noted that the tools we have (such as pw-top and pw-dot) can make it hard to get at some information, such as negotiated formats, rates, etc. They can also be “noisy” when we have a large number of filters and loopbacks.

While we did not have a concrete plan to tackle this, some of us have been playing with LLM-based tooling to generate some helper code for this sort of thing. At least my attempts have been too sloppy to share as yet, but it should be possible to get something useful with a structured approach.

That’s it for now. Watch this space for part 2!