Skip to content

Saturday, 14 February 2026

I recently had the opportunity to sit down with my mentor, Schimon Jehudah, for an intensive technical session.

Hey everyone!

I am Siddharth Chopra, a second year engineering student at the Indian Institute of Technology, Roorkee. I'm really excited to be working on Marknote as a part of the Season of KDE program this year, under the mentorship of Carl Schawn.

Marknote, as it is aptly named, is KDE's own markdown based note taking app. The aim of my project is to improve Marknote by adding the much requested source mode, alongside other enhancements.

Progress so far

3 weeks into the project, I have been successful in adding a working source mode functionality to the editor. When source mode is activated, the contents of the source markdown file are allowed to be edited directly, instead of showing the rendered markdown. This is incredibly useful, in cases where the user needs manual control over the contents of the note, or in case there is some glitch in the rendering (which unfortunately still happens often).

Demo Video

Technical Roadmap & Challenges

The main editor of Marknote comes from the file EditPage.qml. As part of my initial approach, I added a global property here to check if source mode was enabled, and then conditionally changed components of this editor. Although this worked, but it brought along some of its own issues. First of all it made the code unnecessarily complex. It also meant that components like the formatting bar now needed to be repurposed to work with source mode, which is a challenge in itself.

So, my mentor suggested to move the raw editor into a new file, to keep the code maintainable. This led to the original EditPage being split into RichEditPage and RawEditPage. Similarly, the respective backends were also split in two, as the needs of both the editors are significantly distinct.

Additionally, I had to consider specially the source mode for images. Because when loaded, image URL's are not kept intact, instead they are replaced with a hash, that maps to the image in memory. Also, the editor internally uses html for rendering images, which I also had to convert back to markdown for source mode.

And someone who is not a designer by any means, deciding the form and placement of the mode toggle button was in itself a mini lesson in UI design ;) Initially I went with a toggle switch. But when I shared that for feedback, I learnt that a checkable button is the ideal UI element here.

Future Plans

My proposal mentions features apart from the source mode, which I plan to complete. While working on the current feature, I noticed multiple bugs in the app, which I intend to fix as well.

Overall experience

It has been a great experience working with the KDE community, and really exciting to be able to contribute to an app that so many users around the world use every day! I would also like to express my gratitude towards my mentor, for being there for whatever issue I faced!

Friday, 13 February 2026

Let’s go for my web review for the week 2026-07.


The Media Can’t Stop Propping Up Elon Musk’s Phony Supergenius Engineer Mythology

Tags: tech, politics, journalism, business

There’s really a problem with journalism at this point. How come when covering the tech moguls they keep leaving out important context and taking their fables at face value?

https://karlbode.com/the-press-is-still-propping-up-elon-musks-supergenius-engineer-mythology/


But they did read it

Tags: tech, literature, scifi, business, politics

Indeed, don’t assume they misunderstood the sci-fi and fantasy they read and you know. Clearly they just got different opinions about it because their incentives and world views are different from your.

https://tante.cc/2026/02/12/but-they-did-read-it/


Tags: tech, game, dmca, copyright, law

Automated DMCA take downs have been a problem for decades now… They still bring real damage, here is an example.

https://www.techdirt.com/2026/02/12/microsofts-ai-powered-copyright-bots-fucked-up-and-got-an-innocent-game-delisted-from-steam/


Launching Interop 2026

Tags: tech, web, browser, interoperability

This is a very important initiative. For a healthy web platform we need good interoperability between the engines. I’m glad they’re doing it again.

https://hacks.mozilla.org/2026/02/launching-interop-2026/


How I built Fluxer, a Discord-like chat app

Tags: tech, foss, messaging

Clearly early days… Could that become a good place to land for people fleeing off Discord?

https://blog.fluxer.app/how-i-built-fluxer-a-discord-like-chat-app/


New And Upcoming IRCv3 Features

Tags: tech, messaging, irc

It’s nice to still see some activity around IRC.

https://libera.chat/news/new-and-upcoming-features-3


Uses an ESP8266 module and an Arduino sketch to display the local time on a inexpensive analog quartz clock

Tags: tech, hardware, embedded, ntp, time

This is definitely a cool hack. Now I feel like doing something like this to every clock I encounter.

https://github.com/jim11662418/ESP8266_WiFi_Analog_Clock


LLVM: Concerns about low-quality PRs beeing merged into main

Tags: tech, ai, machine-learning, copilot, foss, codereview

Clearly Free Software projects will have to find a way to deal with LLM generated contributions. A very large percentage of them is leading to subtle quality issues. This also very taxing on the reviewers, and you don’t want to burn them out.

https://discourse.llvm.org/t/concerns-about-low-quality-prs-beeing-merged-into-main/89748


An AI Agent Published a Hit Piece on Me

Tags: tech, ai, machine-learning, copilot, foss, commons

I guess when you unleash agents unsupervised their ethos tend to converge on the self-entitled asshole contributors? This raise real questions, this piece explains the situation quite well.

https://theshamblog.com/an-ai-agent-published-a-hit-piece-on-me/


Spying Chrome Extensions: 287 Extensions spying on 37M users

Tags: tech, browser, security, attention-economy, spy

Oh this is bad! The amount of data exfiltrated by those malicious extensions. Data brokers will do anything they can to have something to resell. This is also a security and corporate espionage hazard.

https://qcontinuum.substack.com/p/spying-chrome-extensions-287-extensions-495


ReMemory - Split a recovery key among friends

Tags: tech, tools, security

Accidents can happen in life. This might come in handy if you loose memory for some reason. It requires planning ahead though.

https://eljojo.github.io/rememory/


Penrose

Tags: tech, tools, data-visualization

Looks like a nice option for visualisations.

https://penrose.cs.cmu.edu/


Tags: tech, programming, language, statistics, type-systems

Interesting experiment even though some of the results baffle me (I’d have expected C# higher in the ranking for example). Still this gives some food for thought.

https://boyter.org/posts/boilerplate-tax-ranking-popular-languages-by-density/


The cost of a function call

Tags: tech, c++, optimisation

If you needed a reminder that inlining functions isn’t necessarily an optimisation, here is a fun little experiment.

https://lemire.me/blog/2026/02/08/the-cost-of-a-function-call/


It’s all a blur

Tags: tech, graphics, blur, mathematics

Wondering if blurs can really be reverted? There’s some noise introduced but otherwise you can pretty much reconstruct the original.

https://lcamtuf.substack.com/p/its-all-a-blur


Simplifying Vulkan One Subsystem at a Time

Tags: tech, graphics, vulkan, api, complexity

There are lessons and inspirations to find in how the Vulkan API is managed. The extension system can be unwieldy, but with the right approach it can help consolidate as well.

https://www.khronos.org/blog/simplifying-vulkan-one-subsystem-at-a-time


What Functional Programmers Get Wrong About Systems

Tags: tech, data, architecture, system, type-systems, functional, complexity

Interesting essay looking at how systems evolve their schemas over time. We’re generally ill-equipped to deal with it and this presents options and ideas to that effect. Of course, the more precise you want to be the more complexity you’ll have to deal with.

https://www.iankduncan.com/engineering/2026-02-09-what-functional-programmers-get-wrong-about-systems/


Modular Monolith and Microservices: Modularity is what truly matters

Tags: tech, architecture, modules, microservices, services, complexity

No, modularity doesn’t imply micro services… You don’t need a process and network barrier between your modules. This long post does a good job going through the various architecture options we have.

https://binaryigor.com/modular-monolith-and-microservices-modularity-is-what-truly-matters.html


Using an engineering notebook

Tags: tech, engineering, note-taking, memory, cognition

I used to do that, fell into the “taking notes on the computer”. And clearly it’s not the same, I’m thinking going back to paper notebooks soon.

https://ntietz.com/blog/using-an-engineering-notebook/


On screwing up

Tags: tech, engineering, organisation, team, communication, failure

Everyone makes mistakes, what matters is how you handle them.

https://www.seangoedecke.com/screwing-up/


Why is the sky blue?

Tags: physics, colors

Excellent piece which explains the physics behind the atmospheric colours. Very fascinating stuff.

https://explainers.blog/posts/why-is-the-sky-blue/



Bye for now!

This release brings improvements to generators, better build system integration and several bugfixes.

As always, big thanks to everyone who reported issues and contributed to QCoro. Your help is much appreciated!

Directly Awaiting Qt Types in AsyncGenerator Coroutines

The biggest improvement in this release is that QCoro::AsyncGenerator coroutines now support directly co_awaiting Qt types without the qCoro() wrapper, just like QCoro::Task coroutines already do (#292).

Previously, if you wanted to await a QNetworkReply inside an AsyncGenerator, you had to wrap it with qCoro():

QCoro::AsyncGenerator<QByteArray> fetchPages(QNetworkAccessManager &nam, QStringList urls) {
 for (const auto &url : urls) {
 auto *reply = co_await qCoro(nam.get(QNetworkRequest{QUrl{url}}));
 co_yield reply->readAll();
 }
}

Starting with QCoro 0.13.0, you can co_await directly, just like in QCoro::Task:

QCoro::AsyncGenerator<QByteArray> fetchPages(QNetworkAccessManager &nam, QStringList urls) {
 for (const auto &url : urls) {
 auto *reply = co_await nam.get(QNetworkRequest{QUrl{url}});
 co_yield reply->readAll();
 }
}

Other Features and Changes

  • Generator’s .end() method is now const (and constexpr), so it can be called on const generator objects (#294).
  • GeneratorIterator can now be constructed in an invalid state, allowing lazy initialization of iterators (#318).
  • qcoro.h now only includes QtNetwork and QtDBus headers when those features are actually enabled, resulting in cleaner builds when optional modules are disabled (#280).

Bugfixes

  • Fixed memory leak in QFuture coro wrapper when a task is destroyed while awaiting on a QFuture (#312, Daniel Vr√°til)
  • Fixed include paths when using QCoro with CMake’s FetchContent (#282, Daniel Vr√°til; #310, Nicolas Fella)
  • Fixed QCoroNetworkReply test on Qt 6.10 (#305, Daniel Vr√°til)

Full changelog

See changelog on Github

Support

If you enjoy using QCoro, consider supporting its development on GitHub Sponsors or buy me a coffee on Ko-fi (after all, more coffee means more code, right?).

Thursday, 12 February 2026

The world of free and open-source software (FOSS) is full of big-hearted, altruistic people who love serving society by giving away their labor for free. It’s incredible. These folks are supporting so many people by providing the world with high quality free software, including in KDE, my FOSS community of choice.

But while they do it, who’s supporting them? We don’t talk about this as much.

A recent Reddit post by a volunteer developer burning out reminded me of the topic, and it’s not the first one. Denis Pushkarev of core-js wrote something similar in 2023, and we’re probably all familiar with this XKCD comic:

The topic is also not limited to the FOSS world; it’s broadly applicable to all volunteer activities. Can’t feed the homeless in a soup kitchen if you’re sick and sneezing into the soup! Can’t drive to the library to teach adult reading classes if your car’s broken down.

In order to support others, you need support yourself! Who can provide that support? Here are a bunch of cases that work:

  • Yourself in the present (with a job — related or unrelated to your FOSS work)
  • Yourself in the past (retired)
  • Your partner in the present (married to a primary or sole income-earner)
  • Your partner in the past (partner left you lots of money after death or divorce)
  • Your parents in the present (you’re their dependent)
  • Your parents in the past (born rich or received a big inheritance later)
  • The state (disabled, a student, or on a similar program)

All these cases work. They provide enough money to live, and you still get to work on FOSS!

There are lots of other good options, but here are some of the bad ones that don’t work:

  • Other people via donations: you never get enough donations, and if you put the effort into fundraising required to make it work, that becomes your job.
  • Yourself in the future: if you’re living off loans, you’re screwing over future you!
  • Nobody: if you’re eating up your savings, you’ll eventually run out of money and be destitute. If you’re fortunate enough to live in a place where “The state” is an option, it will be at a diminished standard of living.

We must always answer for ourselves the question of how we’re going to be supported while we continue to contribute to the digital commons. If you don’t do it for a living, it’s a critically important question. Never let anyone guilt-trip you into doing volunteer work you don’t have the time or money for! It’s a sure road to burnout or worse.

Airplane safety briefings tell people to “put on your mask before helping others.” Why? Same reason as what we’re talking about here: you can’t support others if you’re not first supported yourself. If you try, you’ll fail, either immediately, or eventually. You must be properly supported yourself before you can be of use to others.

As explained in one of my previous blog posts where I revamped the unresponsive window dialog, KWin isn’t really designed to show regular desktop windows of its own. It instead relies on helper programs to display messages. In case of the “no border” hint, it just launched kdialog, a small utility for displaying various message boxes from shell scripts. This however came with a couple of caveats that have all been addressed now:

KWrite (simple text editor) window without window border. Ontop a dialog “You have selected to show a window without its border. Without the border, you will not be able to enable the border again using the mouse: use the window menu instead, using the Alt+F3 keyboard shortcut”, buttons “OK” and “Restore Border”
Setting no border now lets you restore it in case you really don’t have a keyboard

First of all, the dialog wasn’t attached to the window that provoked it. When the window was closed, minimized, or its border manually restored, it remained until manually dismissed. Secondly, it said “KDialog” and used a generic window icon (that would have been an easy fix of course). Further, the user might not even have kdialog installed which actually was the case on KDE Linux until very recently. Ultimately and most importantly, it told you that you were screwed if you didn’t have a keyboard but didn’t offer and help if you really went without one. I therefore added an option to restore the border right from the dialog. Should you have a dedicated global shortcut configured for this action, it will also be mentioned in the dialog.

The dialog when manually setting a window full-screen has similarly been overhauled, including an undo option. While at it, I removed the last remaining code calling kdialog, too: the “Alt+tab switcher is broken” message. It is now a proper KNotification. Something you should never see, of course.

Another dialog that I gave some attention was the prompt when copying a file would overwrite its destination. If you have Kompare installed and copy a plain text file (that includes scripts, source code, and so on), the dialog displays a “Compare Files” button. It already guesstimated whether the files are similar but now you can actually see it for yourself.

Dialog prompt asking “Would you like to overwrite the destination?”, Source (from a ZIP file) and Destination (in Downloads folder) both show a preview image and size and modified time.
Extracting a file from Ark now displays information about the source, too

KIO’s PreviewJob that asynchronously generates a file preview now provides the result of its internal stat call, too. This means that once you receive the preview, you also get the file’s properties, such as size, modified time, and file type basically for free. The rename dialog then displays this information in case it wasn’t provided by the application already. Dolphin now also makes use of this information while browsing a folder which should improve responsiveness when browsing NFS and similarly mounted network shares. At least when previews are enabled, it no longer determines file types synchronously in most scenarios.

Since the rename dialog is able to fetch file information on demand, Ark, KDE’s archiver tool, rewrites the source URL it displays in the dialog to a zip:/ URL (or tar:/ or whatever supported archive type). This way the dialog can display a preview transparently through the Archive KIO worker which also gained the ability to determine a file’s type from its contents. In case you didn’t know, you can configure Dolphin to open archives like regular folders.

Finally, most labels in KIO that show the mtime/ctime/atime no longer include the time zone unless it is different from the system time zone. Showing “Central European Standard Time” in full after every date was a bit silly. Unfortunately, QLocale isn’t very flexible and only knows “short” (19:00) or “long” (19:00:00 Central European Standard Time) formats. You can’t explicitly tell it to generate a time string with seconds, or with 12/24 hour clock, or a date with weekday but no year, and unfortunately the “long” time format includes the full time zone name.

Tuesday, 10 February 2026

Guide: Creating a C++ Extension for Falkon Browser This guide walks through creating a basic “Hello World” plugin in falkon. 1.

The final final 5.2.x release has been made, and the first beta for 5.3.0/6.0.0 is out!

Read on for a look at development news and the Krita-Artists forum's featured artwork from last month.

Development Report

5.2.15 Released

5.2.15, another bugfix release, is out, featuring a few more Android, touch input, and general bug fixes. This is really the last 5.2.x release this time.

Check out the 5.2.15 release post and stay up-to-date!

First Beta for 5.3.0/6.0.0 Released

The next major release will be a dual release; 5.3.0 is built on the familiar Qt5 framework, while 6.0.0 is ported to the newer Qt6 framework. Qt6 allows for better Wayland compositor support on Linux among other things, but unfamiliarity and lack of testing means Krita 6.0.0 will be less stable than 5.3.0. Krita 6 is also not yet available on Android.

The first betas for this release are out. Help out by testing the two years' worth of changes since 5.2 and the Qt6 porting work, and report any bugs you find so they can be fixed before the final release! Learn more in the beta1 release post.

Some of Last Month's Bugfixes

The team has been busy polishing new features and fixing old bugs for the beta release.

Many issues with gradients, layer styles, and resource loading were fixed by Dmitry.

  • Layer Styles now use their own copy of gradients, preventing the gradients from unexpectedly changing when the global version is modified, as well as related crashes. (bug 1, bug 2, bug 3, bug 4; change)
  • The Bevel-and-Emboss Layer Style now saves patterns correctly. (bug; change)
  • Brush presets with embedded resources sharing a name with a different resource are now loaded correctly. (CCbug; change)
  • Fixed editing or removing resource bundles causing a database error on startup. (bug; change)

More text fixes by Wolthera:

  • Made sure new texts are made with the current foreground color. (change)
  • Fixed an issue where light-colored text made Krita stall and then the text disappeared (caused by reading uninitialized data as font metrics). (bug; change)
  • Fixed selecting fonts whose full name is the same as their family name. (bug; change)
  • Fixed freeze when editing text contour while bound rect snapping is enabled. (change)
  • Fixed setting subscript and superscript on the entire paragraph. (bug; change)

The G'MIC filter plugin has been updated by Ivan to version 3.6.6. (change)

On macOS, Krita's G'MIC is now faster, being built with OpenMP's multithreading as was already the case on other platforms. (change)

Community Report

January 2026 Monthly Art Challenge Results

The winner of the "Vintage Travel Poster" challenge is…

Titan by Elixiah

Titan by Elixiah

Join This Month's Art Challenge!

For February's theme, last month's winner Elixiah passed the choice of topic to runner-up MossGreenEddie, who has chosen "Alien World Building", with the optional challenge of including food in some way. How might members of an extra-terrestrial society interact? Cook up something out of this world!

This month's featured forum artwork, as voted in the Best of Krita-Artists - December 2025/January 2026:

Aging by ShangZhou0

Aging by ShangZhou0

Cat's Eye by _CR

Cat's Eye by _CR

Digital Watercolor Landscapes by daroart37

Digital Watercolor Landscapes by daroart37

Photo drawing study: hands by pavuk0.2

Photo drawing study: hands by pavuk0.2

Rooster 2025 by z586t

Rooster 2025 by z586t

Participate in next month's nominations and voting to voice your opinion on the Best of Krita-Artists - January/February 2026.

Krita is Free - But You Can Contribute!

Krita is free to use and modify, but it can only exist with the contributions of the community. A small sponsored team alongside volunteer programmers, artists, writers, testers, translators, and more from across the world keep development going.

If this software has value to you, consider donating to the Krita Development Fund. Or Get Involved and put your skills to use making Krita and its community better!

Krita's mascot Kiki putting money in a piggy bank

Additional Changes

Krita Plus (Stable, 5.2.15):

  • Gradients: Fix an issue where reloading a gradient removes the transparency, by using premultipled alpha in SVG gradients. (bug; change by Dmitry Kazakov)
  • Shortcuts: Prevent Transform/Move Tool move shortcuts from triggering when the tools aren't active, which could override other shortcuts (such as next/previous frame). (bug; change by Dmitry Kazakov)
  • Overview Docker: Fix touch-dragging to pan, by disabling the non-existent context menu. (bug; change by Carsten Hartenfels)
  • Tablets: Add workarounds for Xiaomi devices, which can be toggled in Configure Krita -> Tablet Settings: Translate the stylus's Page Up and Page Down buttons into right mouse and middle mouse buttons. Ignore jagged tablet stylus position history. Use a steeper default pressure curve so that it's possible to reach 100% pressure. (change by Carsten Hartenfels)

Krita Plus (Stable pre-release, 5.3.0/6.0.0-beta1):

  • Comics Panel Editing Tool: Fix crash and other issues with remove gutter mode. (bug; change by Agata Cacko)
  • Transform Tool: Liquify: Fix broken 'Accurate with Instant Preview' when zoomed out, and issues when liquifying on the edge of a selection. (bug 1, bug 2; change by Agata Cacko)
  • Transform Tool: Show feedback that hidden or locked layers cannot be transformed, when attempted. (change by Agata Cacko)
  • Move Tool: Disallow moving layers hidden by isolation mode. (change by Ricky Ringler)
  • Freehand Path Tool: Fix preview not updating when changing color. (change by Luna Lovecraft)
  • General: Fix crash when closing image after deleting a vector layer under some circumstances. (bug; change by Luna Lovecraft)
  • General: Avoid Krita becoming unresponsive due to slow layer thumbnail generation. When thumbnail generation is slow, a warning in the statusbar mentions that the thumbnails have been switched to low quality mode and suggests using the new Image->Purge Unused Image Data action to clean up empty spaces of the image. (bug; change by Dmitry Kazakov)
  • Layers Docker: Prevent Ctrl+drag-and-drop from selecting the layer. (change by Luna Lovecraft)
  • Layers Docker: Fixes for selecting layers with Shift or Ctrl. (bug 1, bug 2; change by Luna Lovecraft)
  • Touch Input: Prevent touch scrolling from triggering context menus. (bug; change by Carsten Hartenfels)
  • Brush Editor: Fix Texture Options' offset sliders not working after enabling Pattern. (bug; change by Luna Lovecraft)
  • Android: Recorder Docker: Remove the default recording folder path to avoid saving to a user-inaccessible appdata directory. The path can be set manually, which will move any existing recordings from the old default folder. (Note that rendering timelapse videos is not possible on Android Krita, but the frames and their document can be copied to another OS and rendered there.) (bug; change by Carsten Hartenfels)
  • macOS: Fix lack of standard Qt text translations such as Yes/No buttons and the macOS application menu. (bug 1, bug 2; change by Ivan Yossi)

Krita Plus (Stable pre-release, 5.3.0/6.0.0-beta1+):

  • File Formats: HEIF: Fix crash when opening HEIF/AVIF images in YCrBr. (bug; change by Dmitry Kazakov)

Krita Next (Unstable, 5.4.0/6.1.0-prealpha):

  • General: Show hi-dpi versions of the recent file thumbnails on the welcome page. (change by Agata Cacko)

Nightly Builds

These pre-release versions of Krita are built every day.

Note that there are currently no Qt6 builds for Android.

Test out the upcoming Stable release in Krita Plus (5.3.0/6.0.0-prealpha): Linux Qt6 Qt5 — Windows Qt6 Qt5 — macOS Qt6 Qt5 — Android arm64 Qt5 – Android arm32 Qt5 – Android x86_64 Qt5

Or test out the latest Experimental features in Krita Next (5.4.0/6.1.0-prealpha). Feedback and bug reports are appreciated!: Linux Qt6 Qt5 — Windows Qt6 Qt5 — macOS Qt6 Qt5 — Android arm64 Qt5 – Android arm32 Qt5 – Android x86_64 Qt5

Monday, 9 February 2026

In my last post, I laid out the vision for Kapsule—a container-based extensibility layer for KDE Linux built on top of Incus. The pitch was simple: give users real, persistent development environments without compromising the immutable base system. At the time, it was a functional proof of concept living in my personal namespace.

Well, things have moved fast.

rocket launching

It's in KDE Linux now

Kapsule is integrated into KDE Linux. It shipped. People are using it. It hasn't caught fire.

The reception in #kde-linux:kde.org has been generally positive—which, if you've ever shipped anything to a community of Linux enthusiasts, you know is about the best outcome you can hope for. No pitchforks, some genuine excitement, and a few good bug reports. I'll take it.

Special shoutout to @aks:mx.scalie.zone, who has been daily-driving Kapsule and—crucially—hasn't had it blow up in their face. Having a real user exercising the system outside of my own testing has been invaluable.

Bug fixes

Before shipping, I spent a full day rigorously testing every main scenario I could think of—and writing integration tests to back them up. (Those tests run on my own machine for now, since the CI/CD pipelines don't exist yet. More on that below.) The result: the first version that landed in KDE Linux was quite stable. There was only one minor issue that isn't blocking anything.

CI/CD

I'm in the process of building out proper CI/CD pipelines. This is the unglamorous-but-essential work that turns a "project some guy hacked together" into something that can be maintained and contributed to by others. Automated builds, automated tests, the whole deal. Not exciting to talk about, but it's what separates hobby projects from real infrastructure.

Konsole integration

This is the one I'm most excited about. In the last post, I mentioned that Konsole gained container integration via !1171, and that I'd need to wire up an IContainerDetector for Kapsule. That work is underway, with two merge requests up:

!1178 (Phase 2) adds containers directly to the New Tab menu. You can see your available containers right there alongside your regular profiles—pick one, and you get a terminal session inside that container. Simple.

containers in new tab menu

A big chunk of this work was refactoring the container listing to be fully async. There are a lot of cases where listing containers can take a while—distrobox list calls docker ps, which pokes docker.socket, which might be waiting on systemd-networkd-wait-online.target—and we absolutely cannot block the UI thread during all of that.

!1179 (Phase 3) takes it a step further: you can associate a container with a Konsole profile. This is what gets us to the "it just works" experience—configure your default profile to open in your container, and every new terminal is automatically inside it.

container profile configuration

These lay the groundwork for Konsole to be aware of Kapsule containers. The end goal hasn't changed: open a terminal, you're in your container, you don't have to think about it. We're not at the "it just works" stage yet, but the foundation is being poured.

Future work: configurable host integration

switchboard operator

Right now, Kapsule gives you some control over how tightly the container integrates with the host. The --session flag on kap create lets you control whether the host's D-Bus session socket gets mounted into the container. That's a good start, I think I need to go further.

The bigger issue is filesystem mounts. Currently, Kapsule unconditionally mounts / to /.kapsule/host and the user's home directory straight through to /home/user. That means anything running inside the container has full read-write access to your entire home directory.

That's fine when you trust everything you're running, but some tools are less predictable than others. There are horror stories floating around of autonomous coding agents hallucinating paths and rm -rfing directories they had no business touching. Containers are a natural mitigation for this—if something goes sideways, the blast radius is limited to what you explicitly shared. But that only works if you can actually control what gets shared.

The fix is making filesystem mounts configurable. Instead of unconditionally exposing everything, you should be able to say "this container only gets access to ~/src/some-project" and nothing else. Want a fully integrated container that feels like your host? Mount everything. Want a sandboxed environment for running less predictable tools? Expose only what's needed. The trust model should be a dial, not a switch.

A word of caution, though: this is a mitigation for accidents and non-deterministic tools, not a security boundary for running genuinely untrusted workloads. Kapsule containers share the host's Wayland, PulseAudio, and PipeWire sockets—that's a lot of attack surface if you're worried about malicious code. For truly untrusted workloads, VMs would be a much better fit, and Incus already supports those. It's not on my roadmap, but all the building blocks are there if someone wants to explore that direction.

New on the radar: exporting apps

Here's one I didn't see coming. aks asked about running a GUI application installed inside a container and having it show up on the host like a normal app. I realized... I didn't have that functionality at all.

The naive approach is straightforward enough: drop a .desktop file into ~/.local/share/applications that launches the app inside the container. But I really don't like that solution, and here's why:

  • Stale entries. If the container gets deleted, or Kapsule itself gets uninstalled, those .desktop files just sit there like ghosts. You click them, nothing happens, and now you're debugging why your app launcher is showing dead entries.
  • No ownership tracking. There's no built-in mechanism to say "this .desktop file belongs to this container, managed by Kapsule." If something goes wrong, there's no clean way to find and remove all the artifacts.

What I really want is a way to tie exported application entries to their owning container and to Kapsule itself. That way, when a container goes away, its exported apps go away too. Clean. Automatic. No orphans.

This might require changes to kbuildsycoca—the KDE system configuration cache builder—to support some kind of ownership or provenance metadata for .desktop files. I need to investigate whether that's feasible or if there's a better approach entirely. It's the kind of problem where the quick hack is obvious but the right solution requires some thought.

Taking a break (in theory)

chained to a desk

I'm going to do my best to not touch Kapsule until the weekend. I have a day job that I've been somewhat neglecting in favor of hacking on this, and my employer probably expects me to, you know, do the thing they're paying me for.

We'll see how good my self-control is.

Ever since C++20 introduced coroutine support, I was wondering how this could integrate with Qt. Apparently I wasn’t the only one: before long, QCoro popped up. A really cool library! But it doesn’t use the existing future and promise types in Qt; instead it introduces its own types and mechanisms to support coroutines. I kept wondering why no-one just made QFuture and QPromise compatible – it would certainly be a more lightweight wrapper then.

With a recent project at work being a gargantuan mess of QFuture::then() continuations (ever tried async looping constructs with continuations only? 🥴) I had enough of a reason to finally sit down and implement this myself. The result: https://gitlab.com/pumphaus/qawaitablefuture.

Example

#include <qawaitablefuture/qawaitablefuture.h>

QFuture<QByteArray> fetchUrl(const QUrl &url)
{
    QNetworkAccessManager nam;
    QNetworkRequest request(url);

    QNetworkReply *reply = nam.get(request);

    co_await QtFuture::connect(reply, &QNetworkReply::finished);
    reply->deleteLater();

    if (reply->error()) {
        throw std::runtime_error(reply->errorString().toStdString());
    }
    co_return reply->readAll();
}

It looks a lot like what you’d write with QCoro, but it all fits in a single header and uses native QFuture features to – for example – connect to a signal. It’s really just syntax sugar around QFuture::then(). Well, that, and a bit of effort to propagate cancellation and exceptions. Cancellation propagation works both ways: if you co_await a canceled QFuture, the “outer” QFuture of coroutine will be canceled as well. If you cancelChain() a suspended coroutine-backed QFuture, cancellation will be propagated into the currently awaited QFuture.

What’s especially neat: You can configure where your coroutine will be resumed with co_await continueOn(...). It supports the same arguments as QFuture::then(), so for example:

QFuture<void> SomeClass::someMember()
{
    co_await QAwaitableFuture::continueOn(this);
    co_await someLongRunningProcess();
    // Due to continueOn(this), if "this" is destroyed during someLongRunningProcess(),
    // the coroutine will be destroyed after the suspension point (-> outer QFuture will be canceled)
    // and you won't access a dangling reference here.
    co_return this->frobnicate();
}

QFuture<int> multithreadedProcess()
{
    co_await QAwaitableFuture::continueOn(QtFuture::Launch::Async);

    double result1 = co_await foo();
    // resumes on a free thread in the thread pool
    process(result1);

    double result2 = co_await bar(result1);
    // resumes on a free thread in the thread pool
    double result3 = transmogrify(result2);

    co_return co_await baz(result3);
}

See the docs for QFuture::then() for details.

Also, if you want to check the canceled flag or report progress, you can access the actual QPromise that’s backing the coroutine:

QFuture<int> heavyComputation()
{
    QPromise<int> &promise = co_await QAwaitableFuture::promise();
    promise.setProgressRange(0, 100);

    double result = 0;

    for (int i = 0; i < 100; ++i) {
        promise.setProgressValue(i);
        if (promise.isCanceled()) {
            co_return result;
        }
        frobnicationStep(&result, i);
    }
    co_return result;
} 

Outlook

I’m looking to upstream this. It’s too late for Qt 6.11 (already in feature freeze), but maybe 6.12? There have been some proposals for coroutine support on Qt’s Gerrit already, but none made it past the proof-of-concept stage. Hopefully this one will make it. Let’s see.

Otherwise, just use the single header from the qawaitablefuture repo. It an be included as a git submodule, or you just vendor the header as-is.

Happy hacking!

Caveat: GCC < 13

There was a nasty bug in GCC’s coroutine support: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101367 It affects all GCC versions before 13.0.0 and effectively prevents you from writing co_await foo([&] { ... }); – i.e. you cannot await an expression involving a temporary lambda. You can rewrite this out as auto f = foo([&] { ... }); co_await f; and it will work. But there’s no warning at compile time. As soon as the lambda with captures is a temporary expression inside the co_await, it will crash and burn at runtime. Fixed with GCC13+, but took me a while to figure out why things went haywire on Ubuntu 22.04 (defaults to GCC11).